pmx-canvas 0.2.7 → 0.3.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 (57) hide show
  1. package/CHANGELOG.md +110 -1
  2. package/Readme.md +13 -11
  3. package/dist/json-render/index.css +1 -1
  4. package/dist/types/mcp/canvas-access.d.ts +0 -7
  5. package/dist/types/server/ax-state-manager.d.ts +3 -2
  6. package/dist/types/server/bundled-skills.d.ts +3 -3
  7. package/dist/types/server/canvas-state.d.ts +2 -0
  8. package/dist/types/server/ext-app-lookup.d.ts +1 -3
  9. package/dist/types/server/operations/composites.d.ts +29 -23
  10. package/dist/types/server/operations/http.d.ts +2 -1
  11. package/dist/types/shared/ax-intent.d.ts +1 -1
  12. package/docs/RELEASE.md +23 -8
  13. package/docs/ax-host-adapter-contract.md +7 -7
  14. package/docs/ax-state-contract.md +7 -6
  15. package/docs/bun-webview-integration.md +5 -5
  16. package/docs/cli.md +1 -1
  17. package/docs/http-api.md +7 -3
  18. package/docs/mcp.md +121 -100
  19. package/docs/node-types.md +45 -35
  20. package/docs/plans/plan-006-mcp-tool-consolidation.md +3 -1
  21. package/docs/plans/plan-008-registry-finish.md +2 -0
  22. package/docs/plans/plan-009-tech-debt-backlog.md +51 -0
  23. package/docs/screenshot.png +0 -0
  24. package/docs/tech-debt-assessment-2026-06.md +1 -1
  25. package/docs/tech-debt-assessment-2026-07.md +135 -0
  26. package/package.json +1 -1
  27. package/skills/data-analysis/SKILL.md +3 -3
  28. package/skills/frontend-design/SKILL.md +3 -2
  29. package/skills/json-render-mcp/SKILL.md +4 -3
  30. package/skills/playwright-cli/SKILL.md +1 -1
  31. package/skills/pmx-canvas/SKILL.md +9 -6
  32. package/skills/pmx-canvas/evals/evals.json +2 -2
  33. package/skills/pmx-canvas/references/ax-html-control-surface.md +3 -1
  34. package/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +2 -2
  35. package/skills/pmx-canvas/references/full-reference.md +48 -53
  36. package/skills/pmx-canvas/references/html-primitives.md +3 -3
  37. package/skills/tufte-viz/SKILL.md +5 -4
  38. package/skills/web-artifacts-builder/SKILL.md +4 -4
  39. package/src/mcp/canvas-access.ts +4 -99
  40. package/src/mcp/server.ts +74 -201
  41. package/src/server/ax-state-manager.ts +3 -2
  42. package/src/server/bundled-skills.ts +3 -3
  43. package/src/server/canvas-schema.ts +46 -29
  44. package/src/server/canvas-state.ts +5 -0
  45. package/src/server/code-graph.ts +1 -1
  46. package/src/server/ext-app-lookup.ts +1 -8
  47. package/src/server/index.ts +1 -1
  48. package/src/server/intent-registry.ts +1 -1
  49. package/src/server/operations/composites.ts +39 -39
  50. package/src/server/operations/http.ts +3 -2
  51. package/src/server/operations/mcp.ts +7 -6
  52. package/src/server/operations/ops/app.ts +4 -4
  53. package/src/server/operations/ops/nodes.ts +5 -5
  54. package/src/server/operations/ops/query.ts +19 -8
  55. package/src/server/operations/ops/snapshots.ts +6 -6
  56. package/src/server/server.ts +30 -2
  57. package/src/shared/ax-intent.ts +1 -1
@@ -78,8 +78,7 @@ section below.
78
78
  5. **Signal then mutate (default behavior)** — signal with `canvas_intent { action: "signal", … }`
79
79
  to telegraph the move before nearly every mutation, then create with the right composite: `canvas_node` (markdown/status/file/webpage/html
80
80
  incl. primitives), `canvas_render` (json-render/graph), `canvas_app` (mcp-app/diagram/
81
- web-artifact). Prefer composites — the legacy single-purpose tools are deprecated (removed in
82
- v0.3).
81
+ web-artifact). Use composites — the legacy single-purpose tools were removed in v0.3.0.
83
82
  6. **Arrange + validate** — `canvas_view { action: "arrange" }` after batch adds, then
84
83
  `canvas_query { action: "validate" }` to catch collisions / dangling edges before finishing.
