Restructure reactive islands: remove Overview link, Demo → Examples, add event bridge demo
- Remove "Overview" nav link (index.sx IS the summary) - Rename "Demo" → "Examples" in nav and page title - Remove "Plan" and "Phase 2" from nav (all items done — status table remains in overview) - Add "Marshes" to nav (was missing, content already existed) - Add live event bridge demo island (data-sx-emit → signal via on-event) - Add event bridge section (#14) to examples page - Keep "demo" route as alias for backward compat Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -253,18 +253,14 @@
|
||||
:summary "SX as its own compiler. OCaml as substrate (closest to CEK), Koka as alternative (compile-time linearity), ultimately self-hosting. One language, every target.")))
|
||||
|
||||
(define reactive-islands-nav-items (list
|
||||
(dict :label "Overview" :href "/sx/(geography.(reactive))"
|
||||
:summary "Architecture, four levels (L0-L3), and current implementation status.")
|
||||
(dict :label "Demo" :href "/sx/(geography.(reactive.demo))"
|
||||
:summary "Live demonstration of signals, computed, effects, batch, and defisland — all transpiled from spec.")
|
||||
(dict :label "Examples" :href "/sx/(geography.(reactive.examples))"
|
||||
:summary "Live interactive islands — counter, temperature, stopwatch, lists, input binding, portals, error boundaries, stores, event bridge.")
|
||||
(dict :label "Event Bridge" :href "/sx/(geography.(reactive.event-bridge))"
|
||||
:summary "DOM events for htmx lake → island communication. Server-rendered buttons dispatch custom events that island effects listen for.")
|
||||
(dict :label "Named Stores" :href "/sx/(geography.(reactive.named-stores))"
|
||||
:summary "Page-level signal containers via def-store/use-store — persist across island destruction/recreation.")
|
||||
(dict :label "Plan" :href "/sx/(geography.(reactive.plan))"
|
||||
:summary "The full design document — rendering boundary, state flow, signal primitives, island lifecycle.")
|
||||
(dict :label "Phase 2" :href "/sx/(geography.(reactive.phase2))"
|
||||
:summary "Input binding, keyed lists, reactive class/style, refs, portals, error boundaries, suspense, transitions.")))
|
||||
(dict :label "Marshes" :href "/sx/(geography.(reactive.marshes))"
|
||||
:summary "Where reactivity and hypermedia interpenetrate — server writes to signals, reactive transforms, client state modifies hypermedia.")))
|
||||
|
||||
(define bootstrappers-nav-items (list
|
||||
(dict :label "Overview" :href "/sx/(language.(bootstrapper))")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Demo page — shows what's been implemented
|
||||
;; Examples page — live interactive islands, one per section
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~reactive-islands/demo/reactive-islands-demo-content ()
|
||||
(~docs/page :title "Reactive Islands Demo"
|
||||
(~docs/page :title "Reactive Islands — Examples"
|
||||
|
||||
(~docs/section :title "What this demonstrates" :id "what"
|
||||
(p (strong "These are live interactive islands") " — not static code snippets. Click the buttons. The signal runtime is defined in " (code "signals.sx") " (374 lines of s-expressions), then bootstrapped to JavaScript by " (code "bootstrap_js.py") ". No hand-written signal logic in JavaScript.")
|
||||
(p "The transpiled " (code "sx-browser.js") " registers " (code "signal") ", " (code "deref") ", " (code "reset!") ", " (code "swap!") ", " (code "computed") ", " (code "effect") ", and " (code "batch") " as SX primitives — callable from " (code "defisland") " bodies defined in " (code ".sx") " files."))
|
||||
(~docs/section :title "Live interactive islands" :id "intro"
|
||||
(p (strong "Every example below is a live interactive island") " — not a static code snippet. Click the buttons, type in the inputs. The signal runtime is defined in " (code "signals.sx") ", bootstrapped to JavaScript. No hand-written signal logic.")
|
||||
(p "Each island uses " (code "defisland") " with signals (" (code "signal") ", " (code "deref") ", " (code "reset!") ", " (code "swap!") "), derived values (" (code "computed") "), side effects (" (code "effect") "), and batch updates (" (code "batch") ")."))
|
||||
|
||||
(~docs/section :title "1. Signal + Computed + Effect" :id "demo-counter"
|
||||
(p "A signal holds a value. A computed derives from it. Click the buttons — the counter and doubled value update instantly, no server round-trip.")
|
||||
@@ -88,12 +88,18 @@
|
||||
(~docs/code :code (highlight ";; Island A — creates/writes the store\n(defisland ~reactive-islands/demo/store-writer ()\n (let ((store (def-store \"theme\" (fn ()\n (dict \"color\" (signal \"violet\")\n \"dark\" (signal false))))))\n (select :bind (get store \"color\")\n (option :value \"violet\" \"Violet\")\n (option :value \"blue\" \"Blue\"))\n (input :type \"checkbox\" :bind (get store \"dark\"))))\n\n;; Island B — reads the same store, different island\n(defisland ~reactive-islands/demo/store-reader ()\n (let ((store (use-store \"theme\")))\n (div :class (str \"bg-\" (deref (get store \"color\")) \"-100\")\n \"Styled by signals from Island A\")))" "lisp"))
|
||||
(p "React equivalent: " (code "createContext") " + " (code "useContext") " or Redux/Zustand. Stores are simpler — just named dicts of signals at page scope. " (code "def-store") " creates once, " (code "use-store") " retrieves. Stores survive island disposal but clear on full page navigation."))
|
||||
|
||||
(~docs/section :title "14. How defisland Works" :id "how-defisland"
|
||||
(~docs/section :title "14. Event Bridge" :id "demo-event-bridge"
|
||||
(p "Server-rendered content inside an island (an htmx \"lake\") can communicate with island signals via DOM custom events. Buttons with " (code "data-sx-emit") " dispatch events that island effects catch.")
|
||||
(~reactive-islands/index/demo-event-bridge)
|
||||
(~docs/code :code (highlight ";; Island listens for custom events from server-rendered content\n(defisland ~reactive-islands/demo/event-bridge ()\n (let ((messages (signal (list))))\n ;; Bridge: auto-listen for \"inbox:message\" events\n (bridge-event container \"inbox:message\" messages\n (fn (detail) (append (deref messages) (get detail \"text\"))))\n (div\n ;; Lake content (server-rendered) has data-sx-emit buttons\n (div :id \"lake\"\n :sx-get \"/my-content\"\n :sx-swap \"innerHTML\"\n :sx-trigger \"load\")\n ;; Island reads the signal reactively\n (ul (map (fn (msg) (li msg)) (deref messages))))))" "lisp"))
|
||||
(p "The " (code "data-sx-emit") " attribute is processed by the client engine — it adds a click handler that dispatches a CustomEvent with the JSON from " (code "data-sx-emit-detail") ". The event bubbles up to the island container where " (code "bridge-event") " catches it."))
|
||||
|
||||
(~docs/section :title "15. How defisland Works" :id "how-defisland"
|
||||
(p (code "defisland") " creates a reactive component. Same calling convention as " (code "defcomp") " — keyword args, rest children — but with a reactive boundary. Inside an island, " (code "deref") " subscribes DOM nodes to signals.")
|
||||
(~docs/code :code (highlight ";; Definition — same syntax as defcomp\n(defisland ~reactive-islands/demo/counter (&key initial)\n (let ((count (signal (or initial 0))))\n (div\n (span (deref count)) ;; reactive text node\n (button :on-click (fn (e) (swap! count inc)) ;; event handler\n \"+\"))))\n\n;; Usage — same as any component\n(~reactive-islands/demo/counter :initial 42)\n\n;; Server-side rendering:\n;; <div data-sx-island=\"counter\" data-sx-state='{\"initial\":42}'>\n;; <span>42</span><button>+</button>\n;; </div>\n;;\n;; Client hydrates: signals + effects + event handlers attach" "lisp"))
|
||||
(p "Each " (code "deref") " call registers the enclosing DOM node as a subscriber. Signal changes update " (em "only") " the subscribed nodes — no virtual DOM, no diffing, no component re-renders."))
|
||||
|
||||
(~docs/section :title "15. Test suite" :id "demo-tests"
|
||||
(~docs/section :title "16. Test suite" :id "demo-tests"
|
||||
(p "17 tests verify the signal runtime against the spec. All pass in the Python test runner (which uses the hand-written evaluator with native platform primitives).")
|
||||
(~docs/code :code (highlight ";; Signal basics (6 tests)\n(assert-true (signal? (signal 42)))\n(assert-equal 42 (deref (signal 42)))\n(assert-equal 5 (deref 5)) ;; non-signal passthrough\n\n;; reset! changes value\n(let ((s (signal 0)))\n (reset! s 10)\n (assert-equal 10 (deref s)))\n\n;; reset! does NOT notify when value unchanged (identical? check)\n\n;; Computed (3 tests)\n(let ((a (signal 3)) (b (signal 4))\n (sum (computed (fn () (+ (deref a) (deref b))))))\n (assert-equal 7 (deref sum))\n (reset! a 10)\n (assert-equal 14 (deref sum)))\n\n;; Effects (4 tests) — immediate run, re-run on change, dispose, cleanup\n;; Batch (1 test) — defers notifications, deduplicates subscribers\n;; defisland (3 tests) — creates island, callable, accepts children" "lisp"))
|
||||
(p :class "mt-2 text-sm text-stone-500" "Run: " (code "python3 shared/sx/tests/run.py signals")))
|
||||
|
||||
@@ -483,6 +483,45 @@
|
||||
"Styled by signals from Island A"))))
|
||||
|
||||
|
||||
;; 14. Event bridge — lake→island communication via custom DOM events
|
||||
(defisland ~reactive-islands/index/demo-event-bridge ()
|
||||
(let ((messages (signal (list)))
|
||||
(container nil))
|
||||
;; Bridge: listen for "inbox:message" events from server-rendered content
|
||||
(effect (fn ()
|
||||
(when container
|
||||
(on-event container "inbox:message"
|
||||
(fn (e)
|
||||
(swap! messages (fn (old)
|
||||
(append old (get (event-detail e) "text")))))))))
|
||||
(div :ref (dict "current" nil)
|
||||
(p :class "text-xs font-semibold text-stone-500 mb-2" "Event Bridge Demo")
|
||||
(p :class "text-sm text-stone-600 mb-2"
|
||||
"The buttons below simulate server-rendered content dispatching events into the island.")
|
||||
(div :class "flex gap-2 mb-3"
|
||||
(button
|
||||
:data-sx-emit "inbox:message"
|
||||
:data-sx-emit-detail "{\"text\":\"Hello from the lake!\"}"
|
||||
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||
"Send \"Hello\"")
|
||||
(button
|
||||
:data-sx-emit "inbox:message"
|
||||
:data-sx-emit-detail "{\"text\":\"Another message\"}"
|
||||
:class "px-3 py-1.5 bg-blue-600 text-white rounded text-sm hover:bg-blue-700"
|
||||
"Send \"Another\"")
|
||||
(button
|
||||
:on-click (fn (e) (reset! messages (list)))
|
||||
:class "px-3 py-1.5 bg-stone-200 text-stone-700 rounded text-sm hover:bg-stone-300"
|
||||
"Clear"))
|
||||
(div :class "min-h-12 p-3 rounded bg-stone-100 border border-stone-200"
|
||||
(if (empty? (deref messages))
|
||||
(p :class "text-stone-400 text-sm" "No messages yet.")
|
||||
(ul :class "space-y-1"
|
||||
(map (fn (msg)
|
||||
(li :class "text-sm text-stone-700" (str "→ " msg)))
|
||||
(deref messages))))))))
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Demo page — shows what's been implemented
|
||||
;; Examples page — shows what's been implemented
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
@@ -611,11 +611,11 @@
|
||||
:layout :sx-docs
|
||||
:content (~layouts/doc :path (str "/sx/(geography.(reactive." slug "))")
|
||||
(case slug
|
||||
"examples" (~reactive-islands/demo/reactive-islands-demo-content)
|
||||
"demo" (~reactive-islands/demo/reactive-islands-demo-content)
|
||||
"event-bridge" (~reactive-islands/event-bridge/reactive-islands-event-bridge-content)
|
||||
"named-stores" (~reactive-islands/named-stores/reactive-islands-named-stores-content)
|
||||
"plan" (~reactive-islands/plan/reactive-islands-plan-content)
|
||||
"phase2" (~reactive-islands/phase2/reactive-islands-phase2-content)
|
||||
"marshes" (~reactive-islands/marshes/reactive-islands-marshes-content)
|
||||
:else (~reactive-islands/index/reactive-islands-index-content))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user