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:
@@ -15,7 +15,9 @@
|
||||
sx-css sx-css-classes
|
||||
component-hash component-defs
|
||||
pages-sx page-sx
|
||||
asset-url sx-js-hash body-js-hash)
|
||||
asset-url sx-js-hash body-js-hash
|
||||
head-scripts inline-css inline-head-js
|
||||
init-sx body-scripts)
|
||||
(<>
|
||||
(raw! "<!doctype html>")
|
||||
(html :lang "en"
|
||||
@@ -26,21 +28,32 @@
|
||||
(meta :name "theme-color" :content "#ffffff")
|
||||
(title title)
|
||||
(when meta-html (raw! meta-html))
|
||||
(style (raw! "@media (min-width: 768px) { .js-mobile-sentinel { display:none !important; } }"))
|
||||
(meta :name "csrf-token" :content csrf)
|
||||
(style :id "sx-css" (raw! (or sx-css "")))
|
||||
(meta :name "sx-css-classes" :content (or sx-css-classes ""))
|
||||
;; CDN scripts
|
||||
(script :src "https://unpkg.com/prismjs/prism.js")
|
||||
(script :src "https://unpkg.com/prismjs/components/prism-javascript.min.js")
|
||||
(script :src "https://unpkg.com/prismjs/components/prism-python.min.js")
|
||||
(script :src "https://unpkg.com/prismjs/components/prism-bash.min.js")
|
||||
(script :src "https://cdn.jsdelivr.net/npm/sweetalert2@11")
|
||||
;; Inline JS
|
||||
(script (raw! "if(matchMedia('(hover:hover) and (pointer:fine)').matches){document.documentElement.classList.add('hover-capable')}"))
|
||||
(script (raw! "document.addEventListener('click',function(e){var t=e.target.closest('[data-close-details]');if(!t)return;var d=t.closest('details');if(d)d.removeAttribute('open')})"))
|
||||
;; Inline CSS
|
||||
(style (raw! "details[data-toggle-group=\"mobile-panels\"]>summary{list-style:none}
|
||||
;; CDN / head scripts — configurable per app
|
||||
;; Pass a list (even empty) to override defaults; nil = use defaults
|
||||
(if (not (nil? head-scripts))
|
||||
(map (fn (src) (script :src src)) head-scripts)
|
||||
;; Default: Prism + SweetAlert (legacy apps)
|
||||
(<>
|
||||
(script :src "https://unpkg.com/prismjs/prism.js")
|
||||
(script :src "https://unpkg.com/prismjs/components/prism-javascript.min.js")
|
||||
(script :src "https://unpkg.com/prismjs/components/prism-python.min.js")
|
||||
(script :src "https://unpkg.com/prismjs/components/prism-bash.min.js")
|
||||
(script :src "https://cdn.jsdelivr.net/npm/sweetalert2@11")))
|
||||
;; Inline JS — skipped when app provides its own inline-head-js (even empty)
|
||||
(if (not (nil? inline-head-js))
|
||||
(when (not (empty? inline-head-js)) (script (raw! inline-head-js)))
|
||||
(<>
|
||||
(script (raw! "if(matchMedia('(hover:hover) and (pointer:fine)').matches){document.documentElement.classList.add('hover-capable')}"))
|
||||
(script (raw! "document.addEventListener('click',function(e){var t=e.target.closest('[data-close-details]');if(!t)return;var d=t.closest('details');if(d)d.removeAttribute('open')})"))))
|
||||
;; Inline CSS — configurable per app
|
||||
;; Pass a string (even empty) to override defaults; nil = use defaults
|
||||
(if (not (nil? inline-css))
|
||||
(style (raw! inline-css))
|
||||
;; Default: all shared styles (legacy apps)
|
||||
(style (raw! "details[data-toggle-group=\"mobile-panels\"]>summary{list-style:none}
|
||||
details[data-toggle-group=\"mobile-panels\"]>summary::-webkit-details-marker{display:none}
|
||||
@media(min-width:768px){.nav-group:focus-within .submenu,.nav-group:hover .submenu{display:block}}
|
||||
img{max-width:100%;height:auto}
|
||||
@@ -50,13 +63,21 @@ img{max-width:100%;height:auto}
|
||||
details.group{overflow:hidden}details.group>summary{list-style:none}details.group>summary::-webkit-details-marker{display:none}
|
||||
.sx-indicator{display:none}.sx-request .sx-indicator{display:inline-flex}
|
||||
.sx-error .sx-indicator{display:none}.sx-loading .sx-indicator{display:inline-flex}
|
||||
.js-wrap.open .js-pop{display:block}.js-wrap.open .js-backdrop{display:block}")))
|
||||
.js-wrap.open .js-pop{display:block}.js-wrap.open .js-backdrop{display:block}"))))
|
||||
(body :class "bg-stone-50 text-stone-900"
|
||||
(script :type "text/sx" :data-components true :data-hash component-hash
|
||||
(raw! (or component-defs "")))
|
||||
(when init-sx
|
||||
(script :type "text/sx" :data-init true
|
||||
(raw! init-sx)))
|
||||
(script :type "text/sx-pages"
|
||||
(raw! (or pages-sx "")))
|
||||
(script :type "text/sx" :data-mount "body"
|
||||
(raw! (or page-sx "")))
|
||||
(script :src (str asset-url "/scripts/sx-browser.js?v=" sx-js-hash))
|
||||
(script :src (str asset-url "/scripts/body.js?v=" body-js-hash))))))
|
||||
;; Body scripts — configurable per app
|
||||
;; Pass a list (even empty) to override defaults; nil = use defaults
|
||||
(if (not (nil? body-scripts))
|
||||
(map (fn (src) (script :src src)) body-scripts)
|
||||
;; Default: body.js (legacy apps)
|
||||
(script :src (str asset-url "/scripts/body.js?v=" body-js-hash)))))))
|
||||
|
||||
Reference in New Issue
Block a user