Wire reactive islands end-to-end: live interactive demos on the demo page

- 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>
This commit is contained in:
2026-03-08 11:57:58 +00:00
parent 50a184faf2
commit 9a0173419a
9 changed files with 855 additions and 220 deletions

View File

@@ -125,6 +125,13 @@
ns
(render-dom-element name args env ns)
;; deref in island scope → reactive text node
(and (= name "deref") *island-scope*)
(let ((sig-or-val (trampoline (eval-expr (first args) env))))
(if (signal? sig-or-val)
(reactive-text sig-or-val)
(create-text-node (str (deref sig-or-val)))))
;; Fallback — evaluate then render
:else
(render-to-dom (trampoline (eval-expr expr env)) env ns)))

View File

@@ -416,6 +416,7 @@ class JSEmitter:
"set-timeout": "setTimeout_",
"set-interval": "setInterval_",
"clear-timeout": "clearTimeout_",
"clear-interval": "clearInterval_",
"request-animation-frame": "requestAnimationFrame_",
"csrf-token": "csrfToken",
"cross-origin?": "isCrossOrigin",
@@ -2003,7 +2004,7 @@ def compile_ref_to_js(
if name in adapter_set and name in adapter_platform:
parts.append(adapter_platform[name])
parts.append(fixups_js(has_html, has_sx, has_dom))
parts.append(fixups_js(has_html, has_sx, has_dom, has_signals))
if has_continuations:
parts.append(CONTINUATIONS_JS)
if has_dom:
@@ -3126,6 +3127,7 @@ PLATFORM_ORCHESTRATION_JS = """
function setTimeout_(fn, ms) { return setTimeout(fn, ms || 0); }
function setInterval_(fn, ms) { return setInterval(fn, ms || 1000); }
function clearTimeout_(id) { clearTimeout(id); }
function clearInterval_(id) { clearInterval(id); }
function requestAnimationFrame_(fn) {
if (typeof requestAnimationFrame !== "undefined") requestAnimationFrame(fn);
else setTimeout(fn, 16);
@@ -4023,7 +4025,7 @@ PLATFORM_BOOT_JS = """
"""
def fixups_js(has_html, has_sx, has_dom):
def fixups_js(has_html, has_sx, has_dom, has_signals=False):
lines = ['''
// =========================================================================
// Post-transpilation fixups
@@ -4044,6 +4046,31 @@ def fixups_js(has_html, has_sx, has_dom):
lines.append(' if (typeof aser === "function") PRIMITIVES["aser"] = aser;')
if has_dom:
lines.append(' if (typeof renderToDom === "function") PRIMITIVES["render-to-dom"] = renderToDom;')
if has_signals:
lines.append('''
// Expose signal functions as primitives so runtime-evaluated SX code
// (e.g. island bodies from .sx files) can call them
PRIMITIVES["signal"] = createSignal;
PRIMITIVES["signal?"] = isSignal;
PRIMITIVES["deref"] = deref;
PRIMITIVES["reset!"] = reset_b;
PRIMITIVES["swap!"] = swap_b;
PRIMITIVES["computed"] = computed;
PRIMITIVES["effect"] = effect;
PRIMITIVES["batch"] = batch;
PRIMITIVES["dispose"] = dispose;
// Reactive DOM helpers for island code
PRIMITIVES["reactive-text"] = reactiveText;
PRIMITIVES["create-text-node"] = createTextNode;
PRIMITIVES["dom-set-text-content"] = domSetTextContent;
PRIMITIVES["dom-listen"] = domListen;
PRIMITIVES["dom-dispatch"] = domDispatch;
PRIMITIVES["event-detail"] = eventDetail;
PRIMITIVES["def-store"] = defStore;
PRIMITIVES["use-store"] = useStore;
PRIMITIVES["emit-event"] = emitEvent;
PRIMITIVES["on-event"] = onEvent;
PRIMITIVES["bridge-event"] = bridgeEvent;''')
return "\n".join(lines)