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>
176 lines
6.0 KiB
Plaintext
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)))))
|