Parser: skip unit suffix when next ident is a comparison keyword (starts, ends, contains, matches, is, does, in, precedes, follows). Fixes "123 starts with '12'" returning "123starts" instead of true. eval-hs: use hs-compile directly instead of hs-to-sx-from-source with "return " prefix, which was causing the parser to consume the comparison as a string suffix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
96 lines
6.8 KiB
Plaintext
96 lines
6.8 KiB
Plaintext
;; Reactive Application Runtime — 7 Feature Layers
|
|
;; Zero new platform primitives. All functions composing existing signals + DOM ops.
|
|
;; Lives under Applications: /(applications.(reactive-runtime))
|
|
;; ---------------------------------------------------------------------------
|
|
;; Overview page
|
|
;; ---------------------------------------------------------------------------
|
|
(defcomp ()
|
|
(~docs/page :title "Reactive Application Runtime"
|
|
|
|
(p (~tw :tokens "text-stone-500 text-sm italic mb-8")
|
|
"Seven feature layers that take SX from reactive documents to a full application runtime. "
|
|
"Every layer is a pure SX function composing existing primitives — zero new CEK frames, "
|
|
"zero new platform primitives. The existing platform interface is sufficient "
|
|
"for any application pattern.")
|
|
|
|
;; =====================================================================
|
|
;; Motivation
|
|
;; =====================================================================
|
|
|
|
(~docs/section :title "Motivation" :id "motivation"
|
|
|
|
(p "SX has signals, computed, effects, batch, islands, lakes, stores, and bridge events — "
|
|
"sufficient for reactive documents (forms, toggles, counters, live search). "
|
|
"But complex client-heavy apps (drawing tools, editors, games) need structure on top:")
|
|
|
|
(table (~tw :tokens "w-full mb-6 text-sm")
|
|
(thead
|
|
(tr (th (~tw :tokens "text-left p-2") "Have") (th (~tw :tokens "text-left p-2") "Need")))
|
|
(tbody
|
|
(tr (td (~tw :tokens "p-2") "Signal holds a value") (td (~tw :tokens "p-2") "Ref holds a value " (em "without") " reactivity"))
|
|
(tr (td (~tw :tokens "p-2") "Effect runs on change") (td (~tw :tokens "p-2") "Loop runs " (em "continuously")))
|
|
(tr (td (~tw :tokens "p-2") "Store shares state") (td (~tw :tokens "p-2") "Machine manages " (em "modal") " state"))
|
|
(tr (td (~tw :tokens "p-2") "reset!/swap! update") (td (~tw :tokens "p-2") "Commands update " (em "with history")))
|
|
(tr (td (~tw :tokens "p-2") "DOM rendering") (td (~tw :tokens "p-2") "Foreign calls " (em "any host API")))
|
|
(tr (td (~tw :tokens "p-2") "Server-first hydration") (td (~tw :tokens "p-2") "Client-first " (em "app shell"))))))
|
|
|
|
;; =====================================================================
|
|
;; The Seven Layers
|
|
;; =====================================================================
|
|
|
|
(~docs/section :title "The Seven Layers" :id "layers"
|
|
|
|
(table (~tw :tokens "w-full mb-6 text-sm")
|
|
(thead
|
|
(tr (th (~tw :tokens "text-left p-2") "Layer") (th (~tw :tokens "text-left p-2") "What") (th (~tw :tokens "text-left p-2") "Builds On")))
|
|
(tbody
|
|
(tr (td (~tw :tokens "p-2 font-mono") "L0") (td (~tw :tokens "p-2") "Ref — mutable box without reactivity") (td (~tw :tokens "p-2") "register-in-scope"))
|
|
(tr (td (~tw :tokens "p-2 font-mono") "L1") (td (~tw :tokens "p-2") "Foreign — host API interop") (td (~tw :tokens "p-2") "host-call, host-get, host-set!"))
|
|
(tr (td (~tw :tokens "p-2 font-mono") "L2") (td (~tw :tokens "p-2") "State machines — modal state as signal") (td (~tw :tokens "p-2") "signal, deref, reset!"))
|
|
(tr (td (~tw :tokens "p-2 font-mono") "L3") (td (~tw :tokens "p-2") "Commands — undo/redo signal stores") (td (~tw :tokens "p-2") "signal, computed, L0"))
|
|
(tr (td (~tw :tokens "p-2 font-mono") "L4") (td (~tw :tokens "p-2") "Render loop — continuous rAF") (td (~tw :tokens "p-2") "L0, request-animation-frame"))
|
|
(tr (td (~tw :tokens "p-2 font-mono") "L5") (td (~tw :tokens "p-2") "Keyed lists — explicit key reconciliation") (td (~tw :tokens "p-2") "reactive-list (existing)"))
|
|
(tr (td (~tw :tokens "p-2 font-mono") "L6") (td (~tw :tokens "p-2") "App shell — client-first rendering") (td (~tw :tokens "p-2") "sx-mount, all above")))))
|
|
|
|
;; =====================================================================
|
|
;; Implementation Plan
|
|
;; =====================================================================
|
|
|
|
(~docs/section :title "Implementation Plan" :id "implementation"
|
|
|
|
(p "All seven layers live in a single spec module: " (code "web/reactive-runtime.sx") ". "
|
|
"Every layer is a plain " (code "define") " function — no macros, no new special forms. "
|
|
"The OCaml evaluator bootstraps them to JavaScript via the standard transpiler pipeline.")
|
|
|
|
(~docs/subsection :title "Implementation Order"
|
|
(~docs/code :src (highlight
|
|
"L0 Ref → standalone, trivial (~35 LOC)\nL1 Foreign FFI → standalone, function factories (~100 LOC)\nL5 Keyed Lists → enhances existing reactive-list (~155 LOC)\nL2 State Machine → uses signals + dicts (~200 LOC)\nL4 Render Loop → uses L0 refs + existing rAF (~140 LOC)\nL3 Commands → extends stores, uses signals (~320 LOC)\nL6 App Shell → orchestrates all above (~330 LOC)\n Total: ~1280 LOC"
|
|
"text"))
|
|
|
|
(p "L0 and L1 are independent foundations. L5 enhances existing code. "
|
|
"L2 and L4 depend on L0. L3 builds on signals. L6 ties everything together."))
|
|
|
|
(~docs/subsection :title "Build Integration"
|
|
(p "One new entry in " (code "SPEC_MODULES") " (hosts/javascript/platform.py):")
|
|
(~docs/code :src (highlight
|
|
"SPEC_MODULES = {\n ...\n \"reactive-runtime\": (\"reactive-runtime.sx\", \"reactive-runtime (application patterns)\"),\n}\nSPEC_MODULE_ORDER = [..., \"reactive-runtime\"]"
|
|
"python"))
|
|
(p "Auto-included when the " (code "dom") " adapter is present. "
|
|
"Depends on " (code "signals") " (loaded first via module ordering)."))
|
|
|
|
(~docs/subsection :title "Existing Primitives Used"
|
|
(table (~tw :tokens "w-full mb-6 text-sm")
|
|
(thead
|
|
(tr (th (~tw :tokens "text-left p-2") "Primitive") (th (~tw :tokens "text-left p-2") "Used By")))
|
|
(tbody
|
|
(tr (td (~tw :tokens "p-2") (code "signal, deref, reset!, swap!, computed, effect")) (td (~tw :tokens "p-2") "L2, L3, L4"))
|
|
(tr (td (~tw :tokens "p-2") (code "host-call, host-get, host-set!")) (td (~tw :tokens "p-2") "L1 (Foreign FFI)"))
|
|
(tr (td (~tw :tokens "p-2") (code "request-animation-frame")) (td (~tw :tokens "p-2") "L4 (Render Loop)"))
|
|
(tr (td (~tw :tokens "p-2") (code "register-in-scope, scope-push!, scope-pop!")) (td (~tw :tokens "p-2") "L0, L4"))
|
|
(tr (td (~tw :tokens "p-2") (code "sx-mount, sx-hydrate-islands")) (td (~tw :tokens "p-2") "L6 (App Shell)"))
|
|
(tr (td (~tw :tokens "p-2") (code "DOM ops (dom-insert-after, dom-remove, …)")) (td (~tw :tokens "p-2") "L5 (Keyed Lists)"))))
|
|
|
|
(p (~tw :tokens "text-stone-600 italic")
|
|
"Zero new platform primitives validates that the existing interface is complete "
|
|
"for any application pattern.")))))
|