Skip to content
Sign in
Image studioNode canvasMenu

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.

ProA text to image workflow with a rewrite step, a reference image and a sky mask. Run it to watch the graph work through in order; drag nodes, draw wires, pan and zoom.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/node-canvas

Usage

tsx
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.

ProPlain frames with every port type. Start a wire and only matching inputs light up; a wrong drop says why.

States

ProA failed run with retry, a finished run read only, empty and loading.

Props

PropTypeNotes
nodes, defaultNodes, onNodesChangeCanvasNode[]Position, ports, run state and data
edges, defaultEdges, onEdgesChangeCanvasEdge[]source, sourcePort, target, targetPort
nodeTypesRecord<string, NodeRenderer>Renderers get node, selected, update and updateData
selection, defaultSelection, onSelectionChangestring[]Selected node ids
onConnect(edge, replaced?) => voidAfter a wire lands
validateConnection(source, target) => string | nullReturn a reason to refuse
onNodeDrop(type, at) => voidA NodePalette entry dropped on the plane
onNodeRetry(id) => voidShows Retry on failed nodes
readOnlybooleanPan and zoom only
minimap, controlsbooleanBoth on by default; the minimap hides under 520 px
snapGridnumberDefault 8. 0 turns snapping off
wireColor"type" | "neutral"Default type
defaultViewport, minZoom, maxZoomWithout a viewport the graph fits on mount
apiRefRef<NodeCanvasApi>fitView, zoomIn, zoomOut, center, reveal, setViewport
toolbar, emptyState, loading, labelTop 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

KeyAction
TabThe plane, then each node, its ports and controls
Arrows on the planePan; Shift pans further
Arrows on a nodeMove it, and any selected nodes with it, by the grid; Shift moves 5 steps
Space on a nodeAdd it to or take it out of the selection
Enter on a nodeGo to its first control; Esc in a control comes back
Enter on a portStart a wire; Enter on a matching port ends it, Esc cancels
Delete on a portRemove its wires
Delete, BackspaceRemove the selected nodes and wires
⌘ or Ctrl + ASelect every node
+, -Zoom in and out
Shift + 1, Shift + 0Fit 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.