Skip to main content

node-graph

Reusable node-graph editor shell. Schema-agnostic — consumed by panels/modular/ (Songbird Modular DSP graph), and designed for a future video-compositing editor to mount as well. See the modular plan §4.1 in handoffs/future/2026-05-12-songbird-modular.md.

What it owns

  • NodeGraphCanvas.tsx — the layout + viewport-state owner + pointer state machine. Hosts the world-space transformed layer and routes pointer events to pan / drag / cable-drag.
  • NodeBlock.tsx — one block: header, port columns, param widgets. Param widgets pick from Knob (linear/log) or StyledSelect (stepped) based on ParamMeta.taper.
  • Cables.tsx — SVG cubic-Bezier cables between port world positions plus the dashed ghost cable while dragging.
  • AddNodeMenu.tsx — searchable, category-grouped popover for the ”+ Add node” button.
  • portLayout.ts — pure math for port world positions and cable paths. Splits flat port lists into directional columns.
  • types.ts — the contract: NodeMeta, PortMeta, ParamMeta, ConnectionPlacement, NodePlacement, NodeGraphHandlers, Viewport.
  • createGraphStore — Zustand factory for viewport, selection, and drag-in-flight state. Each panel gets its own store instance.

What it doesn’t own

  • Sync engine integration — the consumer wires NodeGraphHandlers to its sync commands.
  • Per-node custom UIs and visualisers — passed in via nodeUiRegistry / visualiserRegistry props.
  • Side drawers (template browsers, inspectors) — passed in via the drawerSlot prop.

Status

Phase 2c (this commit): pan/zoom/drag, cable drag-and-drop with SVG ghost preview, real param widgets (Knob / StyledSelect), and a searchable add-node popover. All DOM — no GL layer yet. Phase 2d (next): TSL cables in a single global R3F Canvas behind the DOM blocks; per-node TSL visualisers via drei <View> mounted by template-id world rects.

Tests

cargo test doesn’t cover React — use ./utils/validate vitest for unit tests next to components, and ./utils/validate ts for type checks.