Skip to content

One canvas, drawn once - #499

Merged
WaylandYang merged 1 commit into
devfrom
feat/one-canvas
Sep 9, 2026
Merged

WaylandYang merged 1 commit into
devfrom
feat/one-canvas

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

Closes #496. Prerequisite for #497.

Graph.tsx and OntologySchemaGraph.tsx each built a full sigma renderer, and the two were the same machine with different data. graphCanvas.ts is now that machine, once.

What moved

The constructor. sigmaOptions() holds every setting that decides how a canvas looks and behaves: the four-layer node program, the square program, the fonts, the three label-visibility settings, the label and hover drawing functions, edge events. Six things genuinely differ between the two pages and are parameters — the edge program set, the camera ratio limits, renderEdgeLabels, minEdgeThickness — each with a comment saying why. The comment recording the two label-threshold experiments now exists once instead of verbatim twice.

The interaction state table. hoveredNode, selectedNode, neighborNode, mutedNode, softMutedNode. Each page's reducer keeps its own logic — which node is in which state is a page question, and the two answer it differently — and calls these to paint. The graph page's reducer keeps its time filter and playback fade; the schema page's keeps its "a selected relation lights its endpoints" branch.

The camera. nodePosition, nodeInView, focusNode were on the schema page and are now shared, because #497 needs them on the graph page.

The drag. attachDrag holds the shape both had: press records a candidate, four pixels of movement promotes it to a drag, the bounding box freezes for the duration and is released on mouseup. What the two pages do during a drag differs and stays theirs, as three callbacks — the graph page feeds the cursor into the force simulation, the schema page records the position into its own map.

lerpColor and parseRgba move to graphVisuals.ts beside mix, with a note on the division of labour: mix takes hex and drives the node tint recipe, lerpColor takes rgba() too and drives fades.

One behaviour changes

The mute factor was 0.52 on one page and 0.55 on the other, with nothing anywhere saying why. The design notes say 0.52, so that is what the shared MUTE_SCALE is, and the schema page's muted nodes are now 3% smaller. Invisible at that size — and it is exactly the drift this extraction exists to stop.

Everything else is a move. The reducers, the drag, the camera helpers are the same code in a different file.

Verified

Against a copy of the benchmark base — 1415 entities, 916 classes — with a dev server on this branch:

  • The state table returns the old numbers, read off the live renderer. Base 13 renders at 9.1 (×0.7 default); selected at 13.26 (×1.02) with zIndex 3 and its ring; a neighbour with base 8.2 at 6.232 (×0.76); a non-neighbour at 6.76 (×0.52) with its label cleared. Hover puts a node at zIndex 4.
  • Drag works on both pages. On the graph page downNode fired, the node moved 117 graph units, and the custom bounding box was set during the drag and cleared on release. On the schema page a node dragged from the right edge to the bottom right and stayed there.
  • Selection works on both, including the schema page's camera move, which goes through the relocated focusNode and nodeInView.
  • pnpm build clean: style guard, tsc, vite.

No visual change is expected, so there is nothing new to look at; the screenshots above are the two pages rendering as before.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: WaylandYang <wayland0916@gmail.com>
@WaylandYang
WaylandYang merged commit 3601089 into dev Sep 9, 2026
4 checks passed
@WaylandYang
WaylandYang deleted the feat/one-canvas branch September 9, 2026 02:39
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.

Two pages each build their own sigma canvas

1 participant