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:
28
sx/app.py
28
sx/app.py
@@ -67,6 +67,34 @@ def create_app() -> "Quart":
|
||||
**extra_kw,
|
||||
)
|
||||
|
||||
# Minimal shell — no Prism, no SweetAlert, no body.js
|
||||
# sx docs uses custom highlight.py, not Prism; body.js is for legacy apps
|
||||
app.config["SX_SHELL"] = {
|
||||
"head_scripts": [], # no CDN scripts
|
||||
"body_scripts": [], # no body.js
|
||||
"inline_head_js": "", # no pre-boot JS (hover-capable, close-details unused)
|
||||
"inline_css": (
|
||||
".sx-indicator{display:none}"
|
||||
".sx-request .sx-indicator{display:inline-flex}"
|
||||
"@keyframes sxJiggle{0%,100%{transform:translateX(0)}"
|
||||
"25%{transform:translateX(-.5px)}75%{transform:translateX(.5px)}}"
|
||||
"a.sx-request{animation:sxJiggle .3s ease-in-out infinite}"
|
||||
),
|
||||
# Nav link aria-selected update on client-side routing — pure SX
|
||||
"init_sx": (
|
||||
'(dom-listen (dom-body) "sx:clientRoute"'
|
||||
' (fn (e)'
|
||||
' (let ((p (get (event-detail e) "pathname")))'
|
||||
' (when p'
|
||||
' (for-each'
|
||||
' (fn (a) (dom-set-attr a "aria-selected" "false"))'
|
||||
' (dom-query-all "nav a[aria-selected]"))'
|
||||
' (for-each'
|
||||
' (fn (a) (dom-set-attr a "aria-selected" "true"))'
|
||||
' (dom-query-all (str "nav a[href=\\"" p "\\"]")))))))'
|
||||
),
|
||||
}
|
||||
|
||||
from sxc.pages import setup_sx_pages
|
||||
setup_sx_pages()
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"))))
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
(defcomp ~doc-page (&key title &rest children)
|
||||
(div :class "max-w-4xl mx-auto px-6 py-8"
|
||||
(h1 :class "text-4xl font-bold text-stone-900 mb-8 text-center" title)
|
||||
(div :class "prose prose-stone max-w-none space-y-6" children)))
|
||||
|
||||
(defcomp ~doc-section (&key title id &rest children)
|
||||
|
||||
Reference in New Issue
Block a user