pmx-canvas 0.1.29 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +219 -0
  2. package/Readme.md +20 -10
  3. package/dist/canvas/global.css +51 -56
  4. package/dist/canvas/index.js +80 -163
  5. package/dist/canvas/surface-theme.css +142 -0
  6. package/dist/json-render/index.js +103 -103
  7. package/dist/types/client/nodes/HtmlNode.d.ts +0 -7
  8. package/dist/types/client/nodes/ax-node-actions.d.ts +18 -0
  9. package/dist/types/client/nodes/surface-url.d.ts +22 -0
  10. package/dist/types/client/state/attention-bridge.d.ts +3 -0
  11. package/dist/types/client/state/intent-bridge.d.ts +17 -0
  12. package/dist/types/json-render/renderer/index.d.ts +2 -0
  13. package/dist/types/json-render/schema.d.ts +2 -0
  14. package/dist/types/json-render/server.d.ts +2 -0
  15. package/dist/types/mcp/canvas-access.d.ts +47 -0
  16. package/dist/types/server/ax-interaction.d.ts +210 -0
  17. package/dist/types/server/ax-state.d.ts +67 -1
  18. package/dist/types/server/canvas-db.d.ts +4 -0
  19. package/dist/types/server/canvas-serialization.d.ts +2 -0
  20. package/dist/types/server/canvas-state.d.ts +54 -2
  21. package/dist/types/server/html-surface.d.ts +46 -0
  22. package/dist/types/server/index.d.ts +63 -5
  23. package/dist/types/server/mutation-history.d.ts +1 -1
  24. package/dist/types/server/placement.d.ts +1 -1
  25. package/dist/types/server/server.d.ts +12 -0
  26. package/dist/types/shared/surface.d.ts +19 -0
  27. package/docs/cli.md +30 -0
  28. package/docs/http-api.md +55 -0
  29. package/docs/mcp.md +40 -2
  30. package/docs/node-types.md +26 -0
  31. package/docs/plans/plan-004-pmx-ax-primitives.md +623 -394
  32. package/docs/sdk.md +23 -1
  33. package/package.json +2 -2
  34. package/skills/pmx-canvas/SKILL.md +107 -9
  35. package/src/cli/agent.ts +177 -0
  36. package/src/cli/index.ts +8 -1
  37. package/src/client/canvas/CanvasNode.tsx +8 -4
  38. package/src/client/canvas/DockedNode.tsx +38 -38
  39. package/src/client/canvas/ExpandedNodeOverlay.tsx +12 -0
  40. package/src/client/nodes/ContextNode.tsx +17 -0
  41. package/src/client/nodes/ExtAppFrame.tsx +40 -3
  42. package/src/client/nodes/FileNode.tsx +26 -0
  43. package/src/client/nodes/HtmlNode.tsx +60 -188
  44. package/src/client/nodes/McpAppNode.tsx +47 -2
  45. package/src/client/nodes/StatusNode.tsx +20 -0
  46. package/src/client/nodes/ax-node-actions.ts +39 -0
  47. package/src/client/nodes/surface-url.ts +48 -0
  48. package/src/client/state/attention-bridge.ts +5 -0
  49. package/src/client/state/intent-bridge.ts +33 -0
  50. package/src/client/theme/global.css +51 -56
  51. package/src/client/theme/surface-theme.css +142 -0
  52. package/src/json-render/renderer/index.tsx +31 -0
  53. package/src/json-render/schema.ts +4 -0
  54. package/src/json-render/server.ts +13 -0
  55. package/src/mcp/canvas-access.ts +198 -1
  56. package/src/mcp/server.ts +232 -2
  57. package/src/server/ax-context.ts +3 -0
  58. package/src/server/ax-interaction.ts +549 -0
  59. package/src/server/ax-state.ts +188 -2
  60. package/src/server/canvas-db.ts +20 -0
  61. package/src/server/canvas-operations.ts +11 -0
  62. package/src/server/canvas-serialization.ts +9 -0
  63. package/src/server/canvas-state.ts +201 -26
  64. package/src/server/html-surface.ts +190 -0
  65. package/src/server/index.ts +122 -7
  66. package/src/server/mutation-history.ts +5 -0
  67. package/src/server/placement.ts +5 -1
  68. package/src/server/server.ts +360 -0
  69. package/src/shared/surface.ts +38 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,223 @@
