pmx-canvas 0.1.18 → 0.1.20

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 (70) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/Readme.md +19 -6
  3. package/dist/canvas/global.css +35 -2
  4. package/dist/canvas/index.js +70 -69
  5. package/dist/json-render/index.js +109 -109
  6. package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
  7. package/dist/types/client/icons.d.ts +2 -0
  8. package/dist/types/client/state/canvas-store.d.ts +2 -0
  9. package/dist/types/client/types.d.ts +2 -1
  10. package/dist/types/json-render/charts/components.d.ts +5 -1
  11. package/dist/types/json-render/renderer/index.d.ts +1 -0
  12. package/dist/types/json-render/server.d.ts +1 -0
  13. package/dist/types/mcp/canvas-access.d.ts +3 -0
  14. package/dist/types/server/canvas-operations.d.ts +4 -0
  15. package/dist/types/server/canvas-schema.d.ts +19 -3
  16. package/dist/types/server/canvas-serialization.d.ts +1 -0
  17. package/dist/types/server/canvas-state.d.ts +8 -2
  18. package/dist/types/server/html-primitives.d.ts +34 -0
  19. package/dist/types/server/index.d.ts +19 -0
  20. package/docs/RELEASE.md +153 -0
  21. package/docs/bun-webview-integration.md +296 -0
  22. package/docs/cli.md +143 -0
  23. package/docs/evals/e2e-cli-coverage.md +61 -0
  24. package/docs/http-api.md +201 -0
  25. package/docs/mcp.md +137 -0
  26. package/docs/node-types.md +272 -0
  27. package/docs/plans/.gitkeep +0 -0
  28. package/docs/plans/plan-001-semantic-watch-mvp.md +335 -0
  29. package/docs/plans/plan-002-human-attention-layer-design-spec.md +679 -0
  30. package/docs/plans/plan-003-human-attention-layer-implementation-plan.md +572 -0
  31. package/docs/reactive-canvas-proposal.md +578 -0
  32. package/docs/release-review-0.1.0.md +38 -0
  33. package/docs/screenshot.png +0 -0
  34. package/docs/screenshots/demo-workbench-dark.png +0 -0
  35. package/docs/screenshots/demo-workbench-light.png +0 -0
  36. package/docs/screenshots/welcome-dark.png +0 -0
  37. package/docs/screenshots/welcome-light.png +0 -0
  38. package/docs/sdk.md +103 -0
  39. package/package.json +2 -1
  40. package/skills/pmx-canvas/SKILL.md +8 -0
  41. package/src/cli/agent.ts +167 -5
  42. package/src/client/App.tsx +20 -1
  43. package/src/client/canvas/AnnotationLayer.tsx +33 -12
  44. package/src/client/canvas/CanvasViewport.tsx +88 -7
  45. package/src/client/canvas/CommandPalette.tsx +1 -1
  46. package/src/client/canvas/ContextMenu.tsx +2 -2
  47. package/src/client/canvas/ExpandedNodeOverlay.tsx +7 -1
  48. package/src/client/icons.tsx +13 -0
  49. package/src/client/nodes/McpAppNode.tsx +12 -4
  50. package/src/client/state/canvas-store.ts +15 -5
  51. package/src/client/state/sse-bridge.ts +4 -3
  52. package/src/client/theme/global.css +35 -2
  53. package/src/client/types.ts +2 -1
  54. package/src/json-render/charts/components.tsx +41 -7
  55. package/src/json-render/charts/extra-components.tsx +13 -12
  56. package/src/json-render/renderer/index.tsx +1 -0
  57. package/src/json-render/server.ts +3 -1
  58. package/src/mcp/canvas-access.ts +25 -0
  59. package/src/mcp/server.ts +85 -27
  60. package/src/server/agent-context.ts +17 -0
  61. package/src/server/canvas-operations.ts +91 -38
  62. package/src/server/canvas-schema.ts +83 -3
  63. package/src/server/canvas-serialization.ts +9 -2
  64. package/src/server/canvas-state.ts +27 -9
  65. package/src/server/demo-state.json +1143 -0
  66. package/src/server/demo.ts +25 -777
  67. package/src/server/html-primitives.ts +990 -0
  68. package/src/server/index.ts +43 -2
  69. package/src/server/server.ts +140 -14
  70. package/src/server/spatial-analysis.ts +3 -3
