diff --git a/CHANGELOG.md b/CHANGELOG.md index c3f7c5c85..b70944f98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Performance +- Index Figma clipboard children once during import instead of rescanning every pasted node, keeping large flat pastes linear. (#500) - Reduce peak memory during `.fig` export by sharing immutable binary resources with the isolated export graph. ### Fixed diff --git a/packages/core/src/clipboard.ts b/packages/core/src/clipboard.ts index e16acc0bb..5ba9a51a9 100644 --- a/packages/core/src/clipboard.ts +++ b/packages/core/src/clipboard.ts @@ -139,25 +139,31 @@ export function figmaNodesBounds( interface ClipboardImportMaps { guidMap: Map parentMap: Map + childMap: Map } function buildClipboardMaps(nodeChanges: KiwiNodeChange[]): ClipboardImportMaps { const guidMap = new Map() const parentMap = new Map() + const childMap = new Map() for (const nc of nodeChanges) { if (!nc.guid) continue const id = `${nc.guid.sessionID}:${nc.guid.localID}` guidMap.set(id, nc) if (nc.parentIndex?.guid) { - parentMap.set(id, `${nc.parentIndex.guid.sessionID}:${nc.parentIndex.guid.localID}`) + const parentId = `${nc.parentIndex.guid.sessionID}:${nc.parentIndex.guid.localID}` + parentMap.set(id, parentId) + const siblings = childMap.get(parentId) + if (siblings) siblings.push(id) + else childMap.set(parentId, [id]) } } - return { guidMap, parentMap } + return { guidMap, parentMap, childMap } } function findInternalNodeIds( guidMap: Map, - parentMap: Map + childMap: Map ): { internalCanvasIds: Set; internalFigmaIds: Set } { const internalCanvasIds = new Set() for (const [id, nc] of guidMap) { @@ -169,8 +175,8 @@ function findInternalNodeIds( const internalFigmaIds = new Set() function markInternal(id: string) { internalFigmaIds.add(id) - for (const [childId, pid] of parentMap) { - if (pid === id && !internalFigmaIds.has(childId)) markInternal(childId) + for (const childId of childMap.get(id) ?? []) { + if (!internalFigmaIds.has(childId)) markInternal(childId) } } for (const canvasId of internalCanvasIds) markInternal(canvasId) @@ -230,8 +236,8 @@ export function importClipboardNodes( offsetY = 0, blobs: Uint8Array[] = [] ): string[] { - const { guidMap, parentMap } = buildClipboardMaps(nodeChanges) - const { internalCanvasIds, internalFigmaIds } = findInternalNodeIds(guidMap, parentMap) + const { guidMap, parentMap, childMap } = buildClipboardMaps(nodeChanges) + const { internalCanvasIds, internalFigmaIds } = findInternalNodeIds(guidMap, childMap) const { topLevel, internalTopLevel } = classifyTopLevelNodes( guidMap, parentMap, @@ -262,12 +268,9 @@ export function importClipboardNodes( created.set(figmaId, node.id) if (ourParentId === targetParentId && !internalFigmaIds.has(figmaId)) createdIds.push(node.id) - const children: string[] = [] - for (const [childId, pid] of parentMap) { - if (pid === figmaId && !NON_VISUAL_TYPES.has(guidMap.get(childId)?.type ?? '')) { - children.push(childId) - } - } + const children = (childMap.get(figmaId) ?? []).filter( + (childId) => !NON_VISUAL_TYPES.has(guidMap.get(childId)?.type ?? '') + ) sortChildren(children, nc, guidMap) for (const childId of children) { createNode(childId, node.id)