diff --git a/craft/src/state/PageContext.tsx b/craft/src/state/PageContext.tsx index 1bf67df..9b73e15 100644 --- a/craft/src/state/PageContext.tsx +++ b/craft/src/state/PageContext.tsx @@ -325,6 +325,24 @@ export const PageProvider: React.FC<{ children: ReactNode }> = ({ children }) => for (const child of node.nodes ?? []) { childIds.push(walk(child, id)); } + // ColumnLayout uses Craft.js linkedNodes with fixed ids (col-0, col-1, ...). + // The AI emits children as direct `nodes`, but ColumnLayout's render ignores + // them and creates fresh column Elements — the AI's children become orphans + // and any subsequent toNodeTree walk hits an Invariant. Move direct children + // into linkedNodes so they render in the columns the user actually sees. + if (typeName === 'ColumnLayout' && childIds.length > 0) { + const linked: Record = {}; + childIds.forEach((cid, i) => { + linked[`col-${i}`] = cid; + if (nodes[cid]) (nodes[cid] as any).isCanvas = true; // columns are canvases + }); + (nodes[id] as any).nodes = []; + (nodes[id] as any).linkedNodes = linked; + // Reflect the actual column count on the component so its render matches. + const cur = (nodes[id] as any).props || {}; + if (!cur.columns || cur.columns !== childIds.length) cur.columns = childIds.length; + (nodes[id] as any).props = cur; + } return id; }; const rootId = walk(tree, null); diff --git a/craft/src/utils/apply-ai-response.ts b/craft/src/utils/apply-ai-response.ts index 260e78d..99aadab 100644 --- a/craft/src/utils/apply-ai-response.ts +++ b/craft/src/utils/apply-ai-response.ts @@ -86,6 +86,20 @@ function buildNodeTree(query: any, tree: SerializedTreeNode): NodeTree { const childId = walk(child, id); craftNodes[id].data.nodes.push(childId); } + // ColumnLayout uses linkedNodes (col-0, col-1, ...) — not direct children. + if (node.type.resolvedName === 'ColumnLayout' && craftNodes[id].data.nodes.length > 0) { + const linked: Record = {}; + craftNodes[id].data.nodes.forEach((cid: string, i: number) => { + linked[`col-${i}`] = cid; + if (craftNodes[cid]) craftNodes[cid].data.isCanvas = true; + }); + craftNodes[id].data.nodes = []; + craftNodes[id].data.linkedNodes = linked; + const colCount = Object.keys(linked).length; + if (!craftNodes[id].data.props.columns || craftNodes[id].data.props.columns !== colCount) { + craftNodes[id].data.props.columns = colCount; + } + } return id; };