Graph view: rendering settings panel (node size, edge width, arrowhead)

Three sliders behind a gear in the search panel (user-decided
2026-09-13), persisted per browser in localStorage with clamped
sanitizing on load. Default node size dropped 8 → 6 for dense graphs.

Mechanics: the mount effect is split — the data effect builds the
graphology graph + one frozen forceAtlas2 pass per snapshot; the render
effect mounts sigma and re-runs on settings change only. Sizes are
stamped onto the same graph instance (layout never recomputed), and the
arrowhead program is constructed per rebuild with ratios solved from
the settings (head px = thickness × lengthToThicknessRatio × 2; wideness
keeps the stock 0.8 shape). The camera position carries across rebuilds
via a ref, so a slider tweak never re-fits or re-lays-out the view;
useDeferredValue smooths slider drags.

Live-verified in the browser: sliders persist to localStorage, the
renderer rebuilds with zero console errors, hover/picking survives the
rebuild, and a zoomed-out camera stays zoomed out across a settings
change (labels-gate opacity as the observable). 355/355 tests,
typecheck, lint, build green.
This commit is contained in:
amosblomqvist
2026-09-13 02:54:36 +03:00
parent c3cc55c585
commit d6b229865a
2 changed files with 208 additions and 28 deletions
+2
View File
@@ -95,6 +95,8 @@ When the graph fills up, the canvas needs a readable at-a-glance structure: **cl
### Rendering (DECIDED — research report 2026-08-22)
**Render settings (2026-09-13):** three basic controls in the graph view — node size, edge width, arrowhead length — persisted per browser. Settings changes rebuild only the renderer: layout positions and the camera never move. (Edge-length / spacing controls deliberately parked until a cluster-aware spacing design exists.)
- **Library: sigma.js v3 + graphology + graphology-layout-forceatlas2.** ~40 KB gzipped all-in — half of vis-network, a third of cytoscape, far below react-force-graph (~250 KB deps) and cosmos.gl (luma.gl stack).
- **Layout: synchronous run-then-freeze.** `forceAtlas2.assign(graph, { iterations: 100, settings: inferSettings(graph) })` runs once, writes x/y, and no animation loop ever starts — near-zero idle CPU (sigma only redraws on camera/graph events). <150 ms at hundreds of nodes; at ~2k enable `barnesHutOptimize`, then the bundled Web Worker, then server-cached positions — in that order, only if latency bites.
- **Labels at zoom:** sigma's `labelRenderedSizeThreshold` replicates Obsidian's "text fade threshold" exactly.