From a4fa7df45cfab98526a6dda0ba92f093206625a6 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Thu, 5 Mar 2026 00:05:45 +0300 Subject: [PATCH] Code review: restore BFS for DSD propagation, fix preComputedRoot cache key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace O(N²) iterative convergence loop with BFS that correctly handles intermediate DSD-targeted clones (push to queue regardless of visited, only skip size update for dsdSizeSet nodes) - Fix preComputedRoot cache lookup: was using childId instead of child.componentId as key - Remove stray blank line --- packages/core/src/kiwi/instance-overrides.ts | 67 +++++++++++--------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/packages/core/src/kiwi/instance-overrides.ts b/packages/core/src/kiwi/instance-overrides.ts index c4f94206c..9c57616d3 100644 --- a/packages/core/src/kiwi/instance-overrides.ts +++ b/packages/core/src/kiwi/instance-overrides.ts @@ -161,7 +161,7 @@ export function populateAndApplyOverrides( for (const childId of parent.childIds) { const child = graph.getNode(childId) if (!child?.componentId) continue - const childRoot = preComputedRoot.get(childId) ?? getComponentRoot(child.componentId) + const childRoot = preComputedRoot.get(child.componentId) ?? getComponentRoot(child.componentId) if (childRoot === targetRoot) { if (rootMatch) { ambiguous = true; break } rootMatch = childId @@ -444,34 +444,44 @@ export function populateAndApplyOverrides( // their own values; nodes only touched for position/geometry inherit // size from their source. if (dsdModified.size > 0) { - const synced = new Set() - let changed = true - while (changed) { - changed = false - for (const node of graph.getAllNodes()) { - if (!node.componentId || synced.has(node.id)) continue - const source = graph.getNode(node.componentId) - if (!source) continue - if (!dsdModified.has(node.componentId) && !synced.has(node.componentId)) continue - // Nodes with explicitly DSD-set size keep their values but still - // act as chain links so their clones are reached - if (dsdSizeSet.has(node.id)) { - synced.add(node.id) - changed = true - continue + const clonesOf = new Map() + for (const node of graph.getAllNodes()) { + if (!node.componentId) continue + let arr = clonesOf.get(node.componentId) + if (!arr) { + arr = [] + clonesOf.set(node.componentId, arr) + } + arr.push(node.id) + } + + // BFS from DSD-modified nodes. Unlike the old version, intermediate + // clones that are also in dsdModified are NOT skipped — they act as + // chain links. Nodes in dsdSizeSet keep their explicit size but still + // propagate to their clones. + const queue = [...dsdModified] + const visited = new Set() + for (let sourceId = queue.shift(); sourceId !== undefined; sourceId = queue.shift()) { + const source = graph.getNode(sourceId) + if (!source) continue + const clones = clonesOf.get(sourceId) + if (!clones) continue + for (const cloneId of clones) { + if (visited.has(cloneId)) continue + visited.add(cloneId) + const clone = graph.getNode(cloneId) + if (!clone) continue + if (!dsdSizeSet.has(cloneId)) { + const cu: Partial = {} + if (source.width !== clone.width) cu.width = source.width + if (source.height !== clone.height) cu.height = source.height + if (source.x !== clone.x) cu.x = source.x + if (source.y !== clone.y) cu.y = source.y + if (source.fillGeometry !== clone.fillGeometry) cu.fillGeometry = structuredClone(source.fillGeometry) + if (source.strokeGeometry !== clone.strokeGeometry) cu.strokeGeometry = structuredClone(source.strokeGeometry) + if (Object.keys(cu).length > 0) graph.updateNode(cloneId, cu) } - const cu: Partial = {} - if (source.width !== node.width) cu.width = source.width - if (source.height !== node.height) cu.height = source.height - if (source.x !== node.x) cu.x = source.x - if (source.y !== node.y) cu.y = source.y - if (source.fillGeometry !== node.fillGeometry) cu.fillGeometry = structuredClone(source.fillGeometry) - if (source.strokeGeometry !== node.strokeGeometry) cu.strokeGeometry = structuredClone(source.strokeGeometry) - if (Object.keys(cu).length > 0) { - graph.updateNode(node.id, cu) - } - synced.add(node.id) - changed = true + queue.push(cloneId) } } } @@ -494,7 +504,6 @@ export function populateAndApplyOverrides( if (!guids?.length) continue const targetId = resolveOverrideTarget(nodeId, guids) - if (!targetId) continue overriddenNodes.add(targetId)