diff --git a/frontend/.Jules/bolt.md b/frontend/.Jules/bolt.md new file mode 100644 index 000000000..6b77a6b25 --- /dev/null +++ b/frontend/.Jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-05-24 - NetworkGraph O(N) array creation bounds +**Learning:** Using `Array.from(map.values()).slice(0, N)` creates an array of the entire map in memory (O(N) operations) before truncating it. This can block the main thread and consume significant memory for large datasets in React components when we only need a few items. +**Action:** Replace `Array.from(iterable).slice(0, N)` with a bounded `for...of` loop that breaks early when the desired number of items is reached, keeping the complexity O(1). diff --git a/frontend/src/components/NetworkGraph.tsx b/frontend/src/components/NetworkGraph.tsx index f9eb61c71..20baf3ab7 100644 --- a/frontend/src/components/NetworkGraph.tsx +++ b/frontend/src/components/NetworkGraph.tsx @@ -277,28 +277,49 @@ export default function NetworkGraph() { } }, [nodes, edges, nodeMap, edgeMap]); + // ⚑ Bolt: Replace O(N) array mapping and filtering with O(1) bounded iteration + // 🎯 Why: Mapping and filtering the entire node array blocks the main thread for large graphs when we only need 5 labels const nodeLabels = useMemo(() => { - return nodes - .map((node) => String(node.label ?? node.id)) - .filter(Boolean) - .slice(0, 5); + const labels = []; + for (const node of nodes) { + if (labels.length >= 5) break; + const label = String(node.label ?? node.id); + if (label) labels.push(label); + } + return labels; }, [nodes]); const firstEdge = edges[0] ?? null; + // ⚑ Bolt: Replace O(N) Array.from().slice() with O(1) bounded iteration + // 🎯 Why: Converting the entire map to an array blocks the main thread for large graphs when we only need 5 items const relationshipOptions = useMemo(() => { - return Array.from(edgeMap.values()).slice(0, 5).map((edge, index) => ({ - edge, - id: String(edge.id), - label: `관계 ${index + 1}: ${describeEdge(edge, nodeMap)}`, - })); + const options = []; + let index = 0; + for (const edge of edgeMap.values()) { + if (options.length >= 5) break; + options.push({ + edge, + id: String(edge.id), + label: `관계 ${index + 1}: ${describeEdge(edge, nodeMap)}`, + }); + index++; + } + return options; }, [edgeMap, nodeMap]); + // ⚑ Bolt: Replace O(N) Array.from().slice() with O(1) bounded iteration + // 🎯 Why: Converting the entire map to an array blocks the main thread for large graphs when we only need 8 items const nodeOptions = useMemo(() => { - return Array.from(nodeInstanceMap.values()).slice(0, 8).map((node) => ({ - id: String(node.id), - label: `λ…Έλ“œ: ${String(node.label ?? node.id)}`, - node, - })); + const options = []; + for (const node of nodeInstanceMap.values()) { + if (options.length >= 8) break; + options.push({ + id: String(node.id), + label: `λ…Έλ“œ: ${String(node.label ?? node.id)}`, + node, + }); + } + return options; }, [nodeInstanceMap]); const selectRelationship = (edge: Edge, status: string) => {