diff --git a/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx b/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx index 42ec425..fd516e2 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/GraphView.tsx @@ -854,12 +854,9 @@ function buildCustomFlowNode( * Visual overlay: cheap O(n) pass that applies selection, search, and tour * state onto already-positioned nodes. Avoids triggering ELK relayout. * - * TODO(Task 14): selection neighbor highlighting currently walks raw graph - * edges with file-id endpoints. When a neighbor lives inside a collapsed - * container, its container atom should still light up (via - * isFocusedViaChild) — but today it doesn't, because the neighbor set - * is keyed by file ids. Map neighbors through nodeToContainer when - * applying overlays to container nodes. + * Container atoms whose children are focused or selected light up via + * `isFocusedViaChild` — neighbor sets are mapped through `nodeToContainer` + * so collapsed containers still show the relationship. * * Also folds in Stage 2 outputs: * - Expanded children are emitted as React Flow children (`parentId` + diff --git a/understand-anything-plugin/packages/dashboard/src/utils/layout.ts b/understand-anything-plugin/packages/dashboard/src/utils/layout.ts index 359a1a3..cd73fb6 100644 --- a/understand-anything-plugin/packages/dashboard/src/utils/layout.ts +++ b/understand-anything-plugin/packages/dashboard/src/utils/layout.ts @@ -227,12 +227,35 @@ export function mergeElkPositions( nodes: T[], positioned: ElkInput, ): T[] { - const posMap = new Map(); + const positionedMap = new Map< + string, + { x: number; y: number; width?: number; height?: number } + >(); for (const c of positioned.children ?? []) { - posMap.set(c.id, { x: c.x ?? 0, y: c.y ?? 0 }); + positionedMap.set(c.id, { + x: c.x ?? 0, + y: c.y ?? 0, + width: c.width, + height: c.height, + }); } - return nodes.map((n) => ({ - ...n, - position: posMap.get(n.id) ?? n.position ?? { x: 0, y: 0 }, - })); + return nodes.map((n) => { + const merged = positionedMap.get(n.id); + if (!merged) { + return { + ...n, + position: n.position ?? { x: 0, y: 0 }, + }; + } + // Propagate width/height for container nodes so a tick-driven + // Stage 1 re-layout (Task 15) can resize the visible atom to match + // the actual Stage 2 footprint. ELK echoes back the same width/height + // we passed in for non-container nodes, so this is a no-op for them. + return { + ...n, + position: { x: merged.x, y: merged.y }, + ...(merged.width != null ? { width: merged.width } : {}), + ...(merged.height != null ? { height: merged.height } : {}), + }; + }); }