Canvas chatPro
A chat on an infinite canvas where prompts and answers are nodes you can branch from, drag, pan and zoom.
On this page
Installation
Usage
import { CanvasChat } from "@/components/ui/chatcn/pro/canvas-chat"
<CanvasChat
nodes={nodes}
onSend={({ text, parentId }) => ask(text, parentId)}
onNodeMove={(id, position) => save(id, position)}
status={status}
onStop={stop}
/>The canvas lays the conversation out as a tidy tree and draws each reply on a curved edge from the answer it grew from. Select any answer, or press Branch from here, and the composer says where the next prompt goes; send it and the new prompt slides out of its parent along a fresh edge. The open path is drawn in ink. Drag a node to place it (edges follow every frame) and store the position with onNodeMove; pinned positions come back through x and y. Pan by dragging the background, holding Space or scrolling; zoom to the cursor with Ctrl or a pinch. The first view starts at the top at a readable size; Fit shows the whole tree. A failed answer, when selected, sends the next prompt as another take on its question.
Examples
States
Props
| Prop | Type | Notes |
|---|---|---|
nodes | { id, parentId, role, text, status?, error?, label? }[] | A flat list; each node points at its parent. parentId: null starts a tree |
x, y on a node | number | Pins it; otherwise it is laid out |
selectedId, defaultSelectedId, onSelectedChange | string | null | The node new prompts branch from; defaults to the newest answer |
onSend | ({ text, parentId }) => void | From the built in composer |
onNodeMove | (id, { x, y }) => void | When a drag or a nudge ends |
status, onStop | "idle" | "submitting" | "streaming" | Send becomes Stop |
minimap | boolean | Default on, hidden below 560 px |
composer | boolean | Off for a read only canvas |
clampHeight | number | Answers taller than this fold behind Show all. Default 240 |
renderAnswer | (node) => ReactNode | Defaults to Markdown |
disabled | boolean | No dragging or sending |
labels | Partial<…Labels> | Every string, for translation |
Keyboard
| Key | Action |
|---|---|
| Up and Down | Parent, and the child on the open path |
| Left and Right | Previous and next branch |
| Home | The first prompt |
| Enter | Branch from the focused node |
| Shift and an arrow | Nudge the focused node 24 px |
| +, - and 0 | Zoom in, out and back to 100% |
| Shift 1 | Fit the tree to the view |
| Space and drag | Pan |
Accessibility
- Nodes are a single tab stop with arrow keys between them; each is named by its role, an excerpt and its branch count.
- Focus pans the camera just enough to bring the node into view.
- The composer always says which node it branches from, in text.
- Zoom and fit controls are a toolbar with labelled buttons and their shortcuts in tooltips.
Motion
A new branch starts at its parent and eases into place, about 300 ms with a critically damped follow, while its edge is redrawn every frame so the curve stretches behind it. Dragging, panning and pinching track the pointer 1:1. Zoom and fit from the buttons glide in 280 ms and jump from the keyboard. Reduced motion places everything at once.