pmx-canvas 0.2.6 → 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.
- package/CHANGELOG.md +156 -1
- package/Readme.md +13 -11
- package/dist/canvas/index.js +18 -18
- package/dist/json-render/index.css +1 -1
- package/dist/types/mcp/canvas-access.d.ts +23 -7
- package/dist/types/server/ax-state-manager.d.ts +3 -2
- package/dist/types/server/bundled-skills.d.ts +3 -3
- package/dist/types/server/canvas-state.d.ts +2 -0
- package/dist/types/server/ext-app-lookup.d.ts +1 -3
- package/dist/types/server/operations/composites.d.ts +29 -23
- package/dist/types/server/operations/http.d.ts +2 -1
- package/dist/types/shared/ax-intent.d.ts +1 -1
- package/docs/RELEASE.md +23 -8
- package/docs/ax-host-adapter-contract.md +7 -7
- package/docs/ax-state-contract.md +7 -6
- package/docs/bun-webview-integration.md +5 -5
- package/docs/cli.md +1 -1
- package/docs/http-api.md +7 -3
- package/docs/mcp.md +121 -100
- package/docs/node-types.md +45 -35
- package/docs/plans/plan-006-mcp-tool-consolidation.md +3 -1
- package/docs/plans/plan-008-registry-finish.md +2 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +51 -0
- package/docs/screenshot.png +0 -0
- package/docs/tech-debt-assessment-2026-06.md +1 -1
- package/docs/tech-debt-assessment-2026-07.md +135 -0
- package/package.json +1 -1
- package/skills/data-analysis/SKILL.md +3 -3
- package/skills/frontend-design/SKILL.md +3 -2
- package/skills/json-render-mcp/SKILL.md +4 -3
- package/skills/playwright-cli/SKILL.md +1 -1
- package/skills/pmx-canvas/SKILL.md +20 -15
- package/skills/pmx-canvas/evals/evals.json +2 -2
- package/skills/pmx-canvas/references/ax-html-control-surface.md +3 -1
- package/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +2 -2
- package/skills/pmx-canvas/references/full-reference.md +65 -62
- package/skills/pmx-canvas/references/html-primitives.md +3 -3
- package/skills/tufte-viz/SKILL.md +5 -4
- package/skills/web-artifacts-builder/SKILL.md +4 -4
- package/src/client/App.tsx +2 -1
- package/src/mcp/canvas-access.ts +115 -107
- package/src/mcp/server.ts +74 -201
- package/src/server/ax-state-manager.ts +3 -2
- package/src/server/bundled-skills.ts +3 -3
- package/src/server/canvas-schema.ts +46 -29
- package/src/server/canvas-state.ts +5 -0
- package/src/server/code-graph.ts +1 -1
- package/src/server/ext-app-lookup.ts +1 -8
- package/src/server/index.ts +1 -1
- package/src/server/intent-registry.ts +1 -1
- package/src/server/operations/composites.ts +39 -39
- package/src/server/operations/http.ts +3 -2
- package/src/server/operations/mcp.ts +7 -6
- package/src/server/operations/ops/app.ts +4 -4
- package/src/server/operations/ops/nodes.ts +5 -5
- package/src/server/operations/ops/query.ts +19 -8
- package/src/server/operations/ops/snapshots.ts +6 -6
- package/src/server/server.ts +40 -4
- package/src/shared/ax-intent.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Excalidraw Diagram Authoring
|
|
2
2
|
|
|
3
|
-
Use this guide when creating diagrams through PMX Canvas with `
|
|
3
|
+
Use this guide when creating diagrams through PMX Canvas with `canvas_app { action: "diagram" }` or
|
|
4
4
|
`pmx-canvas external-app add --kind excalidraw`.
|
|
5
5
|
|
|
6
6
|
## Why Text Can Still Drift
|
|
@@ -142,4 +142,4 @@ If a title, footer, or rightmost label is clipped, the camera is wrong even if t
|
|
|
142
142
|
- Camera has at least `80px` padding around all visible content.
|
|
143
143
|
- Titles and footers are not near the camera edge.
|
|
144
144
|
- Arrows have explicit `points` and enough space for labels.
|
|
145
|
-
- Calls go through PMX (`
|
|
145
|
+
- Calls go through PMX (`canvas_app { action: "diagram" }` or `external-app add --kind excalidraw`) unless you manually apply these rules to raw Excalidraw MCP input.
|
|
@@ -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).
|
|
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" /
|
|
@@ -157,10 +156,10 @@ file, sends SIGTERM, and cleans up on exit.
|
|
|
157
156
|
|
|
158
157
|
### Verify workspace identity BEFORE mutating (required)
|
|
159
158
|
|
|
160
|
-
> **"Start once, reuse always" has one hard exception
|
|
161
|
-
> another workspace.** A healthy, responsive server on the default port
|
|
162
|
-
> *different project's* canvas (a leftover daemon, or another repo's
|
|
163
|
-
> corrupt the wrong board. Always preflight:
|
|
159
|
+
> **"Start once, reuse always" has one hard exception for direct CLI/HTTP work: never mutate a
|
|
160
|
+
> listener that belongs to another workspace.** A healthy, responsive server on the default port
|
|
161
|
+
> `4313` can be serving a *different project's* canvas (a leftover daemon, or another repo's
|
|
162
|
+
> session). Mutating it directly would corrupt the wrong board. Always preflight:
|
|
164
163
|
|
|
165
164
|
1. **Read `GET /health`** (or `pmx-canvas serve status`). Both return a top-level
|
|
166
165
|
`workspace` field. **`workspace` MUST equal your intended workspace root**
|
|
@@ -172,7 +171,13 @@ file, sends SIGTERM, and cleans up on exit.
|
|
|
172
171
|
`pmx-canvas serve --daemon --no-open --port=<free-port>` — and target that port. (`PMX_CANVAS_PORT`
|
|
173
172
|
alone may still attach to an existing `4313` listener; prefer an explicit `--port`.) Then
|
|
174
173
|
**re-read `/health`** to confirm the workspace now matches.
|
|
175
|
-
4. **
|
|
174
|
+
4. **MCP transport exception:** `pmx-canvas --mcp` launched from an incidental host dir may attach to
|
|
175
|
+
the healthy daemon already on the preferred port when no explicit workspace root is set, so writes
|
|
176
|
+
land in the visible workbench instead of a hidden fallback workspace. Host adapters should set
|
|
177
|
+
`PMX_CANVAS_WORKSPACE_ROOT=<abs project root>` for deterministic targeting; set
|
|
178
|
+
`PMX_CANVAS_ALLOW_WORKSPACE_SPLIT=1` or a distinct `PMX_CANVAS_PORT` only when a separate canvas is
|
|
179
|
+
intentional.
|
|
180
|
+
5. **After any version upgrade**, run a behavior canary (e.g. a batch `node.add` with no `type`
|
|
176
181
|
must return `400`) to confirm the listener is the version you expect, not a stale old daemon.
|
|
177
182
|
|
|
178
183
|
## Browser Workflows
|
|
@@ -386,10 +391,12 @@ Agents tend to pack boards too tightly. Give nodes room to breathe — readabili
|
|
|
386
391
|
|
|
387
392
|
### Colors (Semantic)
|
|
388
393
|
|
|
389
|
-
A `color` parameter is honored only for **group** nodes (frame accent) and
|
|
390
|
-
(series/accent color). It is **not** a parameter for `markdown` / `status` /
|
|
391
|
-
top-level `color` on those is
|
|
392
|
-
|
|
394
|
+
A `color` parameter is honored as a **renderer** color only for **group** nodes (frame accent) and
|
|
395
|
+
**graph** nodes (series/accent color). It is **not** a renderer parameter for `markdown` / `status` /
|
|
396
|
+
`context` nodes: a top-level `color` on those is dropped on both HTTP and the CLI, and while an
|
|
397
|
+
arbitrary `data.color` you POST under `data` persists like any other `data.*` metadata, it is **not**
|
|
398
|
+
read as a render color for basic node types (report Finding H — renderer-semantic, not raw-storage,
|
|
399
|
+
contract). Their meaning comes from the node **type** and value instead: a `status` node colors its indicator from its
|
|
393
400
|
**phase** (`idle`/`running`/`planning`/`thinking`/`drafting`/`tooling`/`review`/`waiting-approval`/
|
|
394
401
|
`waiting` — e.g. `review` → green, `running` → accent; an unrecognized phase renders gray), and a
|
|
395
402
|
`trace` node from its `status` field (`success` → green, `failed` → red, `running` → accent). To
|
|
@@ -405,16 +412,15 @@ When you do set a color (group/graph), use this palette consistently to convey m
|
|
|
405
412
|
|
|
406
413
|
## MCP Tools Reference
|
|
407
414
|
|
|
408
|
-
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
|
|
409
416
|
set of first-class standalones. The composites fold the older
|
|
410
417
|
single-purpose tools behind an `action` (and, for `canvas_ax_gate`, a `kind`) discriminator —
|
|
411
418
|
**field names are unchanged**; only the tool name + the `action`/`kind` selector differ.
|
|
412
419
|
|
|
413
|
-
> **Legacy single-purpose tools
|
|
414
|
-
> `canvas_update_node`, `canvas_request_approval`, `canvas_add_work_item`, …)
|
|
415
|
-
>
|
|
416
|
-
>
|
|
417
|
-
> 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.
|
|
418
424
|
|
|
419
425
|
### The 15 composites
|
|
420
426
|
|
|
@@ -491,19 +497,19 @@ investigation board" intent, then run the batch with that linked `intentId` (use
|
|
|
491
497
|
### Standalones (first-class — not deprecated)
|
|
492
498
|
|
|
493
499
|
These stay separate by design (trust-boundary, firehose, execution-intent, or not-yet-consolidated
|
|
494
|
-
surfaces): `canvas_batch`, `canvas_pin_nodes`, `canvas_screenshot`,
|
|
495
|
-
`canvas_refresh_webpage_node`, `canvas_ax_interaction`,
|
|
500
|
+
surfaces): `canvas_batch`, `canvas_pin_nodes`, `canvas_screenshot`, `canvas_ax_interaction`,
|
|
496
501
|
`canvas_ingest_activity`, `canvas_invoke_command`, and the snapshot tools (`canvas_snapshot`,
|
|
497
502
|
`canvas_list_snapshots`, `canvas_restore`, `canvas_delete_snapshot`, `canvas_gc_snapshots`,
|
|
498
|
-
`canvas_diff` — a `canvas_snapshot` composite
|
|
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).
|
|
499
505
|
|
|
500
|
-
> **
|
|
501
|
-
> `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` /
|
|
502
507
|
> `canvas_build_web_artifact` → **`canvas_app`**; `canvas_webview_start` / `canvas_webview_status` /
|
|
503
508
|
> `canvas_webview_stop` / `canvas_resize` / `canvas_evaluate` → **`canvas_webview`**;
|
|
504
509
|
> `canvas_add_html_node` / `canvas_add_html_primitive` → **`canvas_node { action:"add", type:"html" }`**
|
|
505
510
|
> (pass `primitive` for a primitive); `canvas_add_node` / `canvas_update_node` / `canvas_remove_node`
|
|
506
511
|
> → **`canvas_node`**; `canvas_remove_annotation` → **`canvas_view { action:"remove-annotation" }`**;
|
|
512
|
+
> `canvas_refresh_webpage_node` → **`canvas_node { action:"update", id, refresh: true }`**;
|
|
507
513
|
> `canvas_validate` (board collisions / containment / dangling edges) → **`canvas_query` `validate`**;
|
|
508
514
|
> `canvas_validate_spec` (json-render / graph spec dry-run) → **`canvas_render` `validate`**.
|
|
509
515
|
|
|
@@ -545,10 +551,10 @@ delete+recreate; preserves edges, pins, position)
|
|
|
545
551
|
|
|
546
552
|
**`canvas_node { action: "get", id }`** — Get a single node's full data by `id`.
|
|
547
553
|
|
|
548
|
-
**`
|
|
554
|
+
**`canvas_view { action: "remove-annotation", id }`** — Remove a human-drawn annotation by `id`. Use when
|
|
549
555
|
context gives you the annotation ID; use WebView first if you need to identify a mark by shape or location.
|
|
550
556
|
|
|
551
|
-
**`
|
|
557
|
+
**`canvas_node { action: "update", id, refresh: true }`** — Re-fetch the URL stored on a `webpage` node
|
|
552
558
|
- `id` (required): webpage node to refresh
|
|
553
559
|
- Optional `url`: replace the stored URL before refreshing (use when the human moved the page)
|
|
554
560
|
- Returns the refreshed node with updated `pageTitle` and cached extracted text
|
|
@@ -561,7 +567,7 @@ context gives you the annotation ID; use WebView first if you need to identify a
|
|
|
561
567
|
// → returns { id: 'node-abc' }
|
|
562
568
|
|
|
563
569
|
// …later, after the human reopens the canvas…
|
|
564
|
-
|
|
570
|
+
canvas_node({ action: 'update', id: 'node-abc', refresh: true })
|
|
565
571
|
// → re-fetches the URL, updates pageTitle + extracted text, keeps the node ID and position
|
|
566
572
|
```
|
|
567
573
|
|
|
@@ -613,7 +619,7 @@ the `tufte-viz` skill (`skills/tufte-viz/SKILL.md`). Key rules:
|
|
|
613
619
|
- For more than ~4 overlapping series, build small multiples (several small graph nodes on a shared
|
|
614
620
|
scale, arranged in a grid/group) instead of one multi-color chart.
|
|
615
621
|
|
|
616
|
-
**`
|
|
622
|
+
**`canvas_app { action: "build-artifact" }`** — Build and optionally open a bundled web artifact
|
|
617
623
|
- Required: `title`, `appTsx` (source string contents, not a file path)
|
|
618
624
|
- CLI `--app-file` reads a file before calling the same build path; MCP callers must pass the source contents
|
|
619
625
|
- Cold builds commonly take 45-60 seconds; use a long client timeout such as 300000 ms or more
|
|
@@ -623,7 +629,7 @@ ID extraction for mixed tool responses:
|
|
|
623
629
|
- Most add-style tools return a flat `id`; web artifacts return `id` plus `nodeId`; snapshots return `id` plus nested `snapshot.id`.
|
|
624
630
|
- Defensive extractor: `const getId = (r) => r.id ?? r.nodeId ?? r.snapshot?.id;`
|
|
625
631
|
|
|
626
|
-
**`
|
|
632
|
+
**`canvas_app { action: "open-mcp-app" }`** — Open a tool-backed external MCP app node
|
|
627
633
|
- Required: `toolName`, `transport`
|
|
628
634
|
- `transport` is either `{ type: "stdio", command, args?, cwd?, env? }` or `{ type: "http", url, headers? }`
|
|
629
635
|
- This is lower-level than `pmx-canvas external-app add --kind excalidraw`; use `canvas_app { action: "diagram" }` for the built-in Excalidraw preset
|
|
@@ -765,35 +771,31 @@ JavaScript inside the workbench page.
|
|
|
765
771
|
|
|
766
772
|
The WebView automation runs on Bun's WebKit-based WebView (macOS) or a headless
|
|
767
773
|
Chromium fallback (Linux). It does **not** open a visible window; it's an
|
|
768
|
-
additional headless renderer attached to the same canvas server, so
|
|
769
|
-
|
|
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.
|
|
770
776
|
|
|
771
|
-
|
|
772
|
-
> standalone tools below map to `canvas_webview { action: "status" | "start" | "stop" |
|
|
773
|
-
> "evaluate" | "resize" }` respectively. Field names are unchanged.
|
|
774
|
-
|
|
775
|
-
**`canvas_webview_status`** — Inspect the current automation session
|
|
777
|
+
**`canvas_webview { action: "status" }`** — Inspect the current automation session
|
|
776
778
|
- Returns `{ supported, active, headlessOnly, url, backend, width, height, dataStoreDir, startedAt, lastError }`
|
|
777
779
|
(the viewport size is `width` / `height`, not `viewportWidth` / `viewportHeight`)
|
|
778
780
|
- Call before `start` to check whether a session is already alive
|
|
779
781
|
|
|
780
|
-
**`
|
|
782
|
+
**`canvas_webview { action: "start" }`** — Start (or replace) the automation session
|
|
781
783
|
- Optional: `backend` (`webkit` macOS-only, or `chrome`), `width`, `height`
|
|
782
784
|
- The session opens `/workbench` at the canvas URL, waits for the SPA to
|
|
783
|
-
hydrate, and reports back via `
|
|
785
|
+
hydrate, and reports back via `canvas_webview { action: "status" }`
|
|
784
786
|
|
|
785
|
-
**`
|
|
787
|
+
**`canvas_webview { action: "stop" }`** — Tear down the automation session
|
|
786
788
|
|
|
787
|
-
**`
|
|
789
|
+
**`canvas_webview { action: "evaluate" }`** — Run JavaScript inside the workbench page and return the result
|
|
788
790
|
- Required: exactly one of `expression` (single JS expression) or `script` (multi-statement body)
|
|
789
791
|
- `script` is wrapped in an async IIFE, so top-level `await` works inside script bodies
|
|
790
792
|
- Useful for asserting DOM state after a sequence of canvas mutations
|
|
791
|
-
- Do not use `fetch()` inside `
|
|
792
|
-
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.
|
|
793
795
|
- Example: read the count of rendered `.canvas-node` elements:
|
|
794
796
|
|
|
795
797
|
```typescript
|
|
796
|
-
|
|
798
|
+
canvas_webview({ action: 'evaluate', expression: 'document.querySelectorAll(".canvas-node").length' })
|
|
797
799
|
```
|
|
798
800
|
|
|
799
801
|
Useful workbench selectors:
|
|
@@ -803,19 +805,20 @@ Useful workbench selectors:
|
|
|
803
805
|
to inspect or screenshot annotation shapes; MCP/context resources only expose compact
|
|
804
806
|
annotation target summaries, not the raw visual shape. Humans can remove marks with
|
|
805
807
|
the eraser toolbar button; agents can remove a known annotation ID with
|
|
806
|
-
`
|
|
808
|
+
`canvas_view { action: "remove-annotation" }`.
|
|
807
809
|
- Canvas chrome: `.hud-layer`, `.canvas-toolbar`, `.connection-dot`, `.canvas-bootstrap-card`
|
|
808
810
|
- Nodes do not expose stable `data-node-id` attributes. Use `canvas_query` (`layout` / `search`) or MCP resource data for exact node IDs.
|
|
809
811
|
|
|
810
812
|
Async script example:
|
|
811
813
|
|
|
812
814
|
```typescript
|
|
813
|
-
|
|
815
|
+
canvas_webview({
|
|
816
|
+
action: 'evaluate',
|
|
814
817
|
script: 'const title = await Promise.resolve(document.title); return title;',
|
|
815
818
|
})
|
|
816
819
|
```
|
|
817
820
|
|
|
818
|
-
**`
|
|
821
|
+
**`canvas_webview { action: "resize" }`** — Change the WebView viewport
|
|
819
822
|
- Required: `width`, `height`
|
|
820
823
|
- Use before `canvas_screenshot` when the human needs a specific aspect ratio
|
|
821
824
|
|
|
@@ -823,20 +826,20 @@ canvas_evaluate({
|
|
|
823
826
|
- Optional: `format` (`png` default), `fullPage` (boolean)
|
|
824
827
|
- Returns both an MCP image payload (renderable inline by capable agents) and
|
|
825
828
|
a path under `.pmx-canvas/screenshots/` so the human can view the file
|
|
826
|
-
- Pair with `
|
|
829
|
+
- Pair with `canvas_webview { action: "resize" }` to control the framing
|
|
827
830
|
|
|
828
831
|
Typical flow when you want to show a result:
|
|
829
832
|
|
|
830
833
|
```typescript
|
|
831
|
-
|
|
834
|
+
canvas_webview({ action: 'start', width: 1440, height: 900 });
|
|
832
835
|
// …mutations…
|
|
833
836
|
canvas_screenshot({ fullPage: true });
|
|
834
|
-
|
|
837
|
+
canvas_webview({ action: 'stop' });
|
|
835
838
|
```
|
|
836
839
|
|
|
837
840
|
### Diagrams (Excalidraw MCP app preset)
|
|
838
841
|
|
|
839
|
-
**`
|
|
842
|
+
**`canvas_app { action: "diagram" }`** — Draw a hand-drawn diagram on the canvas via the hosted
|
|
840
843
|
[Excalidraw MCP app](https://github.com/excalidraw/excalidraw-mcp)
|
|
841
844
|
- Required: `elements` — an array of Excalidraw elements (rectangles, ellipses, diamonds, arrows,
|
|
842
845
|
text). Can also be a JSON-array string.
|
|
@@ -860,7 +863,7 @@ canvas_webview_stop();
|
|
|
860
863
|
|
|
861
864
|
### External MCP apps (bring your own)
|
|
862
865
|
|
|
863
|
-
**`
|
|
866
|
+
**`canvas_app { action: "open-mcp-app" }`** — Open any external [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps)
|
|
864
867
|
server's `ui://` resource as an iframe node on the canvas
|
|
865
868
|
- Required: `toolName`, `transport` (`http` URL or `stdio` command)
|
|
866
869
|
- Optional: `serverName`, `toolArguments`, `title`, `x`, `y`, `width`, `height`
|
|
@@ -869,7 +872,7 @@ server's `ui://` resource as an iframe node on the canvas
|
|
|
869
872
|
|
|
870
873
|
### Web Artifacts
|
|
871
874
|
|
|
872
|
-
**`
|
|
875
|
+
**`canvas_app { action: "build-artifact" }`** — Build a single-file HTML artifact from React/Tailwind source
|
|
873
876
|
- Required: `title`, `appTsx`
|
|
874
877
|
- Optional: `indexCss`, `mainTsx`, `indexHtml`, extra `files`, `projectPath`, `outputPath`, `deps`, `includeLogs`
|
|
875
878
|
- By default it opens the result on the canvas as an embedded app node
|
|
@@ -883,7 +886,7 @@ server's `ui://` resource as an iframe node on the canvas
|
|
|
883
886
|
|
|
884
887
|
### HTML Nodes (Sandboxed iframe)
|
|
885
888
|
|
|
886
|
-
**`
|
|
889
|
+
**`canvas_node { action: "add", type: "html", html }`** — Add a normal self-contained HTML document rendered in a sandboxed iframe
|
|
887
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.
|
|
888
891
|
- Optional: `title`, `summary`, `agentSummary`, `presentation`, `slideTitles`, `embeddedNodeIds`, `embeddedUrls`, `x`, `y`, `width` (default 720), `height` (default 640), `strictSize`
|
|
889
892
|
- Iframe sandbox is `allow-scripts` only — no same-origin access, no top-navigation, no forms
|
|
@@ -893,7 +896,7 @@ server's `ui://` resource as an iframe node on the canvas
|
|
|
893
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`.
|
|
894
897
|
- PMX stores a semantic sidecar (`agentSummary`, `contentSummary`, embedded references) so HTML nodes remain understandable in search, pinned context, and spatial context
|
|
895
898
|
|
|
896
|
-
**`
|
|
899
|
+
**`canvas_node { action: "add", type: "html", primitive, data }`** — Generate a reusable HTML communication primitive as a sandboxed `html` node
|
|
897
900
|
- Required: `kind`; run `canvas_render { action: "describe-schema" }` and read `htmlPrimitives` for the current catalog
|
|
898
901
|
- Optional: `title`, `data`, `x`, `y`, `width`, `height`, `strictSize`
|
|
899
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
|
|
@@ -953,7 +956,7 @@ Use native `json-render` and `graph` nodes when the output should stay fully ins
|
|
|
953
956
|
1. Use `canvas_render { action: "add-json-render" }` for dashboards, forms, summaries, and interactive UI panels
|
|
954
957
|
2. Use `canvas_render { action: "add-graph" }` for charts and trend visualizations
|
|
955
958
|
3. Use the repo-local `json-render-*` skills when you need help authoring or refining the spec itself
|
|
956
|
-
4. Use `
|
|
959
|
+
4. Use `canvas_app { action: "build-artifact" }` instead when the result needs a full custom React app rather than a schema-driven UI
|
|
957
960
|
|
|
958
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.
|
|
959
962
|
|
|
@@ -1099,9 +1102,9 @@ AX interactions are gated per node type. The lists below are each type's **ceili
|
|
|
1099
1102
|
| `group` | `ax.focus.set`, `ax.work.create`, `ax.command.invoke`, `ax.event.record` |
|
|
1100
1103
|
|
|
1101
1104
|
**Opt-in** — set `axCapabilities.enabled = true` (MCP: pass `axCapabilities` to
|
|
1102
|
-
`
|
|
1103
|
-
`html` body are accepted **top-level on both `POST /api/canvas/node`
|
|
1104
|
-
`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):
|
|
1105
1108
|
|
|
1106
1109
|
| Node type | Allowed AX interaction types |
|
|
1107
1110
|
|-----------|------------------------------|
|
|
@@ -1128,7 +1131,7 @@ inbox as a canvas node that BOTH emits AX interactions AND renders the current A
|
|
|
1128
1131
|
state. The read side mirrors the write side:
|
|
1129
1132
|
|
|
1130
1133
|
- **Opt in** (html/mcp-app are off by default): create with
|
|
1131
|
-
`
|
|
1134
|
+
`canvas_node({ action: "add", type: "html", html, axCapabilities: { enabled: true, allowed: ["ax.work.create","ax.work.update"] } })`,
|
|
1132
1135
|
or flip an existing node on with
|
|
1133
1136
|
`canvas_node({ action: "update", id, axCapabilities: { enabled: true, allowed: [...] } })`.
|
|
1134
1137
|
json-render / graph nodes are enabled by default.
|
|
@@ -1155,7 +1158,7 @@ state. The read side mirrors the write side:
|
|
|
1155
1158
|
against `window.PMX_AX`, not direct `fetch()` (the artifact iframe is sandboxed
|
|
1156
1159
|
opaque-origin, so it can't call the API directly).
|
|
1157
1160
|
|
|
1158
|
-
Minimal html work board (drop-in via `
|
|
1161
|
+
Minimal html work board (drop-in via `canvas_node { action: "add", type: "html" }`, `axCapabilities.enabled: true`):
|
|
1159
1162
|
|
|
1160
1163
|
```html
|
|
1161
1164
|
<button id="add">+ Task</button> <span id="ok"></span>
|
|
@@ -1207,10 +1210,10 @@ The `canvas://spatial-context` resource reveals how the human has organized info
|
|
|
1207
1210
|
- **Pinned neighborhoods** — For each pinned node, nearby unpinned nodes are listed. These
|
|
1208
1211
|
are the human's implicit context — things they consider related to what they pinned.
|
|
1209
1212
|
- **Annotations** — Human-drawn markup is summarized by target/bounds only, e.g. an
|
|
1210
|
-
annotation over a node or empty canvas region. Use WebView (`
|
|
1211
|
-
`
|
|
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
|
|
1212
1215
|
arrow, line, circle, or other drawn shape. Remove known annotations with
|
|
1213
|
-
`
|
|
1216
|
+
`canvas_view { action: "remove-annotation" }`; otherwise use WebView to identify the mark first.
|
|
1214
1217
|
- **Board density matters** — On a dense board, spatial context can still read like one large
|
|
1215
1218
|
gallery unless groups and spacing separate the major regions clearly.
|
|
1216
1219
|
|
|
@@ -1359,7 +1362,7 @@ Understand a codebase by visualizing file relationships:
|
|
|
1359
1362
|
When the user wants a real browser app instead of static notes:
|
|
1360
1363
|
|
|
1361
1364
|
1. Use the `web-artifacts-builder` skill if the UI needs React state, routing, or shadcn-style components
|
|
1362
|
-
2. Build with `
|
|
1365
|
+
2. Build with `canvas_app { action: "build-artifact" }`
|
|
1363
1366
|
3. Keep `openInCanvas` enabled unless the user explicitly wants only the output file
|
|
1364
1367
|
4. Use the returned `projectPath` as the reusable source workspace for iterations
|
|
1365
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
|
-
`
|
|
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 `
|
|
15
|
-
- Keep using `
|
|
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 `
|
|
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 (`
|
|
132
|
-
every panel; the sequence tells the macro story while each panel
|
|
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 `
|
|
25
|
-
bundled runtime scripts, writes reusable source under
|
|
26
|
-
emits a bundled HTML file under `.pmx-canvas/artifacts/`,
|
|
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
|
package/src/client/App.tsx
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
walkGraph,
|
|
39
39
|
} from './state/canvas-store';
|
|
40
40
|
import { connectSSE } from './state/sse-bridge';
|
|
41
|
+
import { intents } from './state/intent-store';
|
|
41
42
|
import { saveCanvasTheme } from './state/intent-bridge';
|
|
42
43
|
import {
|
|
43
44
|
IconArrange,
|
|
@@ -556,7 +557,7 @@ export function App() {
|
|
|
556
557
|
annotationMode={annotationTool !== null}
|
|
557
558
|
annotationTool={annotationTool}
|
|
558
559
|
/>
|
|
559
|
-
{hasInitialLayout && allNodes.filter((n) => !n.dockPosition).length === 0 && (
|
|
560
|
+
{hasInitialLayout && allNodes.filter((n) => !n.dockPosition).length === 0 && intents.value.size === 0 && (
|
|
560
561
|
<WelcomeCard onOpenPalette={() => setPaletteOpen(true)} />
|
|
561
562
|
)}
|
|
562
563
|
{selectedNodeIds.value.size > 0 && <SelectionBar />}
|