Files
rose-ash/sx/sx/routing-analyzer.sx
giles 584445a843 SPA navigation, page component refactors, WASM rebuild
Refactor page components (docs, examples, specs, reference, layouts)
and adapters (adapter-sx, boot-helpers, orchestration) across sx/ and
web/ directories. Add Playwright SPA navigation tests. Rebuild WASM
kernel with updated bytecode. Add OCaml primitives for request handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:00:51 +00:00

176 lines
6.0 KiB
Plaintext

(defcomp
~routing-analyzer/content
(&key pages total-pages client-count server-count registry-sample)
(~docs/page
:title "Routing Analyzer"
(p
:class "text-stone-600 mb-6"
"Live classification of all "
(strong (str total-pages))
" pages by routing mode. "
"Pages without "
(code ":data")
" dependencies are "
(span :class "text-green-700 font-medium" "client-routable")
" — after initial load they render instantly from the page registry without a server roundtrip. "
"Pages with data dependencies fall back to "
(span :class "text-amber-700 font-medium" "server fetch")
" transparently. Powered by "
(a
:href "/sx/(language.(spec.router))"
:class "text-violet-700 underline"
"router.sx")
" route matching and "
(a
:href "/sx/(language.(spec.deps))"
:class "text-violet-700 underline"
"deps.sx")
" IO detection.")
(div
:class "mb-8 grid grid-cols-4 gap-4"
(~analyzer/stat
:label "Total Pages"
:value (str total-pages)
:cls "text-violet-600")
(~analyzer/stat
:label "Client-Routable"
:value (str client-count)
:cls "text-green-600")
(~analyzer/stat
:label "Server-Only"
:value (str server-count)
:cls "text-amber-600")
(~analyzer/stat
:label "Client Ratio"
:value (str (round (* (/ client-count total-pages) 100)) "%")
:cls "text-blue-600"))
(div
:class "mb-8"
(div
:class "flex items-center gap-2 mb-2"
(span :class "text-sm font-medium text-stone-600" "Client")
(div :class "flex-1")
(span :class "text-sm font-medium text-stone-600" "Server"))
(div
:class "w-full bg-amber-200 rounded-full h-4 overflow-hidden"
(div
:class "bg-green-500 h-4 rounded-l-full transition-all"
:style (str "width: " (round (* (/ client-count total-pages) 100)) "%"))))
(~docs/section
:title "Route Table"
:id "routes"
(div
:class "space-y-2"
(map
(fn
(page)
(~routing-analyzer/routing-row
:name (get page "name")
:path (get page "path")
:mode (get page "mode")
:has-data (get page "has-data")
:content-expr (get page "content-expr")
:reason (get page "reason")))
pages)))
(~docs/section
:title "Page Registry Format"
:id "registry"
(p
:class "text-stone-600 mb-4"
"The server serializes page metadata as SX dict literals inside "
(code "<script type=\"text/sx-pages\">")
". The client's parser reads these at boot, building a route table with parsed URL patterns. "
"No JSON involved — the same SX parser handles everything.")
(when
(not (empty? registry-sample))
(div
:class "not-prose"
(pre
:class "text-xs leading-relaxed whitespace-pre-wrap overflow-x-auto bg-stone-100 rounded border border-stone-200 p-4"
(code (highlight registry-sample "lisp"))))))
(~docs/section
:title "How Client Routing Works"
:id "how"
(ol
:class "list-decimal pl-5 space-y-2 text-stone-700"
(li
(strong "Boot: ")
"boot.sx finds "
(code "<script type=\"text/sx-pages\">")
", calls "
(code "parse")
" on the SX content, then "
(code "parse-route-pattern")
" on each page's path to build "
(code "_page-routes")
".")
(li
(strong "Click: ")
"orchestration.sx intercepts boost link clicks via "
(code "bind-client-route-link")
". Extracts the pathname from the href.")
(li
(strong "Match: ")
(code "find-matching-route")
" from router.sx tests the pathname against all parsed patterns. Returns the first match with extracted URL params.")
(li
(strong "Check: ")
"If the matched page has "
(code ":has-data true")
", skip to server fetch. Otherwise proceed to client eval.")
(li
(strong "Eval: ")
(code "try-eval-content")
" merges the component env + URL params + closure, then parses and renders the content expression to DOM.")
(li
(strong "Swap: ")
"On success, the rendered DOM replaces "
(code "#sx-content")
" contents, "
(code "pushState")
" updates the URL, and the console logs "
(code "sx:route client /path")
".")
(li
(strong "Fallback: ")
"If anything fails (no match, eval error, missing component), the click falls through to a standard server fetch. Console logs "
(code "sx:route server /path")
". The user sees no difference.")))))
(defcomp
~routing-analyzer/routing-row
(&key
(name :as string)
(path :as string)
(mode :as string)
(has-data :as boolean)
(content-expr :as string?)
(reason :as string?))
(div
:class (str
"rounded border p-3 flex items-center gap-3 "
(if
(= mode "client")
"border-green-200 bg-green-50"
"border-amber-200 bg-amber-50"))
(span
:class (str
"inline-block px-2 py-0.5 rounded text-xs font-bold uppercase "
(if
(= mode "client")
"bg-green-600 text-white"
"bg-amber-500 text-white"))
mode)
(div
:class "flex-1 min-w-0"
(div
:class "flex items-center gap-2"
(span :class "font-mono font-semibold text-stone-800 text-sm" name)
(span :class "text-stone-400 text-xs font-mono" path))
(when reason (div :class "text-xs text-stone-500 mt-0.5" reason)))
(when
content-expr
(div
:class "hidden md:block max-w-xs truncate"
(code :class "text-xs text-stone-500" content-expr)))))