Lambda multi-body fix: sf-lambda used (nth args 1), dropping all but the first
body expression. Fixed to collect all body expressions and wrap in (begin ...).
This was foundational — every multi-expression lambda in every island silently
dropped expressions after the first.
Reactive islands: fix dom-parent marker timing (first effect run before marker
is in DOM), fix :key eager evaluation, fix error boundary scope isolation,
fix resource/suspense reactive cond tracking, fix inc not available as JS var.
New essay: "React is Hypermedia" — argues that reactive islands are hypermedia
controls whose behavior is specified in SX, not a departure from hypermedia.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- suspense render-dom form: shows fallback while resource loads, swaps
to body content when resource signal resolves
- resource async signal: wraps promise into signal with loading/data/error
dict, auto-transitions on resolve/reject via promise-then
- transition: defers signal writes to requestIdleCallback, sets pending
signal for UI feedback during expensive operations
- Added schedule-idle, promise-then platform functions
- All Phase 2 features now marked Done in status tables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- :class-map dict toggles classes reactively via classList.add/remove
- :style-map dict sets inline styles reactively via el.style[prop]
- ref/ref-get/ref-set! mutable boxes (non-reactive, like useRef)
- :ref attribute sets ref.current to DOM element after rendering
- portal render-dom form renders children into remote target element
- Portal content auto-removed on island disposal via register-in-scope
- Added #portal-root div to page shell template
- Added stop-propagation and dom-focus platform functions
- Demo islands for all three features on the demo page
- Updated status tables: all P0/P1 features marked Done
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Items with :key attributes are matched by key across renders — existing
DOM nodes are reused, stale nodes removed, new nodes inserted in order.
Falls back to clear-and-rerender without keys.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Effect and computed auto-register disposers with island scope via
register-in-scope; dispose-islands-in called before every swap point
(orchestration.sx) to clean up intervals/subscriptions on navigation.
- Map + deref inside islands auto-upgrades to reactive-list for signal-
bound list rendering. Demo island with add/remove items.
- New :bind attribute for two-way signal-input binding (text, checkbox,
radio, textarea, select). bind-input in adapter-dom.sx handles both
signal→element (effect) and element→signal (event listener).
- Phase 2 plan page at /reactive-islands/phase2 covering input binding,
keyed reconciliation, reactive class/style, refs, portals, error
boundaries, suspense, and transitions.
- Updated status tables in overview and plan pages.
- Fixed stopwatch reset (fn body needs do wrapper for multiple exprs).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three bugs prevented islands from working during SX wire navigation:
1. components_for_request() only bundled Component and Macro defs, not
Island defs — client never received defisland definitions during
navigation (components_for_page for initial HTML shell was correct).
2. hydrate-island used morph-children which can't transfer addEventListener
event handlers from freshly rendered DOM to existing nodes. Changed to
clear+append so reactive DOM with live signal subscriptions is inserted
directly.
3. asyncRenderToDom (client-side async page eval) checked _component but
not _island on ~-prefixed names — islands fell through to generic eval
which failed. Now delegates to renderDomIsland.
4. setInterval_/setTimeout_ passed SX Lambda objects directly to native
timers. JS coerced them to "[object Object]" and tried to eval as code,
causing "missing ] after element list". Added _wrapSxFn to convert SX
lambdas to JS functions before passing to timers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rebuild sx-browser.js with signals spec module (was missing entirely)
- Register signal functions (signal, deref, effect, computed, etc.) as
PRIMITIVES so runtime-evaluated SX code in island bodies can call them
- Add reactive deref detection in adapter-dom.sx: (deref sig) in island
scope creates reactive-text node instead of static text
- Add Island SSR support in html.py (_render_island with data-sx-island)
- Add Island bundling in jinja_bridge.py (defisland defs sent to client)
- Update deps.py to track Island dependencies alongside Component
- Add defisland to _ASER_FORMS in async_eval.py
- Add clear-interval platform primitive (was missing)
- Create four live demo islands: counter, temperature, imperative, stopwatch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- adapter-dom.sx: detect :on-click/:on-submit etc. in render-dom-element
— if attr starts with "on-" and value is callable, wire via dom-listen
- orchestration.sx: add process-emit-elements for data-sx-emit attrs
— auto-dispatch custom events on click with optional JSON detail
- bootstrap_js.py: add processEmitElements RENAME
- Regenerate sx-ref.js with all changes
- Update reactive-islands status table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New spec file signals.sx defines the signal runtime: signal, computed,
effect, deref, reset!, swap!, batch, dispose, and island scope tracking.
eval.sx: defisland special form + island? type predicate in eval-call.
boundary.sx: signal primitive declarations (Tier 3).
render.sx: defisland in definition-form?.
adapter-dom.sx: render-dom-island with reactive context, reactive-text,
reactive-attr, reactive-fragment, reactive-list helpers.
adapter-html.sx: render-html-island for SSR with data-sx-island/state.
adapter-sx.sx: island? handling in wire format serialization.
special-forms.sx: defisland declaration with docs and example.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The entire parallel CSS system (StyleValue type, style dictionary,
keyword atom resolver, content-addressed class generation, runtime
CSS injection, localStorage caching) was built but never adopted —
the codebase already uses :class strings with defcomp components
for all styling. Remove ~3,000 lines of unused infrastructure.
Deleted:
- cssx.sx spec module (317 lines)
- style_dict.py (782 lines) and style_resolver.py (254 lines)
- StyleValue type, defkeyframes special form, build-keyframes platform fn
- Style dict JSON delivery (<script type="text/sx-styles">), cookies, localStorage
- css/merge-styles primitives, inject-style-value, fnv1a-hash platform interface
Simplified:
- defstyle now binds any value (string, function) — no StyleValue type needed
- render-attrs no longer special-cases :style StyleValue → class conversion
- Boot sequence skips style dict init step
Preserved:
- tw.css parsing + CSS class delivery (SX-Css headers, <style id="sx-css">)
- All component infrastructure (defcomp, caching, bundling, deps)
- defstyle as a binding form for reusable class strings
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
render-dom-unknown-component now calls (error ...) instead of
creating a styled div. This lets tryEvalContent catch the error
and fall back to server fetch, instead of rendering "Unknown
component: ~name" into the page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split monolithic render.sx into core (tag registries, shared utils) plus
four adapter .sx files: adapter-html (server HTML strings), adapter-sx
(SX wire format), adapter-dom (browser DOM nodes), and engine (SxEngine
triggers, morphing, swaps). All adapters written in s-expressions with
platform interface declarations for JS bridge functions.
Bootstrap compiler now accepts --adapters flag to emit targeted builds:
-a html → server-only (1108 lines)
-a dom,engine → browser-only (1634 lines)
-a html,sx → server with SX wire (1169 lines)
(default) → all adapters (1800 lines)
Fixes: keyword arg i-counter desync in reduce across all adapters,
render-aware special forms (let/if/when/cond/map) in HTML adapter,
component children double-escaping, ~prefixed macro dispatch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>