feat(export): add fully offline 3D HTML renderer - #2344
Conversation
- New WebGL renderer (3d-force-graph, SRI-pinned like vis-network), selected with --viz 2d|3d on the main pipeline, cluster-only/label and export html, or with GRAPHIFY_VIZ_MODE. - to_html() now builds a renderer-agnostic view model both renderers consume. 2d stays the default and its output is byte-identical, guarded by a test. - 3D navigation: search, click-to-inspect, community filters, a "Show" control isolating 1-6 hops around the selection, an opt-in name overlay and keyboard shortcuts. Neighbour links keep the escaped data-nid listener from Graphify-Labs#1838. - Skill flag list gains --viz 3d via the core fragment; the aider/devin monoliths stay frozen against their pinned v8 blob.
There was a problem hiding this comment.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify review — findings
This PR adds an opt-in --viz 3d mode that renders graph.html as an offline WebGL 3D view using a pinned, embedded 3d-force-graph bundle, while keeping the existing 2D renderer as the default. The CLI is updated to parse and validate the new --viz 2d|3d flag on both the cluster/build path and the export html subcommand, threading the mode through to to_html. Supporting changes include documentation/CHANGELOG updates, README wording for the >5000-node aggregate behavior, a .gitignore entry, and the vendored 3d-force-graph library plus related skill/expectation fixtures.
Worth a look
- --viz value not normalized before passing to to_html —
graphify/cli.py:1912· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 4691 functions depend on the 4463 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
_make_graph()— 27 callers, 6 callees - new:
$o()— 14 callers, 10 callees - worse:
to_html()— 19 callers, 7 callees - new:
Xt()— 80 callers, 1 callees - new:
qt()— 64 callers, 1 callees - new:
render()— 19 callers, 3 callees - new:
getPoint()— 5 callers, 8 callees - new:
op()— 12 callers, 3 callees - …and 38 more
Verification — 4691 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 4648 function(s) in the blast radius were not formally verified this run
· 1 grounded finding(s) anchored inline below; 45 more finding(s) on lines outside this diff (see the check run).
| return build_from_json(json.loads((FIXTURES / "extraction.json").read_text())) | ||
|
|
||
|
|
||
| def render(mode=None, **kwargs): |
There was a problem hiding this comment.
render()
19 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
Summary
Add an opt-in, natively rendered WebGL view to the existing HTML export:
graphify export html --viz 3dThe default remains
--viz 2d. Both renderers consume the same sanitized view model, so community labels, colors, node sizing, edge direction/confidence, learning status, and aggregated community views stay consistent.Unlike the current 2D CDN page and earlier 3D proposals, the generated 3D document embeds the exact reviewed renderer bundle. It can be copied to an offline or restricted machine and opened directly in a browser without a server or network request.
Why this shape
A renderer selector is preferable to a standalone
graph3dcommand or a one-off boolean flag:graphify export htmlas the canonical export pipeline;2dremains backwards-compatible and byte-identical by default;/graphify,cluster-only,label, and explicit HTML export can share the same selection semantics.Renderer and offline packaging
This uses
3d-force-graph1.80.0, whose UMD distribution bundles Three.js/WebGL, d3-force-3d, and its browser runtime dependencies.The reviewed minified asset is packaged under
graphify/vendor/and embedded in the output withimportlib.resources:1.80.0;63b6c2d8f04abbcba3c6dbe8e7e67ad4e786752551cc5b185812b88b97672ff53a6850d3a1d93ad6a3949137c89dac52;This intentionally adds no Python runtime dependency and no JavaScript build toolchain to Graphify.
3D interaction model
The 3D view provides:
--viz 2dwhen WebGL is unavailable.Community names come from
.graphify_labels.json. Colors remain deterministic by community ID, while node scale follows degree or aggregated member count, matching the existing 2D semantics.Large-graph safety and performance
This does not remove Graphify's existing safety boundary. The default 5,000-node limit and community aggregation behavior are preserved for both renderers.
The force layout uses:
A reproducible synthetic 5,000-node path-graph check in headless Chrome with SwiftShader and networking disabled improved from 17.85 seconds with a constant 150-tick warmup to 7.96 seconds with the adaptive policy. This is a guardrail measurement, not a universal throughput claim; force-layout cost still depends heavily on graph density and browser/GPU.
Security and failure handling
js_safe, including</script>boundary protection.data-nidvalues and a delegated listener, with no inlineonclickinterpolation.<script src>, stylesheet, font, or texture dependency.--vizvalues now fail explicitly instead of silently producing a 2D page.Compatibility
>=3.10; the implementation uses Python 3.10-compatible typing andimportlib.resources.mode="2d"output.GRAPHIFY_VIZ_MODE=3dis supported, with an explicit function/CLI argument taking precedence.Verification
uv sync --all-extras --frozenuv run --frozen ruff check .uv run --frozen pre-commit run --all-filesuv build --sdist --wheeltwine checkon wheel and sdist: passedBandit and pip-audit retain pre-existing repository/lockfile findings; this change adds no Python dependency and introduced no new finding in the changed source.
Prior work and coordination
This is intended as a consolidation/successor rather than unrelated duplicate work:
v8.graphify graph3d— interactive 3D force-directed HTML viewer #2170 and feat: add --3d flag for 3D HTML visualization #225.Maintainers can use this PR as the consolidation point or ask me to narrow/restructure it around #2235; authorship of the foundational renderer commit has deliberately been preserved.
Reviewer notes
The generated skill artifacts account for most of the file count: the human-edited source is
tools/skillgen/fragments/core/core.md, and the checked artifacts were regenerated through the repository's normal skillgen workflow. The core implementation surface is limited to the CLI, shared HTML model, new 3D renderer, vendored browser asset/notices, tests, and documentation.