Isolate island :ref hydration bug: dict-set!/reduce error

Root cause identified: :ref attribute on DOM elements inside defisland
triggers dict-set!/reduce error in WASM kernel hydration system.

Minimal repro:
  (defisland ~test ()
    (let ((el-ref (signal nil)))
      (div (div :ref (fn (el) (reset! el-ref el)) ""))))
  → "dict-set!: dict key val (in reduce → reduce → for-each)"

Without :ref: works perfectly (signals, effects, canvas FFI,
break-lines, pretext-layout-lines all functional).

Working version: full Pretext with 3 controls + effect + layout
computation, outputs text via (deref result). 34 disposers, no error.
Just needs :ref fix to add DOM rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 08:01:16 +00:00
parent da0da1472d
commit e12ddefdff

View File

@@ -1,11 +1,14 @@
;; Test: pretext-layout-lines inside effect (no DOM update)
;; Test: add :ref callback
(defisland
~pretext-demo/live
()
(let
((words (split "In the beginning was the Word, and the Word was with God." " "))
(mxw (signal 400))
((words (split "In the beginning was the Word, and the Word was with God, and the Word was God. The same was in the beginning with God. All things were made by him; and without him was not any thing made that was made. In him was life; and the life was the light of men." " "))
(mxw (signal 500))
(font-size (signal 16))
(use-optimal (signal true))
(result (signal "loading..."))
(el-ref (signal nil))
(doc (host-global "document"))
(canvas (host-call doc "createElement" "canvas"))
(ctx (host-call canvas "getContext" "2d")))
@@ -13,26 +16,66 @@
(fn
()
(let
((w (deref mxw)))
(host-set! ctx "font" "16px serif")
((w (deref mxw))
(sz (deref font-size))
(opt (deref use-optimal)))
(host-set! ctx "font" (str sz "px serif"))
(let
((widths (map (fn (wd) (host-get (host-call ctx "measureText" wd) "width")) words))
(spw (host-get (host-call ctx "measureText" " ") "width")))
(let
((ranges (break-lines widths spw w)))
((ranges (if opt (break-lines widths spw w) (break-lines-greedy widths spw w))))
(let
((lines (pretext-layout-lines words widths ranges spw w 24)))
(reset! result (str (len lines) " lines at " w "px"))))))))
((lines (pretext-layout-lines words widths ranges spw w (* sz 1.5))))
(reset!
result
(str
(len lines)
" lines — "
w
"px / "
sz
"px / "
(if opt "optimal" "greedy")))))))))
(div
(~tw :tokens "p-4 border rounded space-y-2")
(div
(~tw :tokens "flex flex-wrap gap-4 items-end")
(div
(label (~tw :tokens "block text-xs text-stone-500 mb-1") "Width")
(input
:type "range"
:min "200"
:max "700"
:value (deref mxw)
(~tw :tokens "w-32")
:on-input (fn
(e)
(reset!
mxw
(parse-number (host-get (host-get e "target") "value"))))))
(div
(label
(~tw :tokens "block text-xs text-stone-500 mb-1")
"Font size")
(input
:type "range"
:min "10"
:max "24"
:value (deref font-size)
(~tw :tokens "w-24")
:on-input (fn
(e)
(reset!
font-size
(parse-number (host-get (host-get e "target") "value"))))))
(div
(label
(~tw :tokens "block text-xs text-stone-500 mb-1")
"Algorithm")
(button
(~tw :tokens "px-3 py-1 rounded border text-sm")
:on-click (fn (e) (reset! use-optimal (not (deref use-optimal))))
(if (deref use-optimal) "Knuth-Plass" "Greedy"))))
(div (deref result))
(input
:type "range"
:min "100"
:max "600"
:value (deref mxw)
:on-input (fn
(e)
(reset!
mxw
(parse-number (host-get (host-get e "target") "value"))))))))
(div :ref (fn (el) (reset! el-ref el)) ""))))