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.
Files changed (226) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +21 -0
  3. package/Readme.md +865 -0
  4. package/dist/canvas/global.css +3173 -0
  5. package/dist/canvas/index.js +183 -0
  6. package/dist/json-render/index.css +2 -0
  7. package/dist/json-render/index.js +389 -0
  8. package/dist/types/cli/agent.d.ts +13 -0
  9. package/dist/types/cli/index.d.ts +2 -0
  10. package/dist/types/cli/watch.d.ts +5 -0
  11. package/dist/types/client/App.d.ts +1 -0
  12. package/dist/types/client/canvas/AttentionHistory.d.ts +1 -0
  13. package/dist/types/client/canvas/AttentionToast.d.ts +1 -0
  14. package/dist/types/client/canvas/CanvasNode.d.ts +8 -0
  15. package/dist/types/client/canvas/CanvasViewport.d.ts +8 -0
  16. package/dist/types/client/canvas/CommandPalette.d.ts +4 -0
  17. package/dist/types/client/canvas/ContextMenu.d.ts +24 -0
  18. package/dist/types/client/canvas/ContextPinBar.d.ts +1 -0
  19. package/dist/types/client/canvas/ContextPinHud.d.ts +1 -0
  20. package/dist/types/client/canvas/DockedNode.d.ts +4 -0
  21. package/dist/types/client/canvas/EdgeLayer.d.ts +8 -0
  22. package/dist/types/client/canvas/ExpandedNodeOverlay.d.ts +1 -0
  23. package/dist/types/client/canvas/FocusFieldLayer.d.ts +1 -0
  24. package/dist/types/client/canvas/Minimap.d.ts +23 -0
  25. package/dist/types/client/canvas/SelectionBar.d.ts +1 -0
  26. package/dist/types/client/canvas/ShortcutOverlay.d.ts +3 -0
  27. package/dist/types/client/canvas/SnapshotPanel.d.ts +7 -0
  28. package/dist/types/client/canvas/snap-guides.d.ts +23 -0
  29. package/dist/types/client/canvas/use-node-drag.d.ts +15 -0
  30. package/dist/types/client/canvas/use-node-resize.d.ts +15 -0
  31. package/dist/types/client/canvas/use-pan-zoom.d.ts +16 -0
  32. package/dist/types/client/ext-app/bridge.d.ts +161 -0
  33. package/dist/types/client/icons.d.ts +70 -0
  34. package/dist/types/client/index.d.ts +1 -0
  35. package/dist/types/client/nodes/ContextNode.d.ts +34 -0
  36. package/dist/types/client/nodes/ExtAppFrame.d.ts +18 -0
  37. package/dist/types/client/nodes/FileNode.d.ts +5 -0
  38. package/dist/types/client/nodes/GroupNode.d.ts +6 -0
  39. package/dist/types/client/nodes/ImageNode.d.ts +10 -0
  40. package/dist/types/client/nodes/InlineFormatBar.d.ts +7 -0
  41. package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +14 -0
  42. package/dist/types/client/nodes/LedgerNode.d.ts +4 -0
  43. package/dist/types/client/nodes/MarkdownNode.d.ts +6 -0
  44. package/dist/types/client/nodes/McpAppNode.d.ts +4 -0
  45. package/dist/types/client/nodes/MdFormatBar.d.ts +8 -0
  46. package/dist/types/client/nodes/PromptNode.d.ts +5 -0
  47. package/dist/types/client/nodes/ResponseNode.d.ts +5 -0
  48. package/dist/types/client/nodes/StatusNode.d.ts +4 -0
  49. package/dist/types/client/nodes/StatusSummary.d.ts +4 -0
  50. package/dist/types/client/nodes/TraceNode.d.ts +4 -0
  51. package/dist/types/client/nodes/WebpageNode.d.ts +5 -0
  52. package/dist/types/client/nodes/image-warnings.d.ts +6 -0
  53. package/dist/types/client/nodes/inline-editor-commands.d.ts +11 -0
  54. package/dist/types/client/nodes/md-format.d.ts +25 -0
  55. package/dist/types/client/state/attention-bridge.d.ts +3 -0
  56. package/dist/types/client/state/attention-store.d.ts +25 -0
  57. package/dist/types/client/state/canvas-store.d.ts +74 -0
  58. package/dist/types/client/state/intent-bridge.d.ts +158 -0
  59. package/dist/types/client/state/sse-bridge.d.ts +5 -0
  60. package/dist/types/client/theme/tokens.d.ts +27 -0
  61. package/dist/types/client/types.d.ts +40 -0
  62. package/dist/types/client/utils/ext-app-tool-result.d.ts +1 -0
  63. package/dist/types/client/utils/placement.d.ts +1 -0
  64. package/dist/types/client/utils/platform.d.ts +2 -0
  65. package/dist/types/json-render/catalog.d.ts +815 -0
  66. package/dist/types/json-render/charts/components.d.ts +54 -0
  67. package/dist/types/json-render/charts/definitions.d.ts +103 -0
  68. package/dist/types/json-render/charts/extra-components.d.ts +58 -0
  69. package/dist/types/json-render/charts/extra-definitions.d.ts +181 -0
  70. package/dist/types/json-render/renderer/index.d.ts +16 -0
  71. package/dist/types/json-render/schema.d.ts +46 -0
  72. package/dist/types/json-render/server.d.ts +55 -0
  73. package/dist/types/mcp/server.d.ts +22 -0
  74. package/dist/types/server/agent-context.d.ts +21 -0
  75. package/dist/types/server/artifact-paths.d.ts +3 -0
  76. package/dist/types/server/canvas-operations.d.ts +154 -0
  77. package/dist/types/server/canvas-provenance.d.ts +13 -0
  78. package/dist/types/server/canvas-schema.d.ts +49 -0
  79. package/dist/types/server/canvas-serialization.d.ts +25 -0
  80. package/dist/types/server/canvas-state.d.ts +174 -0
  81. package/dist/types/server/canvas-validation.d.ts +33 -0
  82. package/dist/types/server/chart-template.d.ts +29 -0
  83. package/dist/types/server/code-graph.d.ts +67 -0
  84. package/dist/types/server/context-cards.d.ts +24 -0
  85. package/dist/types/server/diagram-presets.d.ts +28 -0
  86. package/dist/types/server/ext-app-call-registry.d.ts +16 -0
  87. package/dist/types/server/ext-app-tool-result.d.ts +1 -0
  88. package/dist/types/server/file-watcher.d.ts +16 -0
  89. package/dist/types/server/index.d.ts +243 -0
  90. package/dist/types/server/mcp-app-candidate.d.ts +25 -0
  91. package/dist/types/server/mcp-app-host.d.ts +65 -0
  92. package/dist/types/server/mcp-app-runtime.d.ts +47 -0
  93. package/dist/types/server/mutation-history.d.ts +105 -0
  94. package/dist/types/server/placement.d.ts +37 -0
  95. package/dist/types/server/server.d.ts +103 -0
  96. package/dist/types/server/spatial-analysis.d.ts +87 -0
  97. package/dist/types/server/trace-manager.d.ts +48 -0
  98. package/dist/types/server/web-artifacts.d.ts +50 -0
  99. package/dist/types/server/webpage-node.d.ts +25 -0
  100. package/dist/types/shared/auto-arrange.d.ts +29 -0
  101. package/dist/types/shared/ext-app-tool-result.d.ts +9 -0
  102. package/dist/types/shared/placement.d.ts +26 -0
  103. package/dist/types/shared/semantic-attention.d.ts +97 -0
  104. package/package.json +109 -0
  105. package/skills/data-analysis/SKILL.md +324 -0
  106. package/skills/doc-coauthoring/SKILL.md +375 -0
  107. package/skills/frontend-design/SKILL.md +45 -0
  108. package/skills/json-render-codegen/SKILL.md +112 -0
  109. package/skills/json-render-core/SKILL.md +265 -0
  110. package/skills/json-render-ink/SKILL.md +273 -0
  111. package/skills/json-render-mcp/SKILL.md +132 -0
  112. package/skills/json-render-react/SKILL.md +264 -0
  113. package/skills/json-render-shadcn/SKILL.md +159 -0
  114. package/skills/playwright-cli/SKILL.md +67 -0
  115. package/skills/pmx-canvas/SKILL.md +668 -0
  116. package/skills/pmx-canvas/evals/evals.json +186 -0
  117. package/skills/pmx-canvas-testing/SKILL.md +78 -0
  118. package/skills/published-consumer-e2e/SKILL.md +43 -0
  119. package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +241 -0
  120. package/skills/web-artifacts-builder/SKILL.md +80 -0
  121. package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +167 -0
  122. package/skills/web-artifacts-builder/scripts/init-artifact.sh +425 -0
  123. package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  124. package/skills/web-design-guidelines/SKILL.md +39 -0
  125. package/src/cli/agent.ts +2144 -0
  126. package/src/cli/index.ts +622 -0
  127. package/src/cli/watch.ts +88 -0
  128. package/src/client/App.tsx +507 -0
  129. package/src/client/canvas/AttentionHistory.tsx +81 -0
  130. package/src/client/canvas/AttentionToast.tsx +19 -0
  131. package/src/client/canvas/CanvasNode.tsx +363 -0
  132. package/src/client/canvas/CanvasViewport.tsx +590 -0
  133. package/src/client/canvas/CommandPalette.tsx +302 -0
  134. package/src/client/canvas/ContextMenu.tsx +601 -0
  135. package/src/client/canvas/ContextPinBar.tsx +25 -0
  136. package/src/client/canvas/ContextPinHud.tsx +22 -0
  137. package/src/client/canvas/DockedNode.tsx +66 -0
  138. package/src/client/canvas/EdgeLayer.tsx +280 -0
  139. package/src/client/canvas/ExpandedNodeOverlay.tsx +260 -0
  140. package/src/client/canvas/FocusFieldLayer.tsx +107 -0
  141. package/src/client/canvas/Minimap.tsx +301 -0
  142. package/src/client/canvas/SelectionBar.tsx +69 -0
  143. package/src/client/canvas/ShortcutOverlay.tsx +69 -0
  144. package/src/client/canvas/SnapshotPanel.tsx +236 -0
  145. package/src/client/canvas/snap-guides.ts +170 -0
  146. package/src/client/canvas/use-node-drag.ts +51 -0
  147. package/src/client/canvas/use-node-resize.ts +59 -0
  148. package/src/client/canvas/use-pan-zoom.ts +191 -0
  149. package/src/client/ext-app/bridge.ts +542 -0
  150. package/src/client/icons.tsx +424 -0
  151. package/src/client/index.tsx +7 -0
  152. package/src/client/nodes/ContextNode.tsx +412 -0
  153. package/src/client/nodes/ExtAppFrame.tsx +509 -0
  154. package/src/client/nodes/FileNode.tsx +256 -0
  155. package/src/client/nodes/GroupNode.tsx +39 -0
  156. package/src/client/nodes/ImageNode.tsx +160 -0
  157. package/src/client/nodes/InlineFormatBar.tsx +169 -0
  158. package/src/client/nodes/InlineMarkdownEditor.tsx +123 -0
  159. package/src/client/nodes/LedgerNode.tsx +37 -0
  160. package/src/client/nodes/MarkdownNode.tsx +359 -0
  161. package/src/client/nodes/McpAppNode.tsx +85 -0
  162. package/src/client/nodes/MdFormatBar.tsx +109 -0
  163. package/src/client/nodes/PromptNode.tsx +597 -0
  164. package/src/client/nodes/ResponseNode.tsx +153 -0
  165. package/src/client/nodes/StatusNode.tsx +84 -0
  166. package/src/client/nodes/StatusSummary.tsx +38 -0
  167. package/src/client/nodes/TraceNode.tsx +120 -0
  168. package/src/client/nodes/WebpageNode.tsx +288 -0
  169. package/src/client/nodes/image-warnings.ts +95 -0
  170. package/src/client/nodes/inline-editor-commands.ts +37 -0
  171. package/src/client/nodes/md-format.ts +206 -0
  172. package/src/client/state/attention-bridge.ts +328 -0
  173. package/src/client/state/attention-store.ts +73 -0
  174. package/src/client/state/canvas-store.ts +631 -0
  175. package/src/client/state/intent-bridge.ts +315 -0
  176. package/src/client/state/sse-bridge.ts +965 -0
  177. package/src/client/theme/global.css +3173 -0
  178. package/src/client/theme/tokens.ts +72 -0
  179. package/src/client/types-shims.d.ts +5 -0
  180. package/src/client/types.ts +81 -0
  181. package/src/client/utils/ext-app-tool-result.ts +4 -0
  182. package/src/client/utils/placement.ts +4 -0
  183. package/src/client/utils/platform.ts +2 -0
  184. package/src/json-render/catalog.ts +256 -0
  185. package/src/json-render/charts/components.tsx +198 -0
  186. package/src/json-render/charts/definitions.ts +81 -0
  187. package/src/json-render/charts/extra-components.tsx +267 -0
  188. package/src/json-render/charts/extra-definitions.ts +145 -0
  189. package/src/json-render/renderer/index.css +174 -0
  190. package/src/json-render/renderer/index.tsx +86 -0
  191. package/src/json-render/schema.ts +62 -0
  192. package/src/json-render/server.ts +597 -0
  193. package/src/mcp/server.ts +1377 -0
  194. package/src/server/agent-context.ts +242 -0
  195. package/src/server/artifact-paths.ts +17 -0
  196. package/src/server/canvas-operations.ts +1279 -0
  197. package/src/server/canvas-provenance.ts +243 -0
  198. package/src/server/canvas-schema.ts +432 -0
  199. package/src/server/canvas-serialization.ts +95 -0
  200. package/src/server/canvas-state.ts +1134 -0
  201. package/src/server/canvas-validation.ts +114 -0
  202. package/src/server/chart-template.ts +449 -0
  203. package/src/server/code-graph.ts +370 -0
  204. package/src/server/context-cards.ts +31 -0
  205. package/src/server/diagram-presets.ts +71 -0
  206. package/src/server/ext-app-call-registry.ts +77 -0
  207. package/src/server/ext-app-tool-result.ts +4 -0
  208. package/src/server/file-watcher.ts +121 -0
  209. package/src/server/index.ts +647 -0
  210. package/src/server/mcp-app-candidate.ts +174 -0
  211. package/src/server/mcp-app-host.ts +814 -0
  212. package/src/server/mcp-app-runtime.ts +459 -0
  213. package/src/server/mutation-history.ts +350 -0
  214. package/src/server/placement.ts +125 -0
  215. package/src/server/server.ts +3846 -0
  216. package/src/server/spatial-analysis.ts +356 -0
  217. package/src/server/trace-manager.ts +333 -0
  218. package/src/server/web-artifacts/scripts/bundle-artifact.sh +167 -0
  219. package/src/server/web-artifacts/scripts/init-artifact.sh +426 -0
  220. package/src/server/web-artifacts/scripts/shadcn-components.tar.gz +0 -0
  221. package/src/server/web-artifacts.ts +442 -0
  222. package/src/server/webpage-node.ts +328 -0
  223. package/src/shared/auto-arrange.ts +439 -0
  224. package/src/shared/ext-app-tool-result.ts +76 -0
  225. package/src/shared/placement.ts +81 -0
  226. 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)