From c2a85ed02617eec346de42a19a32b8a6f3233684 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 7 Mar 2026 08:48:48 +0000 Subject: [PATCH] Fix async IO demo: use ~doc-code instead of raw!, fix JS highlight highlight returns SxExpr (SX source with colored spans), not raw HTML. Must render via evaluator (~doc-code :code), not (raw! ...). Also replace JavaScript example with SX (no JS highlighter exists). Co-Authored-By: Claude Opus 4.6 --- sx/sx/async-io-demo.sx | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/sx/sx/async-io-demo.sx b/sx/sx/async-io-demo.sx index 919974e..f20b489 100644 --- a/sx/sx/async-io-demo.sx +++ b/sx/sx/async-io-demo.sx @@ -1,9 +1,13 @@ ;; Async IO demo — Phase 5 client-side rendering with IO primitives. ;; ;; This component calls `highlight` inline — an IO primitive that runs -;; server-side Python (pygments). When rendered on the server, it -;; executes synchronously. When rendered client-side, the async -;; renderer proxies the call via /sx/io/highlight and awaits the result. +;; server-side Python. When rendered on the server, it executes +;; synchronously. When rendered client-side, the async renderer proxies +;; the call via /sx/io/highlight and awaits the result. +;; +;; `highlight` returns SxExpr — SX source with colored spans — which the +;; evaluator renders as DOM. The same SxExpr flows through the IO proxy: +;; server serializes → client parses → async renderer renders to DOM. ;; ;; Open browser console and look for: ;; "sx:route client+async" — async render with IO proxy @@ -15,8 +19,9 @@ (h1 :class "text-2xl font-bold text-stone-900" "Async IO Demo") (p :class "mt-2 text-stone-600" "This page calls " (code :class "bg-stone-100 px-1 rounded text-violet-700" "highlight") - " inline — an IO primitive. On the server it runs Python (pygments). " - "On the client it proxies via " (code :class "bg-stone-100 px-1 rounded text-violet-700" "/sx/io/highlight") + " inline — an IO primitive that returns SX source with colored spans. " + "On the server it runs Python directly. On the client it proxies via " + (code :class "bg-stone-100 px-1 rounded text-violet-700" "/sx/io/highlight") " and the async renderer awaits the result.")) ;; Live syntax-highlighted code blocks — each is an IO call @@ -25,30 +30,30 @@ (div :class "rounded-lg border border-stone-200 bg-white p-5 space-y-3" (h3 :class "text-sm font-medium text-stone-500 uppercase tracking-wide" "SX component definition") - (div :class "rounded bg-stone-900 p-4 text-sm overflow-x-auto" - (raw! (highlight "(defcomp ~card (&key title subtitle &rest children)\n (div :class \"border rounded-lg p-4 shadow-sm\"\n (h2 :class \"text-lg font-bold\" title)\n (when subtitle\n (p :class \"text-stone-500 text-sm\" subtitle))\n (div :class \"mt-3\" children)))" "lisp")))) + (~doc-code :code + (highlight "(defcomp ~card (&key title subtitle &rest children)\n (div :class \"border rounded-lg p-4 shadow-sm\"\n (h2 :class \"text-lg font-bold\" title)\n (when subtitle\n (p :class \"text-stone-500 text-sm\" subtitle))\n (div :class \"mt-3\" children)))" "lisp"))) (div :class "rounded-lg border border-stone-200 bg-white p-5 space-y-3" (h3 :class "text-sm font-medium text-stone-500 uppercase tracking-wide" "Python server code") - (div :class "rounded bg-stone-900 p-4 text-sm overflow-x-auto" - (raw! (highlight "from shared.sx.pages import mount_io_endpoint\n\n# The IO proxy serves any allowed primitive:\n# GET /sx/io/highlight?_arg0=code&_arg1=lisp\nasync def io_proxy(name):\n result = await execute_io(name, args, kwargs, ctx)\n return serialize(result)" "python")))) + (~doc-code :code + (highlight "from shared.sx.pages import mount_io_endpoint\n\n# The IO proxy serves any allowed primitive:\n# GET /sx/io/highlight?_arg0=code&_arg1=lisp\nasync def io_proxy(name):\n result = await execute_io(name, args, kwargs, ctx)\n return serialize(result)" "python"))) (div :class "rounded-lg border border-stone-200 bg-white p-5 space-y-3" - (h3 :class "text-sm font-medium text-stone-500 uppercase tracking-wide" "JavaScript async renderer") - (div :class "rounded bg-stone-900 p-4 text-sm overflow-x-auto" - (raw! (highlight "// The async renderer intercepts IO primitive calls\nfunction asyncEval(expr, env) {\n if (IO_PRIMITIVES[head.name]) {\n return asyncEvalIoCall(name, args, env);\n }\n return asyncTrampoline(evalExpr(expr, env));\n}" "javascript"))))) + (h3 :class "text-sm font-medium text-stone-500 uppercase tracking-wide" "SX async rendering spec") + (~doc-code :code + (highlight ";; try-client-route dispatches on has-io flag\n(if has-io\n ;; Async render: IO primitives proxied via /sx/io/\n (do\n (try-async-eval-content content-src env\n (fn (rendered)\n (when rendered\n (swap-rendered-content target rendered pathname))))\n true)\n ;; Sync render: pure components, no IO\n (let ((rendered (try-eval-content content-src env)))\n (swap-rendered-content target rendered pathname)))" "lisp")))) ;; Architecture explanation (div :class "rounded-lg border border-blue-200 bg-blue-50 p-5 space-y-3" (h2 :class "text-lg font-semibold text-blue-900" "How it works") (ol :class "list-decimal list-inside text-blue-800 space-y-2 text-sm" - (li "Server renders the page — " (code "highlight") " runs Python pygments directly") - (li "Client receives page with component definitions including " (code "~async-io-demo-content")) + (li "Server renders the page — " (code "highlight") " runs Python directly") + (li "Client receives component definitions including " (code "~async-io-demo-content")) (li "On client navigation, " (code "has-io") " flag routes to async renderer") (li "Async renderer encounters " (code "(highlight ...)") " — checks " (code "IO_PRIMITIVES")) (li "Proxied call: " (code "fetch(\"/sx/io/highlight?_arg0=...&_arg1=lisp\")")) - (li "Server executes, returns SX wire format (quoted HTML string)") - (li "Async renderer inserts result via " (code "(raw! ...)") " — renders identically"))) + (li "Server runs highlight, returns SX source (colored span elements)") + (li "Client parses SX → AST, async renderer recursively renders to DOM"))) ;; Verification instructions (div :class "rounded-lg border border-amber-200 bg-amber-50 p-4 text-sm space-y-2"