@@ -0,0 +1,296 @@
1
+ # Bun.WebView Integration
2
+
3
+ PMX Canvas now uses Bun's built-in `Bun.WebView` API for **headless canvas automation**, while keeping the current external browser launcher for the normal interactive canvas window.
4
+
5
+ This document tracks:
6
+
7
+ - what is implemented today
8
+ - what was validated on stable Bun `v1.3.12`
9
+ - what still remains future work
10
+
11
+ ## Current Status
12
+
13
+ **Implemented** for Phase 1.
14
+
15
+ The repo now targets Bun `>=1.3.12`, and PMX Canvas ships an opt-in, server-owned WebView automation session that can:
16
+
17
+ - start a headless browser session for `/workbench`
18
+ - report WebView runtime status
19
+ - evaluate JavaScript in the page
20
+ - resize the automation viewport
21
+ - capture screenshots
22
+ - stop and clean up the automation session explicitly
23
+
24
+ What is **not** implemented:
25
+
26
+ - replacing `openUrlInExternalBrowser()` for the visible user-facing canvas window
27
+ - headed Bun.WebView window support
28
+ - Chrome-only CDP tooling
29
+
30
+ ## Why This Exists
31
+
32
+ The existing browser-launch path in [`src/server/server.ts`](../src/server/server.ts) still opens the interactive canvas with platform-specific shell behavior:
33
+
34
+ - macOS: AppleScript / `osascript`
35
+ - Windows: `cmd /c start` or a resolved browser executable
36
+ - Linux: `xdg-open`
37
+
38
+ That path is still appropriate for humans, but it provides no programmatic control once the browser is open.
39
+
40
+ `Bun.WebView` gives PMX Canvas a controlled browser session that the server can own directly. That unlocks:
41
+
42
+ - agent-visible screenshots
43
+ - browser-backed evaluation of rendered canvas state
44
+ - controlled viewport sizing
45
+ - future browser automation tools without depending on Playwright for the basic runtime
46
+
47
+ ## Implemented Surface
48
+
49
+ ### Server layer
50
+
51
+ Implemented in [`src/server/server.ts`](../src/server/server.ts):
52
+
53
+ - `getCanvasAutomationWebViewStatus()`
54
+ - `startCanvasAutomationWebView(url, options)`
55
+ - `stopCanvasAutomationWebView()`
56
+ - `evaluateCanvasAutomationWebView(expression)`
57
+ - `resizeCanvasAutomationWebView(width, height)`
58
+ - `screenshotCanvasAutomationWebView(options)`
59
+
60
+ Important behavior:
61
+
62
+ - runtime-gated: if `Bun.WebView` is unavailable, PMX Canvas returns a clear error
63
+ - headless-only: PMX Canvas does not rely on `headless: false`
64
+ - explicit lifecycle: start and stop are owned by the server
65
+ - screenshot normalization handles Bun return types correctly, including `Blob`
66
+
67
+ ### HTTP API
68
+
69
+ Implemented in [`src/server/server.ts`](../src/server/server.ts):
70
+
71
+ - `GET /api/workbench/webview`
72
+ - `POST /api/workbench/webview/start`
73
+ - `POST /api/workbench/webview/evaluate`
74
+ - `POST /api/workbench/webview/resize`
75
+ - `POST /api/workbench/webview/screenshot`
76
+ - `DELETE /api/workbench/webview`
77
+
78
+ Current HTTP behavior:
79
+
80
+ - `GET` returns runtime support plus active-session status
81
+ - `POST` starts a new headless automation session for the current `/workbench`
82
+ - `POST /evaluate` runs JavaScript in the active automation session and returns JSON
83
+ - `POST /resize` updates the active automation viewport and returns JSON status
84
+ - `POST /screenshot` returns image bytes for the active automation session
85
+ - `DELETE` stops the current automation session
86
+
87
+ Current options accepted by `POST /api/workbench/webview/start`:
88
+
89
+ - `backend`: `"chrome"` or `"webkit"`
90
+ - `width`
91
+ - `height`
92
+ - `chromePath`
93
+ - `chromeArgv`
94
+ - `dataStoreDir`
95
+
96
+ ### SDK
97
+
98
+ Implemented in [`src/server/index.ts`](../src/server/index.ts):
99
+
100
+ - `canvas.start({ automationWebView: ... })`
101
+ - `canvas.startAutomationWebView(options)`
102
+ - `canvas.stopAutomationWebView()`
103
+ - `canvas.getAutomationWebViewStatus()`
104
+ - `canvas.evaluateAutomationWebView(expression)`
105
+ - `canvas.resizeAutomationWebView(width, height)`
106
+ - `canvas.screenshotAutomationWebView(options)`
107
+
108
+ ### CLI
109
+
110
+ Implemented in [`src/cli/index.ts`](../src/cli/index.ts):
111
+
112
+ - `--webview-automation`
113
+ - `--webview-backend`
114
+ - `--webview-width`
115
+ - `--webview-height`
116
+ - `--webview-chrome-path`
117
+ - `--webview-chrome-argv`
118
+ - `--webview-data-dir`
119
+
120
+ Implemented in [`src/cli/agent.ts`](../src/cli/agent.ts):
121
+
122
+ - `pmx-canvas webview status`
123
+ - `pmx-canvas webview start`
124
+ - `pmx-canvas webview evaluate`
125
+ - `pmx-canvas webview resize`
126
+ - `pmx-canvas webview screenshot`
127
+ - `pmx-canvas webview stop`
128
+
129
+ Example:
130
+
131
+ ```bash
132
+ pmx-canvas --no-open --webview-automation
133
+ pmx-canvas --webview-automation --webview-backend=chrome
134
+ pmx-canvas webview start --backend chrome --width 1440 --height 900
135
+ pmx-canvas webview evaluate --expression "document.title"
136
+ pmx-canvas webview resize --width 1280 --height 800
137
+ pmx-canvas webview screenshot --output ./canvas.png
138
+ pmx-canvas webview stop
139
+ ```
140
+
141
+ These flags are intentionally automation-only. They do **not** imply a visible Bun-managed window.
142
+
143
+ ### MCP
144
+
145
+ Implemented in [`src/mcp/server.ts`](../src/mcp/server.ts):
146
+
147
+ - `canvas_webview_status`
148
+ - `canvas_webview_start`
149
+ - `canvas_webview_stop`
150
+ - `canvas_evaluate`
151
+ - `canvas_resize`
152
+ - `canvas_screenshot`
153
+
154
+ Current MCP behavior:
155
+
156
+ - automation lifecycle is explicit rather than implicit
157
+ - screenshot returns an MCP image payload plus JSON metadata
158
+ - evaluate/resize/screenshot require an active automation session
159
+ - start accepts the same backend/viewport/data-store options as the server layer
160
+
161
+ ## Backend Strategy
162
+
163
+ PMX Canvas follows Bun's current stable backend reality.
164
+
165
+ ### WebKit backend
166
+
167
+ - macOS only
168
+ - good default on macOS when CDP is not required
169
+ - zero external Chrome dependency
170
+
171
+ ### Chrome backend
172
+
173
+ - required on Linux and Windows
174
+ - also supported on macOS
175
+ - preferred when backend consistency or future CDP-based tooling matters
176
+
177
+ ### Current default behavior
178
+
179
+ PMX Canvas defaults to:
180
+
181
+ - `webkit` on macOS
182
+ - `chrome` elsewhere
183
+
184
+ If `chromePath` or `chromeArgv` is provided, PMX Canvas forces a Chrome-backed session.
185
+
186
+ ## Bun Runtime Requirements
187
+
188
+ The repo now requires:
189
+
190
+ - Bun `>=1.3.12`
191
+
192
+ That change is reflected in:
193
+
194
+ - [`package.json`](../package.json)
195
+ - [`.github/workflows/test.yml`](../.github/workflows/test.yml)
196
+
197
+ PMX Canvas still keeps a runtime guard because older local Bun versions can exist in user environments. If `Bun.WebView` is missing, startup fails cleanly for the automation path instead of partially starting.
198
+
199
+ ## Validation Results
200
+
201
+ Validated on Bun `v1.3.12`.
202
+
203
+ ### Unit coverage
204
+
205
+ Verified by tests in:
206
+
207
+ - [`tests/unit/server-api.test.ts`](../tests/unit/server-api.test.ts)
208
+ - [`tests/unit/cli-webview.test.ts`](../tests/unit/cli-webview.test.ts)
209
+ - [`tests/unit/webview-automation.test.ts`](../tests/unit/webview-automation.test.ts)
210
+
211
+ Covered behavior:
212
+
213
+ - WebView status over HTTP
214
+ - start/stop lifecycle over HTTP
215
+ - evaluate/resize/screenshot over HTTP
216
+ - CLI status/start/evaluate/resize/screenshot/stop commands
217
+ - graceful unsupported-runtime handling
218
+ - SDK start/evaluate/resize/screenshot flow
219
+
220
+ ### Live runtime smoke
221
+
222
+ Validated manually after upgrading the local runtime to Bun `1.3.12`:
223
+
224
+ - `pmx-canvas --port=4529 --no-open --webview-automation --webview-backend=chrome`
225
+ - `GET /api/workbench/webview`
226
+ - `DELETE /api/workbench/webview`
227
+ - SDK smoke with:
228
+ - `startAutomationWebView`
229
+ - `evaluateAutomationWebView('document.title')`
230
+ - `resizeAutomationWebView(...)`
231
+ - `screenshotAutomationWebView(...)`
232
+
233
+ Observed results:
234
+
235
+ - automation session started successfully
236
+ - status endpoint reported `active: true`
237
+ - JS evaluation returned `"PMX Canvas"`
238
+ - viewport resize updated status correctly
239
+ - screenshots returned non-zero image bytes on both WebKit and Chrome backends during smoke testing
240
+
241
+ ## Implementation Notes
242
+
243
+ ### Screenshot normalization bug
244
+
245
+ During real runtime validation, a bug was found in the first implementation:
246
+
247
+ - PMX Canvas assumed `view.screenshot()` returned only `Uint8Array` or `ArrayBuffer`
248
+ - on this machine, Bun could also return a `Blob`
249
+ - that caused zero-byte screenshots in the wrapper even though Bun was returning valid image data
250
+
251
+ This was fixed in [`src/server/server.ts`](../src/server/server.ts) by normalizing:
252
+
253
+ - `Uint8Array`
254
+ - `ArrayBuffer`
255
+ - `Blob`
256
+
257
+ The regression is now covered by [`tests/unit/webview-automation.test.ts`](../tests/unit/webview-automation.test.ts).
258
+
259
+ ### Why the interactive browser path still exists
260
+
261
+ Even with Bun `v1.3.12`, PMX Canvas should still keep `openUrlInExternalBrowser()` for the normal canvas window because Bun's current documented WebView surface is still effectively headless automation. There is no reason yet to promise a headed replacement window to users.
262
+
263
+ ## Known Limits
264
+
265
+ These are current, accepted constraints:
266
+
267
+ - no visible Bun-managed canvas window
268
+ - no persistent automation-session orchestration beyond the single owned server session
269
+ - no Chrome-only CDP tool surface yet
270
+
271
+ ## Future Work
272
+
273
+ ### Near-term
274
+
275
+ The most obvious next steps are:
276
+
277
+ 1. Add an optional `canvas_cdp` MCP tool for Chrome-backed sessions only
278
+ 2. Decide whether the default macOS backend should stay `webkit` or move to `chrome` for stricter cross-platform parity
279
+ 3. Add more backend-specific test coverage if Bun behavior diverges between WebKit and Chrome
280
+
281
+ ### Later
282
+
283
+ Defer these until Bun's API justifies them:
284
+
285
+ 1. Replace `openUrlInExternalBrowser()` with Bun.WebView for the normal interactive canvas window
286
+ 2. Add headed window management semantics
287
+ 3. Build user-facing browser controls around Bun.WebView if Bun documents those behaviors as stable
288
+
289
+ ## Decision Summary
290
+
291
+ The project should continue with this split:
292
+
293
+ - **Interactive canvas for humans**: external browser
294
+ - **Programmatic canvas automation for agents/server workflows**: Bun.WebView
295
+
296
+ That keeps the current user-facing behavior stable while giving PMX Canvas a real browser automation surface that is built into the runtime.
package/docs/cli.md ADDED
@@ -0,0 +1,143 @@
1
+ # CLI reference
2
+
3
+ The CLI is the shell-native way to run and control PMX Canvas. It targets
4
+ `http://localhost:4313` by default — override with `PMX_CANVAS_URL` or
5
+ `PMX_CANVAS_PORT` when the server runs elsewhere.
6
+
7
+ ## Server lifecycle
8
+
9
+ ```bash
10
+ pmx-canvas # Start canvas, open browser
11
+ pmx-canvas --demo # Start with the saved dashboard demo board
12
+ pmx-canvas --port=8080 # Custom port
13
+ pmx-canvas --no-open # Headless (for agents/CI)
14
+ pmx-canvas --theme=light # dark | light | high-contrast
15
+ pmx-canvas --mcp # Run as MCP server (stdio)
16
+ pmx-canvas --webview-automation # Start headless Bun.WebView session
17
+ pmx-canvas open # Open the current workbench in a browser
18
+ ```
19
+
20
+ ### Daemon mode
21
+
22
+ Run detached with pid/log tracking instead of holding a terminal:
23
+
24
+ ```bash
25
+ pmx-canvas serve --daemon --no-open --wait-ms=20000 # Start detached, wait for health
26
+ pmx-canvas serve status # Inspect daemon health + pid
27
+ pmx-canvas serve stop # Stop the daemon for this port
28
+ ```
29
+
30
+ ## Nodes and edges
31
+
32
+ ```bash
33
+ pmx-canvas node add --type webpage --url https://example.com/docs
34
+ pmx-canvas node add --type web-artifact --title "Dashboard" --app-file ./App.tsx
35
+ pmx-canvas node add --type graph --graph-type bar --data-file ./metrics.json --x-key label --y-key value
36
+ pmx-canvas node add --type graph --graph-type bar --data '[{"x":"a","y":1}]' --x-key x --y-key y
37
+ pmx-canvas graph add --graph-type bar --data '[{"x":"a","y":1}]' --x-key x --y-key y # Alias
38
+ pmx-canvas html primitive add --kind choice-grid --data-file ./options.json --title "Options"
39
+ pmx-canvas html primitive schema --summary
40
+ pmx-canvas node add --help --type webpage --json # Schema for one type
41
+
42
+ pmx-canvas external-app add --kind excalidraw --title "Diagram"
43
+
44
+ pmx-canvas edge add --from-search "DVT O3 — GitOps" --to-search "deep work trend" --type relation
45
+ ```
46
+
47
+ `--from-search` / `--to-search` must each resolve to exactly one node — broad
48
+ queries fail rather than guess. Use the full visible title.
49
+
50
+ CLI create commands return the created node shape with normalized title,
51
+ content, and geometry, which makes scripting stacked layouts and batch
52
+ follow-ups easier.
53
+
54
+ ### Graph height flags
55
+
56
+ Graph height flags split by target:
57
+
58
+ - `--node-height` / `--nodeHeight` — the canvas node frame
59
+ - `--chart-height` — the chart content inside the node
60
+ - `--height` — accepted as a frame-height compatibility alias
61
+
62
+ For MCP/HTTP payloads, use `nodeHeight` for the frame and `height` for chart
63
+ content.
64
+
65
+ ## Discovery and validation
66
+
67
+ ```bash
68
+ pmx-canvas node schema --type json-render --component Table --summary
69
+ pmx-canvas validate # Layout validation
70
+ pmx-canvas validate spec --type json-render --spec-file ./dashboard.json --summary
71
+ pmx-canvas validate spec --type html-primitive --kind choice-grid --data-json '{"items":[{"title":"A"}]}' --summary
72
+ ```
73
+
74
+ The schema commands surface the running server's data, which is strictly
75
+ better than guessing flags or payloads.
76
+
77
+ ## Batch and arrange
78
+
79
+ ```bash
80
+ pmx-canvas batch --file ./canvas-ops.json
81
+ ```
82
+
83
+ See [HTTP API → batch](http-api.md#batch-operations) for the operation
84
+ schema; the same JSON works for the CLI batch file.
85
+
86
+ ## Web artifacts
87
+
88
+ ```bash
89
+ pmx-canvas web-artifact build --title "Dashboard" --app-file ./App.tsx --deps recharts --include-logs
90
+ ```
91
+
92
+ Failed or empty CLI bundles print `ok: false`, exit non-zero, and do not
93
+ create a canvas node.
94
+
95
+ ## Watch (semantic deltas)
96
+
97
+ `pmx-canvas watch` consumes the SSE stream and emits compact semantic deltas
98
+ for agents that need low-token updates instead of full layout snapshots. It
99
+ filters noise from harmless moves and reports meaningful events such as pins,
100
+ node additions/removals, group changes, edge connections, and moves that
101
+ change spatial clustering.
102
+
103
+ ```bash
104
+ pmx-canvas watch --events context-pin,move-end
105
+ pmx-canvas watch --json --events context-pin --max-events 1
106
+ ```
107
+
108
+ ## Focus
109
+
110
+ ```bash
111
+ pmx-canvas focus <node-id> # Pan viewport to a node
112
+ pmx-canvas focus <node-id> --no-pan # Select/raise without panning
113
+ ```
114
+
115
+ ## WebView automation
116
+
117
+ Drive a headless Bun.WebView (Chromium or WebKit) pointed at the workbench:
118
+
119
+ ```bash
120
+ pmx-canvas webview status
121
+ pmx-canvas webview start --backend chrome --width 1440 --height 900
122
+ pmx-canvas webview evaluate --expression "document.title"
123
+ pmx-canvas webview resize --width 1280 --height 800
124
+ pmx-canvas webview screenshot --output ./canvas.png
125
+ pmx-canvas webview stop
126
+ ```
127
+
128
+ Use WebView for visual annotation inspection. Agent-readable canvas context only
129
+ reports annotation targets and bounds; it does not describe whether the human
130
+ drew an arrow, line, circle, or other shape. Inspect `.annotation-layer path` or
131
+ take a screenshot when the drawn form matters.
132
+
133
+ Humans draw with the pen toolbar button and remove marks with the eraser button.
134
+ If an agent already knows the annotation ID from context, it can remove it through
135
+ MCP with `canvas_remove_annotation`.
136
+
137
+ ## When to reach for the CLI
138
+
139
+ - Direct terminal control without MCP wiring
140
+ - Shell scripts and CI-friendly automation
141
+ - Schema-driven discovery from the running server
142
+ - Local debugging of canvas, webview, and screenshot flows
143
+ - A control surface that covers normal canvas work without MCP wiring
@@ -0,0 +1,61 @@
1
+ ---
2
+ id: eval-20260424-001
3
+ pattern-key: pmx-canvas.cli-e2e-fresh-workspace
4
+ source: pmx-canvas-0.1.2-e2e-cli-coverage-report
5
+ promoted-rule: "Fresh-workspace CLI coverage must verify node creation, parseable JSON, web-artifact failure behavior, external apps, arrange/validate, and focus no-pan before release."
6
+ promoted-to: package.json test:e2e-cli
7
+ created: 2026-04-24
8
+ last-run: 2026-04-25
9
+ last-result: pass
10
+ ---
11
+
12
+ # PMX Canvas CLI E2E Coverage Eval
13
+
14
+ ## What This Tests
15
+
16
+ Prevents regressions in the published-agent CLI flows that caused the 0.1.2 E2E report failures.
17
+
18
+ ## Precondition
19
+
20
+ - Bun is installed.
21
+ - The repo has been built with `bun run build` when client/browser assets changed.
22
+ - Network is available for the hosted Excalidraw MCP preset and web-artifact dependency install.
23
+ - Port `4567` is free, or `PMX_CANVAS_E2E_PORT` is set to a free port.
24
+
25
+ ## Verification Method
26
+
27
+ Command check:
28
+
29
+ ```bash
30
+ bun run test:e2e-cli
31
+ ```
32
+
33
+ The command runs `scripts/e2e-cli-coverage.sh`, which creates a fresh temp workspace, starts the local PMX Canvas CLI server, and verifies:
34
+
35
+ - `layout` and `node list` emit JSON parseable by Python's `json` module.
36
+ - Core node types can be created through the CLI, including two webpage input paths.
37
+ - Graph nodes accept `--data` as an alias for `--data-json`.
38
+ - All graph variants create successfully: line, bar, pie, area, scatter, radar, stacked-bar, composed.
39
+ - Simple and dashboard-shaped json-render specs create successfully.
40
+ - Generic `node add --type mcp-app` is rejected with guidance.
41
+ - `external-app add --kind excalidraw` creates a tool-backed app node.
42
+ - Broken web-artifact builds return `ok: false`, exit non-zero, and do not create a node.
43
+ - One successful web-artifact build emits a substantial bundled React/Recharts app, opens a node, and browser-verifies the real app content renders in the iframe.
44
+ - `focus --no-pan` selects without viewport panning.
45
+ - `arrange --layout grid` and `validate` agree on a valid layout.
46
+ - Search can find artifact nodes and `status` reports expected graph/json-render/web-artifact counts.
47
+
48
+ ## Expected Result
49
+
50
+ **Pass:** `PMX Canvas CLI E2E coverage passed` and exit code 0.
51
+
52
+ **Fail:** Any command exits non-zero, JSON parsing fails, an assertion fails, or the server does not become healthy.
53
+
54
+ ## Recovery Action
55
+
56
+ If this eval fails:
57
+
58
+ 1. Re-run with `PMX_CANVAS_E2E_KEEP_WORKDIR=1 bun run test:e2e-cli` to preserve the temp workspace.
59
+ 2. Inspect the preserved `.pmx-canvas/` state and `pmx-canvas.log` printed by the script.
60
+ 3. Fix the failing CLI/server path and add or update the narrower unit regression.
61
+ 4. Re-run `bun run test:e2e-cli`, then `bun run test:all` before release.
@@ -0,0 +1,201 @@
1
+ # HTTP API reference
2
+
3
+ REST endpoints for all canvas operations + an SSE event stream. Works from
4
+ any language. Default base URL: `http://localhost:4313`.
5
+
6
+ ## Canvas state
7
+
8
+ ```bash
9
+ # Get canvas state
10
+ curl http://localhost:4313/api/canvas/state
11
+
12
+ # Search nodes
13
+ curl "http://localhost:4313/api/canvas/search?q=auth"
14
+
15
+ # Validate the current layout
16
+ curl http://localhost:4313/api/canvas/validate
17
+
18
+ # Inspect running-server schemas
19
+ curl http://localhost:4313/api/canvas/schema
20
+
21
+ # Validate a json-render spec without creating a node
22
+ curl -X POST http://localhost:4313/api/canvas/schema/validate \
23
+ -H "Content-Type: application/json" \
24
+ -d '{"type":"json-render","spec":{"root":"card","elements":{"card":{"type":"Card","props":{"title":"Preview"},"children":[]}}}}'
25
+
26
+ # Validate an HTML primitive without creating a node
27
+ curl -X POST http://localhost:4313/api/canvas/schema/validate \
28
+ -H "Content-Type: application/json" \
29
+ -d '{"type":"html-primitive","kind":"choice-grid","data":{"items":[{"title":"A"}]}}'
30
+ ```
31
+
32
+ ## Nodes
33
+
34
+ ```bash
35
+ # Add a node
36
+ curl -X POST http://localhost:4313/api/canvas/node \
37
+ -H "Content-Type: application/json" \
38
+ -d '{"type":"markdown","title":"Hello","content":"# World"}'
39
+
40
+ # Add an html node (sandboxed iframe)
41
+ curl -X POST http://localhost:4313/api/canvas/node \
42
+ -H "Content-Type: application/json" \
43
+ -d '{"type":"html","title":"Chart","html":"<canvas id=\"c\"></canvas><script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script><script>/* ... */</script>"}'
44
+
45
+ # Add a generated HTML primitive as a sandboxed html node
46
+ curl -X POST http://localhost:4313/api/canvas/node \
47
+ -H "Content-Type: application/json" \
48
+ -d '{"type":"html-primitive","kind":"choice-grid","title":"Options","data":{"items":[{"title":"Small patch","summary":"Least disruption."}]}}'
49
+ ```
50
+
51
+ ## Edges
52
+
53
+ ```bash
54
+ # Add an edge
55
+ curl -X POST http://localhost:4313/api/canvas/edge \
56
+ -H "Content-Type: application/json" \
57
+ -d '{"from":"node-1","to":"node-2","type":"flow","label":"next"}'
58
+
59
+ # Add an edge by unique search match instead of explicit IDs
60
+ curl -X POST http://localhost:4313/api/canvas/edge \
61
+ -H "Content-Type: application/json" \
62
+ -d '{"fromSearch":"DVT O3 — GitOps","toSearch":"deep work trend","type":"relation"}'
63
+ ```
64
+
65
+ Search-based edge creation is intentionally strict: `fromSearch` and
66
+ `toSearch` must each resolve to exactly one node. Broad queries that match
67
+ multiple nodes fail; use the full visible title.
68
+
69
+ ## Annotations
70
+
71
+ ```bash
72
+ # Add a freehand annotation. The default/currentColor stroke follows the active theme.
73
+ curl -X POST http://localhost:4313/api/canvas/annotation \
74
+ -H "Content-Type: application/json" \
75
+ -d '{"points":[{"x":100,"y":120},{"x":220,"y":120}],"color":"currentColor","width":4}'
76
+
77
+ # Remove an annotation
78
+ curl -X DELETE http://localhost:4313/api/canvas/annotation/ann-123
79
+ ```
80
+
81
+ Agent-readable context reports annotation IDs, targets, and bounds. Use WebView
82
+ inspection or screenshots when the drawn shape matters.
83
+
84
+ ## Pins
85
+
86
+ ```bash
87
+ # Pin nodes for agent context
88
+ curl -X POST http://localhost:4313/api/canvas/context-pins \
89
+ -H "Content-Type: application/json" \
90
+ -d '{"nodeIds":["node-1","node-2"]}'
91
+
92
+ # Get pinned context
93
+ curl http://localhost:4313/api/canvas/pinned-context
94
+ ```
95
+
96
+ ## Diagrams (Excalidraw preset)
97
+
98
+ ```bash
99
+ curl -X POST http://localhost:4313/api/canvas/diagram \
100
+ -H "Content-Type: application/json" \
101
+ -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"}'
102
+ ```
103
+
104
+ ## SSE event stream
105
+
106
+ ```bash
107
+ curl -N http://localhost:4313/api/workbench/events
108
+ ```
109
+
110
+ The browser, the CLI `watch` command, and the MCP resource notifications
111
+ all consume this stream. Auto-reconnect with exponential backoff.
112
+
113
+ ## Time travel
114
+
115
+ ```bash
116
+ curl -X POST http://localhost:4313/api/canvas/undo
117
+ curl -X POST http://localhost:4313/api/canvas/redo
118
+ curl http://localhost:4313/api/canvas/history
119
+ ```
120
+
121
+ ## WebView automation
122
+
123
+ ```bash
124
+ # Start WebView automation
125
+ curl -X POST http://localhost:4313/api/workbench/webview/start \
126
+ -H "Content-Type: application/json" \
127
+ -d '{"backend":"chrome","width":1280,"height":800}'
128
+
129
+ # Evaluate JS in the active WebView session
130
+ curl -X POST http://localhost:4313/api/workbench/webview/evaluate \
131
+ -H "Content-Type: application/json" \
132
+ -d '{"expression":"document.title"}'
133
+
134
+ # Resize the active WebView session
135
+ curl -X POST http://localhost:4313/api/workbench/webview/resize \
136
+ -H "Content-Type: application/json" \
137
+ -d '{"width":1440,"height":900}'
138
+
139
+ # Capture a screenshot
140
+ curl -X POST http://localhost:4313/api/workbench/webview/screenshot \
141
+ -H "Content-Type: application/json" \
142
+ -d '{"format":"png"}' \
143
+ --output canvas.png
144
+ ```
145
+
146
+ ## Batch operations
147
+
148
+ Build a canvas in one shot. Earlier results can be referenced from later
149
+ operations via `$assigned-name.field`.
150
+
151
+ ```bash
152
+ curl -X POST http://localhost:4313/api/canvas/batch \
153
+ -H "Content-Type: application/json" \
154
+ -d '{"operations":[{"op":"node.add","assign":"a","args":{"type":"markdown","title":"A"}},{"op":"group.create","args":{"title":"Frame","childIds":["$a.id"]}}]}'
155
+ ```
156
+
157
+ Supported operations:
158
+
159
+ - `node.add`, `node.update`
160
+ - `graph.add`
161
+ - `edge.add`
162
+ - `group.create`, `group.add`, `group.remove`
163
+ - `pin.set`, `pin.add`, `pin.remove`
164
+ - `snapshot.save`
165
+ - `arrange`
166
+
167
+ `node.add` supports `type: "webpage"` inside batch. The batch itself still
168
+ succeeds when the webpage node is created but the fetch fails; the
169
+ per-operation result includes `fetch: { ok, error? }` plus a top-level
170
+ `error` field for the fetch problem.
171
+
172
+ Example with assignments:
173
+
174
+ ```json
175
+ {
176
+ "operations": [
177
+ {
178
+ "op": "graph.add",
179
+ "assign": "wins",
180
+ "args": {
181
+ "title": "Major wins",
182
+ "graphType": "bar",
183
+ "data": [
184
+ { "label": "Docs", "value": 5 },
185
+ { "label": "Tests", "value": 8 }
186
+ ],
187
+ "xKey": "label",
188
+ "yKey": "value"
189
+ }
190
+ },
191
+ {
192
+ "op": "group.create",
193
+ "assign": "frame",
194
+ "args": {
195
+ "title": "Quarterly graphs",
196
+ "childIds": ["$wins.id"]
197
+ }
198
+ }
199
+ ]
200
+ }
201
+ ```