All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m54s
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>
494 lines
21 KiB
Plaintext
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"))
|