pmx-canvas 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/Readme.md +865 -0
- package/dist/canvas/global.css +3173 -0
- package/dist/canvas/index.js +183 -0
- package/dist/json-render/index.css +2 -0
- package/dist/json-render/index.js +389 -0
- package/dist/types/cli/agent.d.ts +13 -0
- package/dist/types/cli/index.d.ts +2 -0
- package/dist/types/cli/watch.d.ts +5 -0
- package/dist/types/client/App.d.ts +1 -0
- package/dist/types/client/canvas/AttentionHistory.d.ts +1 -0
- package/dist/types/client/canvas/AttentionToast.d.ts +1 -0
- package/dist/types/client/canvas/CanvasNode.d.ts +8 -0
- package/dist/types/client/canvas/CanvasViewport.d.ts +8 -0
- package/dist/types/client/canvas/CommandPalette.d.ts +4 -0
- package/dist/types/client/canvas/ContextMenu.d.ts +24 -0
- package/dist/types/client/canvas/ContextPinBar.d.ts +1 -0
- package/dist/types/client/canvas/ContextPinHud.d.ts +1 -0
- package/dist/types/client/canvas/DockedNode.d.ts +4 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +8 -0
- package/dist/types/client/canvas/ExpandedNodeOverlay.d.ts +1 -0
- package/dist/types/client/canvas/FocusFieldLayer.d.ts +1 -0
- package/dist/types/client/canvas/Minimap.d.ts +23 -0
- package/dist/types/client/canvas/SelectionBar.d.ts +1 -0
- package/dist/types/client/canvas/ShortcutOverlay.d.ts +3 -0
- package/dist/types/client/canvas/SnapshotPanel.d.ts +7 -0
- package/dist/types/client/canvas/snap-guides.d.ts +23 -0
- package/dist/types/client/canvas/use-node-drag.d.ts +15 -0
- package/dist/types/client/canvas/use-node-resize.d.ts +15 -0
- package/dist/types/client/canvas/use-pan-zoom.d.ts +16 -0
- package/dist/types/client/ext-app/bridge.d.ts +161 -0
- package/dist/types/client/icons.d.ts +70 -0
- package/dist/types/client/index.d.ts +1 -0
- package/dist/types/client/nodes/ContextNode.d.ts +34 -0
- package/dist/types/client/nodes/ExtAppFrame.d.ts +18 -0
- package/dist/types/client/nodes/FileNode.d.ts +5 -0
- package/dist/types/client/nodes/GroupNode.d.ts +6 -0
- package/dist/types/client/nodes/ImageNode.d.ts +10 -0
- package/dist/types/client/nodes/InlineFormatBar.d.ts +7 -0
- package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +14 -0
- package/dist/types/client/nodes/LedgerNode.d.ts +4 -0
- package/dist/types/client/nodes/MarkdownNode.d.ts +6 -0
- package/dist/types/client/nodes/McpAppNode.d.ts +4 -0
- package/dist/types/client/nodes/MdFormatBar.d.ts +8 -0
- package/dist/types/client/nodes/PromptNode.d.ts +5 -0
- package/dist/types/client/nodes/ResponseNode.d.ts +5 -0
- package/dist/types/client/nodes/StatusNode.d.ts +4 -0
- package/dist/types/client/nodes/StatusSummary.d.ts +4 -0
- package/dist/types/client/nodes/TraceNode.d.ts +4 -0
- package/dist/types/client/nodes/WebpageNode.d.ts +5 -0
- package/dist/types/client/nodes/image-warnings.d.ts +6 -0
- package/dist/types/client/nodes/inline-editor-commands.d.ts +11 -0
- package/dist/types/client/nodes/md-format.d.ts +25 -0
- package/dist/types/client/state/attention-bridge.d.ts +3 -0
- package/dist/types/client/state/attention-store.d.ts +25 -0
- package/dist/types/client/state/canvas-store.d.ts +74 -0
- package/dist/types/client/state/intent-bridge.d.ts +158 -0
- package/dist/types/client/state/sse-bridge.d.ts +5 -0
- package/dist/types/client/theme/tokens.d.ts +27 -0
- package/dist/types/client/types.d.ts +40 -0
- package/dist/types/client/utils/ext-app-tool-result.d.ts +1 -0
- package/dist/types/client/utils/placement.d.ts +1 -0
- package/dist/types/client/utils/platform.d.ts +2 -0
- package/dist/types/json-render/catalog.d.ts +815 -0
- package/dist/types/json-render/charts/components.d.ts +54 -0
- package/dist/types/json-render/charts/definitions.d.ts +103 -0
- package/dist/types/json-render/charts/extra-components.d.ts +58 -0
- package/dist/types/json-render/charts/extra-definitions.d.ts +181 -0
- package/dist/types/json-render/renderer/index.d.ts +16 -0
- package/dist/types/json-render/schema.d.ts +46 -0
- package/dist/types/json-render/server.d.ts +55 -0
- package/dist/types/mcp/server.d.ts +22 -0
- package/dist/types/server/agent-context.d.ts +21 -0
- package/dist/types/server/artifact-paths.d.ts +3 -0
- package/dist/types/server/canvas-operations.d.ts +154 -0
- package/dist/types/server/canvas-provenance.d.ts +13 -0
- package/dist/types/server/canvas-schema.d.ts +49 -0
- package/dist/types/server/canvas-serialization.d.ts +25 -0
- package/dist/types/server/canvas-state.d.ts +174 -0
- package/dist/types/server/canvas-validation.d.ts +33 -0
- package/dist/types/server/chart-template.d.ts +29 -0
- package/dist/types/server/code-graph.d.ts +67 -0
- package/dist/types/server/context-cards.d.ts +24 -0
- package/dist/types/server/diagram-presets.d.ts +28 -0
- package/dist/types/server/ext-app-call-registry.d.ts +16 -0
- package/dist/types/server/ext-app-tool-result.d.ts +1 -0
- package/dist/types/server/file-watcher.d.ts +16 -0
- package/dist/types/server/index.d.ts +243 -0
- package/dist/types/server/mcp-app-candidate.d.ts +25 -0
- package/dist/types/server/mcp-app-host.d.ts +65 -0
- package/dist/types/server/mcp-app-runtime.d.ts +47 -0
- package/dist/types/server/mutation-history.d.ts +105 -0
- package/dist/types/server/placement.d.ts +37 -0
- package/dist/types/server/server.d.ts +103 -0
- package/dist/types/server/spatial-analysis.d.ts +87 -0
- package/dist/types/server/trace-manager.d.ts +48 -0
- package/dist/types/server/web-artifacts.d.ts +50 -0
- package/dist/types/server/webpage-node.d.ts +25 -0
- package/dist/types/shared/auto-arrange.d.ts +29 -0
- package/dist/types/shared/ext-app-tool-result.d.ts +9 -0
- package/dist/types/shared/placement.d.ts +26 -0
- package/dist/types/shared/semantic-attention.d.ts +97 -0
- package/package.json +109 -0
- package/skills/data-analysis/SKILL.md +324 -0
- package/skills/doc-coauthoring/SKILL.md +375 -0
- package/skills/frontend-design/SKILL.md +45 -0
- package/skills/json-render-codegen/SKILL.md +112 -0
- package/skills/json-render-core/SKILL.md +265 -0
- package/skills/json-render-ink/SKILL.md +273 -0
- package/skills/json-render-mcp/SKILL.md +132 -0
- package/skills/json-render-react/SKILL.md +264 -0
- package/skills/json-render-shadcn/SKILL.md +159 -0
- package/skills/playwright-cli/SKILL.md +67 -0
- package/skills/pmx-canvas/SKILL.md +668 -0
- package/skills/pmx-canvas/evals/evals.json +186 -0
- package/skills/pmx-canvas-testing/SKILL.md +78 -0
- package/skills/published-consumer-e2e/SKILL.md +43 -0
- package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +241 -0
- package/skills/web-artifacts-builder/SKILL.md +80 -0
- package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +167 -0
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +425 -0
- package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/skills/web-design-guidelines/SKILL.md +39 -0
- package/src/cli/agent.ts +2144 -0
- package/src/cli/index.ts +622 -0
- package/src/cli/watch.ts +88 -0
- package/src/client/App.tsx +507 -0
- package/src/client/canvas/AttentionHistory.tsx +81 -0
- package/src/client/canvas/AttentionToast.tsx +19 -0
- package/src/client/canvas/CanvasNode.tsx +363 -0
- package/src/client/canvas/CanvasViewport.tsx +590 -0
- package/src/client/canvas/CommandPalette.tsx +302 -0
- package/src/client/canvas/ContextMenu.tsx +601 -0
- package/src/client/canvas/ContextPinBar.tsx +25 -0
- package/src/client/canvas/ContextPinHud.tsx +22 -0
- package/src/client/canvas/DockedNode.tsx +66 -0
- package/src/client/canvas/EdgeLayer.tsx +280 -0
- package/src/client/canvas/ExpandedNodeOverlay.tsx +260 -0
- package/src/client/canvas/FocusFieldLayer.tsx +107 -0
- package/src/client/canvas/Minimap.tsx +301 -0
- package/src/client/canvas/SelectionBar.tsx +69 -0
- package/src/client/canvas/ShortcutOverlay.tsx +69 -0
- package/src/client/canvas/SnapshotPanel.tsx +236 -0
- package/src/client/canvas/snap-guides.ts +170 -0
- package/src/client/canvas/use-node-drag.ts +51 -0
- package/src/client/canvas/use-node-resize.ts +59 -0
- package/src/client/canvas/use-pan-zoom.ts +191 -0
- package/src/client/ext-app/bridge.ts +542 -0
- package/src/client/icons.tsx +424 -0
- package/src/client/index.tsx +7 -0
- package/src/client/nodes/ContextNode.tsx +412 -0
- package/src/client/nodes/ExtAppFrame.tsx +509 -0
- package/src/client/nodes/FileNode.tsx +256 -0
- package/src/client/nodes/GroupNode.tsx +39 -0
- package/src/client/nodes/ImageNode.tsx +160 -0
- package/src/client/nodes/InlineFormatBar.tsx +169 -0
- package/src/client/nodes/InlineMarkdownEditor.tsx +123 -0
- package/src/client/nodes/LedgerNode.tsx +37 -0
- package/src/client/nodes/MarkdownNode.tsx +359 -0
- package/src/client/nodes/McpAppNode.tsx +85 -0
- package/src/client/nodes/MdFormatBar.tsx +109 -0
- package/src/client/nodes/PromptNode.tsx +597 -0
- package/src/client/nodes/ResponseNode.tsx +153 -0
- package/src/client/nodes/StatusNode.tsx +84 -0
- package/src/client/nodes/StatusSummary.tsx +38 -0
- package/src/client/nodes/TraceNode.tsx +120 -0
- package/src/client/nodes/WebpageNode.tsx +288 -0
- package/src/client/nodes/image-warnings.ts +95 -0
- package/src/client/nodes/inline-editor-commands.ts +37 -0
- package/src/client/nodes/md-format.ts +206 -0
- package/src/client/state/attention-bridge.ts +328 -0
- package/src/client/state/attention-store.ts +73 -0
- package/src/client/state/canvas-store.ts +631 -0
- package/src/client/state/intent-bridge.ts +315 -0
- package/src/client/state/sse-bridge.ts +965 -0
- package/src/client/theme/global.css +3173 -0
- package/src/client/theme/tokens.ts +72 -0
- package/src/client/types-shims.d.ts +5 -0
- package/src/client/types.ts +81 -0
- package/src/client/utils/ext-app-tool-result.ts +4 -0
- package/src/client/utils/placement.ts +4 -0
- package/src/client/utils/platform.ts +2 -0
- package/src/json-render/catalog.ts +256 -0
- package/src/json-render/charts/components.tsx +198 -0
- package/src/json-render/charts/definitions.ts +81 -0
- package/src/json-render/charts/extra-components.tsx +267 -0
- package/src/json-render/charts/extra-definitions.ts +145 -0
- package/src/json-render/renderer/index.css +174 -0
- package/src/json-render/renderer/index.tsx +86 -0
- package/src/json-render/schema.ts +62 -0
- package/src/json-render/server.ts +597 -0
- package/src/mcp/server.ts +1377 -0
- package/src/server/agent-context.ts +242 -0
- package/src/server/artifact-paths.ts +17 -0
- package/src/server/canvas-operations.ts +1279 -0
- package/src/server/canvas-provenance.ts +243 -0
- package/src/server/canvas-schema.ts +432 -0
- package/src/server/canvas-serialization.ts +95 -0
- package/src/server/canvas-state.ts +1134 -0
- package/src/server/canvas-validation.ts +114 -0
- package/src/server/chart-template.ts +449 -0
- package/src/server/code-graph.ts +370 -0
- package/src/server/context-cards.ts +31 -0
- package/src/server/diagram-presets.ts +71 -0
- package/src/server/ext-app-call-registry.ts +77 -0
- package/src/server/ext-app-tool-result.ts +4 -0
- package/src/server/file-watcher.ts +121 -0
- package/src/server/index.ts +647 -0
- package/src/server/mcp-app-candidate.ts +174 -0
- package/src/server/mcp-app-host.ts +814 -0
- package/src/server/mcp-app-runtime.ts +459 -0
- package/src/server/mutation-history.ts +350 -0
- package/src/server/placement.ts +125 -0
- package/src/server/server.ts +3846 -0
- package/src/server/spatial-analysis.ts +356 -0
- package/src/server/trace-manager.ts +333 -0
- package/src/server/web-artifacts/scripts/bundle-artifact.sh +167 -0
- package/src/server/web-artifacts/scripts/init-artifact.sh +426 -0
- package/src/server/web-artifacts/scripts/shadcn-components.tar.gz +0 -0
- package/src/server/web-artifacts.ts +442 -0
- package/src/server/webpage-node.ts +328 -0
- package/src/shared/auto-arrange.ts +439 -0
- package/src/shared/ext-app-tool-result.ts +76 -0
- package/src/shared/placement.ts +81 -0
- package/src/shared/semantic-attention.ts +598 -0
package/Readme.md
ADDED
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
# pmx-canvas
|
|
2
|
+
|
|
3
|
+
A spatial canvas workbench for coding agents. Infinite 2D canvas with nodes, edges, pan/zoom, minimap, and real-time sync -- controlled through the CLI, MCP, HTTP API, or a Bun-based JavaScript/TypeScript SDK.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="docs/screenshots/welcome-dark.png" alt="Empty canvas — dark theme" width="49%" />
|
|
7
|
+
<img src="docs/screenshots/welcome-light.png" alt="Empty canvas — light theme" width="49%" />
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img src="docs/screenshots/demo-workbench-dark.png" alt="Structured workbench demo — dark theme" width="49%" />
|
|
12
|
+
<img src="docs/screenshots/demo-workbench-light.png" alt="Structured workbench demo — light theme" width="49%" />
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
PMX Canvas is a spatial thinking surface for coding agents and the humans working with them. Agents lay out plans, files, and status as connected nodes; humans rearrange, group, and pin what matters. That spatial curation — proximity, grouping, pinning — becomes structured context the agent reads in real time via MCP.
|
|
16
|
+
|
|
17
|
+
**Spatial arrangement is communication.** When a human drags three file nodes next to a bug report, the agent knows they're related. The canvas is the agent's **extended working memory**: humans pin nodes to curate context, agents read that curation via `canvas://pinned-context` and MCP resource change notifications.
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
- [Bun](https://bun.sh) >= 1.3.12
|
|
22
|
+
|
|
23
|
+
The published SDK entrypoint is Bun-first: `import { createCanvas } from 'pmx-canvas'` is supported in Bun, while Node.js consumers should use the CLI, MCP server, or HTTP API instead.
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
### Install from npm
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bunx pmx-canvas # Start canvas, open browser
|
|
31
|
+
bunx pmx-canvas --demo # Start with sample nodes
|
|
32
|
+
bunx pmx-canvas --mcp # Run as MCP server
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Install from source
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/pskoett/pmx-canvas.git
|
|
39
|
+
cd pmx-canvas
|
|
40
|
+
bun install
|
|
41
|
+
bun run build
|
|
42
|
+
bun run dev # Start + open browser
|
|
43
|
+
bun run dev:demo # Start with sample nodes
|
|
44
|
+
bun run dev:portless # Start at https://pmx.localhost/workbench (requires global portless)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The canvas opens at `http://localhost:4313`.
|
|
48
|
+
|
|
49
|
+
For local development only, you can give the canvas a stable hostname with
|
|
50
|
+
[Portless](https://github.com/vercel-labs/portless):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install -g portless
|
|
54
|
+
bun run dev:portless
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then open `https://pmx.localhost/workbench`.
|
|
58
|
+
|
|
59
|
+
This is intentionally a repo-local developer workflow. The published
|
|
60
|
+
`bunx pmx-canvas` path still defaults to plain loopback and does not depend on
|
|
61
|
+
Portless being installed.
|
|
62
|
+
|
|
63
|
+
### Test the unpublished CLI from a repo checkout
|
|
64
|
+
|
|
65
|
+
If you want to exercise the real package before publishing, link the repo locally:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/pskoett/pmx-canvas.git
|
|
69
|
+
cd pmx-canvas
|
|
70
|
+
bun install
|
|
71
|
+
bun run build
|
|
72
|
+
bun link
|
|
73
|
+
|
|
74
|
+
# Then from any shell:
|
|
75
|
+
pmx-canvas --help
|
|
76
|
+
pmx-canvas --no-open
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For one-off local runs without linking, `bun run src/cli/index.ts ...` works too.
|
|
80
|
+
|
|
81
|
+
### Recommended ways to drive the canvas
|
|
82
|
+
|
|
83
|
+
- **CLI** for local use, scripting, automation, and terminal-native agents
|
|
84
|
+
- **MCP** for agents that already speak the Model Context Protocol
|
|
85
|
+
|
|
86
|
+
### Connect your agent (MCP)
|
|
87
|
+
|
|
88
|
+
Add to your agent's MCP config:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"canvas": {
|
|
94
|
+
"command": "bunx",
|
|
95
|
+
"args": ["pmx-canvas", "--mcp"]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The canvas auto-starts on first tool call. Works with Claude Code, Cursor, Windsurf, and any MCP-capable agent.
|
|
102
|
+
|
|
103
|
+
## How it works
|
|
104
|
+
|
|
105
|
+
1. Agent creates nodes on the canvas (plans, code, status, investigations)
|
|
106
|
+
2. Agent adds file nodes for the files it's working on -- they update live as the agent edits
|
|
107
|
+
3. Human reviews, rearranges, and **pins** the important nodes
|
|
108
|
+
4. MCP server notifies the agent that pinned context changed
|
|
109
|
+
5. Agent reads `canvas://pinned-context` to get the human's curated focus
|
|
110
|
+
6. Agent uses that context to inform its next actions
|
|
111
|
+
7. The canvas becomes a shared thinking surface
|
|
112
|
+
|
|
113
|
+
## Features
|
|
114
|
+
|
|
115
|
+
### Canvas
|
|
116
|
+
|
|
117
|
+
- Infinite 2D canvas with pan, zoom, and scroll
|
|
118
|
+
- Minimap with click-to-navigate
|
|
119
|
+
- Auto-arrange layouts (grid, column, flow)
|
|
120
|
+
- Multi-select with selection bar actions
|
|
121
|
+
- Snap-to-alignment guides while dragging nodes
|
|
122
|
+
- Keyboard shortcuts (Cmd+0 reset, Cmd+/- zoom, Tab cycle, Esc deselect)
|
|
123
|
+
- Command palette (Cmd+K) -- search nodes and actions
|
|
124
|
+
- In-UI shortcut overlay -- press `?` for the full cheatsheet
|
|
125
|
+
- Context menu on right-click
|
|
126
|
+
- Docked panels -- pin nodes to left/right HUD
|
|
127
|
+
- Expanded view -- click to expand any node to full-screen overlay
|
|
128
|
+
- Inline markdown editor with format bar for rich in-place editing
|
|
129
|
+
- Attention toasts + history -- surface agent mutations the human didn't initiate
|
|
130
|
+
- Layout validation -- detect collisions, containment breaches, and missing edge endpoints
|
|
131
|
+
- Themes: dark (default), light, high-contrast
|
|
132
|
+
- Persistence: auto-saves to `.pmx-canvas/state.json`, restores on restart
|
|
133
|
+
|
|
134
|
+
### Node types
|
|
135
|
+
|
|
136
|
+
| Type | Description |
|
|
137
|
+
|------|-------------|
|
|
138
|
+
| `markdown` | Rich markdown with rendered preview |
|
|
139
|
+
| `status` | Compact status indicator (phase, message, elapsed time) |
|
|
140
|
+
| `context` | Context cards, token usage, workspace grounding |
|
|
141
|
+
| `ledger` | Execution ledger summary |
|
|
142
|
+
| `trace` | Agent trace pills (tool calls, subagent activity) |
|
|
143
|
+
| `file` | Live file viewer with auto-update on disk changes |
|
|
144
|
+
| `image` | Image viewer (file paths, data URIs, URLs) |
|
|
145
|
+
| `webpage` | Persisted webpage snapshot with stored URL, extracted text, and refresh support |
|
|
146
|
+
| `mcp-app` | Hosted MCP app iframes (Excalidraw, Chart.js, etc.) -- see [MCP app nodes](#mcp-app-nodes) |
|
|
147
|
+
| `json-render` | Structured UI from JSON specs |
|
|
148
|
+
| `graph` | Line, bar, and pie charts |
|
|
149
|
+
| `group` | Spatial container/frame that contains other nodes |
|
|
150
|
+
|
|
151
|
+
Thread node types `prompt` and `response` are used internally for agent conversation
|
|
152
|
+
rendering and are not created directly through the public APIs.
|
|
153
|
+
|
|
154
|
+
### Edge types
|
|
155
|
+
|
|
156
|
+
All edges support labels, styles (solid/dashed/dotted), and animation.
|
|
157
|
+
|
|
158
|
+
| Type | Use case |
|
|
159
|
+
|------|----------|
|
|
160
|
+
| `flow` | Sequential steps, data flow |
|
|
161
|
+
| `depends-on` | Dependencies between tasks |
|
|
162
|
+
| `relation` | General relationships |
|
|
163
|
+
| `references` | Cross-references, evidence links |
|
|
164
|
+
|
|
165
|
+
### File nodes
|
|
166
|
+
|
|
167
|
+
File nodes display project files with line numbers and language detection. When an agent edits a file through its normal tools, the canvas node updates automatically via `fs.watch()`.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
canvas_add_node({ type: 'file', content: 'src/server/index.ts' })
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Webpage nodes
|
|
174
|
+
|
|
175
|
+
Webpage nodes store the source URL on the node, fetch the page server-side, and cache extracted text for search, pins, and agent context. Saved canvases keep enough information for an agent to come back later and refresh the node from the original URL.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
canvas_add_node({ type: 'webpage', url: 'https://example.com/docs' }) // content still works, but url is canonical
|
|
179
|
+
canvas_refresh_webpage_node({ id: 'node-abc123' })
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### MCP app nodes
|
|
183
|
+
|
|
184
|
+
`mcp-app` nodes embed other MCP servers' UI resources (`ui://...`) directly on the canvas as
|
|
185
|
+
sandboxed iframes. Any server that implements the [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps)
|
|
186
|
+
can be opened as a node with `canvas_open_mcp_app`.
|
|
187
|
+
|
|
188
|
+
#### Recommended: Excalidraw (hand-drawn diagrams)
|
|
189
|
+
|
|
190
|
+
[Excalidraw](https://github.com/excalidraw/excalidraw-mcp) ships a hosted MCP server at
|
|
191
|
+
`https://mcp.excalidraw.com/mcp` that renders hand-drawn diagrams with streaming draw-on
|
|
192
|
+
animations and fullscreen editing. It is a strong example of an MCP App that fits naturally
|
|
193
|
+
inside PMX Canvas: the app opens as a node, can be moved and pinned like any other node, and
|
|
194
|
+
supports fullscreen editing when you want to expand it into a larger workspace.
|
|
195
|
+
|
|
196
|
+
PMX Canvas ships a preset so an agent can open an Excalidraw diagram in one call, without wiring
|
|
197
|
+
the transport by hand:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
canvas_add_diagram({
|
|
201
|
+
elements: [
|
|
202
|
+
{ type: 'rectangle', id: 'a', x: 80, y: 120, width: 180, height: 80,
|
|
203
|
+
roundness: { type: 3 }, backgroundColor: '#a5d8ff', fillStyle: 'solid',
|
|
204
|
+
label: { text: 'Agent', fontSize: 18 } },
|
|
205
|
+
{ type: 'rectangle', id: 'b', x: 380, y: 120, width: 180, height: 80,
|
|
206
|
+
roundness: { type: 3 }, backgroundColor: '#d0bfff', fillStyle: 'solid',
|
|
207
|
+
label: { text: 'PMX Canvas', fontSize: 18 } },
|
|
208
|
+
{ type: 'arrow', id: 'a1', x: 260, y: 160, width: 120, height: 0,
|
|
209
|
+
startBinding: { elementId: 'a' }, endBinding: { elementId: 'b' },
|
|
210
|
+
label: { text: 'adds nodes' } },
|
|
211
|
+
],
|
|
212
|
+
title: 'Agent → Canvas',
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Under the hood this is just a thin alias for `canvas_open_mcp_app` with the Excalidraw transport
|
|
217
|
+
preset. You can use the hosted Excalidraw server directly, or point the same flow at a local
|
|
218
|
+
`excalidraw-mcp` instance if you prefer running the app yourself. For any other MCP app, use
|
|
219
|
+
`canvas_open_mcp_app` directly:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
canvas_open_mcp_app({
|
|
223
|
+
transport: { type: 'http', url: 'https://mcp.excalidraw.com/mcp' },
|
|
224
|
+
toolName: 'create_view',
|
|
225
|
+
serverName: 'Excalidraw',
|
|
226
|
+
toolArguments: { elements: '[ ... ]' },
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The canvas runs the tool against the remote MCP server, renders the returned `ui://` resource in
|
|
231
|
+
a sandboxed iframe node, and keeps the resource's CSP + permission hints intact. Resize, drag,
|
|
232
|
+
and pin the node like any other canvas node.
|
|
233
|
+
|
|
234
|
+
### Json-render nodes
|
|
235
|
+
|
|
236
|
+
`json-render` nodes turn structured JSON specs into rendered UI panels (dashboards, tables, forms,
|
|
237
|
+
cards) without writing HTML. PMX Canvas ships the [`@json-render/*`](https://www.npmjs.com/package/@json-render/core)
|
|
238
|
+
runtime and component catalog (core + react + shadcn), so agents can describe an interface as a
|
|
239
|
+
spec and render it inline on the canvas.
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
canvas_add_json_render_node({
|
|
243
|
+
title: 'Deploy status',
|
|
244
|
+
spec: {
|
|
245
|
+
root: 'card',
|
|
246
|
+
elements: {
|
|
247
|
+
card: { type: 'Card', props: { title: 'Deploy' }, children: ['status'] },
|
|
248
|
+
status: { type: 'Badge', props: { variant: 'success', label: 'Healthy' } },
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Use `canvas_describe_schema` / `canvas_validate_spec` to introspect the component catalog before
|
|
255
|
+
building a spec -- see [Schema-driven discovery](#schema-driven-discovery).
|
|
256
|
+
|
|
257
|
+
### Web artifacts
|
|
258
|
+
|
|
259
|
+
A **web artifact** is a single-file, fully bundled HTML app (React + Tailwind + shadcn) that an
|
|
260
|
+
agent builds from TSX source and drops onto the canvas as an embedded node. Useful when you want
|
|
261
|
+
a real interactive UI -- charts, forms, mini-dashboards -- instead of a static node.
|
|
262
|
+
|
|
263
|
+
`canvas_build_web_artifact` takes source strings (`App.tsx`, optional `index.css`, `main.tsx`,
|
|
264
|
+
`index.html`, plus extra files), runs the bundled web-artifacts-builder scripts, writes the
|
|
265
|
+
self-contained HTML to `.pmx-canvas/artifacts/<slug>.html`, and (by default) opens it in the canvas.
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
pmx-canvas node add --type web-artifact --title "Dashboard" --app-file ./App.tsx
|
|
269
|
+
pmx-canvas web-artifact build --title "Dashboard" --app-file ./App.tsx --include-logs
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The matching agent skill is at [skills/web-artifacts-builder/SKILL.md](skills/web-artifacts-builder/SKILL.md).
|
|
273
|
+
|
|
274
|
+
### Groups
|
|
275
|
+
|
|
276
|
+
Groups are spatial containers that visually contain other nodes. They render as dashed-border frames with a title bar and optional accent color.
|
|
277
|
+
|
|
278
|
+
- Select 2+ nodes and click "Group" in the selection bar
|
|
279
|
+
- Right-click a group to ungroup
|
|
280
|
+
- Collapsing a group hides children and shows a summary
|
|
281
|
+
- By default, group creation preserves the children's current positions and expands the frame around them
|
|
282
|
+
- Pass `childLayout` to auto-pack children (`grid`, `column`, `flow`)
|
|
283
|
+
- Pass explicit `x`, `y`, `width`, and `height` to create a manual frame and lay children out inside it
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
canvas_create_group({ title: 'Auth Module', childIds: ['node-1', 'node-2'], color: '#4a9eff' })
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Batch operations
|
|
290
|
+
|
|
291
|
+
Batch mode lets you build a canvas in one shot and reference earlier results from later operations.
|
|
292
|
+
|
|
293
|
+
- HTTP: `POST /api/canvas/batch`
|
|
294
|
+
- CLI: `pmx-canvas batch --file ./canvas-ops.json`
|
|
295
|
+
- MCP: `canvas_batch`
|
|
296
|
+
- SDK: `await canvas.runBatch([...])`
|
|
297
|
+
|
|
298
|
+
Supported operations:
|
|
299
|
+
|
|
300
|
+
- `node.add`
|
|
301
|
+
- `node.update`
|
|
302
|
+
- `graph.add`
|
|
303
|
+
- `edge.add`
|
|
304
|
+
- `group.create`
|
|
305
|
+
- `group.add`
|
|
306
|
+
- `group.remove`
|
|
307
|
+
- `pin.set`, `pin.add`, `pin.remove`
|
|
308
|
+
- `snapshot.save`
|
|
309
|
+
- `arrange`
|
|
310
|
+
|
|
311
|
+
`node.add` also supports `type: "webpage"` inside batch. The batch itself still succeeds when the
|
|
312
|
+
webpage node is created but the fetch fails; the per-operation result includes `fetch: { ok, error? }`
|
|
313
|
+
plus a top-level `error` field for the fetch problem.
|
|
314
|
+
|
|
315
|
+
Example:
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"operations": [
|
|
320
|
+
{
|
|
321
|
+
"op": "graph.add",
|
|
322
|
+
"assign": "wins",
|
|
323
|
+
"args": {
|
|
324
|
+
"title": "Major wins",
|
|
325
|
+
"graphType": "bar",
|
|
326
|
+
"data": [
|
|
327
|
+
{ "label": "Docs", "value": 5 },
|
|
328
|
+
{ "label": "Tests", "value": 8 }
|
|
329
|
+
],
|
|
330
|
+
"xKey": "label",
|
|
331
|
+
"yKey": "value"
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
"op": "group.create",
|
|
336
|
+
"assign": "frame",
|
|
337
|
+
"args": {
|
|
338
|
+
"title": "Quarterly graphs",
|
|
339
|
+
"childIds": ["$wins.id"]
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Schema-driven discovery
|
|
347
|
+
|
|
348
|
+
Agents don't have to guess node shapes. The running server exposes its own create schemas,
|
|
349
|
+
json-render component catalog, and node-type examples so an agent can introspect before building.
|
|
350
|
+
|
|
351
|
+
- `canvas_describe_schema` / `GET /api/canvas/schema` -- list all node-create schemas, required
|
|
352
|
+
fields, json-render components, and sample payloads
|
|
353
|
+
- `canvas_validate_spec` / `POST /api/canvas/schema/validate` -- validate a json-render spec or
|
|
354
|
+
graph payload **without** creating a node
|
|
355
|
+
- `canvas_validate` / `GET /api/canvas/validate` -- validate the current layout for collisions,
|
|
356
|
+
containment, and missing edge endpoints
|
|
357
|
+
- `canvas://schema` -- the same schema data as an MCP resource
|
|
358
|
+
|
|
359
|
+
The CLI's `node schema` / `validate spec` subcommands surface the same data from the terminal,
|
|
360
|
+
which is strictly better than guessing flags or payloads.
|
|
361
|
+
|
|
362
|
+
### Persistence
|
|
363
|
+
|
|
364
|
+
Canvas state auto-saves to `.pmx-canvas/state.json` in the workspace root on every mutation (debounced). The file is git-committable -- spatial knowledge persists across sessions and can be shared with a team. Everything the canvas generates (state, snapshots, web artifacts, daemon log/pid) is consolidated under `.pmx-canvas/`. Legacy `.pmx-canvas.json` and `.pmx-canvas-snapshots/` are migrated to the new layout automatically on first boot.
|
|
365
|
+
|
|
366
|
+
Configuration env vars:
|
|
367
|
+
|
|
368
|
+
- `PMX_CANVAS_STATE_FILE` -- override the persistence path
|
|
369
|
+
- `PMX_WEB_CANVAS_PORT` -- server-side default port (server may fall back to another port if taken)
|
|
370
|
+
- `PMX_CANVAS_PORT` -- CLI client-side default target port
|
|
371
|
+
- `PMX_CANVAS_THEME` -- default theme (`dark`, `light`, `high-contrast`)
|
|
372
|
+
- `PMX_CANVAS_DISABLE_BROWSER_OPEN=1` -- skip auto-opening a browser (useful in CI)
|
|
373
|
+
|
|
374
|
+
### Snapshots
|
|
375
|
+
|
|
376
|
+
Named checkpoints of the entire canvas state. Save before a refactor, restore if the approach fails, switch between workstreams.
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
canvas_snapshot({ name: 'before refactor' })
|
|
380
|
+
canvas_restore({ id: 'snap-abc123' })
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Stored in `.pmx-canvas/snapshots/`. Toolbar button opens a panel to save, browse, restore, and delete.
|
|
384
|
+
|
|
385
|
+
### Spatial semantics
|
|
386
|
+
|
|
387
|
+
The canvas understands spatial arrangement and exposes it to agents:
|
|
388
|
+
|
|
389
|
+
- **`canvas://spatial-context`** -- proximity clusters, reading order, pinned neighborhoods
|
|
390
|
+
- **`canvas://pinned-context`** -- includes nearby unpinned nodes for each pin (the human's implicit context)
|
|
391
|
+
- **`canvas_search`** -- find nodes by title/content keywords
|
|
392
|
+
|
|
393
|
+
### Time travel
|
|
394
|
+
|
|
395
|
+
Every mutation is recorded with undo/redo support (last 200 operations):
|
|
396
|
+
|
|
397
|
+
- **`canvas_undo`** / **`canvas_redo`** -- step through history
|
|
398
|
+
- **`canvas://history`** -- readable mutation timeline
|
|
399
|
+
- **`canvas_diff`** -- compare current state vs any saved snapshot
|
|
400
|
+
|
|
401
|
+
### Code graph
|
|
402
|
+
|
|
403
|
+
File nodes automatically detect import dependencies between each other. Add file nodes and watch `depends-on` edges appear as the system parses `import`/`require`/`from` statements across JS/TS, Python, Go, and Rust.
|
|
404
|
+
|
|
405
|
+
- **`canvas://code-graph`** -- dependency structure: central files, isolated files, import chains
|
|
406
|
+
- Auto-edges update live when files change on disk
|
|
407
|
+
|
|
408
|
+
### Real-time sync
|
|
409
|
+
|
|
410
|
+
- SSE push: server broadcasts all changes to connected browsers instantly
|
|
411
|
+
- Bidirectional: browser interactions (drag, resize, pin) sync back to server
|
|
412
|
+
- Auto-reconnect with exponential backoff
|
|
413
|
+
- MCP resource change notifications close the human-to-agent loop
|
|
414
|
+
|
|
415
|
+
### WebView automation
|
|
416
|
+
|
|
417
|
+
Agents can drive a headless Bun.WebView (Chromium or WebKit) pointed at their own workbench,
|
|
418
|
+
so they can screenshot, inspect, and script the live canvas without spawning a user-visible
|
|
419
|
+
browser.
|
|
420
|
+
|
|
421
|
+
- **Why**: let an agent verify its own UI state (what does the canvas actually look like right now?),
|
|
422
|
+
capture screenshots for PR reviews, or script interactions in tests
|
|
423
|
+
- **Controls**: `canvas_webview_start` / `_stop` / `_status` to manage the session,
|
|
424
|
+
`canvas_evaluate` to run JS in the page, `canvas_resize` to change viewport,
|
|
425
|
+
`canvas_screenshot` for PNG/JPEG/WebP captures
|
|
426
|
+
- **Backends**: WebKit on macOS by default; Chrome/Chromium elsewhere (override with `--backend`)
|
|
427
|
+
- **Mirrored surfaces**: CLI (`pmx-canvas webview ...`), HTTP (`/api/workbench/webview/*`),
|
|
428
|
+
SDK (`canvas.startAutomationWebView()` / `evaluateAutomationWebView()` / `screenshotAutomationWebView()`)
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
pmx-canvas --webview-automation # start canvas + headless WebView session
|
|
432
|
+
pmx-canvas webview screenshot --output ./canvas.png
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Daemon mode
|
|
436
|
+
|
|
437
|
+
Run the canvas as a detached background process with pid/log tracking instead of holding a
|
|
438
|
+
terminal:
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
pmx-canvas serve --daemon --no-open # start detached, wait for health
|
|
442
|
+
pmx-canvas serve status # inspect health + pid state
|
|
443
|
+
pmx-canvas serve stop # stop the daemon for this port/pid file
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## Agent skills
|
|
447
|
+
|
|
448
|
+
Installing `pmx-canvas` also ships a library of reusable agent skills under the package's
|
|
449
|
+
`skills/` directory that teach your agent how to use the canvas and adjacent capabilities
|
|
450
|
+
effectively:
|
|
451
|
+
|
|
452
|
+
| Skill | Purpose |
|
|
453
|
+
|-------|---------|
|
|
454
|
+
| [`pmx-canvas`](skills/pmx-canvas/SKILL.md) | Core canvas workflows -- when to create which node type, how to pin for context, batch builds |
|
|
455
|
+
| [`pmx-canvas-testing`](skills/pmx-canvas-testing/SKILL.md) | Testing patterns against the running canvas |
|
|
456
|
+
| [`web-artifacts-builder`](skills/web-artifacts-builder/SKILL.md) | Build single-file React/Tailwind artifacts for the canvas |
|
|
457
|
+
| [`playwright-cli`](skills/playwright-cli/SKILL.md) | Browser automation recipes for canvas + arbitrary pages |
|
|
458
|
+
| [`json-render-*`](skills/) | Drive the `@json-render/*` catalog (core, react, shadcn, mcp, codegen, ink) |
|
|
459
|
+
| [`frontend-design`](skills/frontend-design/SKILL.md), [`web-design-guidelines`](skills/web-design-guidelines/SKILL.md) | Design-quality rules for agent-generated UI |
|
|
460
|
+
| [`doc-coauthoring`](skills/doc-coauthoring/SKILL.md) | Structured doc-writing flow |
|
|
461
|
+
| [`data-analysis`](skills/data-analysis/SKILL.md) | Data exploration patterns |
|
|
462
|
+
| [`published-consumer-e2e`](skills/published-consumer-e2e/SKILL.md) | Smoke-test the published `bunx pmx-canvas` path |
|
|
463
|
+
|
|
464
|
+
Point your agent's skill loader at this directory, or copy the individual `SKILL.md` files into
|
|
465
|
+
your agent's skills tree.
|
|
466
|
+
|
|
467
|
+
## Integration
|
|
468
|
+
|
|
469
|
+
### CLI and MCP (recommended)
|
|
470
|
+
|
|
471
|
+
Use either of the two primary control surfaces depending on how your agent runs:
|
|
472
|
+
|
|
473
|
+
- **CLI** if your workflow is shell-native, scriptable, or driven by terminal commands
|
|
474
|
+
- **MCP** if your agent already uses tool/resource calls over stdio
|
|
475
|
+
|
|
476
|
+
Both paths are first-class for core canvas work. A few advanced capabilities, such as `canvas_diff` and MCP resource subscriptions, remain MCP-only.
|
|
477
|
+
|
|
478
|
+
CLI and MCP are intentionally kept aligned for the main canvas operations, including batch builds,
|
|
479
|
+
layout validation, graph/json-render nodes, group control, snapshots, and search-based edge creation.
|
|
480
|
+
|
|
481
|
+
### MCP server
|
|
482
|
+
|
|
483
|
+
38 tools + 7 resources. Zero config for any MCP-capable agent.
|
|
484
|
+
|
|
485
|
+
<details>
|
|
486
|
+
<summary>MCP tools</summary>
|
|
487
|
+
|
|
488
|
+
| Tool | Description |
|
|
489
|
+
|------|-------------|
|
|
490
|
+
| `canvas_add_node` | Add a node (markdown, status, context, file, webpage, etc.) |
|
|
491
|
+
| `canvas_add_diagram` | Draw a hand-drawn diagram via the hosted Excalidraw MCP app (preset alias for `canvas_open_mcp_app`) |
|
|
492
|
+
| `canvas_open_mcp_app` | Open any [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps) server's `ui://` resource as an iframe node |
|
|
493
|
+
| `canvas_describe_schema` | Describe the running server's create schemas, examples, and json-render catalog |
|
|
494
|
+
| `canvas_validate_spec` | Validate a json-render spec or graph payload without creating a node |
|
|
495
|
+
| `canvas_refresh_webpage_node` | Re-fetch and update a webpage node from its stored URL |
|
|
496
|
+
| `canvas_add_json_render_node` | Create a native json-render node from a validated spec |
|
|
497
|
+
| `canvas_add_graph_node` | Create a native graph node (line, bar, pie) |
|
|
498
|
+
| `canvas_build_web_artifact` | Build a bundled HTML artifact and open it on the canvas |
|
|
499
|
+
| `canvas_update_node` | Update content, position, size, collapsed state |
|
|
500
|
+
| `canvas_remove_node` | Remove a node and its edges |
|
|
501
|
+
| `canvas_get_layout` | Get full canvas state |
|
|
502
|
+
| `canvas_get_node` | Get a single node by ID |
|
|
503
|
+
| `canvas_add_edge` | Connect two nodes |
|
|
504
|
+
| `canvas_remove_edge` | Remove a connection |
|
|
505
|
+
| `canvas_arrange` | Auto-arrange (grid/column/flow) |
|
|
506
|
+
| `canvas_validate` | Validate collisions, containment, and missing edge endpoints |
|
|
507
|
+
| `canvas_focus_node` | Pan viewport to a node |
|
|
508
|
+
| `canvas_pin_nodes` | Pin nodes to include in agent context |
|
|
509
|
+
| `canvas_clear` | Clear all nodes and edges |
|
|
510
|
+
| `canvas_snapshot` | Save current canvas as a named snapshot |
|
|
511
|
+
| `canvas_list_snapshots` | List saved snapshots |
|
|
512
|
+
| `canvas_restore` | Restore canvas from a saved snapshot |
|
|
513
|
+
| `canvas_delete_snapshot` | Delete a saved snapshot |
|
|
514
|
+
| `canvas_search` | Find nodes by title/content keywords |
|
|
515
|
+
| `canvas_undo` | Undo the last canvas mutation |
|
|
516
|
+
| `canvas_redo` | Redo the last undone mutation |
|
|
517
|
+
| `canvas_diff` | Compare current canvas vs a saved snapshot |
|
|
518
|
+
| `canvas_create_group` | Create a group containing specified nodes |
|
|
519
|
+
| `canvas_group_nodes` | Add nodes to an existing group |
|
|
520
|
+
| `canvas_ungroup` | Release all children from a group |
|
|
521
|
+
| `canvas_batch` | Run a batch of canvas operations with `$ref` support |
|
|
522
|
+
| `canvas_webview_status` | Get Bun.WebView automation status for the workbench |
|
|
523
|
+
| `canvas_webview_start` | Start or replace the Bun.WebView automation session |
|
|
524
|
+
| `canvas_webview_stop` | Stop the active Bun.WebView automation session |
|
|
525
|
+
| `canvas_evaluate` | Evaluate JavaScript in the active workbench automation session |
|
|
526
|
+
| `canvas_resize` | Resize the active workbench automation viewport |
|
|
527
|
+
| `canvas_screenshot` | Capture a screenshot from the active workbench automation session |
|
|
528
|
+
|
|
529
|
+
</details>
|
|
530
|
+
|
|
531
|
+
<details>
|
|
532
|
+
<summary>MCP resources</summary>
|
|
533
|
+
|
|
534
|
+
| Resource | Description |
|
|
535
|
+
|----------|-------------|
|
|
536
|
+
| `canvas://pinned-context` | Content of pinned nodes + nearby unpinned neighbors |
|
|
537
|
+
| `canvas://schema` | Running-server create schemas and json-render catalog metadata |
|
|
538
|
+
| `canvas://layout` | Full canvas state (all nodes, edges, viewport) |
|
|
539
|
+
| `canvas://summary` | Compact overview: counts, pinned titles, viewport |
|
|
540
|
+
| `canvas://spatial-context` | Proximity clusters, reading order, pinned neighborhoods |
|
|
541
|
+
| `canvas://history` | Mutation history timeline with undo/redo position |
|
|
542
|
+
| `canvas://code-graph` | Auto-detected file dependency graph |
|
|
543
|
+
|
|
544
|
+
</details>
|
|
545
|
+
|
|
546
|
+
The MCP server emits `notifications/resources/updated` when canvas state changes, enabling real-time human-to-agent collaboration.
|
|
547
|
+
|
|
548
|
+
### HTTP API
|
|
549
|
+
|
|
550
|
+
REST endpoints for all canvas operations + SSE event stream. Works from any language.
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
# Get canvas state
|
|
554
|
+
curl http://localhost:4313/api/canvas/state
|
|
555
|
+
|
|
556
|
+
# Add a node
|
|
557
|
+
curl -X POST http://localhost:4313/api/canvas/node \
|
|
558
|
+
-H "Content-Type: application/json" \
|
|
559
|
+
-d '{"type":"markdown","title":"Hello","content":"# World"}'
|
|
560
|
+
|
|
561
|
+
# Add an edge
|
|
562
|
+
curl -X POST http://localhost:4313/api/canvas/edge \
|
|
563
|
+
-H "Content-Type: application/json" \
|
|
564
|
+
-d '{"from":"node-1","to":"node-2","type":"flow","label":"next"}'
|
|
565
|
+
|
|
566
|
+
# Add an edge by unique search match instead of explicit IDs
|
|
567
|
+
curl -X POST http://localhost:4313/api/canvas/edge \
|
|
568
|
+
-H "Content-Type: application/json" \
|
|
569
|
+
-d '{"fromSearch":"DVT O3 — GitOps","toSearch":"deep work trend","type":"relation"}'
|
|
570
|
+
|
|
571
|
+
# Pin nodes for agent context
|
|
572
|
+
curl -X POST http://localhost:4313/api/canvas/context-pins \
|
|
573
|
+
-H "Content-Type: application/json" \
|
|
574
|
+
-d '{"nodeIds":["node-1","node-2"]}'
|
|
575
|
+
|
|
576
|
+
# Get pinned context
|
|
577
|
+
curl http://localhost:4313/api/canvas/pinned-context
|
|
578
|
+
|
|
579
|
+
# Draw a hand-drawn diagram via the Excalidraw MCP app preset
|
|
580
|
+
curl -X POST http://localhost:4313/api/canvas/diagram \
|
|
581
|
+
-H "Content-Type: application/json" \
|
|
582
|
+
-d '{"elements":[{"type":"rectangle","id":"r1","x":60,"y":60,"width":180,"height":80,"roundness":{"type":3},"backgroundColor":"#a5d8ff","fillStyle":"solid","label":{"text":"Hello","fontSize":18}}],"title":"Diagram"}'
|
|
583
|
+
|
|
584
|
+
# SSE event stream
|
|
585
|
+
curl -N http://localhost:4313/api/workbench/events
|
|
586
|
+
|
|
587
|
+
# Start WebView automation
|
|
588
|
+
curl -X POST http://localhost:4313/api/workbench/webview/start \
|
|
589
|
+
-H "Content-Type: application/json" \
|
|
590
|
+
-d '{"backend":"chrome","width":1280,"height":800}'
|
|
591
|
+
|
|
592
|
+
# Evaluate JS in the active WebView session
|
|
593
|
+
curl -X POST http://localhost:4313/api/workbench/webview/evaluate \
|
|
594
|
+
-H "Content-Type: application/json" \
|
|
595
|
+
-d '{"expression":"document.title"}'
|
|
596
|
+
|
|
597
|
+
# Resize the active WebView session
|
|
598
|
+
curl -X POST http://localhost:4313/api/workbench/webview/resize \
|
|
599
|
+
-H "Content-Type: application/json" \
|
|
600
|
+
-d '{"width":1440,"height":900}'
|
|
601
|
+
|
|
602
|
+
# Capture a screenshot
|
|
603
|
+
curl -X POST http://localhost:4313/api/workbench/webview/screenshot \
|
|
604
|
+
-H "Content-Type: application/json" \
|
|
605
|
+
-d '{"format":"png"}' \
|
|
606
|
+
--output canvas.png
|
|
607
|
+
|
|
608
|
+
# Search nodes
|
|
609
|
+
curl "http://localhost:4313/api/canvas/search?q=auth"
|
|
610
|
+
|
|
611
|
+
# Run a batch build
|
|
612
|
+
curl -X POST http://localhost:4313/api/canvas/batch \
|
|
613
|
+
-H "Content-Type: application/json" \
|
|
614
|
+
-d '{"operations":[{"op":"node.add","assign":"a","args":{"type":"markdown","title":"A"}},{"op":"group.create","args":{"title":"Frame","childIds":["$a.id"]}}]}'
|
|
615
|
+
|
|
616
|
+
# Validate the current layout
|
|
617
|
+
curl http://localhost:4313/api/canvas/validate
|
|
618
|
+
|
|
619
|
+
# Inspect running-server schemas
|
|
620
|
+
curl http://localhost:4313/api/canvas/schema
|
|
621
|
+
|
|
622
|
+
# Validate a json-render spec without creating a node
|
|
623
|
+
curl -X POST http://localhost:4313/api/canvas/schema/validate \
|
|
624
|
+
-H "Content-Type: application/json" \
|
|
625
|
+
-d '{"type":"json-render","spec":{"root":"card","elements":{"card":{"type":"Card","props":{"title":"Preview"},"children":[]}}}}'
|
|
626
|
+
|
|
627
|
+
# Undo / redo
|
|
628
|
+
curl -X POST http://localhost:4313/api/canvas/undo
|
|
629
|
+
curl -X POST http://localhost:4313/api/canvas/redo
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
Search-based edge creation is intentionally strict: `fromSearch` and `toSearch` must each resolve
|
|
633
|
+
to exactly one node. Broad queries such as `"DVT O3"` may fail if multiple nodes match; use the
|
|
634
|
+
full visible title instead.
|
|
635
|
+
|
|
636
|
+
### JavaScript/TypeScript SDK (Bun runtime)
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
639
|
+
import { createCanvas } from 'pmx-canvas';
|
|
640
|
+
|
|
641
|
+
const canvas = createCanvas({ port: 4313 });
|
|
642
|
+
await canvas.start({ open: true });
|
|
643
|
+
|
|
644
|
+
// Add nodes
|
|
645
|
+
const n1 = canvas.addNode({ type: 'markdown', title: 'Plan', content: '# Step 1\nDo the thing.' });
|
|
646
|
+
const n2 = canvas.addNode({ type: 'status', title: 'Build', content: 'passing' });
|
|
647
|
+
const n3 = canvas.addNode({ type: 'file', content: 'src/index.ts' });
|
|
648
|
+
|
|
649
|
+
// Connect them
|
|
650
|
+
canvas.addEdge({ from: n1, to: n2, type: 'flow' });
|
|
651
|
+
|
|
652
|
+
// Group related nodes
|
|
653
|
+
canvas.createGroup({ title: 'Build Pipeline', childIds: [n1, n2] });
|
|
654
|
+
|
|
655
|
+
// Draw a hand-drawn diagram via the Excalidraw MCP-app preset
|
|
656
|
+
await canvas.addDiagram({
|
|
657
|
+
elements: [
|
|
658
|
+
{ type: 'rectangle', id: 'r1', x: 80, y: 80, width: 160, height: 60,
|
|
659
|
+
roundness: { type: 3 }, backgroundColor: '#a5d8ff', fillStyle: 'solid',
|
|
660
|
+
label: { text: 'Agent' } },
|
|
661
|
+
],
|
|
662
|
+
title: 'Quick sketch',
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
// Batch-build a graph and group around it
|
|
666
|
+
await canvas.runBatch([
|
|
667
|
+
{
|
|
668
|
+
op: 'graph.add',
|
|
669
|
+
assign: 'graph',
|
|
670
|
+
args: {
|
|
671
|
+
title: 'Major wins',
|
|
672
|
+
graphType: 'bar',
|
|
673
|
+
data: [
|
|
674
|
+
{ label: 'Docs', value: 5 },
|
|
675
|
+
{ label: 'Tests', value: 8 },
|
|
676
|
+
],
|
|
677
|
+
xKey: 'label',
|
|
678
|
+
yKey: 'value',
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
op: 'group.create',
|
|
683
|
+
args: {
|
|
684
|
+
title: 'Quarterly graphs',
|
|
685
|
+
childIds: ['$graph.id'],
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
]);
|
|
689
|
+
|
|
690
|
+
// Arrange and inspect
|
|
691
|
+
canvas.arrange('grid');
|
|
692
|
+
console.log(canvas.validate());
|
|
693
|
+
console.log(canvas.getLayout());
|
|
694
|
+
|
|
695
|
+
// Optional WebView automation
|
|
696
|
+
const webview = await canvas.startAutomationWebView({ backend: 'chrome', width: 1280, height: 800 });
|
|
697
|
+
console.log(webview.active);
|
|
698
|
+
console.log(await canvas.evaluateAutomationWebView('document.title'));
|
|
699
|
+
await canvas.resizeAutomationWebView(1440, 900);
|
|
700
|
+
const screenshot = await canvas.screenshotAutomationWebView({ format: 'png' });
|
|
701
|
+
console.log(screenshot.byteLength);
|
|
702
|
+
await canvas.stopAutomationWebView();
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### CLI
|
|
706
|
+
|
|
707
|
+
The CLI is the equally recommended shell-native way to run and control PMX Canvas.
|
|
708
|
+
|
|
709
|
+
```bash
|
|
710
|
+
pmx-canvas # Start canvas, open browser
|
|
711
|
+
pmx-canvas --demo # Start with sample nodes
|
|
712
|
+
pmx-canvas --port=8080 # Custom port
|
|
713
|
+
pmx-canvas --no-open # Headless (for agents)
|
|
714
|
+
pmx-canvas serve --daemon --no-open # Start a detached daemon and wait for health
|
|
715
|
+
pmx-canvas serve status # Inspect daemon health + pid state
|
|
716
|
+
pmx-canvas serve stop # Stop the daemon for this port/pid file
|
|
717
|
+
pmx-canvas --theme=light # Light theme (dark, light, high-contrast)
|
|
718
|
+
pmx-canvas --mcp # Run as MCP server (stdio)
|
|
719
|
+
pmx-canvas --webview-automation # Start headless Bun.WebView session
|
|
720
|
+
pmx-canvas open # Open the current workbench in a browser
|
|
721
|
+
pmx-canvas node add --type webpage --url https://example.com/docs
|
|
722
|
+
pmx-canvas node add --type web-artifact --title "Dashboard" --app-file ./App.tsx
|
|
723
|
+
pmx-canvas node add --help --type webpage --json
|
|
724
|
+
pmx-canvas node add --type graph --graph-type bar --data-file ./metrics.json --x-key label --y-key value
|
|
725
|
+
pmx-canvas node schema --type json-render --component Table --summary
|
|
726
|
+
pmx-canvas edge add --from-search "DVT O3 — GitOps" --to-search "deep work trend" --type relation
|
|
727
|
+
pmx-canvas batch --file ./canvas-ops.json
|
|
728
|
+
pmx-canvas validate
|
|
729
|
+
pmx-canvas validate spec --type json-render --spec-file ./dashboard.json --summary
|
|
730
|
+
pmx-canvas web-artifact build --title "Dashboard" --app-file ./App.tsx --include-logs
|
|
731
|
+
pmx-canvas webview status # Show WebView automation status
|
|
732
|
+
pmx-canvas webview start --backend chrome --width 1440 --height 900
|
|
733
|
+
pmx-canvas webview evaluate --expression "document.title"
|
|
734
|
+
pmx-canvas webview resize --width 1280 --height 800
|
|
735
|
+
pmx-canvas webview screenshot --output ./canvas.png
|
|
736
|
+
pmx-canvas webview stop
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
Use the CLI when you want:
|
|
740
|
+
|
|
741
|
+
- direct terminal control without MCP wiring
|
|
742
|
+
- shell scripts and CI-friendly automation
|
|
743
|
+
- schema-driven discovery from the running server instead of guessing flags or payloads
|
|
744
|
+
- local debugging of canvas, webview, and screenshot flows
|
|
745
|
+
- a control surface that covers normal canvas work without MCP wiring
|
|
746
|
+
|
|
747
|
+
The CLI create commands return the created node shape with normalized title/content and geometry,
|
|
748
|
+
which makes scripting stacked layouts and batch follow-up operations easier.
|
|
749
|
+
|
|
750
|
+
## Agent compatibility
|
|
751
|
+
|
|
752
|
+
| Agent | Integration | Config |
|
|
753
|
+
|-------|-------------|--------|
|
|
754
|
+
| Claude Code | MCP server | `"command": "bunx", "args": ["pmx-canvas", "--mcp"]` |
|
|
755
|
+
| Cursor | MCP server | Same MCP config |
|
|
756
|
+
| Windsurf | MCP server | Same MCP config |
|
|
757
|
+
| OpenAI Codex | MCP or HTTP | Same MCP config, or `curl` commands |
|
|
758
|
+
| Any other | HTTP API | Any language can `fetch()` or `curl` |
|
|
759
|
+
|
|
760
|
+
## Architecture
|
|
761
|
+
|
|
762
|
+
```
|
|
763
|
+
Agent (Claude Code / Codex / Cursor / any MCP client)
|
|
764
|
+
|
|
|
765
|
+
|-- MCP Server ---- 38 tools + 7 resources + change notifications
|
|
766
|
+
|-- Bun SDK ------- createCanvas()
|
|
767
|
+
|-- HTTP API ------ REST + SSE at localhost:4313
|
|
768
|
+
|
|
|
769
|
+
v
|
|
770
|
+
Bun.serve HTTP + SSE Server
|
|
771
|
+
| CanvasStateManager (authoritative state)
|
|
772
|
+
| Context pins (human curates -> agent notified)
|
|
773
|
+
| File watcher (fs.watch -> live node updates)
|
|
774
|
+
| Code graph (auto-detected dependencies)
|
|
775
|
+
| Persistence (.pmx-canvas/state.json)
|
|
776
|
+
| Snapshots (.pmx-canvas/snapshots/)
|
|
777
|
+
|
|
|
778
|
+
v
|
|
779
|
+
Browser (Preact SPA at /workbench)
|
|
780
|
+
Pan/zoom canvas with nodes + edges + minimap
|
|
781
|
+
@preact/signals reactive state
|
|
782
|
+
SSE bridge for real-time updates
|
|
783
|
+
Theme toggle (dark/light/high-contrast)
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
## Tech stack
|
|
787
|
+
|
|
788
|
+
- **Runtime:** [Bun](https://bun.sh)
|
|
789
|
+
- **UI:** [Preact](https://preactjs.com) + [@preact/signals](https://github.com/preactjs/signals)
|
|
790
|
+
- **Styling:** CSS custom properties for the main canvas UI, plus a Tailwind-based build for the json-render viewer bundle
|
|
791
|
+
- **Server:** Bun.serve (HTTP + SSE)
|
|
792
|
+
- **MCP:** [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) (stdio transport)
|
|
793
|
+
|
|
794
|
+
## Development
|
|
795
|
+
|
|
796
|
+
```bash
|
|
797
|
+
bun install # Install dependencies
|
|
798
|
+
bun run build # Build client SPA -> dist/canvas/
|
|
799
|
+
bun run dev # Start server + open browser
|
|
800
|
+
bun run dev:demo # Start with sample nodes
|
|
801
|
+
bun run dev:portless # Start at https://pmx.localhost/workbench
|
|
802
|
+
bun run dev:portless:demo # Same, with demo nodes
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### Testing
|
|
806
|
+
|
|
807
|
+
```bash
|
|
808
|
+
bun run test # Unit tests
|
|
809
|
+
bun run test:coverage # Unit tests with text summary + coverage/lcov.info
|
|
810
|
+
bun run test:e2e # Playwright end-to-end tests
|
|
811
|
+
bun run test:all # All tests
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
## Release
|
|
815
|
+
|
|
816
|
+
Use this sequence before publishing a new version:
|
|
817
|
+
|
|
818
|
+
```bash
|
|
819
|
+
bun install --frozen-lockfile
|
|
820
|
+
bun run release:check
|
|
821
|
+
bun run release:smoke
|
|
822
|
+
bun run pack:dry-run
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
Then publish the version currently in `package.json`:
|
|
826
|
+
|
|
827
|
+
```bash
|
|
828
|
+
bun publish
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
If this is the first release from your machine, run `bunx npm login` once so Bun can reuse your npm credentials.
|
|
832
|
+
|
|
833
|
+
### Project structure
|
|
834
|
+
|
|
835
|
+
```
|
|
836
|
+
src/
|
|
837
|
+
server/ # HTTP/SSE server, state management
|
|
838
|
+
index.ts # PmxCanvas class, createCanvas() export
|
|
839
|
+
server.ts # Bun.serve HTTP + SSE, REST endpoints
|
|
840
|
+
canvas-state.ts # CanvasStateManager (authoritative state)
|
|
841
|
+
client/ # Preact SPA (served at /workbench)
|
|
842
|
+
App.tsx # Root component
|
|
843
|
+
canvas/ # Viewport, nodes, edges, minimap
|
|
844
|
+
nodes/ # Node type renderers
|
|
845
|
+
state/ # State management (canvas-store, sse-bridge)
|
|
846
|
+
cli/
|
|
847
|
+
index.ts # CLI entry point
|
|
848
|
+
mcp/
|
|
849
|
+
server.ts # MCP server (tools + resources)
|
|
850
|
+
dist/
|
|
851
|
+
canvas/ # Built client SPA
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
## Contributing
|
|
855
|
+
|
|
856
|
+
Contributions are welcome. Please open an issue first to discuss what you'd like to change.
|
|
857
|
+
|
|
858
|
+
1. Fork the repo
|
|
859
|
+
2. Create a feature branch (`git checkout -b feature/my-change`)
|
|
860
|
+
3. Run `bun run test:all` before submitting
|
|
861
|
+
4. Open a pull request
|
|
862
|
+
|
|
863
|
+
## License
|
|
864
|
+
|
|
865
|
+
[MIT](LICENSE)
|