Migrate ~2,500 lines of SX markup from Python string concatenation in essays.py to proper .sx defcomp definitions: - docs-content.sx: 8 defcomps for docs pages (intro, getting-started, components, evaluator, primitives, css, server-rendering, home) - protocols.sx: 6 defcomps for protocol documentation pages - essays.sx: 9 essay defcomps (pure content, no params) - examples.sx: template defcomp receiving data values, calls highlight internally — Python passes raw code strings, never SX - reference.sx: 6 defcomps for data-driven reference pages essays.py reduced from 2,699 to 619 lines. Docs/protocol/essay functions become one-liners returning component names. Example functions use sx_call to pass data values to the template. Reference functions pass data-built component trees via SxExpr. renders.py: removed _code, _example_code, _placeholder, _clear_components_btn (now handled by .sx templates). helpers.py: removed inline hero code building, uses ~sx-home-content. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
99 lines
7.4 KiB
Plaintext
99 lines
7.4 KiB
Plaintext
;; Protocol documentation pages — fully self-contained
|
|
|
|
(defcomp ~protocol-wire-format-content ()
|
|
(~doc-page :title "Wire Format"
|
|
(~doc-section :title "The text/sx content type" :id "content-type"
|
|
(p :class "text-stone-600"
|
|
"sx responses use content type text/sx. The body is s-expression source code. The client parses and evaluates it, then renders the result into the DOM.")
|
|
(~doc-code :code (highlight "HTTP/1.1 200 OK\nContent-Type: text/sx\nSX-Css-Hash: a1b2c3d4\n\n(div :class \"p-4\"\n (~card :title \"Hello\"))" "bash")))
|
|
(~doc-section :title "Request lifecycle" :id "lifecycle"
|
|
(p :class "text-stone-600"
|
|
"1. User interacts with an element that has sx-get/sx-post/etc.")
|
|
(p :class "text-stone-600"
|
|
"2. sx.js fires sx:beforeRequest, then sends the HTTP request with SX-Request: true header.")
|
|
(p :class "text-stone-600"
|
|
"3. Server builds s-expression tree, scans CSS classes, prepends missing component definitions.")
|
|
(p :class "text-stone-600"
|
|
"4. Client receives text/sx response, parses it, evaluates it, renders to DOM.")
|
|
(p :class "text-stone-600"
|
|
"5. sx.js fires sx:afterSwap and sx:afterSettle.")
|
|
(p :class "text-stone-600"
|
|
"6. Any sx-swap-oob elements are swapped into their targets elsewhere in the DOM."))
|
|
(~doc-section :title "Component definitions" :id "components"
|
|
(p :class "text-stone-600"
|
|
"On full page loads, component definitions are in <script type=\"text/sx\" data-components>. On subsequent sx requests, missing definitions are prepended to the response body. The client caches definitions in localStorage keyed by a content hash."))))
|
|
|
|
(defcomp ~protocol-fragments-content ()
|
|
(~doc-page :title "Cross-Service Fragments"
|
|
(~doc-section :title "Fragment protocol" :id "protocol"
|
|
(p :class "text-stone-600"
|
|
"Rose Ash runs as independent microservices. Each service can expose HTML or sx fragments that other services compose into their pages. Fragment endpoints return text/sx or text/html.")
|
|
(p :class "text-stone-600"
|
|
"The frag resolver is an I/O primitive in the render tree:")
|
|
(~doc-code :code (highlight "(frag \"blog\" \"link-card\" :slug \"hello\")" "lisp")))
|
|
(~doc-section :title "SxExpr wrapping" :id "wrapping"
|
|
(p :class "text-stone-600"
|
|
"When a fragment returns text/sx, the response is wrapped in an SxExpr and embedded directly in the render tree. When it returns text/html, it's wrapped in a ~rich-text component that inserts the HTML via raw!. This allows transparent composition across service boundaries."))
|
|
(~doc-section :title "fetch_fragments()" :id "fetch"
|
|
(p :class "text-stone-600"
|
|
"The Python helper fetch_fragments() fetches multiple fragments in parallel via asyncio.gather(). Fragments are cached in Redis with short TTLs. Each fragment request is HMAC-signed for authentication."))))
|
|
|
|
(defcomp ~protocol-resolver-io-content ()
|
|
(~doc-page :title "Resolver I/O"
|
|
(~doc-section :title "Async I/O primitives" :id "primitives"
|
|
(p :class "text-stone-600"
|
|
"The sx resolver identifies I/O nodes in the render tree, groups them, executes them in parallel via asyncio.gather(), and substitutes results back in.")
|
|
(p :class "text-stone-600"
|
|
"I/O primitives:")
|
|
(ul :class "space-y-2 text-stone-600"
|
|
(li (span :class "font-mono text-violet-700" "frag") " — fetch a cross-service fragment")
|
|
(li (span :class "font-mono text-violet-700" "query") " — read data from another service")
|
|
(li (span :class "font-mono text-violet-700" "action") " — execute a write on another service")
|
|
(li (span :class "font-mono text-violet-700" "current-user") " — resolve the current authenticated user")))
|
|
(~doc-section :title "Execution model" :id "execution"
|
|
(p :class "text-stone-600"
|
|
"The render tree is walked to find I/O nodes. All nodes at the same depth are gathered and executed in parallel. Results replace the I/O nodes in the tree. The walk continues until no more I/O nodes are found. This typically completes in 1-2 passes."))))
|
|
|
|
(defcomp ~protocol-internal-services-content ()
|
|
(~doc-page :title "Internal Services"
|
|
(~doc-note
|
|
(p "Honest note: the internal service protocol is JSON, not sx. Sx is the composition layer on top. The protocols below are the plumbing underneath."))
|
|
(~doc-section :title "HMAC-signed HTTP" :id "hmac"
|
|
(p :class "text-stone-600"
|
|
"Services communicate via HMAC-signed HTTP requests with short timeouts:")
|
|
(ul :class "space-y-2 text-stone-600 font-mono text-sm"
|
|
(li "GET /internal/data/{query} — read data (3s timeout)")
|
|
(li "POST /internal/actions/{action} — execute write (5s timeout)")
|
|
(li "POST /internal/inbox — ActivityPub-shaped event delivery")))
|
|
(~doc-section :title "fetch_data / call_action" :id "fetch"
|
|
(p :class "text-stone-600"
|
|
"Python helpers fetch_data() and call_action() handle HMAC signing, serialization, and error handling. They resolve service URLs from environment variables (INTERNAL_URL_BLOG, etc) and fall back to public URLs in development."))))
|
|
|
|
(defcomp ~protocol-activitypub-content ()
|
|
(~doc-page :title "ActivityPub"
|
|
(~doc-note
|
|
(p "Honest note: ActivityPub wire format is JSON-LD, not sx. This documents how AP integrates with the sx rendering layer."))
|
|
(~doc-section :title "AP activities" :id "activities"
|
|
(p :class "text-stone-600"
|
|
"Rose Ash services communicate cross-domain writes via ActivityPub-shaped activities. Each service has a virtual actor. Activities are JSON-LD objects sent to /internal/inbox endpoints. RSA signatures authenticate the sender."))
|
|
(~doc-section :title "Event bus" :id "bus"
|
|
(p :class "text-stone-600"
|
|
"The event bus dispatches activities to registered handlers. Handlers are async functions that process the activity and may trigger side effects. The bus runs as a background processor in each service."))))
|
|
|
|
(defcomp ~protocol-future-content ()
|
|
(~doc-page :title "Future Possibilities"
|
|
(~doc-note
|
|
(p "This page is speculative. Nothing here is implemented. It documents ideas that may or may not happen."))
|
|
(~doc-section :title "Custom protocol schemes" :id "schemes"
|
|
(p :class "text-stone-600"
|
|
"sx:// and sxs:// as custom URI schemes for content addressing or deep linking. An sx:// URI could resolve to an sx expression from a federated registry. This is technically feasible but practically unnecessary for a single-site deployment."))
|
|
(~doc-section :title "Sx as AP serialization" :id "ap-sx"
|
|
(p :class "text-stone-600"
|
|
"ActivityPub objects could be serialized as s-expressions instead of JSON-LD. S-expressions are more compact and easier to parse. The practical barrier: the entire AP ecosystem expects JSON-LD."))
|
|
(~doc-section :title "Sx-native federation" :id "federation"
|
|
(p :class "text-stone-600"
|
|
"Federated services could exchange sx fragments directly — render a remote user's profile card by fetching its sx source from their server. This requires trust and standardization that doesn't exist yet."))
|
|
(~doc-section :title "Realistic assessment" :id "realistic"
|
|
(p :class "text-stone-600"
|
|
"The most likely near-term improvement is sx:// deep linking for client-side component resolution. Everything else requires ecosystem adoption that one project can't drive alone."))))
|