pmx-canvas 0.7.3 → 0.8.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/.github/extensions/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/.github/extensions/pmx-canvas/extension.mjs +25 -172
- package/.github/extensions/pmx-canvas/mcp-bootstrap.ts +198 -0
- package/.github/extensions/pmx-canvas/runtime.d.mts +41 -0
- package/.github/extensions/pmx-canvas/runtime.json +3 -0
- package/.github/extensions/pmx-canvas/runtime.mjs +194 -0
- package/.github/extensions/pmx-canvas/start-mcp.mjs +51 -0
- package/.github/extensions/pmx-canvas/tools.json +2560 -0
- package/CHANGELOG.md +47 -0
- package/Readme.md +21 -7
- package/dist/canvas/fonts/OFL.txt +93 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
- package/dist/canvas/global.css +723 -672
- package/dist/canvas/index.js +90 -88
- package/dist/canvas/mcp-app.html +7878 -0
- package/dist/canvas/mermaid-entry.js +1 -1
- package/dist/canvas/surface-theme.css +151 -46
- package/dist/json-render/index.css +1 -1
- package/dist/types/cli/commands/record.d.ts +1 -1
- package/dist/types/client/canvas/NodeContextMark.d.ts +24 -0
- package/dist/types/client/canvas/SessionReceipt.d.ts +3 -1
- package/dist/types/client/icons.d.ts +1 -2
- package/dist/types/client/mcp-app/index.d.ts +1 -0
- package/dist/types/client/nodes/ax-node-actions.d.ts +2 -2
- package/dist/types/client/nodes/markdown-sanitize.d.ts +2 -0
- package/dist/types/client/nodes/use-workbench-asset.d.ts +2 -0
- package/dist/types/client/state/attention-store.d.ts +0 -5
- package/dist/types/client/state/context-status-store.d.ts +53 -0
- package/dist/types/client/state/intent-bridge.d.ts +3 -0
- package/dist/types/client/state/near-pin-store.d.ts +16 -0
- package/dist/types/client/state/presence-store.d.ts +5 -0
- package/dist/types/client/state/session-store.d.ts +33 -3
- package/dist/types/client/state/workbench-transport.d.ts +16 -0
- package/dist/types/client/theme/tokens.d.ts +1 -3
- package/dist/types/client/types.d.ts +9 -0
- package/dist/types/json-render/server.d.ts +1 -1
- package/dist/types/mcp/app-server.d.ts +3 -0
- package/dist/types/mcp/canvas-access.d.ts +1 -1
- package/dist/types/mcp/canvas-app.d.ts +4 -0
- package/dist/types/mcp/workbench-app.d.ts +4 -0
- package/dist/types/server/agent-presence.d.ts +1 -1
- package/dist/types/server/ax-state.d.ts +1 -1
- package/dist/types/server/canvas-db.d.ts +8 -0
- package/dist/types/server/canvas-geometry.d.ts +25 -0
- package/dist/types/server/canvas-operations.d.ts +1 -11
- package/dist/types/server/canvas-state.d.ts +25 -3
- package/dist/types/server/context-brief.d.ts +8 -1
- package/dist/types/server/context-reads.d.ts +30 -7
- package/dist/types/server/index.d.ts +6 -1
- package/dist/types/server/mcp-app-runtime.d.ts +1 -0
- package/dist/types/server/operations/http.d.ts +2 -0
- package/dist/types/server/operations/invoker.d.ts +4 -2
- package/dist/types/server/operations/ops/nodes.d.ts +0 -6
- package/dist/types/server/server.d.ts +1 -0
- package/dist/types/server/spatial-analysis.d.ts +2 -0
- package/dist/types/shared/agent-context.d.ts +22 -0
- package/dist/types/shared/agent-presence.d.ts +3 -10
- package/dist/types/shared/canvas-app.d.ts +54 -0
- package/dist/types/shared/themes.d.ts +1 -1
- package/docs/RELEASE.md +7 -3
- package/docs/agent-plugin.md +123 -0
- package/docs/ax-state-contract.md +40 -0
- package/docs/cli.md +7 -2
- package/docs/design/AgentContext.dc.html +186 -0
- package/docs/design/Before.dc.html +208 -0
- package/docs/design/BoardPins.dc.html +151 -0
- package/docs/design/Chrome.dc.html +208 -0
- package/docs/design/Context.dc.html +239 -0
- package/docs/design/Flows.dc.html +181 -0
- package/docs/design/Home.dc.html +209 -0
- package/docs/design/HomeGraph.dc.html +211 -0
- package/docs/design/HomeMap.dc.html +117 -0
- package/docs/design/HomeTeam.dc.html +176 -0
- package/docs/design/Light.dc.html +28 -0
- package/docs/design/Main.dc.html +280 -0
- package/docs/design/NearPin.dc.html +260 -0
- package/docs/design/Pane600.dc.html +120 -0
- package/docs/design/Relations.dc.html +297 -0
- package/docs/design/Themes.dc.html +121 -0
- package/docs/design/TokenSystem.dc.html +136 -0
- package/docs/design/Tokens.dc.html +163 -0
- package/docs/design/Types1.dc.html +177 -0
- package/docs/design/Types2.dc.html +226 -0
- package/docs/design/Types3.dc.html +195 -0
- package/docs/design/Uses.dc.html +152 -0
- package/docs/design/Vision.dc.html +116 -0
- package/docs/design/Zoom.dc.html +82 -0
- package/docs/design/canvas.json +274 -0
- package/docs/design.md +214 -0
- package/docs/environment.md +1 -1
- package/docs/http-api.md +80 -15
- package/docs/mcp.md +198 -3
- package/docs/node-types.md +34 -10
- package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
- package/docs/plans/plan-015-design-foundation-wave-0.md +177 -0
- package/docs/plans/plan-016-context-made-visible-wave-1.md +148 -0
- package/docs/product-vision-2026-09.md +47 -7
- package/docs/screenshot.png +0 -0
- package/docs/sdk.md +1 -0
- package/package.json +10 -4
- package/plugins/pmx-canvas/LICENSE +21 -0
- package/plugins/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/plugins/pmx-canvas/assets/preview.png +0 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +930 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +3 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.mjs +194 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/steering-delivery.mjs +79 -0
- package/plugins/pmx-canvas/mcp.json +12 -0
- package/plugins/pmx-canvas/plugin.json +24 -0
- package/plugins/pmx-canvas/runtime.json +3 -0
- package/plugins/pmx-canvas/runtime.mjs +194 -0
- package/plugins/pmx-canvas/start-mcp.mjs +51 -0
- package/plugins/pmx-canvas/tools.json +2560 -0
- package/skills/pmx-canvas/SKILL.md +10 -7
- package/skills/pmx-canvas/references/full-reference.md +11 -7
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +20 -8
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
- package/skills/pmx-canvas-testing/SKILL.md +5 -4
- package/src/cli/commands/copilot.ts +5 -0
- package/src/cli/commands/view.ts +1 -1
- package/src/cli/index.ts +8 -3
- package/src/client/App.tsx +6 -1
- package/src/client/canvas/AgentPresenceLayer.tsx +3 -3
- package/src/client/canvas/AnnotationLayer.tsx +1 -1
- package/src/client/canvas/CanvasNode.tsx +32 -49
- package/src/client/canvas/CanvasViewport.tsx +0 -2
- package/src/client/canvas/CommandPalette.tsx +3 -2
- package/src/client/canvas/ContextMenu.tsx +11 -0
- package/src/client/canvas/EdgeLayer.tsx +1 -1
- package/src/client/canvas/ExportDialog.tsx +33 -6
- package/src/client/canvas/IntentLayer.tsx +2 -2
- package/src/client/canvas/Minimap.tsx +12 -7
- package/src/client/canvas/NodeContextMark.tsx +156 -0
- package/src/client/canvas/SessionPanel.tsx +4 -7
- package/src/client/canvas/SessionReceipt.tsx +114 -17
- package/src/client/canvas/ShortcutOverlay.tsx +6 -1
- package/src/client/canvas/SnapshotPanel.tsx +11 -2
- package/src/client/canvas/ToolRail.tsx +12 -9
- package/src/client/canvas/TopBar.tsx +16 -11
- package/src/client/icons.tsx +2 -12
- package/src/client/mcp-app/index.tsx +137 -0
- package/src/client/mcp-app/style.css +10 -0
- package/src/client/nodes/AttachmentNode.tsx +8 -1
- package/src/client/nodes/AxStepControls.tsx +1 -1
- package/src/client/nodes/ContextNode.tsx +9 -9
- package/src/client/nodes/ExtAppFrame.tsx +13 -8
- package/src/client/nodes/FileNode.tsx +8 -5
- package/src/client/nodes/ImageNode.tsx +4 -2
- package/src/client/nodes/InlineMarkdownEditor.tsx +2 -1
- package/src/client/nodes/LedgerNode.tsx +2 -2
- package/src/client/nodes/MarkdownNode.tsx +44 -9
- package/src/client/nodes/McpAppNode.tsx +6 -4
- package/src/client/nodes/PromptNode.tsx +5 -17
- package/src/client/nodes/ResponseNode.tsx +3 -15
- package/src/client/nodes/StatusNode.tsx +2 -2
- package/src/client/nodes/StatusSummary.tsx +3 -1
- package/src/client/nodes/TraceNode.tsx +6 -4
- package/src/client/nodes/WebpageNode.tsx +1 -1
- package/src/client/nodes/ax-node-actions.ts +2 -2
- package/src/client/nodes/iframe-document-url.ts +3 -2
- package/src/client/nodes/markdown-sanitize.ts +10 -0
- package/src/client/nodes/surface-url.ts +10 -0
- package/src/client/nodes/use-surface-frame.ts +3 -1
- package/src/client/nodes/use-workbench-asset.ts +26 -0
- package/src/client/state/attention-bridge.ts +0 -8
- package/src/client/state/attention-store.ts +0 -17
- package/src/client/state/canvas-store.ts +4 -2
- package/src/client/state/context-status-store.ts +66 -0
- package/src/client/state/human-store.ts +2 -0
- package/src/client/state/iframe-mode.ts +5 -0
- package/src/client/state/intent-bridge.ts +10 -4
- package/src/client/state/near-pin-store.ts +53 -0
- package/src/client/state/presence-store.ts +5 -10
- package/src/client/state/session-store.ts +106 -9
- package/src/client/state/sse-bridge.ts +26 -7
- package/src/client/state/theme-override.ts +1 -1
- package/src/client/state/workbench-transport.ts +36 -0
- package/src/client/theme/global.css +723 -672
- package/src/client/theme/surface-theme.css +151 -46
- package/src/client/theme/tokens.ts +7 -11
- package/src/client/types.ts +10 -0
- package/src/json-render/server.ts +71 -14
- package/src/mcp/app-server.ts +64 -0
- package/src/mcp/canvas-access.ts +14 -11
- package/src/mcp/canvas-app.ts +318 -0
- package/src/mcp/server.ts +1 -1
- package/src/mcp/workbench-app.ts +211 -0
- package/src/server/agent-presence.ts +45 -6
- package/src/server/ax-context.ts +1 -1
- package/src/server/ax-state.ts +1 -1
- package/src/server/board-export.ts +5 -2
- package/src/server/canvas-db.ts +46 -12
- package/src/server/canvas-geometry.ts +64 -0
- package/src/server/canvas-operations.ts +6 -24
- package/src/server/canvas-state.ts +105 -15
- package/src/server/canvas-validation.ts +19 -4
- package/src/server/context-brief.ts +39 -2
- package/src/server/context-reads.ts +86 -15
- package/src/server/demo-state.json +31 -31
- package/src/server/html-surface.ts +4 -5
- package/src/server/index.ts +19 -3
- package/src/server/mcp-app-runtime.ts +11 -0
- package/src/server/operations/composites.ts +4 -3
- package/src/server/operations/http.ts +4 -4
- package/src/server/operations/invoker.ts +13 -4
- package/src/server/operations/ops/ax-read.ts +1 -1
- package/src/server/operations/ops/ax-timeline.ts +37 -0
- package/src/server/operations/ops/boards.ts +1 -1
- package/src/server/operations/ops/canvas-wire.ts +3 -6
- package/src/server/operations/ops/intent.ts +31 -2
- package/src/server/operations/ops/json-render.ts +1 -1
- package/src/server/operations/ops/nodes.ts +8 -76
- package/src/server/operations/ops/query.ts +8 -2
- package/src/server/operations/ops/snapshots.ts +4 -1
- package/src/server/operations/ops/viewport.ts +29 -5
- package/src/server/operations/registry.ts +38 -4
- package/src/server/scope-fence.ts +55 -10
- package/src/server/server.ts +83 -5
- package/src/server/spatial-analysis.ts +2 -2
- package/src/server/workboard.ts +6 -1
- package/src/{server → shared}/agent-context.ts +2 -2
- package/src/shared/agent-presence.ts +3 -15
- package/src/shared/canvas-app.ts +62 -0
- package/src/shared/themes.ts +5 -5
- package/src/client/canvas/FocusFieldLayer.tsx +0 -116
- package/src/client/canvas/kind-colors.ts +0 -23
|
@@ -20,7 +20,8 @@ At the start of board work, read `canvas://context?consumer=<stable-unique-id>`
|
|
|
20
20
|
(or `canvas_query { action: "context", consumer: "<stable-unique-id>" }`) before
|
|
21
21
|
falling back to broader resources. The budget is UTF-16 code units, not tokens.
|
|
22
22
|
Use one stable consumer ID per agent/adapter only when durable deltas are wanted;
|
|
23
|
-
omit it for a fresh read. Pins are highest priority
|
|
23
|
+
omit it for a fresh read. Pins are highest priority, then the cards near each pin
|
|
24
|
+
(reason `near`, title + short summary only). If a source card is clipped or near,
|
|
24
25
|
pull that card explicitly by its returned board/card IDs.
|
|
25
26
|
|
|
26
27
|
When authoring a board, establish its README without waiting for the human to
|
|
@@ -43,7 +44,7 @@ for the authoritative stop, viewport, persistence, reset, group-order, and captu
|
|
|
43
44
|
|
|
44
45
|
## Runtime prerequisites
|
|
45
46
|
|
|
46
|
-
PMX Canvas 0.
|
|
47
|
+
PMX Canvas 0.8.0 requires **Bun >=1.4.2**, including when installed through npm.
|
|
47
48
|
Check `bun --version` and ensure Bun is on the MCP host's PATH, not just your shell's;
|
|
48
49
|
use an absolute executable path if needed. Install/configure only when requested.
|
|
49
50
|
For pinned installation, MCP configuration, managed services, and disposable verification,
|
|
@@ -116,7 +117,8 @@ the served catalog is a startup snapshot. Local mirrors still use `skills sync`
|
|
|
116
117
|
`canvas_view { action: "fit", nodeIds: [...] }` with exactly the new ids. See
|
|
117
118
|
**In-View Placement & Sizing** below — auto-placement is board-relative, not camera-relative.
|
|
118
119
|
8. **Verify context pins.** Pin with `canvas_pin_nodes` or the browser's **Pin as context**, then
|
|
119
|
-
read `canvas://pinned-context`.
|
|
120
|
+
read `canvas://pinned-context`. When you pin, pass a short `reason` — the human sees who pinned a
|
|
121
|
+
node and why.
|
|
120
122
|
9. **Clean up temporary nodes.** Remove retry/test fixtures and restore the baseline snapshot when
|
|
121
123
|
the task requires leaving the board unchanged.
|
|
122
124
|
|
|
@@ -219,7 +221,7 @@ always authoritative, independent of the installed release version.
|
|
|
219
221
|
| `canvas_ax_state` | `get`, `set-focus`, `set-policy`, `report-capability`, `presence`, `set-presence` |
|
|
220
222
|
| `canvas_ax_work` | `add`, `update`, `annotate` |
|
|
221
223
|
| `canvas_ax_gate` | `request`, `resolve`, `await` with `approval`, `elicitation`, or `mode` |
|
|
222
|
-
| `canvas_ax_timeline` | `read`, `record-event`, `add-evidence`, `send-steering`, `reads` |
|
|
224
|
+
| `canvas_ax_timeline` | `read`, `record-event`, `add-evidence`, `send-steering`, `reads`, `read-status` |
|
|
223
225
|
| `canvas_ax_delivery` | `claim`, `mark` |
|
|
224
226
|
| `canvas_board` | `list`, `get`, `create`, `create-from`, `open`, `update` |
|
|
225
227
|
| `canvas_snapshot` | `save`, `list`, `restore`, `delete`, `gc`, `diff` |
|
|
@@ -303,8 +305,9 @@ reports any node below its floor as an advisory `sizeWarnings` entry — treat a
|
|
|
303
305
|
as layout work left to do.
|
|
304
306
|
Creation responses may include `sizeAdjustment` (`defaulted`, `clamped-to-minimum`, or
|
|
305
307
|
`fit-to-children`); use its `applied` geometry rather than assuming the request won. Json-render
|
|
306
|
-
validation/creation
|
|
307
|
-
|
|
308
|
+
validation/creation binds a literal form-control `value` to `state.form` automatically; it still
|
|
309
|
+
warns for `$state` expressions, controls inside a repeat, or a non-object `state.form`, which render
|
|
310
|
+
but do not update runtime state until bound with `$bindState`/`$bindItem`.
|
|
308
311
|
Bound JSON input and dynamic lists are local UI state, not saved canvas data. Focus-mode
|
|
309
312
|
expand/close preserves that state for the same spec in the current tab; an authored spec update,
|
|
310
313
|
board switch or page reload resets it. Persist important edits explicitly through the app's
|
|
@@ -456,7 +459,7 @@ Use the visible workbench when the human is actively curating layout:
|
|
|
456
459
|
- Embedding hosts can open `/workbench?theme=<name>` (or `?theme=auto` to follow the host's
|
|
457
460
|
light/dark appearance) for a session-local default theme that never changes the server-global
|
|
458
461
|
theme other clients see; an explicit pick from the theme menu ends the override. The bundled
|
|
459
|
-
Copilot extension opens its panel with `?theme=
|
|
462
|
+
Copilot extension opens its panel with `?theme=daylight`.
|
|
460
463
|
|
|
461
464
|
After changing files under `src/client/`, rebuild with `bun run build` before manual browser
|
|
462
465
|
verification.
|
|
@@ -128,11 +128,11 @@ pmx-canvas # Start and open browser (port 4313)
|
|
|
128
128
|
pmx-canvas --no-open # Start without opening browser (for agents)
|
|
129
129
|
pmx-canvas --port=8080 # Custom port
|
|
130
130
|
pmx-canvas --demo # Start with sample content
|
|
131
|
-
pmx-canvas --theme=
|
|
131
|
+
pmx-canvas --theme=daylight # Daylight theme
|
|
132
132
|
pmx-canvas --version # Print installed version and exit
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
`--theme` accepts `
|
|
135
|
+
`--theme` accepts `harbor` (default), `daylight`, `high-contrast`, `midnight`, `sepia`, `arctic`, `ember`, `forest`, or `volt` (0.4.x; the rail's theme picker lists the same nine). Same value can be set via
|
|
136
136
|
the `PMX_CANVAS_THEME` environment variable, or picked live from the left tool rail.
|
|
137
137
|
|
|
138
138
|
Start the canvas once per session, then reuse it. Use `--no-open` when running as an agent — the
|
|
@@ -452,7 +452,7 @@ single-purpose tools behind an `action` (and, for `canvas_ax_gate`, a `kind`) di
|
|
|
452
452
|
| `canvas_ax_state` | `get` · `set-focus` · `set-policy` · `report-capability` · `presence` · `set-presence` | Read AX state; set AX focus; patch tool/prompt policy (the scope fence is human-set, read-only here); report host capability; read / set agent presence (phase, cursor, focus, attached session) |
|
|
453
453
|
| `canvas_ax_work` | `add` · `update` · `annotate` | Canvas-bound work items + review annotations |
|
|
454
454
|
| `canvas_ax_gate` | `request` · `resolve` · `await` × `kind` `approval` \| `elicitation` \| `mode` | Attributed gate lifecycle (request → await → resolve); attribution does not itself grant authority |
|
|
455
|
-
| `canvas_ax_timeline` | `read` · `record-event` · `add-evidence` · `send-steering` · `reads` | The bounded AX diagnostics timeline, and the context read log (`reads`) |
|
|
455
|
+
| `canvas_ax_timeline` | `read` · `record-event` · `add-evidence` · `send-steering` · `reads` · `read-status` | The bounded AX diagnostics timeline, and the context read log (`reads`) |
|
|
456
456
|
| `canvas_ax_delivery` | `claim` · `mark` | Adapterless steering delivery (claim → act → mark) |
|
|
457
457
|
| `canvas_intent` | `signal` · `update` · `clear` | Ghost Cursor of Intent — announce a move before making it |
|
|
458
458
|
|
|
@@ -508,7 +508,7 @@ coming and can veto it mid-thought. Intents are ephemeral presence: never
|
|
|
508
508
|
persisted, auto-expiring (~8s), never in `canvas_query layout`.
|
|
509
509
|
|
|
510
510
|
Narrate → linked mutation → automatic settle:
|
|
511
|
-
1. `canvas_intent { action: "signal", kind: "create",
|
|
511
|
+
1. `canvas_intent { action: "signal", kind: "create", nodeType: "markdown", label: "Add evidence", reason: "capturing the failing test", confidence: 0.8 }` → returns `intent.id` and the `position` the canvas chose.
|
|
512
512
|
2. Make the real move and pass that id: `canvas_node { action: "add", intentId: <intent.id>, ... }`.
|
|
513
513
|
3. PMX rejects the mutation if the intent was vetoed or expired; otherwise the
|
|
514
514
|
successful mutation settles the ghost into the resulting node automatically.
|
|
@@ -522,9 +522,13 @@ mcp-app) and `canvas_webview` do **not** accept an `intentId` and reject it with
|
|
|
522
522
|
400 — to telegraph one of those, signal a ghost, then `clear` it (or let it
|
|
523
523
|
expire) and run the open *without* an `intentId`.
|
|
524
524
|
|
|
525
|
-
Per kind, pass the anchor it renders against: `position` for `
|
|
526
|
-
`nodeId` for `move`/`edit`/`remove`, `edge: { from, to, type }` for
|
|
527
|
-
|
|
525
|
+
Per kind, pass the anchor it renders against: `position` for `move` (optional for
|
|
526
|
+
`create`), `nodeId` for `move`/`edit`/`remove`, `edge: { from, to, type }` for
|
|
527
|
+
`connect`. **Omit `position` on a create unless you know where the node goes:** the
|
|
528
|
+
canvas then forms the ghost where it will auto-place the node, keeps staged ghosts
|
|
529
|
+
from stacking, and lands the linked create on its ghost. Never pass a placeholder
|
|
530
|
+
such as `{ x: 0, y: 0 }`. The payoff is **legibility** — `reason` is shown beneath
|
|
531
|
+
the ghost.
|
|
528
532
|
|
|
529
533
|
**When to use vs skip.** Signal for adds, removes, and moves of visible nodes;
|
|
530
534
|
connecting nodes; creating groups; layout reorganizations; meaningful title/content
|
|
@@ -4,6 +4,10 @@ Use this reference when PMX Canvas is running inside the GitHub Copilot app as a
|
|
|
4
4
|
extension. The adapter is intentionally thin: PMX Canvas remains the state owner, and the extension
|
|
5
5
|
maps Copilot SDK features onto PMX AX primitives.
|
|
6
6
|
|
|
7
|
+
The [standalone agent plugin](../../../docs/agent-plugin.md) bundles this adapter and PMX's
|
|
8
|
+
MCP launcher. Its native canvas is Copilot-only; other Agent Plugins-compatible clients use
|
|
9
|
+
the portable MCP tools and browser workbench.
|
|
10
|
+
|
|
7
11
|
## Adapter Identity
|
|
8
12
|
|
|
9
13
|
- Extension path: `.github/extensions/pmx-canvas/extension.mjs`
|
|
@@ -17,9 +21,9 @@ maps Copilot SDK features onto PMX AX primitives.
|
|
|
17
21
|
packaged extension directory into the repository:
|
|
18
22
|
`mkdir -p .github/extensions && cp -R node_modules/pmx-canvas/.github/extensions/pmx-canvas .github/extensions/`
|
|
19
23
|
2. Reload Copilot app extensions with `extensions_reload` so `project:pmx-canvas` is registered.
|
|
20
|
-
3.
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
3. The adapter discovers a matching workspace daemon or starts the pinned package version
|
|
25
|
+
in `runtime.json` with Bun. It needs Bun on `PATH`, not a global `pmx-canvas` executable.
|
|
26
|
+
The shared daemon stays running when the extension exits.
|
|
23
27
|
4. Open the canvas with `extensionId: "project:pmx-canvas"`, `canvasId: "pmx-canvas"`, and a stable
|
|
24
28
|
`instanceId`.
|
|
25
29
|
5. If the first `invoke_canvas_action` immediately after `open_canvas` returns
|
|
@@ -66,6 +70,8 @@ end it after each task. Explicit agent IDs are reserved for genuinely independen
|
|
|
66
70
|
The Copilot app webview can leave nested `srcdoc` and `blob:` iframes blank, so PMX should route
|
|
67
71
|
generated frame HTML through `/api/canvas/frame-documents/...` instead.
|
|
68
72
|
- Connects to a matching local PMX server for the current workspace, or starts one when needed.
|
|
73
|
+
- Uses the session's working directory for startup, context, presence, and steering; Copilot's
|
|
74
|
+
`workspacePath` points to session artifacts and is not the project root.
|
|
69
75
|
- Reads `/api/canvas/ax/context` and injects pinned/focused context from
|
|
70
76
|
`onUserPromptSubmitted`.
|
|
71
77
|
- Exposes adapter actions for status, AX context refresh, AX focus, and explicit session steering.
|
|
@@ -137,16 +143,22 @@ All fields are optional:
|
|
|
137
143
|
}
|
|
138
144
|
```
|
|
139
145
|
|
|
140
|
-
Default discovery
|
|
146
|
+
Default discovery:
|
|
141
147
|
|
|
142
|
-
1. `serverUrl` input.
|
|
143
|
-
2. `
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
1. An explicit `serverUrl` input or `PMX_CANVAS_URL`, without silently choosing another target.
|
|
149
|
+
2. Workspace daemon pid-file ports, the preferred `PMX_CANVAS_PORT` / `PMX_WEB_CANVAS_PORT`,
|
|
150
|
+
and the normal MCP fallback ports, verified against the workspace's real path.
|
|
151
|
+
3. Serialized daemon startup for the session working directory when `autoStart` is not `false`.
|
|
146
152
|
|
|
147
153
|
The adapter rejects an unrelated running PMX server unless `allowWorkspaceMismatch` is true,
|
|
148
154
|
including when `serverUrl` is explicit.
|
|
149
155
|
|
|
156
|
+
To package this adapter in another plugin, copy `extension.mjs`, `steering-delivery.mjs`,
|
|
157
|
+
`runtime.mjs`, and `runtime.json`. For the portable MCP connector, also copy `start-mcp.mjs`,
|
|
158
|
+
`tools.json`, and `THIRD_PARTY_LICENSES.txt`, and invoke `start-mcp.mjs` with Bun. The agent calls
|
|
159
|
+
`canvas_connect_workspace` with its absolute project directory before using board tools;
|
|
160
|
+
plugin initialization must never adopt the installation directory as a project.
|
|
161
|
+
|
|
150
162
|
If discovery fails, the panel shows a connection page, not a partial workbench. It checks the
|
|
151
163
|
original target automatically; **Check connection** retries immediately and **Start server**
|
|
152
164
|
explicitly allows local startup even if `autoStart` was disabled. After recovery it navigates
|
|
@@ -4,7 +4,7 @@ Use this reference when the `pmx-canvas` skill is installed but the `pmx-canvas`
|
|
|
4
4
|
|
|
5
5
|
## Prerequisites and version
|
|
6
6
|
|
|
7
|
-
The examples pin **PMX Canvas 0.
|
|
7
|
+
The examples pin **PMX Canvas 0.8.0**, which requires **Bun >=1.4.2** even when
|
|
8
8
|
installed through npm: the CLI runs through Bun, not Node. Check `bun --version`
|
|
9
9
|
first. If missing or older, follow the [Bun installation instructions](https://bun.sh/docs/installation)
|
|
10
10
|
with the user's authorization. Ensure Bun is on the MCP host's PATH as well as the
|
|
@@ -17,11 +17,11 @@ in `SKILL.md`. Do not pair newer checkout guidance with an older runtime unknowi
|
|
|
17
17
|
## Install from npm
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install -g pmx-canvas@0.
|
|
20
|
+
npm install -g pmx-canvas@0.8.0
|
|
21
21
|
pmx-canvas --version
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Without a global install, use `bunx pmx-canvas@0.
|
|
24
|
+
Without a global install, use `bunx pmx-canvas@0.8.0` in place of `pmx-canvas` in
|
|
25
25
|
the commands below. Do not use unpinned `bunx pmx-canvas` for a version-specific check.
|
|
26
26
|
|
|
27
27
|
## Choose the workspace and target
|
|
@@ -91,7 +91,7 @@ discards empty env values, remove `PMX_CANVAS_URL` from its launch environment i
|
|
|
91
91
|
"mcpServers": {
|
|
92
92
|
"canvas": {
|
|
93
93
|
"command": "bunx",
|
|
94
|
-
"args": ["pmx-canvas@0.
|
|
94
|
+
"args": ["pmx-canvas@0.8.0", "--mcp"],
|
|
95
95
|
"env": {
|
|
96
96
|
"PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
|
|
97
97
|
"PMX_CANVAS_PORT": "14313",
|
|
@@ -135,7 +135,7 @@ Amp executes, not only on the user's laptop. Merge this into that workspace's
|
|
|
135
135
|
"amp.mcpServers": {
|
|
136
136
|
"canvas": {
|
|
137
137
|
"command": "bunx",
|
|
138
|
-
"args": ["pmx-canvas@0.
|
|
138
|
+
"args": ["pmx-canvas@0.8.0", "--mcp"],
|
|
139
139
|
"includeTools": ["canvas_*"],
|
|
140
140
|
"env": {
|
|
141
141
|
"PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
|
|
@@ -169,7 +169,7 @@ The agent must not run this approval itself or move the command to global settin
|
|
|
169
169
|
`--mcp-config` to bypass workspace trust. Approval authorizes execution of this MCP
|
|
170
170
|
command; it is not a new Canvas import consent or permission to mutate arbitrary boards.
|
|
171
171
|
After approval, reload MCP connections (`reload_mcp` in Amp) and require `connected`.
|
|
172
|
-
Discover `canvas` tools with `tool_search`; for the full unfiltered 0.
|
|
172
|
+
Discover `canvas` tools with `tool_search`; for the full unfiltered 0.8.0 server there
|
|
173
173
|
are 24 tools. If still absent, inspect the server status, command/PATH and target instead
|
|
174
174
|
of reinstalling blindly. See [Amp MCP documentation](https://ampcode.com/docs/customize/mcp).
|
|
175
175
|
|
|
@@ -68,10 +68,11 @@ and check `$?`, then read the file.
|
|
|
68
68
|
|
|
69
69
|
- In the product source checkout, Bun tests live under `tests/unit/`.
|
|
70
70
|
- In the product source checkout, Playwright browser smoke lives under `tests/e2e/`.
|
|
71
|
-
- Playwright runs
|
|
72
|
-
`tests/e2e/reference-pane.pw.ts
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
- Playwright runs `desktop` (1440×900, the whole suite except the reference cases) and three
|
|
72
|
+
reference projects for `tests/e2e/reference-pane.pw.ts`: `narrow-600`, `middle-1024` and
|
|
73
|
+
`wide-1920`. The workbench lives anywhere from a narrow side pane to a full window, so every
|
|
74
|
+
node type must paint at all three. A new node type fails its coverage test until it gets a
|
|
75
|
+
reference case. Run one width alone with `bash scripts/run-playwright.sh --project=narrow-600`.
|
|
75
76
|
- Product CI runs those checkout suites: Bun coverage plus the browser smoke flow.
|
|
76
77
|
- An isolated installed consumer instead tests the package's public transports and host behavior;
|
|
77
78
|
it does not run or depend on the product checkout's source suites.
|
|
@@ -20,6 +20,11 @@ cmd(
|
|
|
20
20
|
const sourcePaths = [
|
|
21
21
|
fileURLToPath(new URL('../../../.github/extensions/pmx-canvas/extension.mjs', import.meta.url)),
|
|
22
22
|
fileURLToPath(new URL('../../../.github/extensions/pmx-canvas/steering-delivery.mjs', import.meta.url)),
|
|
23
|
+
fileURLToPath(new URL('../../../.github/extensions/pmx-canvas/runtime.mjs', import.meta.url)),
|
|
24
|
+
fileURLToPath(new URL('../../../.github/extensions/pmx-canvas/runtime.json', import.meta.url)),
|
|
25
|
+
fileURLToPath(new URL('../../../.github/extensions/pmx-canvas/start-mcp.mjs', import.meta.url)),
|
|
26
|
+
fileURLToPath(new URL('../../../.github/extensions/pmx-canvas/tools.json', import.meta.url)),
|
|
27
|
+
fileURLToPath(new URL('../../../.github/extensions/pmx-canvas/THIRD_PARTY_LICENSES.txt', import.meta.url)),
|
|
23
28
|
];
|
|
24
29
|
const missingSource = sourcePaths.find((sourcePath) => !existsSync(sourcePath));
|
|
25
30
|
if (missingSource) {
|
package/src/cli/commands/view.ts
CHANGED
|
@@ -179,7 +179,7 @@ cmd(
|
|
|
179
179
|
[
|
|
180
180
|
'pmx-canvas serve',
|
|
181
181
|
'pmx-canvas serve --port=8080 --no-open',
|
|
182
|
-
'pmx-canvas serve --demo --theme=
|
|
182
|
+
'pmx-canvas serve --demo --theme=daylight',
|
|
183
183
|
'pmx-canvas --no-open --webview-automation',
|
|
184
184
|
],
|
|
185
185
|
async (_args) => {
|
package/src/cli/index.ts
CHANGED
|
@@ -149,9 +149,10 @@ function commandAfterGlobalTargetFlags(argv: string[]): string {
|
|
|
149
149
|
return '';
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function runMcpServerProcess(): Promise<void> {
|
|
152
|
+
function runMcpServerProcess(app = false): Promise<void> {
|
|
153
153
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
154
|
-
const
|
|
154
|
+
const entry = app ? resolve(cliDir, '..', 'mcp', 'app-server.ts') : mcpServerEntry;
|
|
155
|
+
const child = spawn(process.execPath, ['run', entry], {
|
|
155
156
|
stdio: 'inherit',
|
|
156
157
|
env: process.env,
|
|
157
158
|
});
|
|
@@ -222,6 +223,9 @@ if (firstArg === 'serve') {
|
|
|
222
223
|
const routedCommand = commandAfterGlobalTargetFlags(args);
|
|
223
224
|
if (AGENT_COMMANDS.has(routedCommand) && routedCommand !== 'serve') {
|
|
224
225
|
await runAgentCli(args);
|
|
226
|
+
} else if (args.includes('--mcp-app')) {
|
|
227
|
+
// Private MCP Apps profile: focused tools and a self-contained UI resource.
|
|
228
|
+
await runMcpServerProcess(true);
|
|
225
229
|
} else if (args.includes('--mcp')) {
|
|
226
230
|
// MCP server mode: stdio transport, auto-starts canvas on first tool call
|
|
227
231
|
await runMcpServerProcess();
|
|
@@ -274,7 +278,7 @@ Server options:
|
|
|
274
278
|
--log-file=PATH Daemon log file (default: ./.pmx-canvas/daemon-${port}.log)
|
|
275
279
|
--pid-file=PATH Optional daemon PID file (default: ./.pmx-canvas/daemon-${port}.pid)
|
|
276
280
|
--wait-ms=MS Health-check wait budget for daemon mode (default: 10000)
|
|
277
|
-
--theme=THEME Theme:
|
|
281
|
+
--theme=THEME Theme: harbor (default), daylight, high-contrast, midnight, sepia, arctic, ember, forest, volt
|
|
278
282
|
--webview-automation Start a headless Bun.WebView automation session for /workbench
|
|
279
283
|
--webview-backend=BACKEND Bun.WebView backend: chrome or webkit
|
|
280
284
|
--webview-width=PX Automation WebView width (default: 1280)
|
|
@@ -283,6 +287,7 @@ Server options:
|
|
|
283
287
|
--webview-chrome-argv=CSV Extra Chrome args for Bun.WebView, comma-separated
|
|
284
288
|
--webview-data-dir=PATH Persist automation browser storage in PATH
|
|
285
289
|
--mcp Run as MCP server (stdio transport)
|
|
290
|
+
--mcp-app Run the private MCP App profile (stdio; shared active board)
|
|
286
291
|
--help, -h Show this help
|
|
287
292
|
|
|
288
293
|
Agent CLI (works against running server):
|
package/src/client/App.tsx
CHANGED
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
import { connectSSE } from './state/sse-bridge';
|
|
55
55
|
import { intents } from './state/intent-store';
|
|
56
56
|
import { sessionActive } from './state/presence-store';
|
|
57
|
+
import { isHostedWorkbench } from './state/workbench-transport';
|
|
57
58
|
import {
|
|
58
59
|
createGroupFromClient,
|
|
59
60
|
removeEdgeFromClient,
|
|
@@ -376,7 +377,11 @@ export function App() {
|
|
|
376
377
|
intents.value.size === 0 && <EmptyState onOpenPalette={() => setPaletteOpen(true)} />
|
|
377
378
|
)}
|
|
378
379
|
{selectedNodeIds.value.size > 0 && <SelectionBar />}
|
|
379
|
-
{sessionIsActive
|
|
380
|
+
{sessionIsActive && !isHostedWorkbench() ? (
|
|
381
|
+
<CommandBar />
|
|
382
|
+
) : (
|
|
383
|
+
contextPinnedNodeIds.value.size > 0 && <ContextPinBar />
|
|
384
|
+
)}
|
|
380
385
|
<SessionReceipt onOpenSnapshots={() => setSnapshotOpen(true)} />
|
|
381
386
|
<TextPrompt />
|
|
382
387
|
<ExportDialog />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRef } from 'preact/hooks';
|
|
2
|
-
import {
|
|
2
|
+
import { agentPhaseLabel, type AgentPresence } from '../../shared/agent-presence.js';
|
|
3
3
|
import { nodes, viewport } from '../state/canvas-store';
|
|
4
|
-
import { agentPresences, presenceWorldPosition } from '../state/presence-store';
|
|
4
|
+
import { agentPresences, presenceWorldPosition, writerColor } from '../state/presence-store';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Agent presence layer (rail-chrome-v2 phase 3): one cursor + phase chip per
|
|
@@ -49,7 +49,7 @@ export function AgentPresenceLayer() {
|
|
|
49
49
|
data-session-id={presence.sessionId}
|
|
50
50
|
style={{
|
|
51
51
|
transform: `translate(${position.x}px, ${position.y}px)`,
|
|
52
|
-
'--identity-color':
|
|
52
|
+
'--identity-color': writerColor(presence.sessionId),
|
|
53
53
|
}}
|
|
54
54
|
>
|
|
55
55
|
<div class="agent-cursor-inner" style={{ transform: `scale(${counterScale})` }}>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
|
2
2
|
import { mutatingNodeIds, sessionActive } from '../state/presence-store';
|
|
3
|
-
import {
|
|
3
|
+
import { attentionPulseNodeIds } from '../state/attention-store';
|
|
4
|
+
import { isAgentPin, NearPinMark, NodeContextMark } from './NodeContextMark';
|
|
5
|
+
import { activityLensNodeIds } from '../state/session-store';
|
|
4
6
|
import {
|
|
5
7
|
activeNodeId,
|
|
6
8
|
activeNeighborNodeIds,
|
|
@@ -34,20 +36,9 @@ import {
|
|
|
34
36
|
ungroupFromClient,
|
|
35
37
|
updateNodeFromClient,
|
|
36
38
|
} from '../state/intent-bridge';
|
|
37
|
-
import { KIND_COLOR } from './kind-colors';
|
|
38
39
|
import { reportHumanGrab, takeOverNode, yieldedNodes } from '../state/human-store';
|
|
39
40
|
import { AxStepControls } from '../nodes/AxStepControls';
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
getNodeIcon,
|
|
43
|
-
IconChevronRight,
|
|
44
|
-
IconChevronDown,
|
|
45
|
-
IconClose,
|
|
46
|
-
IconMore,
|
|
47
|
-
IconExternalLink,
|
|
48
|
-
IconExpand,
|
|
49
|
-
IconSparkle,
|
|
50
|
-
} from '../icons';
|
|
41
|
+
import { getNodeIcon, IconChevronRight, IconClose, IconMore, IconExpand, IconPin } from '../icons';
|
|
51
42
|
import { EXPANDABLE_TYPES, TYPE_LABELS } from '../types';
|
|
52
43
|
import type { CanvasNodeState } from '../types';
|
|
53
44
|
import { AUTO_FIT_TITLEBAR_HEIGHT, computeAutoFitHeight, shouldAutoFitNode } from './auto-fit';
|
|
@@ -137,8 +128,6 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
137
128
|
const isActive = activeNodeId.value === node.id;
|
|
138
129
|
const isSelected = selectedNodeIds.value.has(node.id);
|
|
139
130
|
const isContextPinned = contextPinnedNodeIds.value.has(node.id);
|
|
140
|
-
const isAttentionPrimary = attentionPrimaryNodeIds.value.has(node.id);
|
|
141
|
-
const isAttentionSecondary = !isAttentionPrimary && attentionSecondaryNodeIds.value.has(node.id);
|
|
142
131
|
const isAttentionPulse = attentionPulseNodeIds.value.has(node.id);
|
|
143
132
|
const isNeighbor = !isActive && activeNeighborNodeIds.value.has(node.id);
|
|
144
133
|
const searchSet = searchHighlightIds.value;
|
|
@@ -428,8 +417,6 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
428
417
|
isSearchDimmed ? 'search-dimmed' : '',
|
|
429
418
|
isSelected ? 'selected' : '',
|
|
430
419
|
isContextPinned ? 'context-pinned' : '',
|
|
431
|
-
isAttentionPrimary ? 'attention-focus-primary' : '',
|
|
432
|
-
isAttentionSecondary ? 'attention-focus-secondary' : '',
|
|
433
420
|
isAttentionPulse ? 'attention-pulse' : '',
|
|
434
421
|
isPinned ? 'pinned' : '',
|
|
435
422
|
isTrace ? 'trace-node' : '',
|
|
@@ -437,6 +424,7 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
437
424
|
isGroup ? 'group-node' : '',
|
|
438
425
|
isStrictSize ? 'strict-size' : '',
|
|
439
426
|
isAgentMutating ? 'agent-mutating' : '',
|
|
427
|
+
activityLensNodeIds.value && !activityLensNodeIds.value.has(node.id) ? 'lens-dimmed' : '',
|
|
440
428
|
]
|
|
441
429
|
.filter(Boolean)
|
|
442
430
|
.join(' ');
|
|
@@ -488,7 +476,7 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
488
476
|
<div class="group-chip-name">{title}</div>
|
|
489
477
|
<div class="group-chip-meta">
|
|
490
478
|
{groupKindDots.map((kind) => (
|
|
491
|
-
<span key={kind} class="group-kind-dot"
|
|
479
|
+
<span key={kind} class="group-kind-dot" aria-hidden="true" />
|
|
492
480
|
))}
|
|
493
481
|
<span class="group-chip-count">
|
|
494
482
|
{groupChildren.length} node{groupChildren.length === 1 ? '' : 's'}
|
|
@@ -679,40 +667,16 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
679
667
|
{title}
|
|
680
668
|
</span>
|
|
681
669
|
)}
|
|
670
|
+
<NearPinMark node={node} pinned={isContextPinned} />
|
|
671
|
+
<NodeContextMark node={node} pinned={isContextPinned} />
|
|
682
672
|
<div class="node-controls">
|
|
683
673
|
{isPinned && (
|
|
684
674
|
<span class="pin-indicator" title="Pinned">
|
|
685
675
|
⊙
|
|
686
676
|
</span>
|
|
687
677
|
)}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
class={`ctx-pin-btn${isContextPinned ? ' ctx-pin-active' : ''}`}
|
|
691
|
-
onClick={(e) => {
|
|
692
|
-
e.stopPropagation();
|
|
693
|
-
toggleContextPin(node.id);
|
|
694
|
-
}}
|
|
695
|
-
title={isContextPinned ? 'Remove from context' : 'Add to context'}
|
|
696
|
-
>
|
|
697
|
-
<IconSparkle size={14} />
|
|
698
|
-
</button>
|
|
699
|
-
{/* Open as site — full-page standalone view of this node's surface,
|
|
700
|
-
served from /api/canvas/surface/:id (same document as the canvas
|
|
701
|
-
iframe). Opens via the system browser so embedded hosts do not trap
|
|
702
|
-
it in their own webview. */}
|
|
703
|
-
{canOpenAsSite(node) && (
|
|
704
|
-
<button
|
|
705
|
-
type="button"
|
|
706
|
-
onClick={(e) => {
|
|
707
|
-
e.stopPropagation();
|
|
708
|
-
void openNodeAsSite(node);
|
|
709
|
-
}}
|
|
710
|
-
title="Open as site"
|
|
711
|
-
>
|
|
712
|
-
<IconExternalLink size={14} />
|
|
713
|
-
</button>
|
|
714
|
-
)}
|
|
715
|
-
{/* Expand — opens node as full-viewport overlay for focused work */}
|
|
678
|
+
{/* Expand and the context pin always show; the rest sit behind ⋯ and ×
|
|
679
|
+
on hover (docs/design/Chrome.dc.html). */}
|
|
716
680
|
{EXPANDABLE_TYPES.has(node.type) && (
|
|
717
681
|
<button
|
|
718
682
|
type="button"
|
|
@@ -727,18 +691,37 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
727
691
|
)}
|
|
728
692
|
<button
|
|
729
693
|
type="button"
|
|
694
|
+
class={`ctx-pin-btn${isContextPinned ? ' ctx-pin-active' : ''}`}
|
|
730
695
|
onClick={(e) => {
|
|
731
696
|
e.stopPropagation();
|
|
732
|
-
|
|
697
|
+
toggleContextPin(node.id);
|
|
698
|
+
}}
|
|
699
|
+
title={isContextPinned ? 'Remove from context' : 'Add to context'}
|
|
700
|
+
aria-label={isContextPinned ? 'Remove from context' : 'Add to context'}
|
|
701
|
+
aria-pressed={isContextPinned}
|
|
702
|
+
>
|
|
703
|
+
<span class="ctx-pin-mark">
|
|
704
|
+
<IconPin />
|
|
705
|
+
{isContextPinned && isAgentPin(node.id) && <span class="ctx-pin-agent-dot" aria-hidden="true" />}
|
|
706
|
+
</span>
|
|
707
|
+
</button>
|
|
708
|
+
<button
|
|
709
|
+
type="button"
|
|
710
|
+
class="node-hover-control"
|
|
711
|
+
onClick={(e) => {
|
|
712
|
+
e.stopPropagation();
|
|
713
|
+
onContextMenu?.(e, node.id);
|
|
733
714
|
}}
|
|
734
|
-
title=
|
|
715
|
+
title="More actions"
|
|
716
|
+
aria-label="More actions"
|
|
735
717
|
>
|
|
736
|
-
|
|
718
|
+
<IconMore size={14} />
|
|
737
719
|
</button>
|
|
738
720
|
{/* Report #64: status nodes get the same remove control as every other
|
|
739
721
|
node type (backend removal + undo/history handle status uniformly). */}
|
|
740
722
|
<button
|
|
741
723
|
type="button"
|
|
724
|
+
class="node-hover-control"
|
|
742
725
|
onClick={(e) => {
|
|
743
726
|
e.stopPropagation();
|
|
744
727
|
void removeNodeFromClient(node.id);
|
|
@@ -38,7 +38,6 @@ import {
|
|
|
38
38
|
} from '../state/canvas-store';
|
|
39
39
|
import { createEdgeFromClient, createNodeFromClient } from '../state/intent-bridge';
|
|
40
40
|
import type { AnnotationTool, CanvasAnnotation, CanvasNodeState } from '../types';
|
|
41
|
-
import { FocusFieldLayer } from './FocusFieldLayer';
|
|
42
41
|
import { importFiles } from './import-files';
|
|
43
42
|
import { IntentLayer } from './IntentLayer';
|
|
44
43
|
import { AgentPresenceLayer } from './AgentPresenceLayer';
|
|
@@ -855,7 +854,6 @@ export function CanvasViewport({
|
|
|
855
854
|
left: 0,
|
|
856
855
|
}}
|
|
857
856
|
>
|
|
858
|
-
<FocusFieldLayer />
|
|
859
857
|
<ScopeFenceLayer />
|
|
860
858
|
<IntentLayer />
|
|
861
859
|
<AgentPresenceLayer />
|
|
@@ -16,6 +16,7 @@ import { startSession } from '../state/session-store';
|
|
|
16
16
|
import { TYPE_LABELS, type CanvasNodeState } from '../types';
|
|
17
17
|
import { invalidateTokenCache } from '../theme/tokens';
|
|
18
18
|
import { clearThemeOverride } from '../state/theme-override';
|
|
19
|
+
import { canvasThemeScheme } from '../../shared/themes.js';
|
|
19
20
|
import { getNodeIcon, IconArrange, IconFitAll, IconMinimap, IconMoon, IconNodeMarkdown, IconSteer } from '../icons';
|
|
20
21
|
import { activeBoardId, openBoard } from '../state/boards-store';
|
|
21
22
|
import { requestJson } from '../state/intent-bridge';
|
|
@@ -269,11 +270,11 @@ export function CommandPalette({ onClose, onToggleMinimap }: { onClose: () => vo
|
|
|
269
270
|
},
|
|
270
271
|
},
|
|
271
272
|
{
|
|
272
|
-
label: 'Toggle theme (
|
|
273
|
+
label: 'Toggle theme (Harbor/Daylight)',
|
|
273
274
|
icon: IconMoon,
|
|
274
275
|
iconTone: 'muted',
|
|
275
276
|
action: () => {
|
|
276
|
-
const next = canvasTheme.value === 'dark' ? '
|
|
277
|
+
const next = canvasThemeScheme(canvasTheme.value) === 'dark' ? 'daylight' : 'harbor';
|
|
277
278
|
// An explicit pick ends any ?theme= session override, same as the
|
|
278
279
|
// toolbar picker — otherwise the session stays sticky-overridden.
|
|
279
280
|
clearThemeOverride();
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
updateEdgeFromClient,
|
|
27
27
|
updateNodeFromClient,
|
|
28
28
|
} from '../state/intent-bridge';
|
|
29
|
+
import { canOpenAsSite, openNodeAsSite } from '../nodes/surface-url';
|
|
29
30
|
import { askText } from './TextPrompt';
|
|
30
31
|
import { EXPANDABLE_TYPES } from '../types';
|
|
31
32
|
import type { CanvasNodeState } from '../types';
|
|
@@ -498,6 +499,16 @@ function buildNodeMenuItems(node: CanvasNodeState): MenuItem[] {
|
|
|
498
499
|
action: () => toggleCollapsed(node.id),
|
|
499
500
|
});
|
|
500
501
|
|
|
502
|
+
// Open as site — full-page standalone view of this node's surface, served
|
|
503
|
+
// from /api/canvas/surface/:id (same document as the canvas iframe). Opens
|
|
504
|
+
// via the system browser so embedded hosts do not trap it in their webview.
|
|
505
|
+
if (canOpenAsSite(node)) {
|
|
506
|
+
items.push({
|
|
507
|
+
label: 'Open in new tab',
|
|
508
|
+
action: () => void openNodeAsSite(node),
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
501
512
|
// Context pin — add/remove from the human-curated agent context (report #63).
|
|
502
513
|
// This is the PRIMARY "pin" in PMX's model ("pin nodes to curate context"); it
|
|
503
514
|
// matches the SelectionBar's "Pin as context" and updates the context count + the
|
|
@@ -12,7 +12,7 @@ import type { CanvasEdge, CanvasNodeState } from '../types';
|
|
|
12
12
|
// ── Edge type visual styles ──────────────────────────────────
|
|
13
13
|
const EDGE_COLORS: Record<CanvasEdge['type'], string> = {
|
|
14
14
|
relation: 'var(--c-muted)',
|
|
15
|
-
'depends-on': 'var(--c-
|
|
15
|
+
'depends-on': 'var(--c-rel-informs)',
|
|
16
16
|
flow: 'var(--c-accent)',
|
|
17
17
|
// --c-dim is a very low-contrast hairline in dark palettes (forest: #5D7566
|
|
18
18
|
// on #0C1712); the dashed style is what distinguishes `references`.
|