🚨 CRITICAL BUG: Empty Visualization

Issue: "I still see no nodes" despite data loading correctly

Affected: All graphs with > 500 nodes (currently: 1449 nodes)

Current Broken Flow

Step 1: User opens visualization
window.addEventListener('DOMContentLoaded', ...)
Step 2: Data loads successfully ✅
fetch("chunk-graph.json") .then(data => { allNodes = data.nodes; // ✅ 1449 nodes allLinks = data.links; // ✅ 360825 links
Step 3: Branch based on node count ⚠️
if (data.nodes.length > 500) { // 1449 > 500 → TRUE layoutSelector.value = 'dagre'; switchToCytoscapeLayout('dagre'); // ⚠️ SKIP visualizeGraph() } else { visualizeGraph(data); // ← NEVER CALLED! }
Step 4: switchToCytoscapeLayout() runs ❌
function switchToCytoscapeLayout(layoutName) { // ... const visibleNodesList = allNodes.filter(n => visibleNodes.has(n.id)); // ↑ // visibleNodes.size = 0 (NEVER INITIALIZED!) // Result: visibleNodesList = [] ❌
Step 5: No nodes added to Cytoscape ❌
visibleNodesList.forEach(node => { // Empty array - NEVER EXECUTES! cyElements.push({ data: { id: node.id, ... } }); });
Step 6: Cytoscape renders empty graph ❌
cy = cytoscape({ container: cyContainer, elements: cyElements, // [] - EMPTY! ... });
Result: User sees blank screen 🚨

The Missing Initialization

// In visualizeGraph() - line 1417-1418 (NEVER CALLED for large graphs) function visualizeGraph(data) { // ... visibleNodes = new Set(rootNodes.map(n => n.id)); // ← THIS IS SKIPPED! collapsedNodes = new Set(rootNodes.map(n => n.id)); // ... }

State Comparison

Variable Expected (< 500 nodes) Actual (> 500 nodes)
allNodes.length 1449 ✅ 1449 ✅
allLinks.length 360825 ✅ 360825 ✅
visibleNodes.size ~50 (root nodes) ✅ 0 ❌
visibleNodesList.length ~50 ✅ 0 ❌
cyElements.length ~100 (nodes + edges) ✅ 0 ❌
cy.nodes().length ~50 ✅ 0 ❌

The Fix (Option 3 - Simplest)

// Line 2762-2769 - AFTER FIX const layoutSelector = document.getElementById('layoutSelector'); // ALWAYS initialize through visualizeGraph visualizeGraph(data); // ✅ Initializes visibleNodes, collapsedNodes, rootNodes // Then switch to Dagre for large graphs if (layoutSelector && data.nodes && data.nodes.length > 500) { layoutSelector.value = 'dagre'; switchToCytoscapeLayout('dagre'); // ✅ NOW visibleNodes is initialized! }

Why This Fix Works

Consistent initialization path - all graphs go through visualizeGraph()
No code duplication - root node logic stays in one place
visibleNodes always initialized - works for all graph sizes
Graceful layout switching - D3 → Cytoscape for large graphs
Minimal performance impact - visualizeGraph is fast, Dagre overwrites layout

Testing Checklist

☐ Test with < 500 nodes (should still work)
☐ Test with > 500 nodes (currently broken, should fix)
☐ Test with 1449 nodes (user's actual case)
☐ Verify Dagre layout shows root nodes
☐ Verify node expansion/collapse still works
☐ Verify layout switching (force ↔ dagre ↔ circle)
☐ Check browser console for errors

🚨 Impact Assessment

Severity: CRITICAL

Users Affected: 100% of users with > 500 nodes (medium-to-large projects)

Symptoms: Blank visualization, no error message

Data Loss: None (data loads correctly, just not displayed)

Fix Complexity: LOW (3 line change)

Recommended Action: Apply fix immediately and test