Frontmatter
| id | 8850 |
| title | Fix VDOM ID Mismatch in Cross-Window Move (LivePreview Helix Detach) |
| state | Closed |
| labels | bugairegressioncore |
| assignees | tobiu |
| createdAt | Jan 21, 2026, 1:46 PM |
| updatedAt | Jan 21, 2026, 4:18 PM |
| githubUrl | https://github.com/neomjs/neo/issues/8850 |
| author | tobiu |
| commentsCount | 1 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Jan 21, 2026, 4:18 PM |
Fix VDOM ID Mismatch in Cross-Window Move (LivePreview Helix Detach)
tobiu assigned to @tobiu on Jan 21, 2026, 3:12 PM

tobiu
Jan 21, 2026, 3:12 PM
Input from Gemini 3 Pro:
✦ Fixed critical regressions in VDOM Teleportation and SharedWorker support.
Summary of Fixes:
Race Condition (
src/mixin/VdomLifecycle.mjs):
- Problem:
executeVdomUpdatewas synchronously snapshotting the VDOM viacollectPayloadsbefore subsequent synchronous DOM mutations (likeContainer.remove) in the same tick could complete. This resulted in "stale payloads" where removed components persisted as "Ghost Content".- Fix: Added
await new Promise(resolve => setTimeout(resolve, 1))toexecuteVdomUpdate. This forces a Macrotask yield, ensuringcollectPayloadsruns after all pending synchronous logic and microtasks have settled, capturing the correct clean state.Cross-Window Leak (
src/mixin/VdomLifecycle.mjs):
- Problem: Merged components (children) that moved to a new window were still included in the old parent's update batch. This caused deltas meant for the New Window (e.g., hiding a header) to be applied to the Old Window, corrupting the "Ghost Panel" state.
- Fix: Added filtering in
collectPayloadsto strictly exclude components wherecomponent.windowId !== me.windowId.Blocked Update Registry (
src/manager/VDomUpdate.mjs):
- Problem:
unregisterInFlightUpdateusedgetParentIds(component)to clear the "in-flight" status from ancestors. If a component moved during the update, this function returned the new parents, leaving the old parent (Viewport) permanently blocked (isChildUpdating= true).- Fix: Refactored to iterate
descendantInFlightMapdirectly, ensuring all registered ancestors are cleared regardless of hierarchy changes.Component ID Stability (
src/manager/Component.mjs):
- Problem: Placeholders stored in
me.vnodewere missingidif they matchedcomponentId, causing strict diffing errors invdom.Helper.- Fix: Updated
addVnodeComponentReferencesto always assignid.Race Condition (
examples/component/multiWindowHelix/ViewportController.mjs):
- Problem: Logic toggled
header.hiddenbefore re-attaching the panel to the viewport, causing an independent update that raced with the insert.- Fix: Swapped order to ensure correct hierarchy merging.
Result: The Helix example now correctly detaches and re-attaches the controls panel across windows without artifacts, ghost content, or errors.
tobiu closed this issue on Jan 21, 2026, 4:18 PM
The VDOM
createDeltasmethod is failing with an ID mismatch error during a cross-window move operation involvingLivePreview.Scenario:
Portal.view.home.parts.Helix), which rendersexamples.component.multiWindowHelix.Error: createDeltas() must be called for the same node. new: {id: neo-container-35, cId: neo-container-35}, old: {id: undefined, cId: neo-container-35}Analysis: The error
old: {id: undefined, cId: neo-container-35}suggests that thevnodeMaplookup for the old node is failing to find the correct VDOM node, likely becauseidis missing on the stored VNode, even thoughcomponentIdis present.This typically happens when
moveNodeattempts to process a move for a component reference. The Teleportation update might be generating disjoint payloads that interact poorly withmoveNodewhen a component is being moved out of a container (detachment).We need to investigate
vdom.Helper.createDeltasandmoveNodelogic, specifically how they handle component placeholders during batched updates.