- ocaml_sync.py: escape newlines in eval/load_source to prevent
protocol desync (bridge crashed on any multi-line SX)
- Stepper: do-back uses rebuild-preview (O(1) render) instead of
replaying all steps. Hydration effect same. Cookie save on button
click only.
- dom.sx: remove duplicate dom-listen (was shadowing the one at
line 351 that adapter-dom.sx's dom-on wraps)
- orchestration.sx: fix bind-sse-swap close paren count
- safe_eq: Dict equality via __host_handle for DOM node identity
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
adapter-dom.sx defines dom-on as a wrapper around dom-listen (adds
post-render hooks). But dom-listen was never defined — my earlier
dom-on in dom.sx was overwritten by the adapter's version. Rename
to dom-listen so the adapter's dom-on can call it.
This fixes click handlers not firing on island buttons (stepper,
stopwatch, counter, etc.).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Navigation pipeline now works end-to-end:
- outerHTML swap uses dom-replace-child instead of morph-node (morph has
a CEK continuation issue with nested for-each that needs separate fix)
- swap-dom-nodes returns the new element for outerHTML so post-swap
hydrates the correct (new) DOM, not the detached old element
- sx-render uses marker mode: islands rendered as empty span[data-sx-island]
markers, hydrated by post-swap. Prevents duplicate content from island
body expansion + SX response nav rows.
- dispose-island (singular) called on old island before morph, not just
dispose-islands-in (which only disposes sub-islands)
OCaml runtime:
- safe_eq: Dict equality checks __host_handle for DOM node identity
(js_to_value creates new Dict wrappers per call, breaking physical ==)
- contains?: same host handle check
- to_string: trampoline thunks (fixes <thunk> display)
- as_number: trampoline thunks (fixes arithmetic on leaked thunks)
DOM platform:
- dom-remove, dom-attr-list (name/value pairs), dom-child-list (SX list),
dom-is-active-element?, dom-is-input-element?, dom-is-child-of?, dom-on
All 5 reactive-nav Playwright tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
dom-lib and browser-lib were listed in ADAPTER_FILES but never actually
transpiled — their functions only existed as native PLATFORM_*_JS code.
Add them to the build loop so the FFI library wrappers are compiled.
Add hostCall/hostGet/etc. variable aliases for transpiled code, and
console-log to browser.sx for runtime-eval'd SX code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce 8 irreducible host FFI primitives that replace 40+ native DOM
and browser primitives:
host-global — access global object (window/document)
host-get — read property from host object
host-set! — write property on host object
host-call — call method on host object
host-new — construct host object
host-callback — wrap SX function as host callback
host-typeof — check host object type
host-await — await host promise
All DOM and browser operations are now expressible as SX library
functions built on these 8 primitives:
web/lib/dom.sx — createElement, querySelector, appendChild,
setAttribute, addEventListener, classList, etc.
web/lib/browser.sx — localStorage, history, fetch, setTimeout,
promises, console, matchMedia, etc.
The existing native implementations remain as fallback — the library
versions shadow them in transpiled code. Incremental migration: callers
don't change, only the implementation moves from out-of-band to in-band.
JS 957+1080, Python 744, OCaml 952 — zero regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>