Files
rose-ash/sx/sx/page-functions.sx
giles 7d7de86034
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m54s
Fix stepper client-side [object Object] flash and missing CSSX styles
Three issues in the stepper island's client-side rendering:

1. do-step used eval-expr with empty env for ~cssx/tw spreads — component
   not found, result leaked as [object Object]. Fixed: call ~cssx/tw
   directly (in scope from island env) with trampoline.

2. steps-to-preview excluded spreads — SSR preview had no styling.
   Fixed: include spreads in the tree so both SSR and client render
   with CSSX classes.

3. build-children used named let (let loop ...) which produces
   unresolved Thunks in render mode due to the named-let compiler
   desugaring interacting with the render/eval boundary. Fixed:
   rewrote as plain recursive function bc-loop avoiding named let.

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

494 lines
21 KiB
Plaintext

;; SX docs page functions — section + page dispatch for GraphSX URL routing.
;;
;; Page functions return QUOTED expressions (unevaluated ASTs).
;; The router evaluates these via the OCaml kernel (or Python fallback).
;;
;; Pattern:
;; Simple: '(~component-name)
;; Data: (let ((data (helper "name" arg))) `(~component :key ,val))
;;
;; IO: Application data is fetched via the (helper name ...) IO primitive,
;; which dispatches to Python page helpers through the coroutine bridge.
;; This keeps the spec clean — no application functions leak into the kernel.
;; ---------------------------------------------------------------------------
;; Convention-based page dispatch
;; ---------------------------------------------------------------------------
;;
;; Most page functions are boilerplate: slug → component name via a naming
;; convention. Instead of hand-writing case statements, derive the component
;; symbol from the slug at runtime.
;;
;; Naming conventions:
;; essay: "sx-sucks" → ~essays/sx-sucks/essay-sx-sucks
;; plan: "status" → ~plans/status/plan-status-content
;; example: "tabs" → ~examples-content/example-tabs
;; protocol: "fragments" → ~protocols/fragments-content
;; cssx: "patterns" → ~cssx/patterns-content
;; ri-example: "counter" → ~reactive-islands/demo/example-counter
;; Build a component symbol from a slug and a naming pattern.
;; Pattern: prefix + slug + infix + slug + suffix
;; When infix is nil, slug appears once: prefix + slug + suffix
(define slug->component
(fn (slug prefix infix suffix)
(if infix
(make-symbol (str prefix slug infix slug suffix))
(make-symbol (str prefix slug suffix)))))
;; Make a simple slug-dispatcher: given a naming convention, returns a function
;; that maps (slug) → '(~derived-component-name).
;; default-name is a STRING of the component name for the nil-slug (index) case.
;; (We use a string + make-symbol because bare ~symbols get evaluated as lookups.)
(define make-page-fn
(fn (default-name prefix infix suffix)
(fn (slug)
(if (nil? slug)
(list (make-symbol default-name))
(list (slug->component slug prefix infix suffix))))))
;; ---------------------------------------------------------------------------
;; Section functions — structural, pass through content or return index
;; ---------------------------------------------------------------------------
(define home
(fn (content)
(if (nil? content) '(~docs-content/home-content) content)))
(define language
(fn (content)
(if (nil? content) nil content)))
(define geography
(fn (content)
(if (nil? content) '(~geography/index-content) content)))
(define applications
(fn (content)
(if (nil? content) nil content)))
(define etc
(fn (content)
(if (nil? content) nil content)))
;; Sub-section functions
(define hypermedia
(fn (content)
(if (nil? content) nil content)))
(define reactive
(fn (content)
(if (nil? content)
'(~reactive-islands/index/reactive-islands-index-content)
content)))
;; Convention: ~reactive-islands/demo/example-{slug}
(define examples
(make-page-fn "~reactive-islands/demo/reactive-islands-demo-content" "~reactive-islands/demo/example-" nil ""))
(define cek
(fn (slug)
(if (nil? slug)
'(~geography/cek/cek-content)
(case slug
"demo" '(~geography/cek/cek-demo-content)
"freeze" '(~geography/cek/cek-freeze-content)
"content" '(~geography/cek/cek-content-address-content)
:else '(~geography/cek/cek-content)))))
(define provide
(fn (content)
(if (nil? content) '(~geography/provide-content) content)))
(define scopes
(fn (content)
(if (nil? content) '(~geography/scopes-content) content)))
(define spreads
(fn (content)
(if (nil? content) '(~geography/spreads-content) content)))
(define marshes
(fn (slug)
(if (nil? slug)
'(~reactive-islands/marshes/reactive-islands-marshes-content)
(case slug
"hypermedia-feeds" '(~reactive-islands/marshes/example-hypermedia-feeds)
"server-signals" '(~reactive-islands/marshes/example-server-signals)
"on-settle" '(~reactive-islands/marshes/example-on-settle)
"signal-triggers" '(~reactive-islands/marshes/example-signal-triggers)
"view-transform" '(~reactive-islands/marshes/example-view-transform)
:else '(~reactive-islands/marshes/reactive-islands-marshes-content)))))
(define isomorphism
(fn (slug)
(if (nil? slug)
'(~plans/isomorphic/plan-isomorphic-content)
(case slug
"bundle-analyzer"
(let ((data (helper "bundle-analyzer-data")))
`(~analyzer/bundle-analyzer-content
:pages ,(get data "pages")
:total-components ,(get data "total-components")
:total-macros ,(get data "total-macros")
:pure-count ,(get data "pure-count")
:io-count ,(get data "io-count")))
"routing-analyzer"
(let ((data (helper "routing-analyzer-data")))
`(~routing-analyzer/content
:pages ,(get data "pages")
:total-pages ,(get data "total-pages")
:client-count ,(get data "client-count")
:server-count ,(get data "server-count")
:registry-sample ,(get data "registry-sample")))
"data-test"
(let ((data (helper "data-test-data")))
`(~data-test/content
:server-time ,(get data "server-time")
:items ,(get data "items")
:phase ,(get data "phase")
:transport ,(get data "transport")))
"async-io" '(~async-io-demo/content)
"affinity"
(let ((data (helper "affinity-demo-data")))
`(~affinity-demo/content
:components ,(get data "components")
:page-plans ,(get data "page-plans")))
"optimistic"
(let ((data (helper "optimistic-demo-data")))
`(~optimistic-demo/content
:items ,(get data "items")
:server-time ,(get data "server-time")))
"offline"
(let ((data (helper "offline-demo-data")))
`(~offline-demo/content
:notes ,(get data "notes")
:server-time ,(get data "server-time")))
;; "streaming" → handled as special case by Python router
:else '(~plans/isomorphic/plan-isomorphic-content)))))
;; ---------------------------------------------------------------------------
;; Page functions — leaf dispatch to content components
;; ---------------------------------------------------------------------------
;; Docs (under language)
(define doc
(fn (slug)
(if (nil? slug)
'(~docs-content/docs-introduction-content)
(case slug
"introduction" '(~docs-content/docs-introduction-content)
"getting-started" '(~docs-content/docs-getting-started-content)
"components" '(~docs-content/docs-components-content)
"evaluator" '(~docs-content/docs-evaluator-content)
"primitives"
(let ((data (helper "primitives-data")))
`(~docs-content/docs-primitives-content
:prims (~docs/primitives-tables :primitives ,data)))
"special-forms"
(let ((data (helper "special-forms-data")))
`(~docs-content/docs-special-forms-content
:forms (~docs/special-forms-tables :forms ,data)))
"server-rendering" '(~docs-content/docs-server-rendering-content)
:else '(~docs-content/docs-introduction-content)))))
;; Specs (under language)
(define spec
(fn (slug)
(if (nil? slug)
'(~specs/architecture-content)
(case slug
"core"
(let ((files (make-spec-files core-spec-items)))
`(~specs/overview-content :spec-title "Core Language" :spec-files ,files))
"adapters"
(let ((files (make-spec-files adapter-spec-items)))
`(~specs/overview-content :spec-title "Adapters" :spec-files ,files))
"browser"
(let ((files (make-spec-files browser-spec-items)))
`(~specs/overview-content :spec-title "Browser Runtime" :spec-files ,files))
"reactive"
(let ((files (make-spec-files reactive-spec-items)))
`(~specs/overview-content :spec-title "Reactive System" :spec-files ,files))
"host"
(let ((files (make-spec-files host-spec-items)))
`(~specs/overview-content :spec-title "Host Interface" :spec-files ,files))
"extensions"
(let ((files (make-spec-files extension-spec-items)))
`(~specs/overview-content :spec-title "Extensions" :spec-files ,files))
:else (let ((found-spec (find-spec slug)))
(if found-spec
(let ((src (helper "read-spec-file" (get found-spec "filename"))))
`(~specs/detail-content
:spec-title ,(get found-spec "title")
:spec-desc ,(get found-spec "desc")
:spec-filename ,(get found-spec "filename")
:spec-source ,src
:spec-prose ,(get found-spec "prose")))
`(~specs/not-found :slug ,slug)))))))
;; Spec explorer (under language → spec)
;; Uses spec-explore from spec-introspect.sx — the spec examines itself.
(define explore
(fn (slug)
(if (nil? slug)
'(~specs/architecture-content)
(let ((found-spec (find-spec slug)))
(if found-spec
(let ((data (spec-explore
(get found-spec "filename")
(get found-spec "title")
(get found-spec "desc"))))
(if data
`(~specs-explorer/spec-explorer-content :data ,data)
`(~specs/not-found :slug ,slug)))
`(~specs/not-found :slug ,slug))))))
;; Helper used by spec — make-spec-files
(define make-spec-files
(fn (items)
(map (fn (item)
(dict :title (get item "title") :desc (get item "desc")
:prose (get item "prose")
:filename (get item "filename") :href (str "/sx/(language.(spec." (get item "slug") "))")
:source (helper "read-spec-file" (get item "filename"))))
items)))
;; Bootstrappers (under language)
(define bootstrapper
(fn (slug)
(if (nil? slug)
'(~specs/bootstrappers-index-content)
(let ((data (helper "bootstrapper-data" slug)))
(if (get data "bootstrapper-not-found")
`(~specs/not-found :slug ,slug)
(case slug
"self-hosting"
`(~specs/bootstrapper-self-hosting-content
:py-sx-source ,(get data "py-sx-source")
:g0-output ,(get data "g0-output")
:g1-output ,(get data "g1-output")
:defines-matched ,(get data "defines-matched")
:defines-total ,(get data "defines-total")
:g0-lines ,(get data "g0-lines")
:g0-bytes ,(get data "g0-bytes")
:verification-status ,(get data "verification-status"))
"self-hosting-js"
`(~specs/bootstrapper-self-hosting-js-content
:js-sx-source ,(get data "js-sx-source")
:defines-matched ,(get data "defines-matched")
:defines-total ,(get data "defines-total")
:js-sx-lines ,(get data "js-sx-lines")
:verification-status ,(get data "verification-status"))
"python"
`(~specs/bootstrapper-py-content
:bootstrapper-source ,(get data "bootstrapper-source")
:bootstrapped-output ,(get data "bootstrapped-output"))
"page-helpers"
(let ((ph-data (helper "page-helpers-demo-data")))
`(~page-helpers-demo/content
:sf-categories ,(get ph-data "sf-categories")
:sf-total ,(get ph-data "sf-total")
:sf-ms ,(get ph-data "sf-ms")
:ref-sample ,(get ph-data "ref-sample")
:ref-ms ,(get ph-data "ref-ms")
:attr-result ,(get ph-data "attr-result")
:attr-ms ,(get ph-data "attr-ms")
:comp-source ,(get ph-data "comp-source")
:comp-ms ,(get ph-data "comp-ms")
:routing-result ,(get ph-data "routing-result")
:routing-ms ,(get ph-data "routing-ms")
:server-total-ms ,(get ph-data "server-total-ms")
:sf-source ,(get ph-data "sf-source")
:attr-detail ,(get ph-data "attr-detail")
:req-attrs ,(get ph-data "req-attrs")
:attr-keys ,(get ph-data "attr-keys")))
:else
`(~specs/bootstrapper-js-content
:bootstrapper-source ,(get data "bootstrapper-source")
:bootstrapped-output ,(get data "bootstrapped-output"))))))))
;; Testing (under language)
(define test
(fn (slug)
(if (nil? slug)
(let ((data (helper "run-modular-tests""all")))
`(~testing/overview-content
:server-results ,(get data "server-results")
:framework-source ,(get data "framework-source")
:eval-source ,(get data "eval-source")
:parser-source ,(get data "parser-source")
:router-source ,(get data "router-source")
:render-source ,(get data "render-source")
:deps-source ,(get data "deps-source")
:engine-source ,(get data "engine-source")))
(case slug
"runners" '(~testing/runners-content)
:else
(let ((data (helper "run-modular-tests"slug)))
(case slug
"eval" `(~testing/spec-content
:spec-name "eval" :spec-title "Evaluator Tests"
:spec-desc "81 tests covering the core evaluator and all primitives."
:spec-source ,(get data "spec-source")
:framework-source ,(get data "framework-source")
:server-results ,(get data "server-results"))
"parser" `(~testing/spec-content
:spec-name "parser" :spec-title "Parser Tests"
:spec-desc "39 tests covering tokenization and parsing."
:spec-source ,(get data "spec-source")
:framework-source ,(get data "framework-source")
:server-results ,(get data "server-results"))
"router" `(~testing/spec-content
:spec-name "router" :spec-title "Router Tests"
:spec-desc "18 tests covering client-side route matching."
:spec-source ,(get data "spec-source")
:framework-source ,(get data "framework-source")
:server-results ,(get data "server-results"))
"render" `(~testing/spec-content
:spec-name "render" :spec-title "Renderer Tests"
:spec-desc "23 tests covering HTML rendering."
:spec-source ,(get data "spec-source")
:framework-source ,(get data "framework-source")
:server-results ,(get data "server-results"))
"deps" `(~testing/spec-content
:spec-name "deps" :spec-title "Dependency Analysis Tests"
:spec-desc "33 tests covering component dependency analysis."
:spec-source ,(get data "spec-source")
:framework-source ,(get data "framework-source")
:server-results ,(get data "server-results"))
"engine" `(~testing/spec-content
:spec-name "engine" :spec-title "Engine Tests"
:spec-desc "37 tests covering engine pure functions."
:spec-source ,(get data "spec-source")
:framework-source ,(get data "framework-source")
:server-results ,(get data "server-results"))
"orchestration" `(~testing/spec-content
:spec-name "orchestration" :spec-title "Orchestration Tests"
:spec-desc "17 tests covering orchestration."
:spec-source ,(get data "spec-source")
:framework-source ,(get data "framework-source")
:server-results ,(get data "server-results"))
:else `(~testing/overview-content
:server-results ,(get data "server-results"))))))))
;; Reference (under geography → hypermedia)
(define reference
(fn (slug)
(if (nil? slug)
'(~examples/reference-index-content)
(let ((data (helper "reference-data" slug)))
(case slug
"attributes" `(~reference/attrs-content
:req-table (~docs/attr-table-from-data :title "Request Attributes" :attrs ,(get data "req-attrs"))
:beh-table (~docs/attr-table-from-data :title "Behavior Attributes" :attrs ,(get data "beh-attrs"))
:uniq-table (~docs/attr-table-from-data :title "Unique to sx" :attrs ,(get data "uniq-attrs")))
"headers" `(~reference/headers-content
:req-table (~docs/headers-table-from-data :title "Request Headers" :headers ,(get data "req-headers"))
:resp-table (~docs/headers-table-from-data :title "Response Headers" :headers ,(get data "resp-headers")))
"events" `(~reference/events-content
:table (~docs/two-col-table-from-data
:intro "sx fires custom DOM events at various points in the request lifecycle."
:col1 "Event" :col2 "Description" :items ,(get data "events-list")))
"js-api" `(~reference/js-api-content
:table (~docs/two-col-table-from-data
:intro "The client-side sx.js library exposes a public API for programmatic use."
:col1 "Method" :col2 "Description" :items ,(get data "js-api-list")))
:else `(~reference/attrs-content
:req-table (~docs/attr-table-from-data :title "Request Attributes" :attrs ,(get data "req-attrs"))
:beh-table (~docs/attr-table-from-data :title "Behavior Attributes" :attrs ,(get data "beh-attrs"))
:uniq-table (~docs/attr-table-from-data :title "Unique to sx" :attrs ,(get data "uniq-attrs"))))))))
;; Reference detail pages (under geography → hypermedia → reference)
;; Takes two positional args: kind and slug
(define reference-detail
(fn (kind slug)
(if (nil? slug) nil
(case kind
"attributes"
(let ((data (helper "attr-detail-data" slug)))
(if (get data "attr-not-found")
`(~reference/attr-not-found :slug ,slug)
`(~reference/attr-detail-content
:title ,(get data "attr-title")
:description ,(get data "attr-description")
:demo ,(get data "attr-demo")
:example-code ,(get data "attr-example")
:handler-code ,(get data "attr-handler")
:wire-placeholder-id ,(get data "attr-wire-id"))))
"headers"
(let ((data (helper "header-detail-data" slug)))
(if (get data "header-not-found")
`(~reference/attr-not-found :slug ,slug)
`(~reference/header-detail-content
:title ,(get data "header-title")
:direction ,(get data "header-direction")
:description ,(get data "header-description")
:example-code ,(get data "header-example")
:demo ,(get data "header-demo"))))
"events"
(let ((data (helper "event-detail-data" slug)))
(if (get data "event-not-found")
`(~reference/attr-not-found :slug ,slug)
`(~reference/event-detail-content
:title ,(get data "event-title")
:description ,(get data "event-description")
:example-code ,(get data "event-example")
:demo ,(get data "event-demo"))))
:else nil))))
;; Examples (under geography → hypermedia)
;; Convention: ~examples-content/example-{slug}
(define example
(fn (slug)
(if (nil? slug) nil
(list (slug->component slug "~examples-content/example-" nil "")))))
;; SX URLs (under applications)
(define sx-urls
(fn (slug)
'(~sx-urls/urls-content)))
;; CSSX (under applications)
;; Convention: ~cssx/{slug}-content
(define cssx
(make-page-fn "~cssx/overview-content" "~cssx/" nil "-content"))
;; Protocols (under applications)
;; Convention: ~protocols/{slug}-content
(define protocol
(make-page-fn "~protocols/wire-format-content" "~protocols/" nil "-content"))
;; sx-pub (under applications)
(define sx-pub
(fn (slug)
(if (nil? slug)
'(~sx-pub/overview-content)
nil)))
;; Essays (under etc)
;; Convention: ~essays/{slug}/essay-{slug}
(define essay
(make-page-fn "~essays/index/essays-index-content" "~essays/" "/essay-" ""))
;; Philosophy (under etc)
(define philosophy
(fn (slug)
(if (nil? slug)
'(~essays/philosophy-index/content)
(case slug
"sx-manifesto" '(~essay-sx-manifesto)
"godel-escher-bach" '(~essays/godel-escher-bach/essay-godel-escher-bach)
"wittgenstein" '(~essays/sx-and-wittgenstein/essay-sx-and-wittgenstein)
"dennett" '(~essays/sx-and-dennett/essay-sx-and-dennett)
"existentialism" '(~essays/s-existentialism/essay-s-existentialism)
"platonic-sx" '(~essays/platonic-sx/essay-platonic-sx)
:else '(~essays/philosophy-index/content)))))
;; Plans (under etc)
;; Convention: ~plans/{slug}/plan-{slug}-content
(define plan
(make-page-fn "~plans/index/plans-index-content" "~plans/" "/plan-" "-content"))