pmx-canvas 0.1.17 → 0.1.19

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.
@@ -0,0 +1,244 @@
1
+ # Node types
2
+
3
+ Canvas nodes are typed. Each type has a dedicated renderer, schema, and (for
4
+ structured types) a dedicated MCP tool. This page is the user-facing reference
5
+ for what each type is for and how to create one. For tool/HTTP/SDK signatures,
6
+ see [MCP tools](mcp.md), [HTTP API](http-api.md), and [SDK](sdk.md).
7
+
8
+ ## Overview
9
+
10
+ | Type | Purpose |
11
+ |------|---------|
12
+ | `markdown` | Rich markdown with rendered preview |
13
+ | `status` | Compact status indicator (phase, message, elapsed time) |
14
+ | `context` | Context cards, token usage, workspace grounding |
15
+ | `ledger` | Execution ledger summary |
16
+ | `trace` | Agent trace pills (tool calls, subagent activity) |
17
+ | `file` | Live file viewer with auto-update on disk changes |
18
+ | `image` | Image viewer (file paths, data URIs, URLs) |
19
+ | `webpage` | Persisted webpage snapshot with stored URL, extracted text, refresh |
20
+ | `mcp-app` | Tool-backed hosted MCP App iframes (Excalidraw, etc.) |
21
+ | `json-render` | Structured UI from JSON specs (cards, tables, forms) |
22
+ | `graph` | Charts (line, bar, pie, area, scatter, radar, stacked-bar, composed) |
23
+ | `html` | Self-contained HTML/JS in a sandboxed iframe |
24
+ | `web-artifact` | Bundled React/Tailwind artifact (full single-file app) |
25
+ | `group` | Spatial container/frame around other nodes |
26
+
27
+ Thread node types `prompt` and `response` exist internally for agent
28
+ conversation rendering and are not created through public APIs.
29
+
30
+ ## Choosing the right visual tier
31
+
32
+ Three rendering tiers cover increasing levels of complexity. Pick the lowest
33
+ that fits the work — each step adds capability and bundle weight.
34
+
35
+ | Tier | Type | Use when | Bundle weight |
36
+ |------|------|----------|---------------|
37
+ | 1 | `json-render` | You can describe the UI as a spec (forms, tables, dashboards from a component catalog) | None — runtime already loaded |
38
+ | 2 | `html` | You have/can write self-contained HTML+JS (Chart.js, D3, custom widgets, interactive demos) | None — sandboxed iframe |
39
+ | 3 | `web-artifact` | You need a full React/Tailwind app with shadcn components, routing, or shared state | Build step |
40
+
41
+ ## File nodes
42
+
43
+ File nodes display project files with line numbers and language detection.
44
+ When an agent edits a file through its normal tools, the canvas node updates
45
+ automatically via `fs.watch()`.
46
+
47
+ ```ts
48
+ canvas_add_node({ type: 'file', content: 'src/server/index.ts' })
49
+ ```
50
+
51
+ ## Image nodes
52
+
53
+ Image nodes display local paths, remote URLs, and data URIs. File-backed and
54
+ HTTP(S)-backed images preserve provenance so agents can tell where evidence
55
+ came from. Nodes can carry validation status or warnings.
56
+
57
+ ```ts
58
+ canvas_add_node({
59
+ type: 'image',
60
+ content: 'artifacts/dashboard.png',
61
+ data: {
62
+ validationStatus: 'passed',
63
+ validationMessage: 'Screenshot matches the requested dashboard state.',
64
+ },
65
+ })
66
+ ```
67
+
68
+ ## Webpage nodes
69
+
70
+ Webpage nodes store the source URL on the node, fetch the page server-side,
71
+ and cache extracted text for search, pins, and agent context. Saved canvases
72
+ keep enough information for an agent to refresh the node from the original
73
+ URL later.
74
+
75
+ ```ts
76
+ canvas_add_node({ type: 'webpage', url: 'https://example.com/docs' })
77
+ canvas_refresh_webpage_node({ id: 'node-abc123' })
78
+ ```
79
+
80
+ ## MCP App nodes
81
+
82
+ `mcp-app` nodes embed other MCP servers' UI resources (`ui://...`) directly
83
+ on the canvas as sandboxed iframes. Any server implementing the
84
+ [MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps)
85
+ can be opened with `canvas_open_mcp_app`.
86
+
87
+ Generic `pmx-canvas node add --type mcp-app` is intentionally rejected —
88
+ these nodes need tool/session metadata. Use `canvas_open_mcp_app` (or the
89
+ `canvas_add_diagram` Excalidraw preset) instead.
90
+
91
+ ### Excalidraw preset (hand-drawn diagrams)
92
+
93
+ [Excalidraw](https://github.com/excalidraw/excalidraw-mcp) ships a hosted MCP
94
+ server at `https://mcp.excalidraw.com/mcp`. PMX Canvas exposes a one-call
95
+ preset:
96
+
97
+ ```ts
98
+ canvas_add_diagram({
99
+ elements: [
100
+ { type: 'rectangle', id: 'a', x: 80, y: 120, width: 180, height: 80,
101
+ roundness: { type: 3 }, backgroundColor: '#a5d8ff', fillStyle: 'solid',
102
+ label: { text: 'Agent', fontSize: 18 } },
103
+ { type: 'rectangle', id: 'b', x: 380, y: 120, width: 180, height: 80,
104
+ roundness: { type: 3 }, backgroundColor: '#d0bfff', fillStyle: 'solid',
105
+ label: { text: 'PMX Canvas', fontSize: 18 } },
106
+ { type: 'arrow', id: 'a1', x: 260, y: 160, width: 120, height: 0,
107
+ startBinding: { elementId: 'a' }, endBinding: { elementId: 'b' },
108
+ label: { text: 'adds nodes' } },
109
+ ],
110
+ title: 'Agent → Canvas',
111
+ });
112
+ ```
113
+
114
+ For any other MCP App, call `canvas_open_mcp_app` directly with the server's
115
+ transport, tool name, and arguments.
116
+
117
+ ## json-render nodes
118
+
119
+ `json-render` nodes turn structured JSON specs into rendered UI panels
120
+ (dashboards, tables, forms, cards) without writing HTML. PMX Canvas ships the
121
+ [`@json-render/*`](https://www.npmjs.com/package/@json-render/core) runtime
122
+ and component catalog (core + react + shadcn).
123
+
124
+ ```ts
125
+ canvas_add_json_render_node({
126
+ title: 'Deploy status',
127
+ spec: {
128
+ root: 'card',
129
+ elements: {
130
+ card: { type: 'Card', props: { title: 'Deploy' }, children: ['status'] },
131
+ status: { type: 'Badge', props: { variant: 'default', text: 'Healthy' } },
132
+ },
133
+ },
134
+ });
135
+ ```
136
+
137
+ `Badge` uses shadcn variants: `default`, `secondary`, `destructive`,
138
+ `outline`. Older saved specs using `label` or status variants such as
139
+ `success`/`warning` are normalized during validation.
140
+
141
+ Use `canvas_describe_schema` / `canvas_validate_spec` to introspect the
142
+ component catalog before building a spec.
143
+
144
+ ## HTML nodes
145
+
146
+ `html` nodes render a self-contained HTML/JS document in a sandboxed iframe.
147
+ They sit between `json-render` (no custom JS) and `web-artifact` (full bundled
148
+ React app) — perfect for Chart.js, D3, custom widgets, and any HTML you can
149
+ write or paste.
150
+
151
+ The sandbox runs with `allow-scripts` only — no same-origin access, no
152
+ top-level navigation, no form submission. Inline `<script>` and CDN
153
+ `<script src>` both work. The canvas auto-injects its theme tokens
154
+ (`--c-*` and `--color-*` aliases) into the iframe `<head>` so artifacts can
155
+ match the active theme.
156
+
157
+ ```ts
158
+ canvas_add_html_node({
159
+ title: 'Cost projection',
160
+ html: '<canvas id="c"></canvas><script src="https://cdn.jsdelivr.net/npm/chart.js"></script><script>...</script>',
161
+ })
162
+ ```
163
+
164
+ A fragment without `<html>`/`<head>` is wrapped in a full document
165
+ automatically. Default size is 720×640.
166
+
167
+ ## Web artifacts
168
+
169
+ A **web artifact** is a single-file, fully bundled HTML app (React + Tailwind
170
+ + shadcn) the agent builds from TSX source. Use it when the work calls for a
171
+ real interactive app — charts, forms, mini-dashboards — beyond what a static
172
+ node or `html` snippet can express.
173
+
174
+ `canvas_build_web_artifact` takes source strings (`App.tsx`, optional
175
+ `index.css`, `main.tsx`, `index.html`, plus extra files), runs the bundled
176
+ web-artifacts-builder scripts, writes the self-contained HTML to
177
+ `.pmx-canvas/artifacts/<slug>.html`, and (by default) opens it in the canvas.
178
+
179
+ ```bash
180
+ pmx-canvas web-artifact build --title "Dashboard" --app-file ./App.tsx --deps recharts --include-logs
181
+ ```
182
+
183
+ The scaffold includes `recharts`. Pass `--deps name,name2` for additional
184
+ package dependencies. Failed or empty CLI bundles print `ok: false`, exit
185
+ non-zero, and do not create a canvas node.
186
+
187
+ The matching agent skill is at
188
+ [`skills/web-artifacts-builder/SKILL.md`](../skills/web-artifacts-builder/SKILL.md).
189
+
190
+ ## Groups
191
+
192
+ Groups are spatial containers that visually contain other nodes. They render
193
+ as dashed-border frames with a title bar and optional accent color.
194
+
195
+ - Select 2+ nodes and click "Group" in the selection bar
196
+ - Right-click a group to ungroup
197
+ - Collapsing a group hides children and shows a summary
198
+ - By default, group creation preserves the children's current positions and
199
+ expands the frame around them
200
+ - Pass `childLayout` to auto-pack children (`grid`, `column`, `flow`)
201
+ - Pass explicit `x`, `y`, `width`, and `height` to create a manual frame and
202
+ lay children out inside it
203
+
204
+ ```ts
205
+ canvas_create_group({ title: 'Auth Module', childIds: ['node-1', 'node-2'], color: '#4a9eff' })
206
+ ```
207
+
208
+ ## Edge types
209
+
210
+ All edges support labels, styles (solid/dashed/dotted), and animation.
211
+
212
+ | Type | Use case |
213
+ |------|----------|
214
+ | `flow` | Sequential steps, data flow |
215
+ | `depends-on` | Dependencies between tasks |
216
+ | `relation` | General relationships |
217
+ | `references` | Cross-references, evidence links |
218
+
219
+ ## Schema-driven discovery
220
+
221
+ Agents don't have to guess node shapes. The running server exposes its create
222
+ schemas, json-render component catalog, and node-type examples:
223
+
224
+ - `canvas_describe_schema` / `GET /api/canvas/schema` — list all node-create
225
+ schemas, required fields, json-render components, and sample payloads
226
+ - `canvas_validate_spec` / `POST /api/canvas/schema/validate` — validate a
227
+ json-render spec or graph payload **without** creating a node
228
+ - `canvas_validate` / `GET /api/canvas/validate` — validate the current
229
+ layout for collisions, containment, and missing edge endpoints
230
+ - `canvas://schema` — the same data as an MCP resource
231
+
232
+ The CLI's `node schema` / `validate spec` subcommands surface the same data
233
+ from the terminal.
234
+
235
+ MCP node creation uses dedicated tools for structured node families. Read
236
+ `mcp.nodeTypeRouting` from `canvas_describe_schema` when in doubt:
237
+ `json-render` → `canvas_add_json_render_node`,
238
+ `graph` → `canvas_add_graph_node`,
239
+ `html` → `canvas_add_html_node`,
240
+ `web-artifact` → `canvas_build_web_artifact`,
241
+ `mcp-app` → `canvas_open_mcp_app`,
242
+ `group` → `canvas_create_group`.
243
+ Basic nodes (`markdown`, `status`, `file`, `image`, `webpage`) use
244
+ `canvas_add_node`.
File without changes
@@ -0,0 +1,335 @@
1
+ ---
2
+ title: Semantic Watch CLI MVP
3
+ status: draft
4
+ date: 2026-04-18
5
+ ---
6
+
7
+ # Semantic Watch CLI MVP
8
+
9
+ ## Summary
10
+
11
+ Add a new agent-facing CLI command:
12
+
13
+ ```bash
14
+ pmx-canvas watch
15
+ ```
16
+
17
+ The command connects to the existing `/api/workbench/events` SSE stream and emits
18
+ compact, low-token summaries of **meaningful human intent changes** on the canvas.
19
+
20
+ This is **not** a raw browser telemetry tap. The watcher consumes authoritative
21
+ server events, keeps prior layout state locally, computes semantic diffs locally,
22
+ and emits one compact line or one JSON object only when the canvas state makes a
23
+ changed intent legible.
24
+
25
+ ## Problem
26
+
27
+ PMX Canvas promises that spatial arrangement is communication and that pinned
28
+ context closes the human-to-agent loop. In practice, only pins cross that
29
+ boundary reliably today.
30
+
31
+ The current gap:
32
+
33
+ - agents can poll resources like `canvas://pinned-context` and
34
+ `canvas://spatial-context`
35
+ - browsers receive rich SSE updates
36
+ - there is no CLI primitive that turns those updates into a low-noise,
37
+ low-token stream that an agent or hook can follow
38
+
39
+ ## Goals
40
+
41
+ - Provide a long-running `watch` command for agents and hooks.
42
+ - Reuse the existing `/api/workbench/events` SSE stream.
43
+ - Reduce full layout snapshots into compact semantic deltas locally.
44
+ - Keep token cost low by suppressing raw gesture noise.
45
+ - Expose a machine-readable mode for hooks and automation.
46
+
47
+ ## Non-Goals
48
+
49
+ - No new browser-only collaboration protocol.
50
+ - No raw pointer, drag-frame, or selection telemetry.
51
+ - No `select` event in v1.
52
+ - No `collapse` event in v1 until collapsed state is synced immediately and
53
+ authoritatively to the server.
54
+ - No node content/body text in watch output.
55
+ - No attempt to narrate every movement; only semantic movement changes.
56
+
57
+ ## Authoritative Signal Sources
58
+
59
+ The command must only depend on signals the server can already observe.
60
+
61
+ ### Existing sources
62
+
63
+ - `GET /api/workbench/events`
64
+ - initial `canvas-layout-update` snapshot on connect
65
+ - subsequent `canvas-layout-update` envelopes on node/edge/layout mutations
66
+ - `context-pins-changed` on pin updates
67
+ - `GET /api/canvas/pinned-context`
68
+ - initial current pin set on watcher startup
69
+ - local semantic derivation using `buildSpatialContext(...)`
70
+
71
+ ### Explicitly excluded from v1
72
+
73
+ - client-only `selectedNodeIds`
74
+ - locally toggled `collapsed` state that has not yet been persisted
75
+
76
+ ## CLI UX
77
+
78
+ ### Command
79
+
80
+ ```bash
81
+ pmx-canvas watch [options]
82
+ ```
83
+
84
+ ### Output modes
85
+
86
+ - Default: compact human-readable lines
87
+ - `--compact`: explicit compact mode (same as default)
88
+ - `--json`: emit one JSON object per semantic event, JSONL-style
89
+
90
+ `--compact` and `--json` are mutually exclusive.
91
+
92
+ ### Filters
93
+
94
+ ```bash
95
+ --events context-pin,move-end,group,connect,remove
96
+ ```
97
+
98
+ Supported semantic event kinds in v1:
99
+
100
+ - `context-pin`
101
+ - `move-end`
102
+ - `group`
103
+ - `connect`
104
+ - `remove`
105
+
106
+ If omitted, all v1 semantic kinds are enabled.
107
+
108
+ ### Control flags
109
+
110
+ ```bash
111
+ --max-events 3
112
+ ```
113
+
114
+ Stop after emitting N semantic events. This exists mainly to support scripted
115
+ usage and unit tests without changing the default streaming behavior.
116
+
117
+ ## Event Model
118
+
119
+ ### 1. `context-pin`
120
+
121
+ Source:
122
+
123
+ - direct `context-pins-changed` SSE event
124
+
125
+ Payload:
126
+
127
+ - added pinned node IDs/titles
128
+ - removed pinned node IDs/titles
129
+
130
+ Rules:
131
+
132
+ - emit only when the pin set actually changed
133
+ - do not emit on startup bootstrap
134
+
135
+ Compact example:
136
+
137
+ ```text
138
+ context-pin +2 -1: "Bug report", "auth.ts" | removed: "old note"
139
+ ```
140
+
141
+ JSON example:
142
+
143
+ ```json
144
+ {"type":"context-pin","added":[{"id":"n1","title":"Bug report","nodeType":"markdown"}],"removed":[]}
145
+ ```
146
+
147
+ ### 2. `connect`
148
+
149
+ Source:
150
+
151
+ - added edges in a `canvas-layout-update` diff
152
+
153
+ Payload:
154
+
155
+ - added edges with `from`, `to`, edge type, and node titles when available
156
+
157
+ Rules:
158
+
159
+ - emit for newly added edges only
160
+ - ignore unchanged edges
161
+
162
+ Compact example:
163
+
164
+ ```text
165
+ connect 1: "Bug report" -> "auth.ts" (relation)
166
+ ```
167
+
168
+ ### 3. `remove`
169
+
170
+ Source:
171
+
172
+ - removed nodes and removed edges in a `canvas-layout-update` diff
173
+
174
+ Payload:
175
+
176
+ - removed node summaries
177
+ - removed edge summaries
178
+
179
+ Rules:
180
+
181
+ - emit only for removals after bootstrap
182
+ - group all removals from one layout update into one semantic event
183
+
184
+ Compact example:
185
+
186
+ ```text
187
+ remove 2 nodes: "Old note", "scratch.ts"
188
+ ```
189
+
190
+ ### 4. `group`
191
+
192
+ Source:
193
+
194
+ - group node creation or group membership change detected from layout diffs
195
+
196
+ Payload:
197
+
198
+ - created groups
199
+ - membership changes for existing groups
200
+ - child additions/removals by group
201
+
202
+ Rules:
203
+
204
+ - treat new `group` nodes as group creation
205
+ - compare `group.data.children` arrays for membership deltas
206
+ - emit one grouped summary per layout update
207
+
208
+ Compact examples:
209
+
210
+ ```text
211
+ group created: "API Group" (2 children)
212
+ group updated: "API Group" +1 -0 children
213
+ ```
214
+
215
+ ### 5. `move-end`
216
+
217
+ Source:
218
+
219
+ - node position changes between two `canvas-layout-update` snapshots
220
+
221
+ Important:
222
+
223
+ - derive from **layout diffs**, not pointer events
224
+ - emit only when movement caused a semantic change
225
+
226
+ Semantic triggers:
227
+
228
+ - the moved node’s proximity-cluster peers changed
229
+ - the moved node entered or left a pinned neighborhood
230
+ - a pinned node moved and its neighborhood changed
231
+
232
+ Rules:
233
+
234
+ - suppress pure coordinate churn
235
+ - suppress movement that did not change spatial meaning
236
+ - ignore newly created or removed nodes when computing move-end
237
+
238
+ Compact examples:
239
+
240
+ ```text
241
+ move-end: "auth.ts" cluster changed
242
+ move-end: "session.ts" entered pinned neighborhood of "Bug report"
243
+ ```
244
+
245
+ ## Bootstrap Behavior
246
+
247
+ On startup:
248
+
249
+ 1. fetch current pins from `/api/canvas/pinned-context`
250
+ 2. connect to `/api/workbench/events`
251
+ 3. accept the initial `canvas-layout-update` snapshot as the baseline
252
+ 4. emit nothing during bootstrap
253
+
254
+ This avoids startup noise and prevents the watcher from replaying the whole
255
+ canvas as if it were a fresh user action stream.
256
+
257
+ ## Local Reduction Algorithm
258
+
259
+ Maintain local watcher state:
260
+
261
+ - `currentLayout`
262
+ - `currentPinnedIds`
263
+ - derived `previousSpatialContext`
264
+
265
+ On each relevant SSE event:
266
+
267
+ ### `context-pins-changed`
268
+
269
+ - diff old pin set vs new pin set
270
+ - emit `context-pin` if changed
271
+ - update `currentPinnedIds`
272
+ - recompute local spatial context baseline
273
+
274
+ ### `canvas-layout-update`
275
+
276
+ - if no baseline exists, store it and stop
277
+ - diff previous layout vs next layout
278
+ - emit `connect`, `remove`, and `group` events from structural diffs
279
+ - compute old/new spatial context with the current pin set
280
+ - detect moved nodes
281
+ - emit `move-end` only for nodes whose movement changed:
282
+ - cluster peers
283
+ - pinned-neighborhood membership
284
+ - replace baseline with the new layout/spatial context
285
+
286
+ ## Token Budget Rules
287
+
288
+ The watcher exists to save tokens, so its output contract must stay strict.
289
+
290
+ Rules:
291
+
292
+ - no full layout snapshots in emitted output
293
+ - no node body text or file content
294
+ - no duplicate emission for unchanged semantic state
295
+ - one semantic event object/line per meaning change, not per low-level SSE frame
296
+ - compact mode should usually stay under one short line per event
297
+
298
+ ## Implementation Plan
299
+
300
+ ### Files
301
+
302
+ - add a new CLI watcher module for:
303
+ - SSE parsing
304
+ - semantic reduction
305
+ - compact/JSON formatting
306
+ - wire the command into `src/cli/agent.ts`
307
+ - add `watch` to `src/cli/index.ts` command help/router
308
+ - add unit coverage for:
309
+ - context pin diffs
310
+ - connect/remove diffs
311
+ - group diffs
312
+ - move-end semantic suppression and emission
313
+
314
+ ### Reuse
315
+
316
+ - reuse `/api/workbench/events`
317
+ - reuse `/api/canvas/pinned-context`
318
+ - reuse `buildSpatialContext(...)`
319
+
320
+ ## Verification Plan
321
+
322
+ - unit tests for semantic reducer behavior
323
+ - unit tests for CLI watch output/filtering
324
+ - `bun run test`
325
+
326
+ If implementation touches browser-visible UI or client code later, escalate
327
+ verification. For this MVP, CLI/server-side verification is expected to be
328
+ sufficient.
329
+
330
+ ## Deferred Work
331
+
332
+ - watchable `collapse` after immediate server sync exists
333
+ - explicit `cluster-changed` or `neighborhood-changed` top-level event kinds
334
+ - reconnect/backoff behavior for long-lived watches
335
+ - MCP-side semantic watcher surface if needed later