Fix I-2: dangling parent on shell/ColumnLayout-rooted AI-replace trees

treeToCraftState's ROOT re-key branch reparented nodes['ROOT'].nodes
children to 'ROOT' but not nodes['ROOT'].linkedNodes children. For a
ColumnLayout- or Section/BackgroundSection/FormContainer-rooted AI
`replace`, flattenTreeForCraft puts content in linkedNodes (col-N /
section-inner etc.) whose parent was left pointing at the OLD root id
-- which is then deleted, leaving a dangling parent reference that
breaks select/move/delete of those nodes in the Craft.js editor.

Now the same loop that reparents nodes[] children also reparents
linkedNodes children.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 17:31:01 -07:00
parent 25e674badd
commit 9d3bbc2c46
2 changed files with 60 additions and 5 deletions
+10
View File
@@ -86,6 +86,16 @@ export function treeToCraftState(tree: SerializedTreeNode): string {
for (const childId of nodes['ROOT'].nodes) {
if (nodes[childId]) nodes[childId].parent = 'ROOT';
}
// I-2: linkedNodes children (e.g. ColumnLayout's col-0/col-1, or a
// Section/BackgroundSection/FormContainer's SHELL_INNER wrapper) need
// the same reparenting as nodes[] children above. Without this, a
// ColumnLayout/SHELL_INNER-rooted AI `replace` leaves those children's
// `parent` pointing at the OLD root id, which is then `delete`d --
// producing a dangling parent reference that breaks select/move/delete
// of those nodes in the Craft.js editor.
for (const linkedId of Object.values(nodes['ROOT'].linkedNodes)) {
if (nodes[linkedId]) nodes[linkedId].parent = 'ROOT';
}
}
return JSON.stringify(nodes);
}