Fix empty code blocks: rename ~docs/code param, fix batched IO dispatch

Two bugs caused code blocks to render empty across the site:

1. ~docs/code component had parameter named `code` which collided with
   the HTML <code> tag name. Renamed to `src` and updated all 57
   callers. Added font-mono class for explicit monospace.

2. Batched IO dispatch in ocaml_bridge.py only skipped one leading
   number (batch ID) but the format has two (epoch + ID):
   (io-request EPOCH ID "name" args...). Changed to skip all leading
   numbers so the string name is correctly found. This fixes highlight
   and other batchable helpers returning empty results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 18:08:40 +00:00
parent 739d04672b
commit 6f96452f70
58 changed files with 440 additions and 437 deletions

View File

@@ -27,13 +27,13 @@
(~docs/section :title "Minimal example" :id "minimal"
(p :class "text-stone-600"
"An sx response is s-expression source code with content type text/sx:")
(~docs/code :code (highlight "(div :class \"p-4 bg-white rounded\"\n (h1 :class \"text-2xl font-bold\" \"Hello, world!\")\n (p \"This is rendered from an s-expression.\"))" "lisp"))
(~docs/code :src (highlight "(div :class \"p-4 bg-white rounded\"\n (h1 :class \"text-2xl font-bold\" \"Hello, world!\")\n (p \"This is rendered from an s-expression.\"))" "lisp"))
(p :class "text-stone-600"
"Add sx-get to any element to make it fetch and render sx:"))
(~docs/section :title "Hypermedia attributes" :id "attrs"
(p :class "text-stone-600"
"Like htmx, sx adds attributes to HTML elements to trigger HTTP requests:")
(~docs/code :code (highlight "(button\n :sx-get \"/api/data\"\n :sx-target \"#result\"\n :sx-swap \"innerHTML\"\n \"Load data\")" "lisp"))
(~docs/code :src (highlight "(button\n :sx-get \"/api/data\"\n :sx-target \"#result\"\n :sx-swap \"innerHTML\"\n \"Load data\")" "lisp"))
(p :class "text-stone-600"
"sx-get, sx-post, sx-put, sx-delete, sx-patch — all work the same way. The response is parsed as sx and rendered into the target element."))))
@@ -42,10 +42,10 @@
(~docs/section :title "defcomp" :id "defcomp"
(p :class "text-stone-600"
"Components are defined with defcomp. They take keyword parameters and optional children:")
(~docs/code :code (highlight "(defcomp ~docs-content/card (&key title subtitle &rest children)\n (div :class \"border rounded p-4\"\n (h2 :class \"font-bold\" title)\n (when subtitle (p :class \"text-stone-500\" subtitle))\n (div :class \"mt-3\" children)))" "lisp"))
(~docs/code :src (highlight "(defcomp ~docs-content/card (&key title subtitle &rest children)\n (div :class \"border rounded p-4\"\n (h2 :class \"font-bold\" title)\n (when subtitle (p :class \"text-stone-500\" subtitle))\n (div :class \"mt-3\" children)))" "lisp"))
(p :class "text-stone-600"
"Use components with the ~ prefix:")
(~docs/code :code (highlight "(~docs-content/card :title \"My Card\" :subtitle \"A description\"\n (p \"First child\")\n (p \"Second child\"))" "lisp")))
(~docs/code :src (highlight "(~docs-content/card :title \"My Card\" :subtitle \"A description\"\n (p \"First child\")\n (p \"Second child\"))" "lisp")))
(~docs/section :title "Component caching" :id "caching"
(p :class "text-stone-600"
"Component definitions are sent in a <script type=\"text/sx\" data-components> block. The client caches them in localStorage keyed by a content hash. On subsequent page loads, the client sends an SX-Components header listing what it has. The server only sends definitions the client is missing.")
@@ -60,11 +60,11 @@
(~docs/section :title "Special forms" :id "special"
(p :class "text-stone-600"
"Special forms have lazy evaluation — arguments are not evaluated before the form runs:")
(~docs/code :code (highlight ";; Conditionals\n(if condition then-expr else-expr)\n(when condition body...)\n(cond (test1 body1) (test2 body2) (else default))\n\n;; Bindings\n(let ((name value) (name2 value2)) body...)\n(define name value)\n\n;; Functions\n(lambda (x y) (+ x y))\n(fn (x) (* x x))\n\n;; Sequencing\n(do expr1 expr2 expr3)\n(begin expr1 expr2)\n\n;; Threading\n(-> value (fn1 arg) (fn2 arg))" "lisp")))
(~docs/code :src (highlight ";; Conditionals\n(if condition then-expr else-expr)\n(when condition body...)\n(cond (test1 body1) (test2 body2) (else default))\n\n;; Bindings\n(let ((name value) (name2 value2)) body...)\n(define name value)\n\n;; Functions\n(lambda (x y) (+ x y))\n(fn (x) (* x x))\n\n;; Sequencing\n(do expr1 expr2 expr3)\n(begin expr1 expr2)\n\n;; Threading\n(-> value (fn1 arg) (fn2 arg))" "lisp")))
(~docs/section :title "Higher-order forms" :id "higher"
(p :class "text-stone-600"
"These operate on collections with function arguments:")
(~docs/code :code (highlight "(map (fn (x) (* x 2)) (list 1 2 3)) ;; => (2 4 6)\n(filter (fn (x) (> x 2)) (list 1 2 3 4 5)) ;; => (3 4 5)\n(reduce (fn (acc x) (+ acc x)) 0 (list 1 2 3)) ;; => 6\n(some (fn (x) (> x 3)) (list 1 2 3 4)) ;; => true\n(every? (fn (x) (> x 0)) (list 1 2 3)) ;; => true" "lisp")))))
(~docs/code :src (highlight "(map (fn (x) (* x 2)) (list 1 2 3)) ;; => (2 4 6)\n(filter (fn (x) (> x 2)) (list 1 2 3 4 5)) ;; => (3 4 5)\n(reduce (fn (acc x) (+ acc x)) 0 (list 1 2 3)) ;; => 6\n(some (fn (x) (> x 3)) (list 1 2 3 4)) ;; => true\n(every? (fn (x) (> x 0)) (list 1 2 3)) ;; => true" "lisp")))))
(defcomp ~docs-content/docs-primitives-content (&key prims)
(~docs/page :title "Primitives"
@@ -87,7 +87,7 @@
(~docs/section :title "Python API" :id "python"
(p :class "text-stone-600"
"The server-side sx library provides several entry points for rendering:")
(~docs/code :code (highlight "from shared.sx.helpers import sx_page, sx_response, sx_call\nfrom shared.sx.parser import SxExpr\n\n# Build a component call from Python kwargs\nsx_call(\"card\", title=\"Hello\", subtitle=\"World\")\n\n# Return an sx wire-format response\nreturn sx_response(sx_call(\"card\", title=\"Hello\"))\n\n# Return a full HTML page shell with sx boot\nreturn sx_page(ctx, page_sx)" "python")))
(~docs/code :src (highlight "from shared.sx.helpers import sx_page, sx_response, sx_call\nfrom shared.sx.parser import SxExpr\n\n# Build a component call from Python kwargs\nsx_call(\"card\", title=\"Hello\", subtitle=\"World\")\n\n# Return an sx wire-format response\nreturn sx_response(sx_call(\"card\", title=\"Hello\"))\n\n# Return a full HTML page shell with sx boot\nreturn sx_page(ctx, page_sx)" "python")))
(~docs/section :title "sx_call" :id "sx-call"
(p :class "text-stone-600"
"sx_call converts Python kwargs to an s-expression component call. Snake_case becomes kebab-case. SxExpr values are inlined without quoting. None becomes nil. Bools become true/false."))