All routes moved under /sx/ prefix: - / redirects to /sx/ - /sx/ serves home page - /sx/<path:expr> is the catch-all for SX expression URLs - Bare /(...) and /~... redirect to /sx/(...) and /sx/~... - All ~600 hrefs, sx-get attrs, defhandler paths, redirect targets, and blueprint routes updated across 44 files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
97 lines
6.0 KiB
Plaintext
97 lines
6.0 KiB
Plaintext
;; Routing analyzer — live demonstration of client-side routing classification.
|
|
;; Shows which pages route client-side (pure, instant) vs server-side (IO/data).
|
|
;; @css bg-green-100 text-green-800 bg-violet-600 bg-stone-200 text-violet-600 text-stone-600 text-green-600 rounded-full h-2.5 grid-cols-2 bg-blue-100 text-blue-800 bg-amber-100 text-amber-800 grid-cols-4 marker:text-stone-400 bg-blue-50 bg-amber-50 text-blue-700 text-amber-700 border-blue-200 border-amber-200 bg-blue-500 bg-amber-500 grid-cols-3 border-green-200 bg-green-50 text-green-700
|
|
|
|
(defcomp ~routing-analyzer-content (&key pages total-pages client-count
|
|
server-count registry-sample)
|
|
(~doc-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"))
|
|
|
|
;; Route classification bar
|
|
(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)) "%"))))
|
|
|
|
(~doc-section :title "Route Table" :id "routes"
|
|
(div :class "space-y-2"
|
|
(map (fn (page)
|
|
(~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)))
|
|
|
|
(~doc-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"))))))
|
|
|
|
(~doc-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 "#main-panel") " 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-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"))
|
|
;; Mode badge
|
|
(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)
|
|
;; Page info
|
|
(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)))
|
|
;; Content expression
|
|
(when content-expr
|
|
(div :class "hidden md:block max-w-xs truncate"
|
|
(code :class "text-xs text-stone-500" content-expr)))))
|