perf(clipboard): index pasted node children (#511)
- Build parent-to-child indexes once while decoding Figma clipboard data - Reuse the index for hierarchy traversal and internal component discovery - Avoid quadratic scans that blocked large flat pastes
This commit is contained in:
parent
5190a64017
commit
3ee3708d71
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -139,25 +139,31 @@ export function figmaNodesBounds(
|
|||
interface ClipboardImportMaps {
|
||||
guidMap: Map<string, KiwiNodeChange>
|
||||
parentMap: Map<string, string>
|
||||
childMap: Map<string, string[]>
|
||||
}
|
||||
|
||||
function buildClipboardMaps(nodeChanges: KiwiNodeChange[]): ClipboardImportMaps {
|
||||
const guidMap = new Map<string, KiwiNodeChange>()
|
||||
const parentMap = new Map<string, string>()
|
||||
const childMap = new Map<string, string[]>()
|
||||
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<string, KiwiNodeChange>,
|
||||
parentMap: Map<string, string>
|
||||
childMap: Map<string, string[]>
|
||||
): { internalCanvasIds: Set<string>; internalFigmaIds: Set<string> } {
|
||||
const internalCanvasIds = new Set<string>()
|
||||
for (const [id, nc] of guidMap) {
|
||||
|
|
@ -169,8 +175,8 @@ function findInternalNodeIds(
|
|||
const internalFigmaIds = new Set<string>()
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue