feat: replace d3 canvas graph renderer with Sigma.js WebGL renderer#412
feat: replace d3 canvas graph renderer with Sigma.js WebGL renderer#412shaunpatterson wants to merge 9 commits into
Conversation
Swap the hand-rolled d3-force canvas renderer (~1100 lines of manual hit-testing, arc math and label culling) for sigma.js v3 + graphology: - WebGL rendering scales to tens of thousands of nodes (the canvas renderer struggles past a few hundred); labels get automatic collision/density handling. - ForceAtlas2 layout runs in a web worker, so the UI thread never blocks during layout; it auto-stops after 4s. - Parallel edges between the same node pair fan out as distinct curves (@sigma/edge-curve), preserving the sibling-edge behavior. - Nodes sized by degree (capped); neighbor highlighting on hover; drag-to-pin; double-click to expand/collapse preserved. - Implements the GraphContainer ref API (searchNode/focusNode/ zoomToFit) with the same matching semantics as the d3 renderer. - buildGraph keeps the d3-force contract of resolving edge source/target uids to node objects in place - EdgeProperties and GraphParser.collapseNode depend on that mutation. Covered by 12 unit tests. - sigma is mocked under Jest (jsdom has no WebGL2RenderingContext). - scripts/graph-smoke.mjs: puppeteer smoke test that seeds a local Dgraph, logs in, runs a query through the real UI and asserts the WebGL canvases render and search/zoomToFit work. - Drop the d3 dependency (D3Graph was its only consumer). Verified: 12/12 buildGraph tests, production build, and the browser smoke test against Dgraph v25 with ACL (3 nodes / 3 edges rendered via WebGL, search + zoom-to-fit exercised, zero page errors). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three graph-view features on top of the sigma renderer: - Style rules (Neo4j Bloom-style): a Graph styles panel in the toolbar lists every group in the current result with a color picker and node size slider; overrides apply live via sigma reducers and persist in localStorage. lib/graphStyles.js (sanitize/persist/merge) has 11 unit tests. - Layout switcher: Force (ForceAtlas2 worker), Circular, and Packed (circlepack clustered by group) via a toolbar select; static layouts auto-fit the camera. - Legend filtering: clicking a predicate chip in the entity selector hides/shows that predicate's nodes and edges without re-querying; hidden chips render dimmed with strikethrough. Verified in a real browser against Dgraph v25: layouts switch, style panel renders per-group rows and applies color changes, legend chips toggle - zero page errors. Unit suite green, production build passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Scope update: this PR now also includes the graph toolkit built on top of the new renderer (
All three were exercised in a real browser against a live Dgraph v25 (layout switching, style panel color changes, chip toggling) with zero page errors. Unit suite and production build green. |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add Louvain community detection and betweenness centrality (graphology ecosystem) as optional node rendering modes alongside the existing predicate-color / degree-size defaults: - Color by: Predicate (default) or Community - Size by: Degree (default), Centrality (betweenness), or Uniform Metrics are annotated onto the graphology graph in buildGraph and applied in the SigmaGraph node reducer, so default rendering is byte-for-byte unchanged. Betweenness is skipped above 1500 nodes to keep expansion responsive. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a path-finding mode to the graph toolbar. Toggle it on, click a source node then a target, and the shortest route between them (computed breadth-first over the currently rendered subgraph, edges treated as undirected) is highlighted while the rest of the graph dims back. A banner reports the hop count or that no path exists, with a Clear button. Path-finding runs client-side on the loaded graph, so it needs no extra server round-trip and works on exactly what the user can see. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a filter panel to the graph toolbar that hides nodes failing a connectivity (degree) range or an attribute predicate (contains / = / ≠ / > / < / exists). Edges drop out with either endpoint. The panel previews how many nodes are hidden and clears in one click. Filtering is applied in the SigmaGraph reducers via a precomputed hidden-node set, recomputed only when the filter spec or dataset changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Detect ISO datetime attributes on nodes and, when the dataset spans a range, offer a timeline control: a clock toggle reveals a scrubber with play/pause that reveals nodes as their earliest timestamp passes. Nodes without a time stay as structural context; edges drop with either hidden endpoint. The cutoff is applied in the SigmaGraph reducers against a node _time attribute annotated in buildGraph, so scrubbing/playback is a cheap refresh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scope update: four graph-exploration features addedBuilding on the Sigma.js renderer, this branch now also includes four analyst-focused features (each its own commit, each with unit tests; verified end-to-end in the real UI against a local Dgraph):
All applied via the existing SigmaGraph reducer architecture. New deps: |
|
SigmaJS v4 is coming along. There's been a lot of development on that line over the past year. I'd recommend checking that out for long-term support/stability of the product. It improves on SigmaJS v3 quite a bit. Looks like they're also actively developing it and taking feedback with rapid fixes when things are found. gdotv is using it too. Discussion Post: jacomyal/sigma.js#1539 (reply in thread) |
Sigma v4 is now stable enough for real projects (per the jacomyal/sigma.js announcement), so swap in the v4 alpha release. Highlights of the migration: - Drop @sigma/edge-curve: v4 has curved paths built in (pathLine/pathCurved). Edge attributes change from type:'arrow'/'curvedArrow' to path:'line'/'curved' plus an arrow head via extremityArrow(). - Drop edgeProgramClasses / defaultEdgeType in favour of the declarative primitives block (shapes, layers, paths, extremities). - Replace manual node-drag handlers (downNode/moveBody/upNode, setCustomBBox) with v4's built-in enableNodeDrag + autoRescale: 'once' so the viewport doesn't recentre as nodes are dragged. - Keep nodeReducer / edgeReducer as escape hatches: v4 still accepts them and the reducer logic is unchanged, only the signature is widened to (key, data, attrs, state, graphState, graph). - Update sigmaMock.js for the v4 surface (setNodeState/setGraphState, getNodeDisplayData, new module subpaths sigma/types/settings/utils) and the buildGraph test for the new path/head edge attributes.
What
Replaces the hand-rolled d3-force canvas renderer (~1100 lines of manual hit-testing, arc geometry and label culling) with sigma.js v3 + graphology, the WebGL graph rendering stack used for large-scale network visualization.
Why
graphology-layout-forceatlas2/worker) — the UI thread never blocks during layout. Auto-stops after 4s.Feature parity
@sigma/edge-curve)searchNode/focusNode/zoomToFitref API (toolbar)highlightPredicate, active edge emphasisOne contract worth calling out: d3-force used to mutate edge
source/targetfrom uid strings into node object references, and bothEdgePropertiesandGraphParser.collapseNodedepend on that.buildGraphpreserves the mutation, with unit tests pinning it.Testing
buildGraph(graphology construction, endpoint resolution, curvature fanning, degree sizing, position reuse): 12/12 tests pass.WebGL2RenderingContext(full-suite repair is fix: repair broken Jest test suite (16/18 suites failing) #410).npm run build(webpack 5) passes.client/scripts/graph-smoke.mjs, included): seeds a local Dgraph v25 (ACL) cluster, logs in through the real UI, runs a query, asserts the WebGL canvas layers render the expected graph, and exercises search + zoom-to-fit. Passes with zero page errors.d3is dropped from dependencies (D3Graph was its only consumer).🤖 Generated with Claude Code