fix(dashboard): address final review C1 + C2

C1 (cache leak across topology changes):
Container ids derive from folder names (`container:auth`, etc.) and
collide across layers, personas, and node-type filters. Without a
reset, navigating from Layer A to Layer B left Stage 2's cache and
size memory keyed by Layer A children, so Stage 2 short-circuited
and Layer B's children silently disappeared.

Reset containerLayoutCache, containerSizeMemory, and
expandedContainers in:
- drillIntoLayer (most visible — layer drill)
- navigateToOverview (drilling out)
- toggleNodeTypeFilter (filter change shifts container.nodeIds)
- setPersona (persona filters node types)

setGraph already resets all three on full graph reload — unchanged.

C2 (bundle regression):
Add elkjs and graphology rules to vite manualChunks. The main index
chunk drops from 525KB gzipped back to 62KB, with ELK split into a
parallel-loadable 439KB chunk. Restores baseline first-paint cost.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lum1104
2026-05-03 17:14:04 +08:00
Unverified
parent 1329c93dbd
commit 8d068549cb
2 changed files with 27 additions and 1 deletions
@@ -244,6 +244,12 @@ export const useDashboardStore = create<DashboardStore>()((set, get) => ({
...state.nodeTypeFilters,
[category]: !state.nodeTypeFilters[category],
},
// Filter changes shift container.nodeIds; cached child positions
// may reference filtered-out children. Drop the cache so Stage 2
// recomputes against the current set.
containerLayoutCache: new Map(),
containerSizeMemory: new Map(),
expandedContainers: new Set(),
})),
setGraph: (graph) => {
@@ -359,6 +365,12 @@ export const useDashboardStore = create<DashboardStore>()((set, get) => ({
codeViewerOpen: false,
codeViewerNodeId: null,
codeViewerExpanded: false,
// Container ids derive from folder names and collide across layers
// (e.g. `container:auth` exists in many layers). Drop the cache so
// we don't render stale positions for the new layer's children.
containerLayoutCache: new Map(),
containerSizeMemory: new Map(),
expandedContainers: new Set(),
}),
navigateToOverview: () =>
@@ -370,6 +382,9 @@ export const useDashboardStore = create<DashboardStore>()((set, get) => ({
codeViewerOpen: false,
codeViewerNodeId: null,
codeViewerExpanded: false,
containerLayoutCache: new Map(),
containerSizeMemory: new Map(),
expandedContainers: new Set(),
}),
setFocusNode: (nodeId) => set({ focusNodeId: nodeId, selectedNodeId: nodeId }),
@@ -388,7 +403,14 @@ export const useDashboardStore = create<DashboardStore>()((set, get) => ({
set({ searchQuery: query, searchResults });
},
setPersona: (persona) => set({ persona }),
setPersona: (persona) =>
set({
persona,
// Persona changes filter node types, which shifts container.nodeIds.
containerLayoutCache: new Map(),
containerSizeMemory: new Map(),
expandedContainers: new Set(),
}),
openCodeViewer: (nodeId) =>
set({ codeViewerOpen: true, codeViewerNodeId: nodeId, codeViewerExpanded: false }),
@@ -207,6 +207,10 @@ export default defineConfig({
return "react-vendor";
}
if (id.includes("node_modules/@xyflow/")) return "xyflow";
// ELK is ~1.6MB raw — split into its own chunk so it doesn't
// bloat the main bundle. graphology is similarly large.
if (id.includes("node_modules/elkjs/")) return "elk";
if (id.includes("node_modules/graphology")) return "graphology";
if (
id.includes("node_modules/@dagrejs/") ||
id.includes("node_modules/d3-force/")