(defcomp ~reference/ref-get-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-get-result" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Load server time") (div :id "ref-get-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to load."))) (defcomp ~reference/ref-post-demo () (div (~tw :tokens "space-y-3") (form :sx-post "/sx/(geography.(hypermedia.(reference.(api.greet))))" :sx-target "#ref-post-result" :sx-swap "innerHTML" (~tw :tokens "flex gap-2") (input :type "text" :name "name" :placeholder "Your name" (~tw :tokens "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")) (button :type "submit" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Greet")) (div :id "ref-post-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Submit to see greeting."))) (defcomp ~reference/ref-put-demo () (div :id "ref-put-view" (div (~tw :tokens "flex items-center justify-between p-3 bg-stone-100 rounded") (span (~tw :tokens "text-stone-700 text-sm") "Status: " (strong "draft")) (button :sx-put "/sx/(geography.(hypermedia.(reference.(api.status))))" :sx-target "#ref-put-view" :sx-swap "innerHTML" :sx-vals "{\"status\": \"published\"}" (~tw :tokens "px-3 py-1 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Publish")))) (defcomp ~reference/ref-delete-demo () (div (~tw :tokens "space-y-2") (div :id "ref-del-1" (~tw :tokens "flex items-center justify-between p-2 border border-stone-200 rounded") (span (~tw :tokens "text-sm text-stone-700") "Item A") (button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.1)))))" :sx-target "#ref-del-1" :sx-swap "delete" (~tw :tokens "text-red-500 text-sm hover:text-red-700") "Remove")) (div :id "ref-del-2" (~tw :tokens "flex items-center justify-between p-2 border border-stone-200 rounded") (span (~tw :tokens "text-sm text-stone-700") "Item B") (button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.2)))))" :sx-target "#ref-del-2" :sx-swap "delete" (~tw :tokens "text-red-500 text-sm hover:text-red-700") "Remove")) (div :id "ref-del-3" (~tw :tokens "flex items-center justify-between p-2 border border-stone-200 rounded") (span (~tw :tokens "text-sm text-stone-700") "Item C") (button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.3)))))" :sx-target "#ref-del-3" :sx-swap "delete" (~tw :tokens "text-red-500 text-sm hover:text-red-700") "Remove")))) (defcomp ~reference/ref-patch-demo () (div :id "ref-patch-view" (~tw :tokens "space-y-2") (div (~tw :tokens "p-3 bg-stone-100 rounded") (span (~tw :tokens "text-stone-700 text-sm") "Theme: " (strong :id "ref-patch-val" "light"))) (div (~tw :tokens "flex gap-2") (button :sx-patch "/sx/(geography.(hypermedia.(reference.(api.theme))))" :sx-vals "{\"theme\": \"dark\"}" :sx-target "#ref-patch-val" :sx-swap "innerHTML" (~tw :tokens "px-3 py-1 bg-stone-800 text-white rounded text-sm") "Dark") (button :sx-patch "/sx/(geography.(hypermedia.(reference.(api.theme))))" :sx-vals "{\"theme\": \"light\"}" :sx-target "#ref-patch-val" :sx-swap "innerHTML" (~tw :tokens "px-3 py-1 bg-stone-100 border border-stone-300 text-stone-700 rounded text-sm") "Light")))) (defcomp ~reference/ref-trigger-demo () (div (~tw :tokens "space-y-3") (input :type "text" :name "q" :placeholder "Type to search..." :sx-get "/sx/(geography.(hypermedia.(reference.(api.trigger-search))))" :sx-trigger "input changed delay:300ms" :sx-target "#ref-trigger-result" :sx-swap "innerHTML" (~tw :tokens "w-full px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")) (div :id "ref-trigger-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Start typing to trigger a search."))) (defcomp ~reference/ref-target-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "flex gap-2") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-target-a" :sx-swap "innerHTML" (~tw :tokens "px-3 py-1 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Update Box A") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-target-b" :sx-swap "innerHTML" (~tw :tokens "px-3 py-1 bg-emerald-600 text-white rounded text-sm hover:bg-emerald-700") "Update Box B")) (div (~tw :tokens "grid grid-cols-2 gap-3") (div :id "ref-target-a" (~tw :tokens "p-3 rounded border border-violet-200 bg-violet-50 text-sm text-stone-500") "Box A") (div :id "ref-target-b" (~tw :tokens "p-3 rounded border border-emerald-200 bg-emerald-50 text-sm text-stone-500") "Box B")))) (defcomp ~reference/ref-swap-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "flex gap-2 flex-wrap") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.swap-item))))" :sx-target "#ref-swap-list" :sx-swap "beforeend" (~tw :tokens "px-3 py-1 bg-violet-600 text-white rounded text-sm") "beforeend") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.swap-item))))" :sx-target "#ref-swap-list" :sx-swap "afterbegin" (~tw :tokens "px-3 py-1 bg-emerald-600 text-white rounded text-sm") "afterbegin") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.swap-item))))" :sx-target "#ref-swap-list" :sx-swap "innerHTML" (~tw :tokens "px-3 py-1 bg-blue-600 text-white rounded text-sm") "innerHTML")) (div :id "ref-swap-list" (~tw :tokens "p-3 rounded border border-stone-200 space-y-1 min-h-[3rem]") (div (~tw :tokens "text-sm text-stone-500") "Original item")))) (defcomp ~reference/ref-oob-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.oob))))" :sx-target "#ref-oob-main" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Update both boxes") (div (~tw :tokens "grid grid-cols-2 gap-3") (div (~tw :tokens "rounded border border-stone-200 p-3") (div (~tw :tokens "text-xs text-stone-400 mb-1") "Main target") (div :id "ref-oob-main" (~tw :tokens "text-sm text-stone-500") "Waiting...")) (div (~tw :tokens "rounded border border-stone-200 p-3") (div (~tw :tokens "text-xs text-stone-400 mb-1") "OOB target") (div :id "ref-oob-side" (~tw :tokens "text-sm text-stone-500") "Waiting..."))))) (defcomp ~reference/ref-select-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.select-page))))" :sx-target "#ref-select-result" :sx-select "#the-content" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Load (selecting #the-content)") (div :id "ref-select-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Only the selected fragment will appear here."))) (defcomp ~reference/ref-confirm-demo () (div (~tw :tokens "space-y-2") (div :id "ref-confirm-item" (~tw :tokens "flex items-center justify-between p-3 border border-stone-200 rounded") (span (~tw :tokens "text-sm text-stone-700") "Important file.txt") (button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.confirm)))))" :sx-target "#ref-confirm-item" :sx-swap "delete" :sx-confirm "Are you sure you want to delete this file?" (~tw :tokens "px-3 py-1 text-red-500 text-sm border border-red-200 rounded hover:bg-red-50") "Delete")))) (defcomp ~reference/ref-pushurl-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "flex gap-2") (a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))" :sx-get "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))" :sx-target "#sx-content" :sx-select "#sx-content" :sx-swap "outerHTML" :sx-push-url "true" (~tw :tokens "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200") "sx-get page") (a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-post)))" :sx-get "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-post)))" :sx-target "#sx-content" :sx-select "#sx-content" :sx-swap "outerHTML" :sx-push-url "true" (~tw :tokens "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200") "sx-post page")) (p (~tw :tokens "text-sm text-stone-500") "Click a link — the URL bar updates without a full page reload. Use browser back to return."))) (defcomp ~reference/ref-sync-demo () (div (~tw :tokens "space-y-3") (input :type "text" :name "q" :placeholder "Type quickly..." :sx-get "/sx/(geography.(hypermedia.(reference.(api.slow-echo))))" :sx-trigger "input changed delay:100ms" :sx-sync "replace" :sx-target "#ref-sync-result" :sx-swap "innerHTML" (~tw :tokens "w-full px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")) (p (~tw :tokens "text-xs text-stone-400") "With sync:replace, each new keystroke aborts the in-flight request.") (div :id "ref-sync-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Type to see only the latest result."))) (defcomp ~reference/ref-encoding-demo () (div (~tw :tokens "space-y-3") (form :sx-post "/sx/(geography.(hypermedia.(reference.(api.upload-name))))" :sx-encoding "multipart/form-data" :sx-target "#ref-encoding-result" :sx-swap "innerHTML" (~tw :tokens "flex gap-2") (input :type "file" :name "file" (~tw :tokens "flex-1 text-sm text-stone-500 file:mr-2 file:px-3 file:py-1 file:rounded file:border-0 file:text-sm file:bg-violet-50 file:text-violet-700")) (button :type "submit" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Upload")) (div :id "ref-encoding-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Select a file and submit."))) (defcomp ~reference/ref-headers-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.echo-headers))))" :sx-headers {:X-Request-Source "demo" :X-Custom-Token "abc123"} :sx-target "#ref-headers-result" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Send with custom headers") (div :id "ref-headers-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to see echoed headers."))) (defcomp ~reference/ref-include-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "flex gap-2 items-end") (div (label (~tw :tokens "block text-xs text-stone-500 mb-1") "Category") (select :id "ref-inc-cat" :name "category" (~tw :tokens "px-3 py-2 border border-stone-300 rounded text-sm") (option :value "all" "All") (option :value "books" "Books") (option :value "tools" "Tools"))) (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.echo-vals))))" :sx-include "#ref-inc-cat" :sx-target "#ref-include-result" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Filter")) (div :id "ref-include-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click Filter — the select value is included in the request."))) (defcomp ~reference/ref-vals-demo () (div (~tw :tokens "space-y-3") (button :sx-post "/sx/(geography.(hypermedia.(reference.(api.echo-vals))))" :sx-vals "{\"source\": \"demo\", \"page\": \"3\"}" :sx-target "#ref-vals-result" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Send with extra values") (div :id "ref-vals-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to see echoed values."))) (defcomp ~reference/ref-media-demo () (div (~tw :tokens "space-y-3") (a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))" :sx-get "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))" :sx-target "#sx-content" :sx-select "#sx-content" :sx-swap "outerHTML" :sx-push-url "true" :sx-media "(min-width: 768px)" (~tw :tokens "inline-block px-4 py-2 bg-violet-600 text-white rounded text-sm no-underline hover:bg-violet-700") "sx navigation (desktop only)") (p (~tw :tokens "text-sm text-stone-500") "On screens narrower than 768px this link uses normal navigation. On wider screens it uses sx."))) (defcomp ~reference/ref-disable-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "grid grid-cols-2 gap-3") (div (~tw :tokens "p-3 border border-stone-200 rounded") (p (~tw :tokens "text-xs text-stone-400 mb-2") "sx enabled") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-dis-a" :sx-swap "innerHTML" (~tw :tokens "px-3 py-1 bg-violet-600 text-white rounded text-sm") "Load") (div :id "ref-dis-a" (~tw :tokens "mt-2 text-sm text-stone-500") "—")) (div :sx-disable "true" (~tw :tokens "p-3 border border-stone-200 rounded") (p (~tw :tokens "text-xs text-stone-400 mb-2") "sx disabled") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-dis-b" :sx-swap "innerHTML" (~tw :tokens "px-3 py-1 bg-stone-400 text-white rounded text-sm") "Load") (div :id "ref-dis-b" (~tw :tokens "mt-2 text-sm text-stone-500") "Button won't fire sx request"))))) (defcomp ~reference/ref-on-demo () (div (~tw :tokens "space-y-3") (button :sx-on:click "document.getElementById('ref-on-result').textContent = 'Clicked at ' + new Date().toLocaleTimeString()" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Click me") (div :id "ref-on-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click the button — runs JavaScript, no server request."))) (defcomp ~reference/ref-retry-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.flaky))))" :sx-target "#ref-retry-result" :sx-swap "innerHTML" :sx-retry "true" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700") "Call flaky endpoint") (div :id "ref-retry-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "This endpoint fails 2 out of 3 times. sx-retry retries automatically."))) (defcomp ~reference/ref-data-sx-demo () (div (~tw :tokens "space-y-3") (div :data-sx "(div :class \"p-3 bg-violet-50 rounded\" (h3 :class \"font-semibold text-violet-800\" \"Client-rendered\") (p :class \"text-sm text-stone-600\" \"This was evaluated in the browser — no server request.\"))") (p (~tw :tokens "text-xs text-stone-400") "The content above is rendered client-side from the data-sx attribute."))) (defcomp ~reference/ref-data-sx-env-demo () (div (~tw :tokens "space-y-3") (div :data-sx "(div :class \"p-3 bg-emerald-50 rounded\" (h3 :class \"font-semibold text-emerald-800\" title) (p :class \"text-sm text-stone-600\" message))" :data-sx-env "{\"title\": \"Dynamic content\", \"message\": \"Variables passed via data-sx-env are available in the expression.\"}") (p (~tw :tokens "text-xs text-stone-400") "The title and message above come from the data-sx-env JSON."))) (defcomp ~reference/ref-boost-demo () (div (~tw :tokens "space-y-3") (nav :sx-boost "true" (~tw :tokens "flex gap-3") (a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))" (~tw :tokens "text-violet-600 hover:text-violet-800 underline text-sm") "sx-get") (a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-post)))" (~tw :tokens "text-violet-600 hover:text-violet-800 underline text-sm") "sx-post") (a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-target)))" (~tw :tokens "text-violet-600 hover:text-violet-800 underline text-sm") "sx-target")) (p (~tw :tokens "text-xs text-stone-400") "These links use AJAX navigation via sx-boost — no sx-get needed on each link. " "#sx-content"))) (defcomp ~reference/ref-preload-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-preload-result" :sx-swap "innerHTML" :sx-preload "mouseover" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Hover then click (preloaded)") (div :id "ref-preload-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Hover over the button first, then click — the response is instant."))) (defcomp ~reference/ref-preserve-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "flex gap-2 items-center") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-preserve-container" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Swap container") (span (~tw :tokens "text-xs text-stone-400") "The input below keeps its value across swaps.")) (div :id "ref-preserve-container" (~tw :tokens "space-y-2") (input :id "ref-preserved-input" :sx-preserve "true" :type "text" :placeholder "Type here — preserved across swaps" (~tw :tokens "w-full px-3 py-2 border border-stone-300 rounded text-sm")) (div (~tw :tokens "p-2 bg-stone-100 rounded text-sm text-stone-600") "This text will be replaced on swap.")))) (defcomp ~reference/ref-indicator-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "flex gap-3 items-center") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.slow-echo))))" :sx-target "#ref-indicator-result" :sx-swap "innerHTML" :sx-indicator "#ref-spinner" :sx-vals "{\"q\": \"hello\"}" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Load (slow)") (span :id "ref-spinner" (~tw :tokens "text-violet-600 text-sm") :style "display: none" "Loading...")) (div :id "ref-indicator-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to load (indicator shows during request)."))) (defcomp ~reference/ref-validate-demo () (div (~tw :tokens "space-y-3") (form :sx-post "/sx/(geography.(hypermedia.(reference.(api.greet))))" :sx-target "#ref-validate-result" :sx-swap "innerHTML" :sx-validate "true" (~tw :tokens "flex gap-2") (input :type "email" :name "name" :required "true" :placeholder "Enter email (required)" (~tw :tokens "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")) (button :type "submit" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Submit")) (div :id "ref-validate-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Submit with invalid/empty email to see validation."))) (defcomp ~reference/ref-ignore-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-ignore-container" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Swap container") (div :id "ref-ignore-container" (~tw :tokens "space-y-2") (div :sx-ignore "true" (~tw :tokens "p-2 bg-amber-50 rounded border border-amber-200") (p (~tw :tokens "text-sm text-amber-800") "This subtree has sx-ignore — it won't change.") (input :type "text" :placeholder "Type here — ignored during swap" (~tw :tokens "mt-1 w-full px-2 py-1 border border-amber-300 rounded text-sm"))) (div (~tw :tokens "p-2 bg-stone-100 rounded text-sm text-stone-600") "This text WILL be replaced on swap.")))) (defcomp ~reference/ref-optimistic-demo () (div (~tw :tokens "space-y-2") (div :id "ref-opt-item-1" (~tw :tokens "flex items-center justify-between p-2 border border-stone-200 rounded") (span (~tw :tokens "text-sm text-stone-700") "Optimistic item A") (button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.opt1)))))" :sx-target "#ref-opt-item-1" :sx-swap "delete" :sx-optimistic "remove" (~tw :tokens "text-red-500 text-sm hover:text-red-700") "Remove")) (div :id "ref-opt-item-2" (~tw :tokens "flex items-center justify-between p-2 border border-stone-200 rounded") (span (~tw :tokens "text-sm text-stone-700") "Optimistic item B") (button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.opt2)))))" :sx-target "#ref-opt-item-2" :sx-swap "delete" :sx-optimistic "remove" (~tw :tokens "text-red-500 text-sm hover:text-red-700") "Remove")) (p (~tw :tokens "text-xs text-stone-400") "Items fade out immediately on click (optimistic), then are removed when the server responds."))) (defcomp ~reference/ref-replace-url-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-replurl-result" :sx-swap "innerHTML" :sx-replace-url "true" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Load (replaces URL)") (div :id "ref-replurl-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to load — URL changes but no new history entry."))) (defcomp ~reference/ref-disabled-elt-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "flex gap-3 items-center") (button :id "ref-diselt-btn" :sx-get "/sx/(geography.(hypermedia.(reference.(api.slow-echo))))" :sx-target "#ref-diselt-result" :sx-swap "innerHTML" :sx-disabled-elt "#ref-diselt-btn" :sx-vals "{\"q\": \"hello\"}" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm disabled:opacity-50") "Click (disables during request)") (span (~tw :tokens "text-xs text-stone-400") "Button is disabled while request is in-flight.")) (div :id "ref-diselt-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click the button to see it disable during the request."))) (defcomp ~reference/ref-prompt-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.prompt-echo))))" :sx-target "#ref-prompt-result" :sx-swap "innerHTML" :sx-prompt "Enter your name:" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Prompt & send") (div :id "ref-prompt-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to enter a name via prompt — it is sent as the SX-Prompt header."))) (defcomp ~reference/ref-params-demo () (div (~tw :tokens "space-y-3") (form :sx-post "/sx/(geography.(hypermedia.(reference.(api.echo-vals))))" :sx-target "#ref-params-result" :sx-swap "innerHTML" :sx-params "name" (~tw :tokens "flex gap-2") (input :type "text" :name "name" :placeholder "Name (sent)" (~tw :tokens "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")) (input :type "text" :name "secret" :placeholder "Secret (filtered)" (~tw :tokens "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")) (button :type "submit" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Submit")) (div :id "ref-params-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Only 'name' will be sent — 'secret' is filtered by sx-params."))) (defcomp ~reference/ref-sse-demo () (div (~tw :tokens "space-y-3") (div :sx-sse "/sx/(geography.(hypermedia.(reference.(api.sse-time))))" :sx-sse-swap "time" :sx-swap "innerHTML" (div :id "ref-sse-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-600 text-sm font-mono") "Connecting to SSE stream...")) (p (~tw :tokens "text-xs text-stone-400") "Server pushes time updates every 2 seconds via Server-Sent Events."))) (defcomp ~reference/ref-header-prompt-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.prompt-echo))))" :sx-target "#ref-hdr-prompt-result" :sx-swap "innerHTML" :sx-prompt "Enter your name:" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Prompt & send") (div :id "ref-hdr-prompt-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to enter a name via prompt — the value is sent as the SX-Prompt header."))) (defcomp ~reference/ref-header-trigger-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.trigger-event))))" :sx-target "#ref-hdr-trigger-result" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Load with trigger") (div :id "ref-hdr-trigger-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") :sx-on:showNotice "this.style.borderColor = '#8b5cf6'; this.style.borderWidth = '2px'" "Click — the server response includes SX-Trigger: showNotice, which highlights this box."))) (defcomp ~reference/ref-header-retarget-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.retarget))))" :sx-target "#ref-hdr-retarget-main" :sx-swap "innerHTML" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Load (server retargets)") (div (~tw :tokens "grid grid-cols-2 gap-3") (div (~tw :tokens "rounded border border-stone-200 p-3") (div (~tw :tokens "text-xs text-stone-400 mb-1") "Original target") (div :id "ref-hdr-retarget-main" (~tw :tokens "text-sm text-stone-500") "Waiting...")) (div (~tw :tokens "rounded border border-stone-200 p-3") (div (~tw :tokens "text-xs text-stone-400 mb-1") "Retarget destination") (div :id "ref-hdr-retarget-alt" (~tw :tokens "text-sm text-stone-500") "Waiting..."))))) (defcomp ~reference/ref-event-before-request-demo () (div (~tw :tokens "space-y-3") (div (~tw :tokens "flex gap-2 items-center") (input :id "ref-evt-br-input" :type "text" :placeholder "Type something first..." (~tw :tokens "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")) (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-evt-br-result" :sx-swap "innerHTML" :sx-on:sx:beforeRequest "if (!document.getElementById('ref-evt-br-input').value) { event.preventDefault(); document.getElementById('ref-evt-br-result').textContent = 'Cancelled — input is empty!'; }" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Load")) (div :id "ref-evt-br-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Request is cancelled via preventDefault() if the input is empty."))) (defcomp ~reference/ref-event-after-request-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))" :sx-target "#ref-evt-ar-result" :sx-swap "innerHTML" :sx-on:sx:afterRequest "document.getElementById('ref-evt-ar-log').textContent = 'Response status: ' + (event.detail ? event.detail.status : '?')" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Load (logs after response)") (div :id "ref-evt-ar-log" (~tw :tokens "p-2 rounded bg-emerald-50 text-emerald-700 text-sm") "Event log will appear here.") (div :id "ref-evt-ar-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to load — afterRequest fires before the swap."))) (defcomp ~reference/ref-event-after-swap-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.swap-item))))" :sx-target "#ref-evt-as-list" :sx-swap "beforeend" :sx-on:sx:afterSwap "var items = document.querySelectorAll('#ref-evt-as-list > div'); if (items.length) items[items.length-1].scrollIntoView({behavior:'smooth'}); document.getElementById('ref-evt-as-count').textContent = items.length + ' items'" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Add item (scrolls after swap)") (div :id "ref-evt-as-count" (~tw :tokens "text-sm text-emerald-700") "1 items") (div :id "ref-evt-as-list" (~tw :tokens "p-3 rounded border border-stone-200 space-y-1 max-h-32 overflow-y-auto") (div (~tw :tokens "text-sm text-stone-500") "Items will be appended and scrolled into view.")))) (defcomp ~reference/ref-event-response-error-demo () (div (~tw :tokens "space-y-3") (button :sx-get "/sx/(geography.(hypermedia.(reference.(api.error-500))))" :sx-target "#ref-evt-err-result" :sx-swap "innerHTML" :sx-on:sx:responseError "var s=document.getElementById('ref-evt-err-status'); s.style.display='block'; s.textContent='Error ' + (event.detail ? event.detail.status || '?' : '?') + ' received'" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Call failing endpoint") (div :id "ref-evt-err-status" (~tw :tokens "p-2 rounded bg-red-50 text-red-600 text-sm") :style "display: none" "") (div :id "ref-evt-err-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to trigger an error — the sx:responseError event fires."))) (defcomp ~reference/ref-event-validation-failed-demo () (div (~tw :tokens "space-y-3") (form :sx-post "/sx/(geography.(hypermedia.(reference.(api.greet))))" :sx-target "#ref-evt-vf-result" :sx-swap "innerHTML" :sx-validate "true" :sx-on:sx:validationFailed "document.getElementById('ref-evt-vf-status').style.display = 'block'" (~tw :tokens "flex gap-2") (input :type "email" :name "email" :required "true" :placeholder "Email (required)" (~tw :tokens "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500 invalid:border-red-400")) (button :type "submit" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Submit")) (div :id "ref-evt-vf-status" (~tw :tokens "p-2 rounded bg-amber-50 text-amber-700 text-sm") :style "display: none" "Validation failed — form was not submitted.") (div :id "ref-evt-vf-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Submit with empty/invalid email to trigger the event."))) (defcomp ~reference/ref-event-request-error-demo () (div (~tw :tokens "space-y-3") (button :sx-get "https://this-domain-does-not-exist.invalid/api" :sx-target "#ref-evt-re-result" :sx-swap "innerHTML" :sx-on:sx:requestError "document.getElementById('ref-evt-re-status').style.display = 'block'; document.getElementById('ref-evt-re-status').textContent = 'Network error — request never reached a server'" (~tw :tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm") "Request invalid domain") (div :id "ref-evt-re-status" (~tw :tokens "p-2 rounded bg-red-50 text-red-600 text-sm") :style "display: none" "") (div :id "ref-evt-re-result" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-400 text-sm") "Click to trigger a network error — sx:requestError fires."))) (defcomp ~reference/ref-event-client-route-demo () (div (~tw :tokens "space-y-3") (p (~tw :tokens "text-sm text-stone-600") "Open DevTools console, then navigate to a pure page (no :data expression). " "You'll see \"sx:route client /path\" in the console — no network request is made.") (div (~tw :tokens "flex gap-2 flex-wrap") (a :href "/sx/(etc.(essay))" (~tw :tokens "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200") "Essays") (a :href "/sx/(etc.(plan))" (~tw :tokens "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200") "Plans") (a :href "/sx/(applications.(protocol))" (~tw :tokens "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200") "Protocols")) (p (~tw :tokens "text-xs text-stone-400") "The sx:clientRoute event fires on the swap target and bubbles to document.body. " "Apps use it to update nav selection, analytics, or other post-navigation state."))) (defcomp ~reference/ref-event-sse-open-demo () (div (~tw :tokens "space-y-3") (div :sx-sse "/sx/(geography.(hypermedia.(reference.(api.sse-time))))" :sx-sse-swap "time" :sx-swap "innerHTML" :sx-on:sx:sseOpen "document.getElementById('ref-evt-sseopen-status').textContent = 'Connected'; document.getElementById('ref-evt-sseopen-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-emerald-100 text-emerald-700'" (div (~tw :tokens "flex items-center gap-3") (span :id "ref-evt-sseopen-status" (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs bg-amber-100 text-amber-700") "Connecting...") (span (~tw :tokens "text-sm text-stone-500") "SSE stream"))) (p (~tw :tokens "text-xs text-stone-400") "The status badge turns green when the SSE connection opens."))) (defcomp ~reference/ref-event-sse-message-demo () (div (~tw :tokens "space-y-3") (div :sx-sse "/sx/(geography.(hypermedia.(reference.(api.sse-time))))" :sx-sse-swap "time" :sx-swap "innerHTML" :sx-on:sx:sseMessage "var c = parseInt(document.getElementById('ref-evt-ssemsg-count').dataset.count || '0') + 1; document.getElementById('ref-evt-ssemsg-count').dataset.count = c; document.getElementById('ref-evt-ssemsg-count').textContent = c + ' messages received'" (div :id "ref-evt-ssemsg-output" (~tw :tokens "p-3 rounded bg-stone-100 text-stone-600 text-sm font-mono") "Waiting for SSE messages...")) (div :id "ref-evt-ssemsg-count" (~tw :tokens "text-sm text-emerald-700") :data-count "0" "0 messages received"))) (defcomp ~reference/ref-event-sse-error-demo () (div (~tw :tokens "space-y-3") (div :sx-sse "/sx/(geography.(hypermedia.(reference.(api.sse-time))))" :sx-sse-swap "time" :sx-swap "innerHTML" :sx-on:sx:sseError "document.getElementById('ref-evt-sseerr-status').textContent = 'Disconnected'; document.getElementById('ref-evt-sseerr-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-red-100 text-red-700'" :sx-on:sx:sseOpen "document.getElementById('ref-evt-sseerr-status').textContent = 'Connected'; document.getElementById('ref-evt-sseerr-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-emerald-100 text-emerald-700'" (div (~tw :tokens "flex items-center gap-3") (span :id "ref-evt-sseerr-status" (~tw :tokens "inline-block px-2 py-0.5 rounded text-xs bg-amber-100 text-amber-700") "Connecting...") (span (~tw :tokens "text-sm text-stone-500") "SSE stream"))) (p (~tw :tokens "text-xs text-stone-400") "If the SSE connection drops, the badge turns red via sx:sseError.")))