85
84
  7. **Pin through a verified path** — `canvas_pin_nodes` (or the browser "Pin as context" /
@@ -413,16 +412,15 @@ When you do set a color (group/graph), use this palette consistently to convey m
413
412
 
414
413
  ## MCP Tools Reference
415
414
 
416
- PMX Canvas exposes **15 action-discriminated composites** (the recommended surface) plus a
415
+ PMX Canvas exposes **15 action-discriminated composites** (the whole recommended surface) plus a
417
416
  set of first-class standalones. The composites fold the older
418
417
  single-purpose tools behind an `action` (and, for `canvas_ax_gate`, a `kind`) discriminator —
419
418
  **field names are unchanged**; only the tool name + the `action`/`kind` selector differ.
420
419
 
421
- > **Legacy single-purpose tools are Deprecated.** The old names (`canvas_add_node`,
422
- > `canvas_update_node`, `canvas_request_approval`, `canvas_add_work_item`, …) still work but are
423
- > marked `Deprecated:` and are **removed in v0.3**. Prefer the composites. The authoritative
424
- > legacy→composite mapping table lives in [`docs/mcp.md`](../../../docs/mcp.md) — this skill does not
425
- > re-enumerate the deprecated names.
420
+ > **Legacy single-purpose tools were removed in v0.3.0.** The old names (`canvas_add_node`,
421
+ > `canvas_update_node`, `canvas_request_approval`, `canvas_add_work_item`, …) are no longer
422
+ > registered use the composites instead. The authoritative legacy→composite mapping table lives
423
+ > in [`docs/mcp.md`](../../../docs/mcp.md) — this skill does not re-enumerate the removed names.
426
424
 
427
425
  ### The 15 composites
428
426
 
@@ -499,19 +497,19 @@ investigation board" intent, then run the batch with that linked `intentId` (use
499
497
  ### Standalones (first-class — not deprecated)
500
498
 
501
499
  These stay separate by design (trust-boundary, firehose, execution-intent, or not-yet-consolidated
502
- surfaces): `canvas_batch`, `canvas_pin_nodes`, `canvas_screenshot`,
503
- `canvas_refresh_webpage_node`, `canvas_ax_interaction`,
500
+ surfaces): `canvas_batch`, `canvas_pin_nodes`, `canvas_screenshot`, `canvas_ax_interaction`,
504
501
  `canvas_ingest_activity`, `canvas_invoke_command`, and the snapshot tools (`canvas_snapshot`,
505
502
  `canvas_list_snapshots`, `canvas_restore`, `canvas_delete_snapshot`, `canvas_gc_snapshots`,
506
- `canvas_diff` — a `canvas_snapshot` composite is deferred to v0.3).
503
+ `canvas_diff` — deprecated pending a `canvas_snapshot` composite in v0.4; the name collides with
504
+ the current save-snapshot tool, so the composite cannot land additively).
507
505
 
508
- > **Deprecated → use the composite instead** (legacy tools still work but are marked
509
- > `Deprecated:` and are **removed in v0.3**): `canvas_open_mcp_app` / `canvas_add_diagram` /
506
+ > **Removed in v0.3.0 → use the composite instead**: `canvas_open_mcp_app` / `canvas_add_diagram` /
510
507
  > `canvas_build_web_artifact` → **`canvas_app`**; `canvas_webview_start` / `canvas_webview_status` /
511
508
  > `canvas_webview_stop` / `canvas_resize` / `canvas_evaluate` → **`canvas_webview`**;
512
509
  > `canvas_add_html_node` / `canvas_add_html_primitive` → **`canvas_node { action:"add", type:"html" }`**
513
510
  > (pass `primitive` for a primitive); `canvas_add_node` / `canvas_update_node` / `canvas_remove_node`
514
511
  > → **`canvas_node`**; `canvas_remove_annotation` → **`canvas_view { action:"remove-annotation" }`**;
512
+ > `canvas_refresh_webpage_node` → **`canvas_node { action:"update", id, refresh: true }`**;
515
513
  > `canvas_validate` (board collisions / containment / dangling edges) → **`canvas_query` `validate`**;
516
514
  > `canvas_validate_spec` (json-render / graph spec dry-run) → **`canvas_render` `validate`**.
517
515
 
@@ -553,10 +551,10 @@ delete+recreate; preserves edges, pins, position)
553
551
 
554
552
  **`canvas_node { action: "get", id }`** — Get a single node's full data by `id`.
555
553
 
556
- **`canvas_remove_annotation`** (deprecated → `canvas_view { action: "remove-annotation" }`; removed in v0.3) — Remove a human-drawn annotation by `id`. Use when
554
+ **`canvas_view { action: "remove-annotation", id }`** — Remove a human-drawn annotation by `id`. Use when
557
555
  context gives you the annotation ID; use WebView first if you need to identify a mark by shape or location.
558
556
 
559
- **`canvas_refresh_webpage_node`** (standalone) — Re-fetch the URL stored on a `webpage` node
557
+ **`canvas_node { action: "update", id, refresh: true }`** — Re-fetch the URL stored on a `webpage` node
560
558
  - `id` (required): webpage node to refresh
561
559
  - Optional `url`: replace the stored URL before refreshing (use when the human moved the page)
562
560
  - Returns the refreshed node with updated `pageTitle` and cached extracted text
@@ -569,7 +567,7 @@ context gives you the annotation ID; use WebView first if you need to identify a
569
567
  // → returns { id: 'node-abc' }
