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>
258 lines
29 KiB
Plaintext
258 lines
29 KiB
Plaintext
;; ---------------------------------------------------------------------------
|
|
;; Predictive Component Prefetching
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~plans/predictive-prefetch/plan-predictive-prefetch-content ()
|
|
(~docs/page :title "Predictive Component Prefetching"
|
|
|
|
(~docs/section :title "Context" :id "context"
|
|
(p "Phase 3 of the isomorphic roadmap added client-side routing with component dependency checking. When a user clicks a link, " (code "try-client-route") " checks " (code "has-all-deps?") " — if the target page needs components not yet loaded, the client falls back to a server fetch. This works correctly but misses an opportunity: " (strong "we can prefetch those missing components before the click happens."))
|
|
(p "The page registry already carries " (code ":deps") " metadata for every page. The client already knows which components are loaded via " (code "loaded-component-names") ". The gap is a mechanism to " (em "proactively") " resolve the difference — fetching missing component definitions so that by the time the user clicks, client-side routing succeeds.")
|
|
(p "But this goes beyond just hover-to-prefetch. The full spectrum includes: bundling linked routes' components with the initial page load, batch-prefetching after idle, predicting mouse trajectory toward links, and even splitting the component/data fetch so that " (code ":data") " pages can prefetch their components and only fetch data on click. Each strategy trades bandwidth for latency, and pages should be able to declare which tradeoff they want."))
|
|
|
|
(~docs/section :title "Current State" :id "current-state"
|
|
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
|
(table :class "w-full text-left text-sm"
|
|
(thead (tr :class "border-b border-stone-200 bg-stone-100"
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Layer")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "What exists")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Where")))
|
|
(tbody
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Page registry")
|
|
(td :class "px-3 py-2 text-stone-700" "Each page carries " (code ":deps (\"~card\" \"~essay-foo\" ...)"))
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "helpers.py → <script type=\"text/sx-pages\">"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Dep check")
|
|
(td :class "px-3 py-2 text-stone-700" (code "has-all-deps?") " gates client routing")
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "orchestration.sx:546-559"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Component bundle")
|
|
(td :class "px-3 py-2 text-stone-700" "Per-page inline " (code "<script type=\"text/sx\" data-components>"))
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "helpers.py:715, jinja_bridge.py"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Incremental defs")
|
|
(td :class "px-3 py-2 text-stone-700" (code "components_for_request()") " sends only missing defs in SX responses")
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "helpers.py:459-509"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Preload cache")
|
|
(td :class "px-3 py-2 text-stone-700" (code "sx-preload") " prefetches full responses on hover/mousedown")
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "orchestration.sx:686-708"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 text-stone-700" "Route matching")
|
|
(td :class "px-3 py-2 text-stone-700" (code "find-matching-route") " matches pathname to page entry")
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "router.sx"))))))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Prefetch strategies
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Prefetch Strategies" :id "strategies"
|
|
(p "Prefetching is a spectrum from conservative to aggressive. The system should support all of these, configured declaratively per link or per page via " (code "defpage") " metadata and " (code "sx-prefetch") " attributes.")
|
|
|
|
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
|
(table :class "w-full text-left text-sm"
|
|
(thead (tr :class "border-b border-stone-200 bg-stone-100"
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Strategy")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Trigger")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "What prefetches")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Latency on click")))
|
|
(tbody
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-semibold text-stone-800" "Eager bundle")
|
|
(td :class "px-3 py-2 text-stone-700" "Initial page load")
|
|
(td :class "px-3 py-2 text-stone-700" "Components for linked routes included in " (code "<script data-components>"))
|
|
(td :class "px-3 py-2 text-stone-600" "Zero — already in memory"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-semibold text-stone-800" "Idle timer")
|
|
(td :class "px-3 py-2 text-stone-700" "After page settles (requestIdleCallback or setTimeout)")
|
|
(td :class "px-3 py-2 text-stone-700" "Components for visible nav links, batched in one request")
|
|
(td :class "px-3 py-2 text-stone-600" "Zero if idle fetch completed"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-semibold text-stone-800" "Viewport")
|
|
(td :class "px-3 py-2 text-stone-700" "Link scrolls into view (IntersectionObserver)")
|
|
(td :class "px-3 py-2 text-stone-700" "Components for that link's route")
|
|
(td :class "px-3 py-2 text-stone-600" "Zero if user scrolled before clicking"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-semibold text-stone-800" "Mouse approach")
|
|
(td :class "px-3 py-2 text-stone-700" "Cursor moving toward link (trajectory prediction)")
|
|
(td :class "px-3 py-2 text-stone-700" "Components for predicted target")
|
|
(td :class "px-3 py-2 text-stone-600" "Near-zero — fetch starts ~200ms before hover"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-semibold text-stone-800" "Hover")
|
|
(td :class "px-3 py-2 text-stone-700" "mouseover (150ms debounce)")
|
|
(td :class "px-3 py-2 text-stone-700" "Components for hovered link's route")
|
|
(td :class "px-3 py-2 text-stone-600" "Low — typical hover-to-click is 300-500ms"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-semibold text-stone-800" "Mousedown")
|
|
(td :class "px-3 py-2 text-stone-700" "mousedown (0ms debounce)")
|
|
(td :class "px-3 py-2 text-stone-700" "Components for clicked link's route")
|
|
(td :class "px-3 py-2 text-stone-600" "~80ms — mousedown-to-click gap"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-semibold text-stone-800" "Components + data")
|
|
(td :class "px-3 py-2 text-stone-700" "Any of the above")
|
|
(td :class "px-3 py-2 text-stone-700" "Components " (em "and") " page data for " (code ":data") " pages")
|
|
(td :class "px-3 py-2 text-stone-600" "Zero for components; data fetch may still be in flight")))))
|
|
|
|
(~docs/subsection :title "Eager Bundle"
|
|
(p "The server already computes per-page component bundles. For key navigation paths — the main nav bar, section nav — the server can include " (em "linked routes' components") " in the initial bundle, not just the current page's.")
|
|
(~docs/code :src (highlight ";; defpage metadata declares eager prefetch targets\n(defpage docs-page\n :path \"/language/docs/<slug>\"\n :auth :public\n :prefetch :eager ;; bundle deps for all linked pure routes\n :content (case slug ...))" "lisp"))
|
|
(p "Implementation: " (code "components_for_page()") " already scans the page SX for component refs. Extend it to also scan for " (code "href") " attributes, match them against the page registry, and include those pages' deps in the bundle. The cost is a larger initial payload; the benefit is zero-latency navigation within a section."))
|
|
|
|
(~docs/subsection :title "Idle Timer"
|
|
(p "After page load and initial render, use " (code "requestIdleCallback") " (or a fallback " (code "setTimeout") ") to scan visible nav links and batch-prefetch their missing components in a single request.")
|
|
(~docs/code :src (highlight "(define prefetch-visible-links-on-idle\n (fn ()\n (request-idle-callback\n (fn ()\n (let ((links (dom-query-all \"a[href][sx-get]\"))\n (all-missing (list)))\n (for-each\n (fn (link)\n (let ((missing (compute-missing-deps\n (url-pathname (dom-get-attr link \"href\")))))\n (when missing\n (for-each (fn (d) (append! all-missing d))\n missing))))\n links)\n (when (not (empty? all-missing))\n (prefetch-components (dedupe all-missing))))))))" "lisp"))
|
|
(p "Called once from " (code "boot-init") " after initial processing. Batches all missing deps into one network request. Low priority — browser handles it when idle."))
|
|
|
|
(~docs/subsection :title "Mouse Approach (Trajectory Prediction)"
|
|
(p "Don't wait for the cursor to reach the link — predict where it's heading. Track the last few " (code "mousemove") " events, extrapolate the trajectory, and if it points toward a link, start prefetching before the hover event fires.")
|
|
(~docs/code :src (highlight "(define bind-approach-prefetch\n (fn (container)\n ;; Track mouse trajectory within a nav container.\n ;; On each mousemove, extrapolate position ~200ms ahead.\n ;; If projected point intersects a link's bounding box,\n ;; prefetch that link's route deps.\n (let ((last-x 0) (last-y 0) (last-t 0)\n (prefetched (dict)))\n (dom-add-listener container \"mousemove\"\n (fn (e)\n (let ((now (timestamp))\n (dt (- now last-t)))\n (when (> dt 16) ;; ~60fps throttle\n (let ((vx (/ (- (event-x e) last-x) dt))\n (vy (/ (- (event-y e) last-y) dt))\n (px (+ (event-x e) (* vx 200)))\n (py (+ (event-y e) (* vy 200)))\n (target (dom-element-at-point px py)))\n (when (and target (dom-has-attr? target \"href\")\n (not (get prefetched\n (dom-get-attr target \"href\"))))\n (let ((href (dom-get-attr target \"href\")))\n (set! prefetched\n (merge prefetched {href true}))\n (prefetch-route-deps\n (url-pathname href)))))\n (set! last-x (event-x e))\n (set! last-y (event-y e))\n (set! last-t now))))))))" "lisp"))
|
|
(p "This is the most speculative strategy — best suited for dense navigation areas (section sidebars, nav bars) where the cursor trajectory is a strong predictor. The " (code "prefetched") " dict prevents duplicate fetches within the same container interaction."))
|
|
|
|
(~docs/subsection :title "Components + Data (Hybrid Prefetch)"
|
|
(p "The most interesting strategy. For pages with " (code ":data") " dependencies, current behavior is full server fallback. But the page's " (em "components") " are still pure and prefetchable. If we prefetch components ahead of time, the click only needs to fetch " (em "data") " — a much smaller, faster response.")
|
|
(p "This creates a new rendering path:")
|
|
(ol :class "list-decimal pl-5 text-stone-700 space-y-1"
|
|
(li "Prefetch: hover/idle/viewport triggers " (code "prefetch-components") " for the target page")
|
|
(li "Click: client has components, but page has " (code ":data") " — fetch data from server")
|
|
(li "Server returns " (em "only data") " (JSON or SX bindings), not the full rendered page")
|
|
(li "Client evaluates the content expression with prefetched components + fetched data")
|
|
(li "Result: faster than full server render, no redundant component transfer"))
|
|
(~docs/code :src (highlight ";; Declarative: prefetch components, fetch data on click\n(defpage reference-page\n :path \"/reference/<slug>\"\n :auth :public\n :prefetch :components ;; prefetch components, data stays server-fetched\n :data (reference-data slug)\n :content (~reference/attrs-content :attrs attrs))\n\n;; On click, client-side flow:\n;; 1. Components already prefetched (from hover/idle)\n;; 2. GET /reference/attributes → server returns data bindings\n;; 3. Client evals (reference-data slug) result + content expr\n;; 4. Renders locally with cached components" "lisp"))
|
|
(p "This is a stepping stone toward full Phase 4 (client IO bridge) of the isomorphic roadmap — it achieves partial client rendering for data pages without needing a general-purpose client async evaluator. The server is a data service, the client is the renderer."))
|
|
|
|
(~docs/subsection :title "Declarative Configuration"
|
|
(p "All strategies configured via " (code "defpage") " metadata and " (code "sx-prefetch") " attributes on links/containers:")
|
|
(~docs/code :src (highlight ";; Page-level: what to prefetch for routes linking TO this page\n(defpage docs-page\n :path \"/language/docs/<slug>\"\n :prefetch :eager) ;; bundle with linking page\n\n(defpage reference-page\n :path \"/reference/<slug>\"\n :prefetch :components) ;; prefetch components, data on click\n\n;; Link-level: override per-link\n(a :href \"/language/docs/components\"\n :sx-prefetch \"idle\") ;; prefetch after page idle\n\n;; Container-level: approach prediction for nav areas\n(nav :sx-prefetch \"approach\"\n (a :href \"/language/docs/\") (a :href \"/reference/\") ...)" "lisp"))
|
|
(p "Priority cascade: explicit " (code "sx-prefetch") " on link > " (code ":prefetch") " on target defpage > default (hover). The system never prefetches the same components twice — " (code "_prefetch-pending") " and " (code "loaded-component-names") " handle dedup.")))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Design
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Implementation Design" :id "design"
|
|
|
|
(p "Per the SX host architecture principle: all SX-specific logic goes in " (code ".sx") " spec files and gets bootstrapped. The prefetch logic — scanning links, computing missing deps, managing the component cache — must be specced in " (code ".sx") ", not written directly in JS or Python.")
|
|
|
|
(~docs/subsection :title "Phase 1: Component Fetch Endpoint (Python)"
|
|
(p "A new " (strong "public") " endpoint (not " (code "/internal/") " — the client's browser calls it) that returns component definitions by name.")
|
|
(~docs/code :src (highlight "GET /<service-prefix>/sx/components?names=~plans/predictive-prefetch/card,~essay-foo\n\nResponse (text/sx):\n(defcomp ~plans/predictive-prefetch/card (&key title &rest children)\n (div :class \"border rounded p-4\" (h2 title) children))\n(defcomp ~plans/predictive-prefetch/essay-foo (&key id)\n (div (~plans/predictive-prefetch/card :title id)))" "http"))
|
|
(p "The server resolves transitive deps via " (code "deps.py") ", subtracts anything listed in the " (code "SX-Components") " request header (already loaded), serializes and returns. This is essentially " (code "components_for_request()") " driven by an explicit " (code "?names=") " param.")
|
|
(p "Cache-friendly: the response is a pure function of component hash + requested names. " (code "Cache-Control: public, max-age=3600") " with the component hash as ETag."))
|
|
|
|
(~docs/subsection :title "Phase 2: Client Prefetch Logic (SX spec)"
|
|
(p "New functions in " (code "orchestration.sx") " (or a new " (code "prefetch.sx") " if scope warrants):")
|
|
|
|
(div :class "space-y-4"
|
|
(div
|
|
(h4 :class "font-semibold text-stone-700" "1. compute-missing-deps")
|
|
(p "Given a pathname, find the page, return dep names not in " (code "loaded-component-names") ". Returns nil if page not found or has data (can't client-route anyway).")
|
|
(~docs/code :src (highlight "(define compute-missing-deps\n (fn (pathname)\n (let ((match (find-matching-route pathname _page-routes)))\n (when (and match (not (get match \"has-data\")))\n (let ((deps (or (get match \"deps\") (list)))\n (loaded (loaded-component-names)))\n (filter (fn (d) (not (contains? loaded d))) deps))))))" "lisp")))
|
|
|
|
(div
|
|
(h4 :class "font-semibold text-stone-700" "2. prefetch-components")
|
|
(p "Fetch component definitions from the server for a list of names. Deduplicates in-flight requests. On success, parses and registers the returned definitions into the component env.")
|
|
(~docs/code :src (highlight "(define _prefetch-pending (dict))\n\n(define prefetch-components\n (fn (names)\n (let ((key (join \",\" (sort names))))\n (when (not (get _prefetch-pending key))\n (set! _prefetch-pending\n (merge _prefetch-pending {key true}))\n (fetch-components-from-server names\n (fn (sx-text)\n (sx-process-component-text sx-text)\n (dict-remove! _prefetch-pending key)))))))" "lisp")))
|
|
|
|
(div
|
|
(h4 :class "font-semibold text-stone-700" "3. prefetch-route-deps")
|
|
(p "High-level composition: compute missing deps for a route, fetch if any.")
|
|
(~docs/code :src (highlight "(define prefetch-route-deps\n (fn (pathname)\n (let ((missing (compute-missing-deps pathname)))\n (when (and missing (not (empty? missing)))\n (log-info (str \"sx:prefetch \"\n (len missing) \" components for \" pathname))\n (prefetch-components missing)))))" "lisp")))
|
|
|
|
(div
|
|
(h4 :class "font-semibold text-stone-700" "4. Trigger: link hover")
|
|
(p "On mouseover of a boosted link, prefetch its route's missing components. Debounced 150ms to avoid fetching on quick mouse-throughs.")
|
|
(~docs/code :src (highlight "(define bind-prefetch-on-hover\n (fn (link)\n (let ((timer nil))\n (dom-add-listener link \"mouseover\"\n (fn (e)\n (clear-timeout timer)\n (set! timer (set-timeout\n (fn () (prefetch-route-deps\n (url-pathname (dom-get-attr link \"href\"))))\n 150))))\n (dom-add-listener link \"mouseout\"\n (fn (e) (clear-timeout timer))))))" "lisp")))
|
|
|
|
(div
|
|
(h4 :class "font-semibold text-stone-700" "5. Trigger: viewport intersection (opt-in)")
|
|
(p "More aggressive strategy: when a link scrolls into view, prefetch its route's deps. Opt-in via " (code "sx-prefetch=\"visible\"") " attribute.")
|
|
(~docs/code :src (highlight "(define bind-prefetch-on-visible\n (fn (link)\n (observe-intersection link\n (fn () (prefetch-route-deps\n (url-pathname (dom-get-attr link \"href\"))))\n true 0)))" "lisp")))
|
|
|
|
(div
|
|
(h4 :class "font-semibold text-stone-700" "6. Integration into process-elements")
|
|
(p "During the existing hydration pass, for each boosted link:")
|
|
(~docs/code :src (highlight ";; In process-elements, after binding boost behavior:\n(when (and (should-boost-link? link)\n (dom-get-attr link \"href\"))\n (bind-prefetch-on-hover link))\n\n;; Explicit viewport prefetch:\n(when (dom-has-attr? link \"sx-prefetch\")\n (bind-prefetch-on-visible link))" "lisp")))))
|
|
|
|
(~docs/subsection :title "Phase 3: Boundary Declaration"
|
|
(p "Two new IO primitives in " (code "boundary.sx") " (browser-only):")
|
|
(~docs/code :src (highlight ";; IO primitives (browser-only)\n(io fetch-components-from-server (names callback) -> void)\n(io sx-process-component-text (sx-text) -> void)" "lisp"))
|
|
(p "These are thin wrappers around " (code "fetch()") " + the existing component script processing logic already in the boundary adapter."))
|
|
|
|
(~docs/subsection :title "Phase 4: Bootstrap"
|
|
(p (code "bootstrap_js.py") " picks up the new functions from the spec and emits them into " (code "sx-browser.js") ". The two new boundary IO functions get implemented in the JS boundary adapter — the hand-written glue code that the bootstrapper doesn't generate.")
|
|
(~docs/code :src (highlight "// fetch-components-from-server: calls the endpoint\nfunction fetchComponentsFromServer(names, callback) {\n const url = `${routePrefix}/sx/components?names=${names.join(\",\")}`;\n const headers = {\n \"SX-Components\": loadedComponentNames().join(\",\")\n };\n fetch(url, { headers })\n .then(r => r.ok ? r.text() : \"\")\n .then(text => callback(text))\n .catch(() => {}); // silent fail — prefetch is best-effort\n}\n\n// sx-process-component-text: parse defcomp/defmacro into env\nfunction sxProcessComponentText(sxText) {\n if (!sxText) return;\n const frag = document.createElement(\"div\");\n frag.innerHTML =\n `<script type=\"text/sx\" data-components>${sxText}<\\/script>`;\n Sx.processScripts(frag);\n}" "javascript"))))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Request flow
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Request Flow" :id "request-flow"
|
|
(p "End-to-end example: user hovers a link, components prefetch, click goes client-side.")
|
|
(~docs/code :src (highlight "User hovers link \"/language/docs/sx-manifesto\"\n |\n +-- bind-prefetch-on-hover fires (150ms debounce)\n |\n +-- compute-missing-deps(\"/language/docs/sx-manifesto\")\n | +-- find-matching-route -> page with deps:\n | | [\"~essay-sx-manifesto\", \"~doc-code\"]\n | +-- loaded-component-names -> [\"~nav\", \"~footer\", \"~doc-code\"]\n | +-- missing: [\"~essay-sx-manifesto\"]\n |\n +-- prefetch-components([\"~essay-sx-manifesto\"])\n | +-- GET /sx/components?names=~essay-sx-manifesto\n | | Headers: SX-Components: ~plans/environment-images/nav,~footer,~doc-code\n | +-- Server resolves transitive deps\n | | (also needs ~plans/predictive-prefetch/rich-text, subtracts already-loaded)\n | +-- Response:\n | (defcomp ~plans/predictive-prefetch/essay-sx-manifesto ...) \n | (defcomp ~plans/predictive-prefetch/rich-text ...)\n |\n +-- sx-process-component-text registers defcomps in env\n |\n +-- User clicks link\n +-- try-client-route(\"/language/docs/sx-manifesto\")\n +-- has-all-deps? -> true (prefetched!)\n +-- eval content -> DOM\n +-- Client-side render, no server roundtrip" "text")))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; File changes
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "File Changes" :id "file-changes"
|
|
(div :class "overflow-x-auto rounded border border-stone-200"
|
|
(table :class "w-full text-left text-sm"
|
|
(thead (tr :class "border-b border-stone-200 bg-stone-100"
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "File")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Change")
|
|
(th :class "px-3 py-2 font-medium text-stone-600" "Phase")))
|
|
(tbody
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/sx/helpers.py")
|
|
(td :class "px-3 py-2 text-stone-700" "New " (code "sx_components_endpoint()") " route handler")
|
|
(td :class "px-3 py-2 text-stone-600" "1"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/infrastructure/factory.py")
|
|
(td :class "px-3 py-2 text-stone-700" "Register " (code "/sx/components") " route on all SX apps")
|
|
(td :class "px-3 py-2 text-stone-600" "1"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/sx/ref/orchestration.sx")
|
|
(td :class "px-3 py-2 text-stone-700" "Prefetch functions: compute-missing-deps, prefetch-components, prefetch-route-deps, bind-prefetch-on-hover, bind-prefetch-on-visible")
|
|
(td :class "px-3 py-2 text-stone-600" "2"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/sx/ref/boundary.sx")
|
|
(td :class "px-3 py-2 text-stone-700" "Declare " (code "fetch-components-from-server") ", " (code "sx-process-component-text"))
|
|
(td :class "px-3 py-2 text-stone-600" "3"))
|
|
(tr :class "border-b border-stone-100"
|
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/sx/ref/bootstrap_js.py")
|
|
(td :class "px-3 py-2 text-stone-700" "Emit new spec functions, boundary adapter stubs")
|
|
(td :class "px-3 py-2 text-stone-600" "4"))))))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Non-goals & rollout
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Non-Goals (This Phase)" :id "non-goals"
|
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
|
(li (strong "Analytics-driven prediction") " — no ML models or click-frequency heuristics. Trajectory prediction uses geometry, not statistics.")
|
|
(li (strong "Cross-service prefetch") " — components are per-service. A link to a different service domain is always a server navigation.")
|
|
(li (strong "Service worker caching") " — could layer on later, but basic fetch + in-memory registration is sufficient.")
|
|
(li (strong "Full client-side data evaluation") " — the components+data strategy fetches data from the server, it doesn't replicate server IO on the client. That's Phase 4 of the isomorphic roadmap.")))
|
|
|
|
(~docs/section :title "Rollout" :id "rollout"
|
|
(p "Incremental, each step independently valuable:")
|
|
(ol :class "list-decimal pl-5 text-stone-700 space-y-2"
|
|
(li (strong "Component endpoint") " — purely additive. Refactor " (code "components_for_request()") " to accept explicit " (code "?names=") " param.")
|
|
(li (strong "Core spec functions") " — " (code "compute-missing-deps") ", " (code "prefetch-components") ", " (code "prefetch-route-deps") " in orchestration.sx. Testable in isolation.")
|
|
(li (strong "Hover prefetch") " — wire " (code "bind-prefetch-on-hover") " into " (code "process-elements") ". All boosted links get it automatically. Console logs show activity.")
|
|
(li (strong "Idle batch prefetch") " — call " (code "prefetch-visible-links-on-idle") " from " (code "boot-init") ". One request prefetches all visible nav deps after page settles.")
|
|
(li (strong "Viewport + approach") " — opt-in via " (code "sx-prefetch") " attributes. Trajectory prediction for dense nav areas.")
|
|
(li (strong "Eager bundles") " — extend " (code "components_for_page()") " to include linked routes' deps. Heavier initial payload, zero-latency nav.")
|
|
(li (strong "Components + data split") " — new server response mode returning data bindings only. Client renders with prefetched components. Bridges toward Phase 4.")))
|
|
|
|
(~docs/section :title "Relationship to Isomorphic Roadmap" :id "relationship"
|
|
(p "This plan sits between Phase 3 (client-side routing) and Phase 4 (client async & IO bridge) of the "
|
|
(a :href "/sx/(etc.(plan.isomorphic-architecture))" :class "text-violet-700 underline" "isomorphic architecture roadmap")
|
|
". It extends Phase 3 by making more navigations go client-side without needing any IO bridge — purely by ensuring component definitions are available before they're needed.")
|
|
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
|
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "Phase 3 (client-side routing with deps checking). No dependency on Phase 4.")))))
|
|
;; ---------------------------------------------------------------------------
|
|
;; Plan Status Overview
|
|
;; ---------------------------------------------------------------------------
|