fix(visualization): graph renders in legend icon instead of main canvas#477
Open
vaguemit wants to merge 1 commit into
Open
fix(visualization): graph renders in legend icon instead of main canvas#477vaguemit wants to merge 1 commit into
vaguemit wants to merge 1 commit into
Conversation
## Problem
When opening the generated graph.html in a browser, the entire D3.js
force-directed graph (all nodes, edges, zoom behavior, simulation) was
rendering inside a tiny 16x16 pixel SVG icon in the legend sidebar
under the 'NODES' heading, rather than on the full-viewport canvas.
### Root Cause 1 — Wrong SVG element selected (primary bug)
Both _HTML_TEMPLATE and _AGGREGATED_HTML_TEMPLATE used:
var svg = d3.select('svg') // full mode
var svgEl = document.querySelector('svg') // aggregated mode
d3.select('svg') and document.querySelector('svg') select the FIRST
<svg> element in the DOM. The legend contains inline SVG icons for each
node shape (circle, square, triangle, diamond, cross) that appear before
the main <svg id='graph-svg'> canvas in document order. D3 therefore
appended all graph content into the first 16x16 legend icon.
### Root Cause 2 — Dimensions captured as zero at parse time
On file:// URLs, the SVG element has not been laid out by the browser
when the inline <script> executes. This causes svgEl.clientWidth and
svgEl.clientHeight to return 0, so the force simulation centers every
node at (0, 0) via:
.force('center', d3.forceCenter(W/2, H/2)) // -> (0, 0)
.force('x', d3.forceX(W/2)) // -> 0
.force('y', d3.forceY(H/2)) // -> 0
After the simulation settles all nodes are piled at the origin, and
fitGraph() finds a zero-sized bounding box and silently returns.
## Fix
### 1. Use ID selectors throughout (fixes primary bug)
- Added id='graph-svg' to the <svg> element in _AGGREGATED_HTML_TEMPLATE
(it was already present in _HTML_TEMPLATE)
- Changed all selectors from d3.select('svg') / document.querySelector('svg')
to d3.select('#graph-svg') / document.getElementById('graph-svg')
in both templates
### 2. Re-center nodes when simulation ends with correct dimensions
In simulation.on('end') for both templates:
- Re-read W/H using getW()/getH() (which query svgEl.clientWidth/Height)
- If dimensions changed significantly from initial capture (or were < 100px),
translate all node x/y positions by the delta so the cluster moves to
the true viewport center
- Reset forceCenter, forceX, forceY with corrected W/H values
- Update the SVG viewBox
### 3. Robust fitGraph() with requestAnimationFrame retry
Replaced the silent early-return on zero BBox with a retry loop:
function fitGraph(retries) {
if (retries === undefined) retries = 10;
var b = gRoot.node().getBBox();
if (b.width === 0 || b.height === 0) {
if (retries > 0) requestAnimationFrame(function() { fitGraph(retries - 1); });
return;
}
// ... compute transform and apply ...
}
Uses requestAnimationFrame instead of setTimeout for accurate
post-paint timing on both file:// and http:// origins.
### 4. Live dimension helpers (getW / getH)
Both templates already had getW()/getH() helpers querying
svgEl.clientWidth/clientHeight with window.innerWidth/Height fallback.
These are now consistently used everywhere dimensions are needed,
including the resize event listener.
## Files changed
- code_review_graph/visualization.py: _HTML_TEMPLATE and
_AGGREGATED_HTML_TEMPLATE JS sections updated
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When opening the generated graph.html in a browser, the entire D3.js force-directed graph (all nodes, edges, zoom behavior, simulation) was rendering inside a tiny 16x16 pixel SVG icon in the legend sidebar under the 'NODES' heading, rather than on the full-viewport canvas.
Root Cause 1 — Wrong SVG element selected (primary bug)
Both _HTML_TEMPLATE and _AGGREGATED_HTML_TEMPLATE used:
d3.select('svg') and document.querySelector('svg') select the FIRST element in the DOM. The legend contains inline SVG icons for each node shape (circle, square, triangle, diamond, cross) that appear before the main canvas in document order. D3 therefore appended all graph content into the first 16x16 legend icon.
Root Cause 2 — Dimensions captured as zero at parse time
On file:// URLs, the SVG element has not been laid out by the browser when the inline <script> executes. This causes svgEl.clientWidth and svgEl.clientHeight to return 0, so the force simulation centers every node at (0, 0) via:
After the simulation settles all nodes are piled at the origin, and fitGraph() finds a zero-sized bounding box and silently returns.
Fix
1. Use ID selectors throughout (fixes primary bug)
2. Re-center nodes when simulation ends with correct dimensions
In simulation.on('end') for both templates:
3. Robust fitGraph() with requestAnimationFrame retry
Replaced the silent early-return on zero BBox with a retry loop:
Uses requestAnimationFrame instead of setTimeout for accurate post-paint timing on both file:// and http:// origins.
4. Live dimension helpers (getW / getH)
Both templates already had getW()/getH() helpers querying svgEl.clientWidth/clientHeight with window.innerWidth/Height fallback. These are now consistently used everywhere dimensions are needed, including the resize event listener.
Files changed