SX docs: configurable shell, SX-native event handlers, nav fixes

- Configurable page shell (~sx-page-shell kwargs + SX_SHELL app config)
  so each app controls its own assets — sx docs loads only sx-browser.js
- SX-evaluated sx-on:* handlers (eval-expr instead of new Function)
  with DOM primitives registered in PRIMITIVES table
- data-init boot mode for pure SX initialization scripts
- Jiggle animation on links while fetching
- Nav: 3-column grid for centered alignment, is-leaf sizing,
  fix map-indexed param order (index, item), guard mod-by-zero
- Async route eval failure now falls back to server fetch
  instead of silently rendering nothing
- Remove duplicate h1 title from ~doc-page
- Re-bootstrap sx-ref.js + sx-browser.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 11:00:59 +00:00
parent 31a6e708fc
commit 8a5c115557
20 changed files with 5763 additions and 3046 deletions

View File

@@ -2,7 +2,7 @@
(defcomp ~sx-home-content ()
(div :id "main-content" :class "max-w-3xl mx-auto px-4 py-6"
(highlight "(defcomp ~sx-header ()
(~doc-code :code (highlight "(defcomp ~sx-header ()
(a :href \"/\"
:sx-get \"/\" :sx-target \"#main-panel\"
:sx-select \"#main-panel\"
@@ -13,7 +13,7 @@
(p :class \"text-lg text-stone-500 mb-1\"
\"Framework free reactive hypermedia\")
(p :class \"text-xs text-stone-400\"
\"© Giles Bradshaw 2026\")))" "lisp")))
\"© Giles Bradshaw 2026\")))" "lisp"))))
(defcomp ~docs-introduction-content ()
(~doc-page :title "Introduction"

View File

@@ -21,35 +21,41 @@
(p :class "text-xs text-stone-400"
"© Giles Bradshaw 2026")))
;; Current section with annotated prev/next siblings.
;; Desktop: prev ← Current → next (horizontal)
;; Mobile: stacked vertically
(defcomp ~nav-sibling-row (&key node siblings)
(let* ((idx (find-nav-index siblings node))
(count (len siblings))
(prev-idx (mod (+ (- idx 1) count) count))
(next-idx (mod (+ idx 1) count))
(prev-node (nth siblings prev-idx))
(next-node (nth siblings next-idx)))
(div :class "max-w-3xl mx-auto px-4 py-2 flex items-center justify-center gap-4"
(a :href (get prev-node "href")
:sx-get (get prev-node "href") :sx-target "#main-panel"
:sx-select "#main-panel" :sx-swap "outerHTML"
:sx-push-url "true"
:class "text-sm text-stone-500 hover:text-violet-600"
(str "← " (get prev-node "label")))
(a :href (get node "href")
:sx-get (get node "href") :sx-target "#main-panel"
:sx-select "#main-panel" :sx-swap "outerHTML"
:sx-push-url "true"
:class "text-lg font-semibold text-violet-700 px-4"
(get node "label"))
(a :href (get next-node "href")
:sx-get (get next-node "href") :sx-target "#main-panel"
:sx-select "#main-panel" :sx-swap "outerHTML"
:sx-push-url "true"
:class "text-sm text-stone-500 hover:text-violet-600"
(str (get next-node "label") " →")))))
;; @css grid grid-cols-3
;; Current section with prev/next siblings.
;; 3-column grid: prev is right-aligned, current centered, next left-aligned.
;; Current page is larger in the leaf (bottom) row.
(defcomp ~nav-sibling-row (&key node siblings is-leaf)
(let* ((sibs (or siblings (list)))
(count (len sibs)))
(when (> count 0)
(let* ((idx (find-nav-index sibs node))
(prev-idx (mod (+ (- idx 1) count) count))
(next-idx (mod (+ idx 1) count))
(prev-node (nth sibs prev-idx))
(next-node (nth sibs next-idx)))
(div :class "max-w-3xl mx-auto px-4 py-2 grid grid-cols-3 items-center"
(a :href (get prev-node "href")
:sx-get (get prev-node "href") :sx-target "#main-panel"
:sx-select "#main-panel" :sx-swap "outerHTML"
:sx-push-url "true"
:class "text-sm text-stone-500 hover:text-violet-600 text-right"
(str "← " (get prev-node "label")))
(a :href (get node "href")
:sx-get (get node "href") :sx-target "#main-panel"
:sx-select "#main-panel" :sx-swap "outerHTML"
:sx-push-url "true"
:class (if is-leaf
"text-2xl font-bold text-violet-700 text-center px-4"
"text-lg font-semibold text-violet-700 text-center px-4")
(get node "label"))
(a :href (get next-node "href")
:sx-get (get next-node "href") :sx-target "#main-panel"
:sx-select "#main-panel" :sx-swap "outerHTML"
:sx-push-url "true"
:class "text-sm text-stone-500 hover:text-violet-600 text-left"
(str (get next-node "label") " →")))))))
;; Children links — shown as clearly clickable buttons.
(defcomp ~nav-children (&key items)
@@ -70,16 +76,20 @@
;; ---------------------------------------------------------------------------
(defcomp ~sx-doc (&key path &rest children) :affinity :server
(let ((nav-state (resolve-nav-path sx-nav-tree (or path "/"))))
(let* ((nav-state (resolve-nav-path sx-nav-tree (or path "/")))
(trail (or (get nav-state "trail") (list)))
(trail-len (len trail)))
(<>
(div :id "sx-nav" :class "mb-6"
(~sx-header)
;; Sibling arrows for EVERY level in the trail
(map (fn (crumb)
;; Last row (leaf) gets is-leaf for larger current page title
(map-indexed (fn (i crumb)
(~nav-sibling-row
:node (get crumb "node")
:siblings (get crumb "siblings")))
(get nav-state "trail"))
:siblings (get crumb "siblings")
:is-leaf (= i (- trail-len 1))))
trail)
;; Children as button links
(when (get nav-state "children")
(~nav-children :items (get nav-state "children"))))