Nav redesign: embedded breadcrumb navigation with recursive depth
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m25s

Replace menu bar navigation with in-page nav embedded in content area.
Each page shows logo/tagline/copyright, then a sibling row per trail
level (← prev | Current | next →), then children as button links.

- resolve-nav-path: recursive walk with no depth limit
- find-nav-index: rewritten with recursion (set! broken across closures)
- Walk stops on exact href match (prevents /cssx/ drilling into Overview)
- Unicode chars (©, ←, →) inline instead of \u escapes (SX parser doesn't support them)
- All 38 defpages wrapped in (~sx-doc :path ...) for in-page nav
- Layout returns only root header (nav moved out of blue menu bar)
- Standalone layout variants for sx-web.org (return nil)
- New plans: environment-images, runtime-slicing, typed-sx, nav-redesign, sx-web-platform

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 14:37:37 +00:00
parent cad65bcdf1
commit ec1093d372
10 changed files with 1887 additions and 696 deletions

View File

@@ -169,6 +169,14 @@
:summary "Prefetch missing component definitions before the user clicks — hover a link, fetch its deps, navigate client-side.")
(dict :label "Content-Addressed Components" :href "/plans/content-addressed-components"
:summary "Components identified by CID, stored on IPFS, fetched from anywhere. Canonical serialization, content verification, federated sharing.")
(dict :label "Environment Images" :href "/plans/environment-images"
:summary "Serialize evaluated environments as content-addressed images. Spec CID → image CID → every endpoint is fully executable and verifiable.")
(dict :label "Runtime Slicing" :href "/plans/runtime-slicing"
:summary "Tier the client runtime by need: L0 hypermedia (~5KB), L1 DOM ops (~8KB), L2 islands (~15KB), L3 full eval (~44KB). Sliced by slice.sx, translated by js.sx.")
(dict :label "Typed SX" :href "/plans/typed-sx"
:summary "Gradual type system for SX. Optional annotations, checked at registration time, zero runtime cost. types.sx — specced, bootstrapped, catches composition errors.")
(dict :label "Nav Redesign" :href "/plans/nav-redesign"
:summary "Replace menu bars with vertical breadcrumb navigation. Logo → section → page, arrows for siblings, children below. No dropdowns, no hamburger, infinite depth.")
(dict :label "Fragment Protocol" :href "/plans/fragment-protocol"
:summary "Structured sexp request/response for cross-service component transfer.")
(dict :label "Glue Decoupling" :href "/plans/glue-decoupling"
@@ -178,7 +186,9 @@
(dict :label "SX CI Pipeline" :href "/plans/sx-ci"
:summary "Build, test, and deploy in s-expressions — CI pipelines as SX components.")
(dict :label "Live Streaming" :href "/plans/live-streaming"
:summary "SSE and WebSocket transports for re-resolving suspense slots after initial page load — live data, real-time collaboration.")))
:summary "SSE and WebSocket transports for re-resolving suspense slots after initial page load — live data, real-time collaboration.")
(dict :label "sx-web Platform" :href "/plans/sx-web-platform"
:summary "sx-web.org as online development platform — embedded Claude Code, IPFS storage, sx-activity publishing, sx-ci testing. Author, stage, test, deploy from the browser.")))
(define reactive-islands-nav-items (list
(dict :label "Overview" :href "/reactive-islands/"
@@ -281,6 +291,7 @@
;; Generic section nav — builds nav links from a list of items.
;; Replaces _nav_items_sx() and all section-specific nav builders in utils.py.
;; KEPT for backward compat with other apps — SX docs uses ~nav-list instead.
(defcomp ~section-nav (&key items current)
(<> (map (fn (item)
(~nav-link
@@ -289,3 +300,88 @@
:is-selected (when (= (get item "label") current) "true")
:select-colours "aria-selected:bg-violet-200 aria-selected:text-violet-900"))
items)))
;; ---------------------------------------------------------------------------
;; Nav tree — hierarchical navigation for SX docs
;; ---------------------------------------------------------------------------
(define sx-nav-tree
{:label "sx" :href "/"
:children (list
{:label "Docs" :href "/docs/" :children docs-nav-items}
{:label "CSSX" :href "/cssx/" :children cssx-nav-items}
{:label "Reference" :href "/reference/" :children reference-nav-items}
{:label "Protocols" :href "/protocols/" :children protocols-nav-items}
{:label "Examples" :href "/examples/" :children examples-nav-items}
{:label "Essays" :href "/essays/" :children essays-nav-items}
{:label "Philosophy" :href "/philosophy/" :children philosophy-nav-items}
{:label "Specs" :href "/specs/" :children specs-nav-items}
{:label "Bootstrappers" :href "/bootstrappers/" :children bootstrappers-nav-items}
{:label "Testing" :href "/testing/" :children testing-nav-items}
{:label "Isomorphism" :href "/isomorphism/" :children isomorphism-nav-items}
{:label "Plans" :href "/plans/" :children plans-nav-items}
{:label "Reactive Islands" :href "/reactive-islands/" :children reactive-islands-nav-items})})
;; Resolve a URL path to a nav trail + children.
;; Returns {:trail [{:node N :siblings S} ...] :children [...] :depth N}
;; Trail is from outermost selected ancestor to deepest.
(define resolve-nav-path
(fn (tree path)
(let ((trail (list)))
(define walk
(fn (node)
(let ((children (get node "children")))
(when children
(let ((match (find-nav-match children path)))
(when match
(append! trail {:node match :siblings children})
;; Only recurse deeper if this wasn't an exact match
;; (exact match = we found our target, stop)
(when (not (= (get match "href") path))
(walk match))))))))
(walk tree)
(let ((depth (len trail)))
(if (= depth 0)
{:trail trail :children (get tree "children") :depth 0}
(let ((deepest (nth trail (- depth 1))))
{:trail trail
:children (get (get deepest "node") "children")
:depth depth}))))))
;; Find a nav item whose href matches the given path (or path prefix).
(define find-nav-match
(fn (items path)
;; Exact match first
(or (some (fn (item)
(when (= (get item "href") path) item))
items)
;; Prefix match: path starts with item href (for /plans/typed-sx matching /plans/)
(some (fn (item)
(let ((href (get item "href")))
(when (and (ends-with? href "/")
(starts-with? path href))
item)))
items)
;; Path contains section: /plans/typed-sx matches section with /plans/ children
(some (fn (item)
(let ((children (get item "children")))
(when children
(when (some (fn (child)
(= (get child "href") path))
children)
item))))
items))))
;; Find the index of a nav item in a list by matching href.
(define find-nav-index
(fn (items node)
(let ((target-href (get node "href"))
(count (len items)))
(define find-loop
(fn (i)
(if (>= i count)
0
(if (= (get (nth items i) "href") target-href)
i
(find-loop (+ i 1))))))
(find-loop 0))))