Fix navigation: outerHTML swap, island markers, host handle equality, dispose
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>
This commit is contained in:
@@ -375,8 +375,10 @@
|
||||
(dom-has-attr? new-node "data-sx-island")
|
||||
(not (= (dom-get-attr old-node "data-sx-island")
|
||||
(dom-get-attr new-node "data-sx-island"))))
|
||||
(dispose-islands-in old-node)
|
||||
(clear-processed! old-node "island-hydrated"))
|
||||
;; Dispose the old island itself (not just sub-islands)
|
||||
;; to tear down reactive effects before morphing content
|
||||
(dispose-island old-node)
|
||||
(dispose-islands-in old-node))
|
||||
(sync-attrs old-node new-node)
|
||||
;; Skip morphing focused input to preserve user's in-progress edits
|
||||
(when (not (and (dom-is-active-element? old-node)
|
||||
@@ -602,19 +604,22 @@
|
||||
(morph-children target wrapper)))
|
||||
|
||||
"outerHTML"
|
||||
(let ((parent (dom-parent target)))
|
||||
(let ((parent (dom-parent target))
|
||||
(new-el (dom-clone new-nodes)))
|
||||
(if (dom-is-fragment? new-nodes)
|
||||
;; Fragment — morph first child, insert rest
|
||||
;; Fragment — replace target with fragment children
|
||||
(let ((fc (dom-first-child new-nodes)))
|
||||
(if fc
|
||||
(do
|
||||
(morph-node target fc)
|
||||
;; Insert remaining siblings after morphed element
|
||||
(set! new-el (dom-clone fc))
|
||||
(dom-replace-child parent new-el target)
|
||||
(let ((sib (dom-next-sibling fc)))
|
||||
(insert-remaining-siblings parent target sib)))
|
||||
(insert-remaining-siblings parent new-el sib)))
|
||||
(dom-remove-child parent target)))
|
||||
(morph-node target new-nodes))
|
||||
parent)
|
||||
;; Element — replace target with new element
|
||||
(dom-replace-child parent new-el target))
|
||||
;; Return the new element so post-swap can hydrate it
|
||||
new-el)
|
||||
|
||||
"afterend"
|
||||
(dom-insert-after target new-nodes)
|
||||
|
||||
Reference in New Issue
Block a user