Skip to content

feat(export): add fully offline 3D HTML renderer - #2344

Open
AmeerJ97 wants to merge 5 commits into
Graphify-Labs:v8from
AmeerJ97:feat/offline-3d-html-export
Open

feat(export): add fully offline 3D HTML renderer#2344
AmeerJ97 wants to merge 5 commits into
Graphify-Labs:v8from
AmeerJ97:feat/offline-3d-html-export

Conversation

@AmeerJ97

Copy link
Copy Markdown

Summary

Add an opt-in, natively rendered WebGL view to the existing HTML export:

graphify export html --viz 3d

The 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 graph3d command or a one-off boolean flag:

  • it preserves graphify export html as the canonical export pipeline;
  • 2d remains backwards-compatible and byte-identical by default;
  • future renderers can extend the same mode boundary;
  • node-limit aggregation cannot accidentally fall back to a different renderer;
  • /graphify, cluster-only, label, and explicit HTML export can share the same selection semantics.

Renderer and offline packaging

This uses 3d-force-graph 1.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 with importlib.resources:

  • exact version: 1.80.0;
  • exact asset size: 1,313,897 bytes;
  • SHA-384: 63b6c2d8f04abbcba3c6dbe8e7e67ad4e786752551cc5b185812b88b97672ff53a6850d3a1d93ad6a3949137c89dac52;
  • upstream MIT license and transitive runtime notices are included;
  • wheel and sdist package-data declarations are tested by a clean wheel install;
  • literal closing-script sequences are neutralized before embedding.

This intentionally adds no Python runtime dependency and no JavaScript build toolchain to Graphify.

3D interaction model

The 3D view provides:

  • orbit, pan, zoom, reset, and freeze controls;
  • node search and click-to-inspect details;
  • delegated neighbor navigation without inline event handlers;
  • community legend filtering and isolate/toggle shortcuts;
  • selection-centered 1–6 hop focus;
  • opt-in bounded node-name overlays;
  • hyperedge star expansion with clickable hub nodes;
  • learning-status coloring;
  • a clear recovery message directing users to --viz 2d when 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 bounded many-body distance;
  • weak center gravity for disconnected components;
  • bounded warmup and cooldown ticks;
  • graph-size-adaptive warmup (150 / 90 / 50 ticks);
  • arrow-geometry degradation only above 4,000 edges;
  • a 48-label normal budget and 150-label narrowed-view budget;
  • no animation-frame loop while labels are disabled.

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

  • Graph data continues through js_safe, including </script> boundary protection.
  • Tooltip content is escaped once rather than double-escaped.
  • Neighbor selection uses escaped data-nid values and a delegated listener, with no inline onclick interpolation.
  • The 3D page has no external <script src>, stylesheet, font, or texture dependency.
  • Missing and invalid --viz values now fail explicitly instead of silently producing a 2D page.
  • WebGL capability is checked before renderer initialization and produces an actionable in-page fallback message.

Compatibility

  • Python remains >=3.10; the implementation uses Python 3.10-compatible typing and importlib.resources.
  • Existing callers receive the 2D renderer by default.
  • A regression test verifies default output equals explicit mode="2d" output.
  • GRAPHIFY_VIZ_MODE=3d is supported, with an explicit function/CLI argument taking precedence.
  • Community aggregation propagates the requested renderer mode.

Verification

  • uv sync --all-extras --frozen
  • full suite: 3,933 passed, 3 skipped
  • focused 3D/CLI suite on Python 3.10: 67 passed
  • focused 3D/CLI suite on Python 3.12: 67 passed
  • uv run --frozen ruff check .
  • uv run --frozen pre-commit run --all-files
  • targeted Pyright for the changed exporter/tests: 0 errors
  • uv build --sdist --wheel
  • twine check on wheel and sdist: passed
  • clean wheel install generated an offline 3D page successfully
  • headless Chrome with DNS mapped to failure rendered a Three.js canvas
  • headless Chrome with GPU disabled rendered the WebGL recovery guidance
  • Opengrep: no applicable feature finding (one inapplicable Python-3.7 compatibility note; Graphify requires 3.10+)
  • Open Code Review: zero comments across all reviewed production files

Bandit 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:

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.

lopezonchain and others added 5 commits July 31, 2026 04:53
- 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.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_htmlgraphify/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):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regressionrender()

19 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants