host: extract the relate picker into a content-addressed ~relate-picker component
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
The declarative picker markup is now a reusable SX component
(lib/host/sx/relate-picker.sx, defcomp ~relate-picker &key slug kind) instead of
inline markup in the editor. It is a CONTENT-ADDRESSED, CLIENT-EXPANDED component:
- Server: on a full page load render-page expands ~relate-picker server-side
(SEO / no-JS), exactly as before.
- Client: on a boosted SPA nav the edit body serialises to the compact
(~relate-picker :slug … :kind …), and the CLIENT expands it. The component
module is compiled to a content-addressed .sxbc, served immutably from
/sx/h/{hash}, and listed in the page's data-sx-manifest "boot" array so the
client eager-loads it after the web stack — registering its defcomp before any
boosted fragment references it.
Wiring:
- lib/host/sx/relate-picker.sx — the component.
- lib/host/blog.sx — editor emits (~relate-picker :slug s :kind k); the inline
form markup is gone.
- lib/host/static.sx — host/static-manifest-json emits boot:["relate-picker.sxbc"]
(the previously-empty boot array, now used as designed).
- hosts/ocaml/browser/sx-platform.js — loadWebStack eager-loads the page manifest's
boot[] modules (content-addressed) after the web stack.
- bundle.sh + compile-modules.js — copy/compile the component to .sxbc.
- serve.sh + conformance.sh — load the component module server-side.
This gives the host an app-component system: app defcomps shipped to the client by
hash, the same machinery as the kernel modules — the picker is the first, and it's
the model for publishing components externally.
Tests: conformance 272/272 (server expansion); relate-picker.spec.js 6/6 incl. the
boosted-nav populate (proves client-side component load + expansion) and the
error/retry case. WASM stack rebuilt (relate-picker.sxbc @ 6818110a).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -592,25 +592,12 @@
|
||||
(button :type "submit" "remove")))))
|
||||
current))
|
||||
(quote (p :style "opacity:0.7" "None yet."))))
|
||||
;; Declarative SX-htmx picker (no client JS). The form GETs relate-options
|
||||
;; serialising its inputs (kind + the filter q) into the query: on initial
|
||||
;; "load" and on a debounced "input" it innerHTML-swaps the results ul.
|
||||
;; Paging is driven by the "load more" sentinel each page carries. The
|
||||
;; SX engine re-binds these triggers on swapped-in content, so the picker
|
||||
;; works whether the edit page is a full load or a boosted SPA nav.
|
||||
(form :class "relate-picker" :data-slug (unquote slug) :data-kind (unquote kind)
|
||||
:sx-get (unquote (str "/" slug "/relate-options"))
|
||||
:sx-trigger "input delay:200ms, load"
|
||||
:sx-target (unquote (str "#rp-" kind "-results"))
|
||||
:sx-swap "innerHTML"
|
||||
;; a failed initial/filter fetch retries with backoff (1s→30s)
|
||||
:sx-retry "exponential:1000:30000"
|
||||
:style "margin:0"
|
||||
(input :type "hidden" :name "kind" :value (unquote kind))
|
||||
(input :type "text" :name "q" :class "rp-filter" :placeholder "filter…" :autocomplete "off"
|
||||
:style "width:100%;padding:0.4em;box-sizing:border-box")
|
||||
(ul :id (unquote (str "rp-" kind "-results")) :class "rp-results"
|
||||
:style "list-style:none;padding:0;margin:0.5em 0;border:1px solid #ddd")))))))
|
||||
;; The picker is now a reusable, content-addressed SX component
|
||||
;; (lib/host/sx/relate-picker.sx). render-page expands it server-side on a
|
||||
;; full load; on a boosted SPA nav the body serialises to the compact
|
||||
;; (~relate-picker …) and the CLIENT expands it (the component module is
|
||||
;; loaded content-addressed via the manifest at boot).
|
||||
(~relate-picker :slug (unquote slug) :kind (unquote kind)))))))
|
||||
|
||||
;; "Is this post a tag?" toggle — marking a post a tag is just an is-a edge to the
|
||||
;; "tag" type-post, so it reuses the relate/unrelate routes (no new endpoint).
|
||||
|
||||
Reference in New Issue
Block a user