This page covers the canvas surface itself. For TipTap document editing, see Frontend → TipTap. For how XState dispatches updates into the canvas, see Frontend → Streaming Machine.
The two tracks
| Track | Activated by | Renderer | What gets shown |
|---|---|---|---|
| Mode track | canvasStore.openFile(path, mode) | CanvasPanel switches on mode | File-backed surfaces (TipTap, PDF, code, spreadsheet, etc.) |
| Widget track | canvasStore.setWidget(spec, title) | WidgetCanvas via @json-render/react Renderer + orionRegistry | Agent-generated JSON specs (charts, slides, image grids, custom layouts) |
setWidget() clears activeFile, and openFile() clears widgetSpec. ✓ VERIFIED at canvasStore.ts:547-559.
Canvas surface inventory
Four file-open paths converge in canvasStore.openFile
Media files (images, video, audio) always end up in the widget track. The detection point iscanvasStore.openFile() at L281-342 — every entry path converges here for non-tool-call routes. Tool calls have their own media detection in the interceptor (lines 445-469) that fires before openFile() as a perf optimization. ✓ VERIFIED
How a tool call drives the canvas
The interceptor doesn’t receive raw IPC. It readsactivityStream from the XState snapshot — by the time it sees a tool, the event has already been normalized and pushed into the activity stream by XState actions. ✓ VERIFIED at useCanvasToolInterceptor.ts:108.
WidgetSpec fix pipeline
Before any agent-generated spec reachesWidgetCanvas, it goes through a 7-step fix pipeline to handle hallucinations and legacy formats:
Each step solves a real AI-output failure mode — see useCanvasToolInterceptor.ts:395-425 for the canonical order.
AI-WITH-YOU gate at the canvas
Orion’s product principle: AI may propose, stage, draft, and prepare. It may not silently decide meaning, commit structure, or ship the user’s work without an explicit user-owned seam. From.claude/rules/ui/ai-with-you-gate.md.
Where the gate lives per canvas surface:
Key files
src/hooks/useCanvasToolInterceptor.ts
src/hooks/useCanvasToolInterceptor.ts
The Claude/Pi tool → canvas bridge. ~990 LOC. Tool action dispatch table at L169-914. 7-step fix pipeline at L395-425. Small-widget threshold at L731.
src/stores/canvasStore.ts
src/stores/canvasStore.ts
Single Zustand store for all canvas state.
openFile media-detection at L271-399. setWidget at L547-559.src/lib/canvas/file-router.ts
src/lib/canvas/file-router.ts
EXTENSION_MAP (L20-75), getCanvasMode, getCanvasModeForFile. Maps extensions to canvas modes.src/lib/canvas/widget-catalog.ts
src/lib/canvas/widget-catalog.ts
orionCatalog — 40+ Zod-typed components. catalog.prompt() auto-generates the system prompt for AI.src/lib/canvas/registry/index.ts
src/lib/canvas/registry/index.ts
orionRegistry — wiring of catalog → React components in 8 groups (layout, media, text, data, form, ui, social, presentation).src/components/canvas/CanvasPanel.tsx
src/components/canvas/CanvasPanel.tsx
Mode-based rendering container + auto-save logic. ~517 LOC.
src/components/canvas/editors/WidgetCanvas.tsx
src/components/canvas/editors/WidgetCanvas.tsx
@json-render/react Renderer wrapper + Proxy-based action catch-all.Next
Streaming Machine
The XState v5 machine that drives the activity stream and dispatches into the canvas.
TipTap Editor
Extensions, the editor-ref bridge for
set_content, debounced auto-save.