Node canvasPro
A node graph editor for media workflows: typed ports that only take matching wires, pan, pinch and zoom at the pointer, a minimap, snap to grid, live run state and a complete keyboard path.
On this page
Installation
Usage
import { NodeCanvas } from "@/components/ui/chatcn/pro/node-canvas"
import { mediaNodeTypes } from "@/components/ui/chatcn/pro/node-card-set"
<NodeCanvas
nodes={nodes}
onNodesChange={setNodes}
edges={edges}
onEdgesChange={setEdges}
nodeTypes={mediaNodeTypes}
/>Nodes and wires are plain data, controlled or uncontrolled. A node lists its inputs and outputs as typed ports and carries its run state (status, progress, statusDetail, error); you update those from your job events and the canvas draws them. nodeTypes maps node.type to a renderer; anything without one gets the plain frame with its ports, so the canvas works on its own.
A wire only lands on a port of the same type (or any), never on its own node and never where it would make a loop. A refused drop says why in words, such as "Text can't go into an image input". A single input swaps its old wire for the new one; set multiple on an input that collects several. Add your own rule with validateConnection.
Examples
Typed ports
Each type has its own color and shape (image circle, text square, mask diamond, video triangle, audio and number bars, any ring), so a port never depends on color alone. While you wire, matching ports are ringed and the rest step back.
States
Props
| Prop | Type | Notes |
|---|---|---|
nodes, defaultNodes, onNodesChange | CanvasNode[] | Position, ports, run state and data |
edges, defaultEdges, onEdgesChange | CanvasEdge[] | source, sourcePort, target, targetPort |
nodeTypes | Record<string, NodeRenderer> | Renderers get node, selected, update and updateData |
selection, defaultSelection, onSelectionChange | string[] | Selected node ids |
onConnect | (edge, replaced?) => void | After a wire lands |
validateConnection | (source, target) => string | null | Return a reason to refuse |
onNodeDrop | (type, at) => void | A NodePalette entry dropped on the plane |
onNodeRetry | (id) => void | Shows Retry on failed nodes |
readOnly | boolean | Pan and zoom only |
minimap, controls | boolean | Both on by default; the minimap hides under 520 px |
snapGrid | number | Default 8. 0 turns snapping off |
wireColor | "type" | "neutral" | Default type |
defaultViewport, minZoom, maxZoom | Without a viewport the graph fits on mount | |
apiRef | Ref<NodeCanvasApi> | fitView, zoomIn, zoomOut, center, reveal, setViewport |
toolbar, emptyState, loading, label | Top start slot, empty copy, loading label, accessible name |
Build your own node types on NodeFrame (the header, ports, body and error footer) and NodePort; StandaloneNode renders one outside a canvas. chatcn-pro-studio-2b-graph exports the pure parts for the server: checkConnection, connect, runOrder, fitView and the port type styles.
Keyboard
| Key | Action |
|---|---|
Tab | The plane, then each node, its ports and controls |
| Arrows on the plane | Pan; Shift pans further |
| Arrows on a node | Move it, and any selected nodes with it, by the grid; Shift moves 5 steps |
Space on a node | Add it to or take it out of the selection |
Enter on a node | Go to its first control; Esc in a control comes back |
Enter on a port | Start a wire; Enter on a matching port ends it, Esc cancels |
Delete on a port | Remove its wires |
Delete, Backspace | Remove the selected nodes and wires |
⌘ or Ctrl + A | Select every node |
+, - | Zoom in and out |
Shift + 1, Shift + 0 | Fit to view, zoom to 100% |
Accessibility
- The plane is a labelled group with a description of how to pan, zoom, move and wire. Nodes are groups named by their title, then their run state: "Generate, running 60%".
- Ports are buttons whose name starts with their visible label, then the type, side and what they are wired to: "Prompt, text input, wired to Rewrite prompt".
- Wiring, refused drops, removals and deletes are announced in a polite live region. A refused drop also shows its reason on screen.
- The minimap is a pointer shortcut and hidden from assistive tech; the keyboard pans the plane directly. In forced colors the canvas, nodes and controls keep real outlines.
Motion
- Dragging, panning and pinching track 1:1. Zoom and fit from the control buttons glide in 280 ms; from the keyboard they jump.
- A wire into a running node carries a dash that flows toward it, and the node draws a 2 px progress line along its top. Reduced motion stops the flow and keeps the line.
- The snap grid fades in over 150 ms only while a node is being dragged, then fades away: guidance, not a background.