🚨 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