3
3
  All notable changes to `pmx-canvas` are documented here. This project follows
4
4
  [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.1.31] - 2026-06-07
7
+
8
+ ### Added
9
+
10
+ - **Default docked status + context widgets.** A freshly opened canvas now shows
11
+ the agent's status widget docked at the left of the top menu and the context
12
+ widget docked at the right — flanking the toolbar as one continuous bar at the
13
+ same height. They are the same `status-main` / `context-main` nodes the
14
+ agent-event path already creates, just present from the start. Each collapsed
15
+ widget has an expand (▸) and an undock (⊙) control; undocking returns it to the
16
+ canvas as a normal node. Seeded **once on first run** (brand-new workspace
17
+ only) — never added to a canvas that already has content, and deleting or
18
+ undocking them is remembered (they are not re-seeded). `--demo` is unaffected
19
+ (it seeds the demo board instead).
20
+
21
+ ### Changed
22
+
23
+ - **Collapsed docked widgets match the toolbar height.** The collapsed context
24
+ widget now renders inline in the top HUD row (mirroring the status widget)
25
+ instead of as a separate right-edge side-tab, and a shared `--hud-bar-height`
26
+ keeps the toolbar and both docked pills at an identical height.
27
+
28
+ ### Fixed
29
+
30
+ - **Context nodes can be undocked.** Previously a `context` node was re-forced to
31
+ `dockPosition: 'right'` on every write, so it could never leave the dock. The
32
+ right-docked collapsed default is now applied at create time only; updates
33
+ (including undock → `dockPosition: null`) are respected.
34
+
35
+ - **SDK node-response parity (report #31/#32).** `PmxCanvas.addNode`, `getNode`,
36
+ and `addHtmlNode` now return the fully serialized node enriched with a
37
+ `surfaceUrl` (for surface-eligible types) and a `nodeId` alias for `id`,
38
+ matching the HTTP/CLI `node`-create responses field-for-field. `addHtmlNode`
39
+ now returns the created node object instead of a bare id string (consistent
40
+ with `addNode`); read `.id` if you only need the identifier. The internal
41
+ `CanvasAccess` contract is unchanged (still returns a bare id).
42
+
43
+ - **HTML "Open as site" tab title (report #35).** The standalone surface document
44
+ for an `html` node now carries a `<title>` (the node title) when the author
45
+ HTML does not already declare one, so the browser tab shows the node title
46
+ instead of falling back to the raw `/api/canvas/surface/<id>` URL. An
47
+ author-provided `<title>` is never overridden, and the injected title is
48
+ HTML-escaped.
49
+
50
+ ### Notes
51
+
52
+ - **Report #33/#34/#36 — not reproduced.** #33 (elicitation/mode immediate
53
+ resolve) and #34 (delivery "claim" route) were already closed by the tester's
54
+ Codex retest as wrong-route repros. #36 (CLI emitting invalid JSON for html
55
+ primitives) does not reproduce: all 19 primitive kinds return valid JSON
56
+ through both `pmx-canvas html primitive add | jq` and raw `curl | jq`, with
57
+ zero unescaped control characters — the CLI re-serializes via `JSON.stringify`
58
+ and the server uses `Response.json`, both of which always escape U+0000–U+001F.
59
+ The full rendered HTML body is intentionally retained in the create response
60
+ (it is relied upon by consumers and renderer tests; agents wanting a compact
61
+ payload can use the MCP tool, which is already compact by default).
62
+
63
+ ## [0.1.30] - 2026-06-07
64
+
65
+ ### Added
66
+
67
+ - **PMX-AX node interactions (plan-004 Phase 1).** A host-agnostic, capability-gated
68
+ interaction layer over the existing AX primitives. Eligible nodes emit one
69
+ normalized `PmxAxInteraction` envelope (`{ type, sourceNodeId, payload, ... }`)
70
+ that is validated against a per-node-type capability registry (with optional
71
+ per-node `data.axCapabilities` opt-in/narrowing, clamped to a server ceiling)
72
+ and mapped onto the matching AX operation — work item, evidence, approval,
73
+ review, focus, steering, or event. New `POST /api/canvas/ax/interaction`
74
+ endpoint plus full SDK (`PmxCanvas.submitAxInteraction`), CanvasAccess, MCP
75
+ (`canvas_ax_interaction`), and CLI (`pmx-canvas ax interaction`) coverage.
76
+ Interactions are sandbox/transport-agnostic (the same envelope will back native
77
+ nodes, json-render actions, the HTML bridge, MCP apps, and host adapters in
78
+ later phases); `html`/`html-primitive`, `mcp-app`, and internal `prompt`/`response`
79
+ nodes are disabled by default. Accepted and rejected interactions emit SSE
80
+ (`ax-interaction`) plus the underlying AX state event. PMX core still imports no
81
+ host SDK (guarded by `ax-parity.test.ts`).
82
+
83
+ - **PMX-AX sandboxed HTML bridge (plan-004 Phase 3).** Opted-in `html` /
84
+ `html-primitive` nodes can emit AX interactions from inside the sandbox via
85
+ `window.PMX_AX.emit(type, payload)`. The server injects the bridge into the
86
+ surface document only when the node's AX capabilities are enabled; the bridge
87
+ posts a nonce-tagged message to the parent canvas, which validates the nonce +
88
+ node id and submits through the capability-gated endpoint (the server
89
+ re-validates, so the bridge is convenience, not a trust boundary). The iframe
90
+ sandbox stays `allow-scripts` only. (json-render action→AX mapping remains a
91
+ documented follow-up — the viewer needs a parent channel, same shape as this
92
+ bridge.)
93
+
94
+ - **PMX-AX delivery semantics (plan-004 Phase 4).** Steering messages can be
95
+ claimed and acknowledged by adapterless consumers. New
96
+ `GET /api/canvas/ax/delivery/pending?consumer=` (loop-safe — excludes steering
97
+ the consumer itself originated) and `POST /api/canvas/ax/delivery/:id/mark`,
98
+ plus SDK/CanvasAccess (`getPendingSteering`, `markSteeringDelivered`), MCP tools
99
+ (`canvas_claim_ax_delivery`, `canvas_mark_ax_delivery`), MCP resources
100
+ (`canvas://ax-pending-steering`, `canvas://ax-delivery`), an MCP prompt
101
+ template (`pmx-current-context`) so MCP-aware clients can inject PMX context
102
+ without a host adapter, and CLI (`pmx-canvas ax delivery list|mark`).
103
+
104
+ - **PMX-AX elicitation + mode-request primitives (plan-004 Phase 5).** Two new
105
+ canvas-bound, snapshotted AX primitives: `elicitation` (request structured
106
+ human input → respond) and `mode-request` (request a plan/execute/autonomous
107
+ transition → resolve). Full HTTP / SDK / CanvasAccess / MCP
108
+ (`canvas_request_elicitation`, `canvas_respond_elicitation`,
109
+ `canvas_request_mode`, `canvas_resolve_mode`) / CLI coverage, and both are
110
+ executable via the interaction envelope (`ax.elicitation.request`,
111
+ `ax.mode.request`). Command registry and tool/prompt policy primitives are
112
+ intentionally deferred pending the plan's open product questions (which
113
+ commands are first-class; how much prompt/system-message mutation PMX should
114
+ allow by default); the capability registry already reserves `mcp-app` (disabled
115
+ by default), so the MCP-app interaction bridge (Phase 6) is gated and
116
+ forward-compatible until that trust boundary is designed.
117
+
118
+ - **PMX-AX follow-ups — command registry, tool/prompt policy, json-render +
119
+ MCP-app bridges.** The four documented deferrals now ship. (1) A **command
120
+ registry** (`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`,
121
+ `pmx.review`) with a registry-gated `invokeCommand` (records an `agent-event`
122
+ of kind `command`); unknown names are rejected (`400`). Executable via the
123
+ envelope (`ax.command.invoke`) and exposed over HTTP
124
+ (`GET|POST /api/canvas/ax/command`), SDK, CanvasAccess, MCP
125
+ (`canvas_invoke_command`), and CLI (`pmx-canvas ax command list|invoke`).
126
+ (2) A canvas-bound, snapshotted **tool/prompt policy** singleton
127
+ (`tools.allowed|excluded|approvalRequired`, `prompt.systemAppend|mode`) read
128
+ into `canvas://ax-context`; set via `GET|POST /api/canvas/ax/policy`,
129
+ `canvas_set_ax_policy`, and `pmx-canvas ax policy get|set` (patches merge and
130
+ are normalized server-side). (3) The **json-render viewer → AX channel**: a
131
+ spec action named after an AX type (e.g. `on.press → { action:
132
+ "ax.work.create" }`) is forwarded by the viewer bundle to the parent canvas,
133
+ which validates (iframe source + per-viewer nonce + node id) and submits
134
+ through the capability-gated endpoint (`sourceSurface: 'json-render'`). The
135
+ bridge nonce/node-id globals are injected into the viewer HTML only when the
136
+ embedding node requests them. (4) The **MCP-app interaction bridge (Phase 6)**:
137
+ opted-in ext-app `mcp-app` nodes get `window.PMX_AX.emit(...)` injected into
138
+ the app HTML (same nonce-tagged shape as the HTML bridge,
139
+ `sourceSurface: 'mcp-app'`), disabled by default and node-scoped. All emit
140
+ surfaces remain convenience-only — the server re-validates every interaction
141
+ against the node's effective capabilities, so it stays the single trust
142
+ boundary.
143
+
144
+ - **PMX-AX native node controls (plan-004 Phase 2).** Inline AX controls on
145
+ native nodes that submit interactions through the browser: status nodes get a
146
+ "Track as work" button (→ `ax.work.create`), file nodes a "mark as evidence"
147
+ control (→ `ax.evidence.add`), and context nodes a "Set focus" button
148
+ (→ `ax.focus.set`). A client helper (`submitAxInteractionFromClient`) posts to
149
+ the interaction endpoint and surfaces the outcome as a transient toast.
150
+ (json-render action → AX mapping is deferred to the bridge-transport work: the
151
+ json-render viewer consumes actions internally and needs a viewer→parent
152
+ channel, the same shape as the HTML and MCP-app bridges.)
153
+
154
+ - **Open as site — standalone node surfaces.** Every renderable surface node can
155
+ now be opened full-page in its own browser tab via an ↗ "Open as site" button
156
+ (node title bar and expanded overlay), covering `html` / `html-primitive`,
157
+ bundled `web-artifact`, `json-render` / `graph`, `webpage`, and hosted ext-app
158
+ `mcp-app` nodes. A new stable route, `GET /api/canvas/surface/:nodeId`, serves
159
+ (or redirects to) the surface, and the in-canvas `html` iframe now loads that
160
+ **same URL** — one render path, no separate "preview" document, and the URL
161
+ reflects current node state and survives a refresh. Node payloads expose the
162
+ URL as `surfaceUrl` (`canvas_get_node` / `canvas_get_layout`) so agents can
163
+ point a human at "the artifact" without disturbing the canvas. Served HTML
164
+ keeps its opaque-origin posture (`Content-Security-Policy: sandbox`), so author
165
+ code opened top-level cannot reach the canvas origin. The html surface theme is
166
+ served from a new same-origin stylesheet (`/canvas/surface-theme.css`) and
167
+ live-switches via the existing theme bridge. ext-app surfaces render their UI
168
+ but, opened standalone, cannot run interactive tool-calls (no host bridge in a
169
+ bare tab); `webpage` / URL-backed `mcp-app` nodes redirect to their site.
170
+
171
+ ### Fixed
172
+
173
+ - **json-render specs preserve `on` event bindings through validation.** The
174
+ json-render element schema dropped the `on` field during validation, so
175
+ spec-authored action bindings (`on.press`, `on.change`, …) were silently
176
+ stripped before reaching the viewer and never fired. `on` is now retained,
177
+ which both makes general json-render interactivity work and is what lets the
178
+ json-render → AX channel above dispatch its `ax.*` actions. `on` is optional —
179
+ normalization defaults it to an empty bindings object, so specs whose elements
180
+ have no event bindings still validate.
181
+
182
+ - **Canvas iframes are promoted to their own compositing layer**
183
+ (`transform: translateZ(0)` on `.html-node-frame` / `.mcp-app-frame`) to
184
+ mitigate an intermittent blank-iframe glitch — an HTML/app node iframe in
185
+ the zoom/pan-transformed canvas (near the chrome's heavy backdrop-filter
186
+ blur and behind group frames) could occasionally paint blank until a
187
+ resize/zoom forced a repaint. Investigation ruled out a content/load issue
188
+ (the frame document served valid HTML) and a DOM-reorder/reload on grouping
189
+ (reproduced grouping single and multiple HTML nodes with the iframe keeping
190
+ its src and content); the remaining cause is a browser compositor paint
191
+ invalidation, which the dedicated GPU layer addresses.
192
+
193
+ ### Changed
194
+
195
+ - **Group frames are roomier.** Auto-fit group frames now leave more margin
196
+ around their children (`GROUP_PAD` 40 → 56) so the group header and its
197
+ node-count badge stay visible instead of sitting under the top-left child.
198
+ - **Skill guidance: open the canvas first, always.** The bundled
199
+ `pmx-canvas` skill now tells agents to open/focus the workbench themselves
200
+ before mutating nodes on **every** host (Codex in-app Browser, Copilot
201
+ panel, or any plain browser) rather than assuming the host opened it — some
202
+ hosts don't. Added layout guidance too: space nodes generously (more when
203
+ edges connect them and inside groups, so the edge flow is visible) and size
204
+ group frames larger than their children.
205
+
206
+ ### Fixed
207
+
208
+ - **Group operations no longer auto-pack or repack children.** Grouping
209
+ existing nodes (`canvas_group_nodes` / `/api/canvas/group/add`) without
210
+ an explicit `childLayout` now preserves their positions (matching
211
+ `canvas_create_group` and the batch `group.add`), and moving or resizing
212
+ a grouped child re-fits the group frame **without** repacking siblings or
213
+ discarding the child's requested coordinates. Compaction is opt-in via an
214
+ explicit layout. (0.1.29 report Bug #32.)
215
+ - **HTTP/CLI node-create responses now include the `nodeId` alias.** The
216
+ 0.1.29 `id`/`nodeId` alias only reached the MCP responses; the
217
+ `/api/canvas/node` (and graph/json-render) responses used by the CLI now
218
+ expose both keys too. (0.1.29 report Bug #31.)
219
+ - **`canvas_batch` now supports `node.remove`.** The operation is
220
+ implemented (and listed in the tool description) instead of being
221
+ rejected as unsupported. (0.1.29 report Bug #33.)
222
+
6
223
  ## [0.1.29] - 2026-06-06
7
224
 
8
225
  ### Changed
@@ -1415,6 +1632,8 @@ otherwise have to discover by trial and error.
1415
1632
  - Regression coverage for snapshot flat-`id` aliases on both MCP and
1416
1633
  HTTP surfaces, plus async / top-level-`await` WebView script bodies.
1417
1634
 
1635
+ [0.1.31]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.31
1636
+ [0.1.30]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.30
1418
1637
  [0.1.29]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.29
1419
1638
  [0.1.28]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.28
1420
1639
  [0.1.27]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.27
package/Readme.md CHANGED
@@ -37,7 +37,9 @@ neighborhoods) and uses your layout to ground its next action.
37
37
 
38
38
  Files, web pages, screenshots, structured panels, charts, hand-drawn
39
39
  diagrams, embedded MCP Apps, and bundled web artifacts all live on the same
40
- surface. The reach of the canvas is the union of its
40
+ surface. Any rich surface an HTML node or a web artifact — can be **opened as
41
+ a site**, full-page in its own browser tab with one click; the canvas and the
42
+ tab render the same document. The reach of the canvas is the union of its
41
43
  [built-in node types](docs/node-types.md) and **whatever your agent's harness
42
44
  already has access to** — MCP servers, CLIs, file reads, web fetch, anything
43
45
  on its toolbelt.
@@ -65,14 +67,22 @@ canvas into a shared workspace between you and the agent:
65
67
  - **Steering messages & agent-event timeline** — send instructions to the
66
68
  active session, and read a normalized, bounded timeline of prompts, tool
67
69
  runs, evidence (logs/diffs/screenshots/test-output), and failures.
70
+ - **Node interactions** — eligible nodes emit one capability-gated, validated
71
+ interaction envelope from native controls, the sandboxed HTML / MCP-app
72
+ bridges (`window.PMX_AX.emit`), or json-render spec actions. The server is the
73
+ single trust boundary and clamps sandboxed surfaces to their own node.
74
+ - **Elicitations, mode requests, commands & policy** — request structured human
75
+ input, propose a plan/execute/autonomous mode transition, invoke registry
76
+ commands (`pmx.plan`, `pmx.review`, …), and read a tool/prompt policy.
68
77
  - **Host capability** — adapters report what the host can do, for diagnostics.
69
78
 
70
- Canvas-bound state (focus, work items, approvals, review annotations) rides
71
- canvas snapshots and restore; the timeline persists for continuity but is
72
- retention-bounded and never restored by a snapshot. Every primitive is reachable
73
- from MCP, the HTTP API, the SDK, and `pmx-canvas ax …`. The core never depends
74
- on any host SDK, so adapters (e.g. the GitHub Copilot app) map onto the same
75
- neutral surfaces without making PMX Canvas vendor-specific.
79
+ Canvas-bound state (focus, work items, approvals, review annotations,
80
+ elicitations, mode requests, policy) rides canvas snapshots and restore; the
81
+ timeline persists for continuity but is retention-bounded and never restored by a
82
+ snapshot. Every primitive is reachable from MCP, the HTTP API, the SDK, and
83
+ `pmx-canvas ax …`. The core never depends on any host SDK, so adapters (e.g. the
84
+ GitHub Copilot app) map onto the same neutral surfaces without making PMX Canvas
85
+ vendor-specific.
76
86
 
77
87
  ### 05 / Save
78
88
 
@@ -85,8 +95,8 @@ the DB so SQLite WAL data is checkpointed into the file.
85
95
 
86
96
  ### 06 / Any agent
87
97
 
88
- Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (56 tools,
89
- 12 resources, change notifications), the [CLI](docs/cli.md), the
98
+ Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (65 tools,
99
+ 14 resources, change notifications), the [CLI](docs/cli.md), the
90
100
  [HTTP API](docs/http-api.md), or the [Bun SDK](docs/sdk.md). Works with
91
101
  Claude Code, GitHub Copilot CLI, Codex, Cursor, Windsurf, or any agent
92
102
  that can spawn an MCP stdio server, call a CLI, or hit an HTTP endpoint.
@@ -235,7 +245,7 @@ the agent can read `canvas://skills` and pull in companion skills
235
245
  the three-tier visual matrix (json-render → html → web-artifact)
236
246
  - **[CLI reference](docs/cli.md)** — full command surface, daemon mode,
237
247
  watch streams, WebView automation
238
- - **[MCP reference](docs/mcp.md)** — 56 tools, 12 resources, change
248
+ - **[MCP reference](docs/mcp.md)** — 65 tools, 14 resources, change
239
249
  notifications, node-type routing
240
250
  - **[HTTP API](docs/http-api.md)** — REST endpoints, SSE, batch operations
241
251
  - **[Bun SDK](docs/sdk.md)** — `createCanvas()` for TypeScript on Bun
@@ -56,6 +56,10 @@
56
56
  --mono: "IBM Plex Mono", "SF Mono", "Fira Code", monospace;
57
57
  --radius: 10px;
58
58
  --radius-sm: 6px;
59
+ /* Shared height for the top HUD row so the toolbar and the collapsed docked
60
+ status/context widgets that flank it line up to the same height. Matches the
61
+ toolbar's natural content height (icon buttons at 6px padding). */
62
+ --hud-bar-height: 44px;
59
63
  }
60
64
 
61
65
  :root[data-theme="light"] {
@@ -481,6 +485,8 @@ body,
481
485
  align-items: center;
482
486
  gap: 6px;
483
487
  padding: 6px 10px;
488
+ min-height: var(--hud-bar-height);
489
+ box-sizing: border-box;
484
490
  background: var(--c-panel-glass);
485
491
  backdrop-filter: blur(12px);
486
492
  border: 1px solid var(--c-line);
@@ -1409,6 +1415,38 @@ html.is-node-resizing .ext-app-preview-catcher {
1409
1415
  max-width: 320px;
1410
1416
  }
1411
1417
 
1418
+ /* Collapsed docked widget = a single menu-height pill that flanks the toolbar.
1419
+ Pinned to the same height as .canvas-toolbar so the top HUD row reads as one
1420
+ continuous bar (status on the left, context on the right). */
1421
+ .docked-node--collapsed {
1422
+ height: var(--hud-bar-height);
1423
+ box-sizing: border-box;
1424
+ justify-content: center;
1425
+ width: auto;
1426
+ /* Reset the base .docked-node min-width so the collapsed pill hugs its content
1427
+ (badge + count + controls) instead of stretching to a 200px bar. */
1428
+ min-width: 0;
1429
+ }
1430
+ .docked-node--collapsed .docked-node-header {
1431
+ height: 100%;
1432
+ padding: 0 10px;
1433
+ border-bottom: none;
1434
+ }
1435
+ .docked-node-count {
1436
+ min-width: 18px;
1437
+ height: 18px;
1438
+ padding: 0 5px;
1439
+ display: inline-flex;
1440
+ align-items: center;
1441
+ justify-content: center;
1442
+ border-radius: 9px;
1443
+ background: var(--c-accent);
1444
+ color: var(--c-contrast-fg);
1445
+ font-size: 10px;
1446
+ font-weight: 700;
1447
+ flex-shrink: 0;
1448
+ }
1449
+
1412
1450
  .docked-node-header {
1413
1451
  display: flex;
1414
1452
  align-items: center;
@@ -1473,6 +1511,19 @@ html.is-node-resizing .ext-app-preview-catcher {
1473
1511
  border-radius: 0 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px);
1474
1512
  }
1475
1513
 
1514
+ /* Promote canvas iframes to their own GPU compositing layer. An iframe embedded
1515
+ in the zoom/pan-transformed canvas — especially near the heavy backdrop-filter
1516
+ blur used across the chrome and behind group frames — can intermittently paint
1517
+ blank until a resize/zoom forces a repaint (reported for grouped HTML nodes;
1518
+ the same class of compositor glitch as the earlier Excalidraw flicker). A no-op
1519
+ 3D transform forces a stable layer so the iframe keeps painting through
1520
+ surrounding layout/stacking changes. */
1521
+ .mcp-app-frame,
1522
+ .html-node-frame {
1523
+ transform: translateZ(0);
1524
+ backface-visibility: hidden;
1525
+ }
1526
+
1476
1527
  /* ── Prompt nodes ──────────────────────────────────────────── */
1477
1528
  .canvas-node:has(.prompt-node-inner) {
1478
1529
  border-color: var(--c-accent-30);
@@ -1935,62 +1986,6 @@ html.is-node-resizing .ext-app-preview-catcher {
1935
1986
  max-width: 200px;
1936
1987
  }
1937
1988
 
1938
- /* Context dock — collapsed pill mirrors Updates pill, sits above it */
1939
- .context-dock-tab {
1940
- position: fixed;
1941
- top: 92px;
1942
- right: 0;
1943
- display: flex;
1944
- align-items: center;
1945
- gap: 8px;
1946
- padding: 8px 12px 8px 14px;
1947
- background: color-mix(in srgb, var(--c-panel-glass) 96%, transparent);
1948
- backdrop-filter: blur(16px);
1949
- border: 1px solid color-mix(in srgb, var(--c-line) 82%, var(--c-accent) 18%);
1950
- border-right: 0;
1951
- border-radius: 14px 0 0 14px;
1952
- box-shadow: 0 12px 36px var(--c-shadow);
1953
- color: var(--c-text);
1954
- cursor: pointer;
1955
- font: inherit;
1956
- font-size: 11px;
1957
- font-weight: 600;
1958
- letter-spacing: 0.08em;
1959
- text-transform: uppercase;
1960
- z-index: 60;
1961
- }
1962
-
1963
- .context-dock-tab:hover {
1964
- border-color: color-mix(in srgb, var(--c-accent) 40%, var(--c-line) 60%);
1965
- color: var(--c-accent);
1966
- }
1967
-
1968
- .context-dock-tab svg {
1969
- display: block;
1970
- color: var(--c-accent);
1971
- flex-shrink: 0;
1972
- }
1973
-
1974
- .context-dock-tab-label {
1975
- white-space: nowrap;
1976
- }
1977
-
1978
- .context-dock-tab-badge {
1979
- min-width: 18px;
1980
- height: 18px;
1981
- padding: 0 5px;
1982
- display: inline-flex;
1983
- align-items: center;
1984
- justify-content: center;
1985
- border-radius: 9px;
1986
- background: var(--c-accent);
1987
- color: var(--c-contrast-fg);
1988
- font-size: 10px;
1989
- font-weight: 700;
1990
- letter-spacing: 0;
1991
- text-transform: none;
1992
- }
1993
-
1994
1989
  /* Context dock — expanded panel anchored top-right edge.
1995
1990
  Mutually exclusive with the Updates panel (see DockedNode.tsx and
1996
1991
  AttentionHistory.tsx) — opening one collapses the other, so they can both