Workflow canvas
Building and checking agent workflows: a node canvas with a step library, a linear trigger-and-steps builder, and a run inspector showing what each step did.
Loading preview…
Install
- 1
Run this command in your Tailwind CSS project. It adds
components/ai/workflow-canvas-canvas.tsxand installs its dependencies.$ npx shadcn@latest add https://gradientdeck.com/ai-components/r/workflow-canvas-canvas.json
Usage & API
Example
import { WorkflowCanvas } from "@/components/ai/workflow-canvas-canvas"
<WorkflowCanvas
nodes={[]}
edges={[]}
selectedId={null}
onSelect={() => {}}
onMoveNode={() => {}}
onConnect={() => {}}
onAddNode={() => {}}
onUpdateNode={() => {}}
onDeleteNode={() => {}}
/>Props WorkflowCanvas
| Prop | Type & description | Required |
|---|---|---|
nodes | FlowNode[] | Required |
edges | FlowEdge[] | Required |
selectedId | string | null | Required |
onSelect | (id: string | null) => void | Required |
onMoveNode | (id: string, x: number, y: number) => void | Required |
onConnect | (from: string, to: string, branch?: "yes" | "no") => void | Required |
onAddNode | (kind: StepKind, at?: { x: number; y: number }) => void`at` is set when a step is dropped onto the canvas; otherwise place it yourself. | Required |
onUpdateNode | (id: string, patch: { title?: string; field?: { key: string; value: string | boolean } }) => void | Required |
onDeleteNode | (id: string) => void | Required |
run | Record<string, NodeRunState> | Optional |
runTimes | Record<string, string> | Optional |
activeEdges | string[] | Optional |
overlay | ReactNodeSlot rendered over the bottom-left of the canvas (run results, undo). | Optional |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Canvas fills the frame. On a wide screen the library is a left column: Search steps, then AI with Agent, Logic with Condition and Delay, and Apps with Linear issue, Slack message and HTTP request. A note at the bottom says to drag a step onto the canvas or click to add it below the selected one. Narrow screens replace that column with an Add step menu of the same items. Nodes are buttons labelled with the kind and title. An agent node also shows its model value and how many of its tool switches are on. The other kinds show a one-line summary from their fields. Dragging the background pans. Dragging a node calls onMoveNode. Fit runs until the person has panned or zoomed. Properties dock on the right from small breakpoints up, and as a bottom sheet on phones. Fields render as text, textarea, select or switch, and fields that share a group get a heading. An overlay slot sits at the lower left for a run result or an undo.
Example
import { WorkflowSteps } from "@/components/ai/workflow-canvas-steps"
<WorkflowSteps
trigger={trigger}
onTriggerChange={() => {}}
steps={[]}
onStepsChange={() => {}}
createStep={() => {}}
/>Props WorkflowSteps
| Prop | Type & description | Required |
|---|---|---|
trigger | WorkflowStep | Required |
onTriggerChange | (trigger: WorkflowStep) => void | Required |
steps | WorkflowStep[] | Required |
onStepsChange | (steps: WorkflowStep[]) => void | Required |
createStep | (kind: StepKind) => WorkflowStepCalled when a step is inserted, so the host can create it with defaults. | Required |
run | Record<string, StepRunState> | Optional |
runTimes | Record<string, string> | Optional |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Step list is a single column capped in width. A When label sits over the trigger card. Between cards, a plus button opens a menu: Agent, Condition, Create Linear issue, Send Slack message, HTTP request and Delay. Choosing one calls createStep so you can supply the default fields, then inserts and opens the new card. Cards reorder by a drag handle. The header shows an optional step number, the kind icon, the title, a summary, a run mark, an enable switch for everything except the trigger, and a chevron. The summary for an off step is Off, skipped in runs. Expanded, the card has Name, the non-switch fields, switch fields, and Delete step. Agent switches sit under a Tools label. A condition renders two dashed columns of nested cards, with their own reorder and insert controls. There is no pan or zoom.
Example
import { RunInspector } from "@/components/ai/workflow-canvas-run"
<RunInspector
runs={[]}
selectedId="…"
onSelect={() => {}}
onRetry={() => {}}
pane="list"
onPaneChange={() => {}}
/>Props RunInspector
| Prop | Type & description | Required |
|---|---|---|
runs | WorkflowRun[] | Required |
selectedId | string | Required |
onSelect | (id: string) => void | Required |
onRetry | (runId: string, stepId: string) => void | Required |
pane | "list" | "detail"Phones show one pane at a time. | Required |
onPaneChange | (pane: "list" | "detail") => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Run inspector is two panes. Phones show one at a time, with Back to runs returning to the list. The list is headed Runs, last 24 hours, with a filter reading All and Failed plus their counts. An empty failed filter says there were no failed runs in the last 24 hours. Each row is the summary, how long ago, the duration or the word running, and failed or retried when those apply. The detail header repeats the summary, the from line and the started-at time, then Steps as how many ran, Tokens, Tool calls, and a model line, plus a Succeeded, Failed or Running badge with the duration. The body is a shared time axis in seconds and one row per step. Skipped and pending steps are dim and cannot expand. An open step shows the error and Retry from this step when it failed, input and output token counts when present, a tool-calls list with arguments and a duration, and Input and Output pairs.
Choosing a layout
Use the canvas when the flow branches and crosses, and people need to see edges rather than a list. The library is the way to discover steps, grouped as AI, Logic and Apps, with search. Use the step list when the flow is mostly a sequence with one or two conditions. Inserting between cards, dragging to reorder, and turning a step off are clearer in a stack than on a board. A step that is off reads Off, skipped in runs, and fades. The condition's two columns are labelled with the value it tests and with Otherwise, and an empty path says nothing happens on it.
Use the run inspector after a workflow exists, on its runs page, not while someone is still wiring nodes. The list covers the last 24 hours and filters All or Failed. Open a run to see which steps ran, how long they took, and why one failed. Retry from this step is the action on a failed row. It calls onRetry with the run id and the step id. It does not rebuild the graph. For authoring, stay on the canvas or the step list. Model settings and the instructions editor still belong on the agent itself. A step card can show a model field because you put that field on the step. It is not a global model form.
Does the canvas run the workflow when a node is connected?
No. Connecting calls onConnect with the source id, the target id, and yes or no when the drag started on a condition port. Adding, moving, renaming, editing a field and deleting are separate callbacks. Pass run and runTimes if you want spinners and durations drawn on the nodes. The board does not schedule anything.
What does Retry from this step change?
It calls onRetry with the selected run's id and that step's id. The inspector does not replay steps locally. When you update the runs you pass in, the bars, the badge and the open error follow that data. Pending and skipped steps have no retry control because their rows do not expand.