570
568
 
571
569
  // …later, after the human reopens the canvas…
572
- canvas_refresh_webpage_node({ id: 'node-abc' })
570
+ canvas_node({ action: 'update', id: 'node-abc', refresh: true })
573
571
  // → re-fetches the URL, updates pageTitle + extracted text, keeps the node ID and position
574
572
  ```
575
573
 
@@ -621,7 +619,7 @@ the `tufte-viz` skill (`skills/tufte-viz/SKILL.md`). Key rules:
621
619
  - For more than ~4 overlapping series, build small multiples (several small graph nodes on a shared
622
620
  scale, arranged in a grid/group) instead of one multi-color chart.
623
621
 
624
- **`canvas_build_web_artifact`** (deprecated → `canvas_app { action: "build-artifact" }`; removed in v0.3) — Build and optionally open a bundled web artifact
622
+ **`canvas_app { action: "build-artifact" }`** — Build and optionally open a bundled web artifact
625
623
  - Required: `title`, `appTsx` (source string contents, not a file path)
626
624
  - CLI `--app-file` reads a file before calling the same build path; MCP callers must pass the source contents
627
625
  - Cold builds commonly take 45-60 seconds; use a long client timeout such as 300000 ms or more
@@ -631,7 +629,7 @@ ID extraction for mixed tool responses:
631
629
  - Most add-style tools return a flat `id`; web artifacts return `id` plus `nodeId`; snapshots return `id` plus nested `snapshot.id`.
632
630
  - Defensive extractor: `const getId = (r) => r.id ?? r.nodeId ?? r.snapshot?.id;`
633
631
 
634
- **`canvas_open_mcp_app`** (deprecated → `canvas_app { action: "open-mcp-app" }`; removed in v0.3) — Open a tool-backed external MCP app node
632
+ **`canvas_app { action: "open-mcp-app" }`** — Open a tool-backed external MCP app node
635
633
  - Required: `toolName`, `transport`
636
634
  - `transport` is either `{ type: "stdio", command, args?, cwd?, env? }` or `{ type: "http", url, headers? }`
637
635
  - This is lower-level than `pmx-canvas external-app add --kind excalidraw`; use `canvas_app { action: "diagram" }` for the built-in Excalidraw preset
@@ -773,35 +771,31 @@ JavaScript inside the workbench page.
773
771
 
774
772
  The WebView automation runs on Bun's WebKit-based WebView (macOS) or a headless
775
773
  Chromium fallback (Linux). It does **not** open a visible window; it's an
776
- additional headless renderer attached to the same canvas server, so all five
777
- tools below operate on the live canvas state.
774
+ additional headless renderer attached to the same canvas server, so every action
775
+ below operates on the live canvas state through the `canvas_webview` composite.
778
776
 
779
- > **Deprecated use the `canvas_webview` composite** (removed in v0.3): the five
780
- > standalone tools below map to `canvas_webview { action: "status" | "start" | "stop" |
781
- > "evaluate" | "resize" }` respectively. Field names are unchanged.
782
-
783
- **`canvas_webview_status`** — Inspect the current automation session
777
+ **`canvas_webview { action: "status" }`** Inspect the current automation session
784
778
  - Returns `{ supported, active, headlessOnly, url, backend, width, height, dataStoreDir, startedAt, lastError }`
785
779
  (the viewport size is `width` / `height`, not `viewportWidth` / `viewportHeight`)
786
780
  - Call before `start` to check whether a session is already alive
787
781
 
788
- **`canvas_webview_start`** — Start (or replace) the automation session
782
+ **`canvas_webview { action: "start" }`** — Start (or replace) the automation session
789
783
  - Optional: `backend` (`webkit` macOS-only, or `chrome`), `width`, `height`
790
784
  - The session opens `/workbench` at the canvas URL, waits for the SPA to
791
- hydrate, and reports back via `canvas_webview_status`
785
+ hydrate, and reports back via `canvas_webview { action: "status" }`
792
786
 
793
- **`canvas_webview_stop`** — Tear down the automation session
787
+ **`canvas_webview { action: "stop" }`** — Tear down the automation session
794
788
 
795
- **`canvas_evaluate`** — Run JavaScript inside the workbench page and return the result
789
+ **`canvas_webview { action: "evaluate" }`** — Run JavaScript inside the workbench page and return the result
796
790
  - Required: exactly one of `expression` (single JS expression) or `script` (multi-statement body)
797
791
  - `script` is wrapped in an async IIFE, so top-level `await` works inside script bodies
798
792
  - Useful for asserting DOM state after a sequence of canvas mutations
799
- - Do not use `fetch()` inside `canvas_evaluate` to call PMX HTTP APIs; WebView security/CORS
800
- restrictions can block those requests. Use the matching MCP tools instead.
793
+ - Do not use `fetch()` inside `canvas_webview { action: "evaluate" }` to call PMX HTTP APIs; WebView
794
+ security/CORS restrictions can block those requests. Use the matching MCP tools instead.
801
795
  - Example: read the count of rendered `.canvas-node` elements:
802
796
 
803
797
  ```typescript
804
- canvas_evaluate({ expression: 'document.querySelectorAll(".canvas-node").length' })
798
+ canvas_webview({ action: 'evaluate', expression: 'document.querySelectorAll(".canvas-node").length' })
805
799
  ```
806
800
 
807
801
  Useful workbench selectors:
@@ -811,19 +805,20 @@ Useful workbench selectors:
811
805
  to inspect or screenshot annotation shapes; MCP/context resources only expose compact
812
806
  annotation target summaries, not the raw visual shape. Humans can remove marks with
813
807
  the eraser toolbar button; agents can remove a known annotation ID with
814
- `canvas_remove_annotation`.
808
+ `canvas_view { action: "remove-annotation" }`.
815
809
  - Canvas chrome: `.hud-layer`, `.canvas-toolbar`, `.connection-dot`, `.canvas-bootstrap-card`
816
810
  - Nodes do not expose stable `data-node-id` attributes. Use `canvas_query` (`layout` / `search`) or MCP resource data for exact node IDs.
817
811
 
818
812
  Async script example:
819
813
 
820
814
  ```typescript
821
- canvas_evaluate({
815
+ canvas_webview({
816
+ action: 'evaluate',
822
817
  script: 'const title = await Promise.resolve(document.title); return title;',
823
818
  })
824
819
  ```
825
820
 
826
- **`canvas_resize`** — Change the WebView viewport
821
+ **`canvas_webview { action: "resize" }`** — Change the WebView viewport
827
822
  - Required: `width`, `height`
828
823
  - Use before `canvas_screenshot` when the human needs a specific aspect ratio
829
824
 
@@ -831,20 +826,20 @@ canvas_evaluate({
831
826
  - Optional: `format` (`png` default), `fullPage` (boolean)
832
827
  - Returns both an MCP image payload (renderable inline by capable agents) and
833
828
  a path under `.pmx-canvas/screenshots/` so the human can view the file
834
- - Pair with `canvas_resize` to control the framing
829
+ - Pair with `canvas_webview { action: "resize" }` to control the framing
835
830
 
836
831
  Typical flow when you want to show a result:
837
832
 
838
833
  ```typescript
839
- canvas_webview_start({ width: 1440, height: 900 });
834
+ canvas_webview({ action: 'start', width: 1440, height: 900 });
840
835
  // …mutations…
841
836
  canvas_screenshot({ fullPage: true });
842
- canvas_webview_stop();
837
+ canvas_webview({ action: 'stop' });
843
838
  ```
844
839
 
845
840
  ### Diagrams (Excalidraw MCP app preset)
846
841
 
847
- **`canvas_add_diagram`** (deprecated → `canvas_app { action: "diagram" }`; removed in v0.3) — Draw a hand-drawn diagram on the canvas via the hosted
842
+ **`canvas_app { action: "diagram" }`** — Draw a hand-drawn diagram on the canvas via the hosted
848
843
  [Excalidraw MCP app](https://github.com/excalidraw/excalidraw-mcp)
849
844
  - Required: `elements` — an array of Excalidraw elements (rectangles, ellipses, diamonds, arrows,
850
845
  text). Can also be a JSON-array string.
@@ -868,7 +863,7 @@ canvas_webview_stop();
868
863
 
869
864
  ### External MCP apps (bring your own)
870
865
 
871
- **`canvas_open_mcp_app`** (deprecated → `canvas_app { action: "open-mcp-app" }`; removed in v0.3) — Open any external [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps)
866
+ **`canvas_app { action: "open-mcp-app" }`** — Open any external [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps)
872
867
  server's `ui://` resource as an iframe node on the canvas
873
868
  - Required: `toolName`, `transport` (`http` URL or `stdio` command)
874
869
  - Optional: `serverName`, `toolArguments`, `title`, `x`, `y`, `width`, `height`
@@ -877,7 +872,7 @@ server's `ui://` resource as an iframe node on the canvas
877
872
 
878
873
  ### Web Artifacts
879
874
 
880
- **`canvas_build_web_artifact`** (deprecated → `canvas_app { action: "build-artifact" }`; removed in v0.3) — Build a single-file HTML artifact from React/Tailwind source
875
+ **`canvas_app { action: "build-artifact" }`** — Build a single-file HTML artifact from React/Tailwind source
881
876
  - Required: `title`, `appTsx`
882
877
  - Optional: `indexCss`, `mainTsx`, `indexHtml`, extra `files`, `projectPath`, `outputPath`, `deps`, `includeLogs`
883
878
  - By default it opens the result on the canvas as an embedded app node
@@ -891,7 +886,7 @@ server's `ui://` resource as an iframe node on the canvas
891
886
 
892
887
  ### HTML Nodes (Sandboxed iframe)
893
888
 
894
- **`canvas_add_html_node`** (deprecated → `canvas_node { action: "add", type: "html", html }`; removed in v0.3) — Add a normal self-contained HTML document rendered in a sandboxed iframe
889
+ **`canvas_node { action: "add", type: "html", html }`** — Add a normal self-contained HTML document rendered in a sandboxed iframe
895
890
  - Required: `html` (full document or fragment; inline `<script>` and CDN `<script src="...">` are allowed). If `html` is a bare path to an existing local `.html`/`.htm` file, the server reads that file's contents; otherwise it is treated as raw HTML.
896
891
  - Optional: `title`, `summary`, `agentSummary`, `presentation`, `slideTitles`, `embeddedNodeIds`, `embeddedUrls`, `x`, `y`, `width` (default 720), `height` (default 640), `strictSize`
897
892
  - Iframe sandbox is `allow-scripts` only — no same-origin access, no top-navigation, no forms
@@ -901,7 +896,7 @@ server's `ui://` resource as an iframe node on the canvas
901
896
  - Only presentation-marked HTML nodes expose a browser `Present` button. Use it when the HTML is a deck, briefing, or fullscreen review surface; the PMX shell owns the fullscreen overlay and exits via `Esc` or `Exit presentation`.
902
897
  - PMX stores a semantic sidecar (`agentSummary`, `contentSummary`, embedded references) so HTML nodes remain understandable in search, pinned context, and spatial context
903
898
 
904
- **`canvas_add_html_primitive`** (deprecated → `canvas_node { action: "add", type: "html", primitive, data }`; removed in v0.3) — Generate a reusable HTML communication primitive as a sandboxed `html` node
899
+ **`canvas_node { action: "add", type: "html", primitive, data }`** — Generate a reusable HTML communication primitive as a sandboxed `html` node
905
900
  - Required: `kind`; run `canvas_render { action: "describe-schema" }` and read `htmlPrimitives` for the current catalog
906
901
  - Optional: `title`, `data`, `x`, `y`, `width`, `height`, `strictSize`
907
902
  - Use when markdown would be too dense and a structured visual artifact is clearer: tradeoff grids, implementation plans, PR reviews, module maps, design sheets, explainers, reports, and lightweight human-editable boards/editors
@@ -961,7 +956,7 @@ Use native `json-render` and `graph` nodes when the output should stay fully ins
961
956
  1. Use `canvas_render { action: "add-json-render" }` for dashboards, forms, summaries, and interactive UI panels
962
957
  2. Use `canvas_render { action: "add-graph" }` for charts and trend visualizations
963
958
  3. Use the repo-local `json-render-*` skills when you need help authoring or refining the spec itself
964
- 4. Use `canvas_build_web_artifact` instead when the result needs a full custom React app rather than a schema-driven UI
959
+ 4. Use `canvas_app { action: "build-artifact" }` instead when the result needs a full custom React app rather than a schema-driven UI
965
960
 
966
961
  Spec elements support an `on` map (`on.press`, `on.change`, …) binding events to actions (`{ action, params }`) — built-in actions (`setState`, `pushState`, …) or, when named after an AX interaction type, a capability-gated AX emit. e.g. a Button with `on: { press: { action: 'ax.work.create', params: { title: '…' } } }` lets a human turn a panel control into a tracked work item; the viewer forwards it to the canvas, which validates and submits it server-side (clamped to the node's own id). See **Node AX Interactions** above.
967
962
 
@@ -1107,9 +1102,9 @@ AX interactions are gated per node type. The lists below are each type's **ceili
1107
1102
  | `group` | `ax.focus.set`, `ax.work.create`, `ax.command.invoke`, `ax.event.record` |
1108
1103
 
1109
1104
  **Opt-in** — set `axCapabilities.enabled = true` (MCP: pass `axCapabilities` to
1110
- `canvas_add_html_node` / `canvas_node { action: "update" }`. HTTP: `axCapabilities` **and** the
1111
- `html` body are accepted **top-level on both `POST /api/canvas/node` and
1112
- `PATCH /api/canvas/node/<id>`**, or nested under `data` — both work, top-level wins):
1105
+ `canvas_node { action: "add", type: "html" }` / `canvas_node { action: "update" }`. HTTP:
1106
+ `axCapabilities` **and** the `html` body are accepted **top-level on both `POST /api/canvas/node`
1107
+ and `PATCH /api/canvas/node/<id>`**, or nested under `data` — both work, top-level wins):
1113
1108
 
1114
1109
  | Node type | Allowed AX interaction types |
1115
1110
  |-----------|------------------------------|
@@ -1136,7 +1131,7 @@ inbox as a canvas node that BOTH emits AX interactions AND renders the current A
1136
1131
  state. The read side mirrors the write side:
1137
1132
 
1138
1133
  - **Opt in** (html/mcp-app are off by default): create with
1139
- `canvas_add_html_node({ html, axCapabilities: { enabled: true, allowed: ["ax.work.create","ax.work.update"] } })`,
1134
+ `canvas_node({ action: "add", type: "html", html, axCapabilities: { enabled: true, allowed: ["ax.work.create","ax.work.update"] } })`,
1140
1135
  or flip an existing node on with
1141
1136
  `canvas_node({ action: "update", id, axCapabilities: { enabled: true, allowed: [...] } })`.
1142
1137
  json-render / graph nodes are enabled by default.
@@ -1163,7 +1158,7 @@ state. The read side mirrors the write side:
1163
1158
  against `window.PMX_AX`, not direct `fetch()` (the artifact iframe is sandboxed
1164
1159
  opaque-origin, so it can't call the API directly).
1165
1160
 
1166
- Minimal html work board (drop-in via `canvas_add_html_node`, `axCapabilities.enabled: true`):
1161
+ Minimal html work board (drop-in via `canvas_node { action: "add", type: "html" }`, `axCapabilities.enabled: true`):
1167
1162
 
1168
1163
  ```html
1169
1164
  <button id="add">+ Task</button> <span id="ok"></span>
@@ -1215,10 +1210,10 @@ The `canvas://spatial-context` resource reveals how the human has organized info
1215
1210
  - **Pinned neighborhoods** — For each pinned node, nearby unpinned nodes are listed. These
1216
1211
  are the human's implicit context — things they consider related to what they pinned.
1217
1212
  - **Annotations** — Human-drawn markup is summarized by target/bounds only, e.g. an
1218
- annotation over a node or empty canvas region. Use WebView (`canvas_webview_start` +
1219
- `canvas_evaluate`/`canvas_screenshot`) when you need to see whether the mark is an
1213
+ annotation over a node or empty canvas region. Use WebView (`canvas_webview { action: "start" }` +
1214
+ `canvas_webview { action: "evaluate" }`/`canvas_screenshot`) when you need to see whether the mark is an
1220
1215
  arrow, line, circle, or other drawn shape. Remove known annotations with
1221
- `canvas_remove_annotation`; otherwise use WebView to identify the mark first.
1216
+ `canvas_view { action: "remove-annotation" }`; otherwise use WebView to identify the mark first.
1222
1217
  - **Board density matters** — On a dense board, spatial context can still read like one large
1223
1218
  gallery unless groups and spacing separate the major regions clearly.
1224
1219
 
@@ -1367,7 +1362,7 @@ Understand a codebase by visualizing file relationships:
1367
1362
  When the user wants a real browser app instead of static notes:
1368
1363
 
1369
1364
  1. Use the `web-artifacts-builder` skill if the UI needs React state, routing, or shadcn-style components
1370
- 2. Build with `canvas_build_web_artifact`
1365
+ 2. Build with `canvas_app { action: "build-artifact" }`
1371
1366
  3. Keep `openInCanvas` enabled unless the user explicitly wants only the output file
1372
1367
  4. Use the returned `projectPath` as the reusable source workspace for iterations
1373
1368
  5. Use the returned `path` for sharing or for opening the generated artifact outside the canvas
@@ -1,7 +1,7 @@
1
1
  # HTML Primitive Authoring
2
2
 
3
3
  Use this guide when creating reusable sandboxed HTML communication primitives through
4
- `canvas_add_html_primitive` or `pmx-canvas html primitive add`.
4
+ `canvas_node { action: "add", type: "html", primitive, data }` or `pmx-canvas html primitive add`.
5
5
 
6
6
  ## What They Are
7
7
 
@@ -11,8 +11,8 @@ sandboxed iframe node, plus metadata describing the primitive kind and source da
11
11
  - Use them when markdown becomes too dense but a full React web artifact would be too heavy.
12
12
  - Prefer them for communication artifacts: choices, plans, reviews, maps, explainers, reports,
13
13
  lightweight editors, and handoff boards.
14
- - Keep using `canvas_add_html_node` for bespoke one-off HTML and JS.
15
- - Keep using `canvas_build_web_artifact` for multi-component apps, routing, React state, or shadcn UI.
14
+ - Keep using `canvas_node { action: "add", type: "html", html }` for bespoke one-off HTML and JS.
15
+ - Keep using `canvas_app { action: "build-artifact" }` for multi-component apps, routing, React state, or shadcn UI.
16
16
 
17
17
  The design language is inspired by high-density HTML communication patterns: strong hierarchy,
18
18
  metric cards, sticky context panels, annotated snippets, inline SVG figures, and copy/export actions.
@@ -83,7 +83,8 @@ realize them with PMX Canvas `graph` / `json-render` nodes. Color must encode da
83
83
 
84
84
  ## Mapping to the PMX Canvas chart catalog
85
85
 
86
- Realize these designs with `canvas_add_graph_node` (graph nodes) and `canvas_add_json_render_node`.
86
+ Realize these designs with `canvas_render { action: "add-graph" }` (graph nodes) and
87
+ `canvas_render { action: "add-json-render" }`.
87
88
  The chart catalog: `LineChart`, `BarChart`, `PieChart`, `AreaChart`, `ScatterChart`, `RadarChart`,
88
89
  `StackedBarChart`, `ComposedChart`, plus the Tufte primitives `Sparkline`, `DotPlot`, `BulletChart`,
89
90
  and `Slopegraph`.
@@ -128,9 +129,9 @@ are directly identifiable; reserve legends for genuinely many overlapping series
128
129
 
129
130
  When more than ~4 series would overlap in one chart, do **not** cram them into a single multi-color
130
131
  `LineChart`. Create several small `graph` nodes with an **identical shared scale** and consistent
131
- encoding, arranged in a grid (`canvas_arrange` grid, or a `group`). Position means the same thing in
132
- every panel; the sequence tells the macro story while each panel carries the micro detail. This is
133
- almost always better than color-coding 6+ lines.
132
+ encoding, arranged in a grid (`canvas_view { action: "arrange", layout: "grid" }`, or a `group`).
133
+ Position means the same thing in every panel; the sequence tells the macro story while each panel
134
+ carries the micro detail. This is almost always better than color-coding 6+ lines.
134
135
 
135
136
  ## Key Principles Reference
136
137
 
@@ -21,10 +21,10 @@ VERY IMPORTANT: To avoid what is often referred to as "AI slop", avoid using exc
21
21
 
22
22
  ## Quick Start
23
23
 
24
- In `pmx-canvas`, prefer the `canvas_build_web_artifact` MCP tool when available. It uses the same
25
- bundled runtime scripts, writes reusable source under `.pmx-canvas/artifacts/.web-artifacts/`,
26
- emits a bundled HTML file under `.pmx-canvas/artifacts/`, and can open the result directly on the
27
- canvas as an embedded node.
24
+ In `pmx-canvas`, prefer the `canvas_app { action: "build-artifact" }` MCP tool when available. It
25
+ uses the same bundled runtime scripts, writes reusable source under
26
+ `.pmx-canvas/artifacts/.web-artifacts/`, emits a bundled HTML file under `.pmx-canvas/artifacts/`,
27
+ and can open the result directly on the canvas as an embedded node.
28
28
  For browser verification after the build, pair this skill with the local `playwright-cli` skill.
29
29
 
30
30
  ### Step 1: Initialize Project
@@ -16,15 +16,12 @@ import {
16
16
  type OperationInvoker,
17
17
  } from '../server/operations/index.js';
18
18
 
19
- type RefreshWebpageNodeResult = Awaited<ReturnType<PmxCanvas['refreshWebpageNode']>>;
20
- // openMcpApp / addDiagram / buildWebArtifact CanvasAccess methods + their type
21
- // aliases removed with the standalone MCP tools (plan-008 Wave 4): those tools
22
- // migrated to the operation registry (mcpapp.open / diagram.open /
19
+ // openMcpApp / addDiagram / buildWebArtifact / refreshWebpageNode / addHtmlNode /
20
+ // addHtmlPrimitive CanvasAccess methods + their type aliases removed with the
21
+ // standalone MCP tools (plan-008 Wave 4; refresh/html tools removed v0.3.0):
22
+ // those tools migrated to the operation registry (mcpapp.open / diagram.open /
23
23
  // webartifact.build) and the composite/registry tools dispatch via the invoker,
24
24
  // not CanvasAccess. The public SDK PmxCanvas methods are unchanged.
25
- type AddHtmlNodeInput = Parameters<PmxCanvas['addHtmlNode']>[0];
26
- type AddHtmlPrimitiveInput = Parameters<PmxCanvas['addHtmlPrimitive']>[0];
27
- type AddHtmlPrimitiveResult = ReturnType<PmxCanvas['addHtmlPrimitive']>;
28
25
  type AxStateResult = ReturnType<PmxCanvas['getAxState']>;
29
26
  type AxContextResult = ReturnType<PmxCanvas['getAxContext']>;
30
27
  type SubmitAxInteractionInput = Parameters<PmxCanvas['submitAxInteraction']>[0];
@@ -56,11 +53,6 @@ interface HealthResponse {
56
53
  workspace?: string;
57
54
  }
58
55
 
59
- interface NodeResponse {
60
- id?: string;
61
- node?: { id?: string };
62
- }
63
-
64
56
  export interface CanvasAccess {
65
57
  readonly port: number;
66
58
  readonly remoteBaseUrl: string | null;
@@ -68,9 +60,6 @@ export interface CanvasAccess {
68
60
  invoker(): OperationInvoker;
69
61
  getLayout(): Promise<CanvasLayout>;
70
62
  getNode(id: string): Promise<CanvasNodeState | undefined>;
71
- refreshWebpageNode(id: string, url?: string): Promise<RefreshWebpageNodeResult>;
72
- addHtmlNode(input: AddHtmlNodeInput): Promise<string>;
73
- addHtmlPrimitive(input: AddHtmlPrimitiveInput): Promise<AddHtmlPrimitiveResult>;
74
63
  getAxState(): Promise<AxStateResult>;
75
64
  getAxContext(options?: { consumer?: string }): Promise<AxContextResult>;
76
65
  getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult>;
@@ -119,20 +108,6 @@ class LocalCanvasAccess implements CanvasAccess {
119
108
  return this.canvas.getNode(id);
120
109
  }
121
110
 
122
- async refreshWebpageNode(id: string, url?: string): Promise<RefreshWebpageNodeResult> {
123
- return await this.canvas.refreshWebpageNode(id, url);
124
- }
125
-
126
- async addHtmlNode(input: AddHtmlNodeInput): Promise<string> {
127
- // PmxCanvas.addHtmlNode returns the created node; the CanvasAccess contract
128
- // is a bare id string, so extract it (mirrors addNode above).
129
- return this.canvas.addHtmlNode(input).id;
130
- }
131
-
132
- async addHtmlPrimitive(input: AddHtmlPrimitiveInput): Promise<AddHtmlPrimitiveResult> {
133
- return this.canvas.addHtmlPrimitive(input);
134
- }
135
-
136
111
  async getAxState(): Promise<AxStateResult> {
137
112
  return this.canvas.getAxState();
138
113
  }
@@ -249,17 +224,6 @@ class RemoteCanvasAccess implements CanvasAccess {
249
224
  return parsed as T;
250
225
  }
251
226
 
252
- private async requestNodeId(method: string, path: string, body?: unknown): Promise<string> {
253
- const response = await this.requestJson<NodeResponse>(method, path, body);
254
- const id = typeof response.id === 'string'
255
- ? response.id
256
- : typeof response.node?.id === 'string'
257
- ? response.node.id
258
- : '';
259
- if (!id) throw new Error('Canvas response did not include a node id.');
260
- return id;
261
- }
262
-
263
227
  async getLayout(): Promise<CanvasLayout> {
264
228
  return await this.requestJson<CanvasLayout>('GET', '/api/canvas/state?includeBlobs=true');
265
229
  }
@@ -285,65 +249,6 @@ class RemoteCanvasAccess implements CanvasAccess {
285
249
  return parsed as CanvasNodeState;
286
250
  }
287
251
 
288
- async refreshWebpageNode(id: string, url?: string): Promise<RefreshWebpageNodeResult> {
289
- return await this.requestJson<RefreshWebpageNodeResult>('POST', `/api/canvas/node/${encodeURIComponent(id)}/refresh`, {
290
- ...(url ? { url } : {}),
291
- });
292
- }
293
-
294
- async addHtmlNode(input: AddHtmlNodeInput): Promise<string> {
295
- const {
296
- summary,
297
- agentSummary,
298
- description,
299
- presentation,
300
- slideTitles,
301
- embeddedNodeIds,
302
- embeddedUrls,
303
- axCapabilities,
304
- ...rest
305
- } = input as AddHtmlNodeInput & {
306
- summary?: string;
307
- agentSummary?: string;
308
- description?: string;
309
- presentation?: boolean;
310
- slideTitles?: string[];
311
- embeddedNodeIds?: string[];
312
- embeddedUrls?: string[];
313
- axCapabilities?: { enabled?: boolean; allowed?: string[] };
314
- };
315
- return await this.requestNodeId('POST', '/api/canvas/node', {
316
- type: 'html',
317
- ...rest,
318
- data: {
319
- ...(typeof summary === 'string' ? { summary } : {}),
320
- ...(typeof agentSummary === 'string' ? { agentSummary } : {}),
321
- ...(typeof description === 'string' ? { description } : {}),
322
- ...(presentation === true ? { presentation: true } : {}),
323
- ...(Array.isArray(slideTitles) ? { slideTitles } : {}),
324
- ...(Array.isArray(embeddedNodeIds) ? { embeddedNodeIds } : {}),
325
- ...(Array.isArray(embeddedUrls) ? { embeddedUrls } : {}),
326
- ...(axCapabilities ? { axCapabilities } : {}),
327
- },
328
- });
329
- }
330
-
331
- async addHtmlPrimitive(input: AddHtmlPrimitiveInput): Promise<AddHtmlPrimitiveResult> {
332
- const response = await this.requestJson<{
333
- id?: string;
334
- node?: { id?: string };
335
- primitive?: { kind?: string; title?: string; htmlBytes?: number };
336
- }>('POST', '/api/canvas/node', { type: 'html', ...input, primitive: input.kind });
337
- const id = typeof response.id === 'string' ? response.id : response.node?.id;
338
- if (!id) throw new Error('html primitive response did not include a node id.');
339
- return {
340
- id,
341
- kind: input.kind,
342
- title: response.primitive?.title ?? input.title ?? input.kind,
343
- htmlBytes: response.primitive?.htmlBytes ?? 0,
344
- };
345
- }
346
-
347
252
  async getHistory(): Promise<HistoryResult> {
348
253
  return await this.requestJson<HistoryResult>('GET', '/api/canvas/history');
349
254
  }