pmx-canvas 0.4.2 → 0.4.4

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 (37) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +5 -2
  2. package/CHANGELOG.md +60 -0
  3. package/Readme.md +35 -12
  4. package/dist/canvas/global.css +92 -28
  5. package/dist/canvas/index.js +62 -62
  6. package/dist/canvas/surface-theme.css +52 -20
  7. package/dist/types/cli/agent.d.ts +1 -0
  8. package/dist/types/cli/commands/skills.d.ts +41 -0
  9. package/dist/types/client/state/sse-bridge.d.ts +1 -0
  10. package/dist/types/client/state/theme-override.d.ts +27 -0
  11. package/dist/types/shared/themes.d.ts +1 -1
  12. package/docs/RELEASE.md +14 -6
  13. package/docs/bun-webview-integration.md +2 -2
  14. package/docs/cli.md +24 -1
  15. package/docs/environment.md +2 -2
  16. package/docs/http-api.md +14 -3
  17. package/docs/plans/plan-009-tech-debt-backlog.md +5 -2
  18. package/docs/screenshot.png +0 -0
  19. package/package.json +3 -3
  20. package/skills/pmx-canvas/SKILL.md +18 -4
  21. package/skills/pmx-canvas/references/full-reference.md +1 -1
  22. package/skills/pmx-canvas-testing/SKILL.md +4 -0
  23. package/src/cli/agent.ts +1 -0
  24. package/src/cli/commands/skills.ts +183 -0
  25. package/src/cli/index.ts +3 -1
  26. package/src/cli/shared.ts +1 -0
  27. package/src/client/App.tsx +85 -78
  28. package/src/client/canvas/CommandPalette.tsx +4 -0
  29. package/src/client/nodes/ExtAppFrame.tsx +15 -4
  30. package/src/client/nodes/McpAppNode.tsx +6 -1
  31. package/src/client/state/sse-bridge.ts +22 -5
  32. package/src/client/state/theme-override.ts +83 -0
  33. package/src/client/theme/global.css +92 -28
  34. package/src/client/theme/surface-theme.css +52 -20
  35. package/src/server/operations/ops/canvas-wire.ts +10 -2
  36. package/src/server/server.ts +13 -2
  37. package/src/shared/themes.ts +3 -1
@@ -252,7 +252,10 @@ function escapeHtml(value) {
252
252
 
253
253
  function renderShell(instanceId, entry) {
254
254
  const status = entry.pmx?.ok ? "Connected" : "Not connected";
255
- const frameSrc = entry.pmx?.baseUrl ? `${entry.pmx.baseUrl}/workbench` : "about:blank";
255
+ // theme=light is a client-local session default matching the Copilot app's
256
+ // light chrome — it never changes the server-global theme other clients
257
+ // see, and the in-panel theme picker still overrides it.
258
+ const frameSrc = entry.pmx?.baseUrl ? `${entry.pmx.baseUrl}/workbench?theme=light` : "about:blank";
256
259
  const error = entry.pmx?.error ? `<p class="error">${escapeHtml(entry.pmx.error)}</p>` : "";
257
260
  return `<!doctype html>
258
261
  <html>
@@ -731,7 +734,7 @@ const pmxCanvas = createCanvas({
731
734
  return {
732
735
  title: "PMX Canvas",
733
736
  status: "Connected",
734
- url: `${pmx.baseUrl}/workbench`,
737
+ url: `${pmx.baseUrl}/workbench?theme=light`,
735
738
  };
736
739
  }
737
740
 
package/CHANGELOG.md CHANGED
@@ -3,6 +3,64 @@
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.4.4] - 2026-08-06
7
+
8
+ ### Fixed
9
+
10
+ - Amp orb pages now default straight to the polling transport instead of
11
+ waiting out the 3-second SSE watchdog behind the portal's buffering proxy —
12
+ on slow proxy days that wait could trip the "did not finish booting" modal.
13
+ `?transport=sse` still overrides for diagnosis.
14
+ - When the SSE watchdog does fall back to polling mid-boot (non-orb buffering
15
+ proxies), the boot screen now shows "switching to polling" and restarts its
16
+ deadline instead of alarming while the fallback is already recovering.
17
+
18
+ ### Added
19
+
20
+ - A ninth theme, `volt` — built from the Amp app's real palette (green-tinted
21
+ charcoal neutrals, warm green-white text, coral accent). `ember` stays
22
+ unchanged.
23
+ - `pmx-canvas skills sync --yes` refreshes the pmx-canvas skill copies already
24
+ installed in the workspace — whatever agent layout owns them — by comparing
25
+ and replacing whole trees from the installed package; `--check` (or running
26
+ without `--yes`) reports drift and exits 1 without changing anything. A
27
+ directory whose `SKILL.md` identity doesn't match the bundled skill is
28
+ skipped, never replaced, and it never creates a copy where none is
29
+ installed. This automates the sync every release test cycle previously did
30
+ by hand.
31
+
32
+ ## [0.4.3] - 2026-08-05
33
+
34
+ ### Added
35
+
36
+ - Host-default theming: open the workbench with `?theme=<name>` (or
37
+ `?theme=auto` to follow the host's light/dark appearance) and that panel
38
+ gets its own session-local theme without changing the server-global theme
39
+ other clients see. Picking a theme from the toolbar ends the override. The
40
+ bundled GitHub Copilot extension now opens its panel with `?theme=light`.
41
+
42
+ ### Changed
43
+
44
+ - The `ember` theme was retuned to its intended look: neutral charcoal
45
+ surfaces, warm off-white text, and a vivid orange-red accent.
46
+
47
+ ### Fixed
48
+
49
+ - The theme picker and More menus now open anchored to their buttons instead
50
+ of at the toolbar's right edge (0.4.2 report Finding T).
51
+ - `POST /api/canvas/theme` now rejects unknown theme names with a 400 error
52
+ instead of silently keeping the previous theme.
53
+ - An app node whose live session errors after a stressed reconnect (0.4.2
54
+ report Finding U, e.g. "MCP error -32001") now shows a soft
55
+ degraded-interactivity notice over its still-rendered saved content instead
56
+ of a red error banner.
57
+ - Graph, json-render, and app nodes no longer stay blank after a refresh in
58
+ srcdoc mode until scrolled or expanded — inline documents now load eagerly
59
+ (lazy loading only applies in `src` mode, where it defers a real HTTP
60
+ request).
61
+ - The npm package's `test` scripts now explain that the test suite is not
62
+ shipped instead of failing with a confusing "no test files" error.
63
+
6
64
  ## [0.4.2] - 2026-08-04
7
65
 
8
66
  ### Added
@@ -2880,6 +2938,8 @@ otherwise have to discover by trial and error.
2880
2938
  - Regression coverage for snapshot flat-`id` aliases on both MCP and
2881
2939
  HTTP surfaces, plus async / top-level-`await` WebView script bodies.
2882
2940
 
2941
+ [0.4.4]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.4.4
2942
+ [0.4.3]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.4.3
2883
2943
  [0.4.2]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.4.2
2884
2944
  [0.4.1]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.4.1
2885
2945
  [0.4.0]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.4.0
package/Readme.md CHANGED
@@ -130,7 +130,8 @@ surfaces (the core never imports a host SDK).
130
130
 
131
131
  - **GitHub Copilot app** — a committed project canvas extension
132
132
  (`.github/extensions/pmx-canvas/`) opens the live PMX workbench in a native
133
- Copilot panel, injects pinned/focused AX context on prompt submission, and
133
+ Copilot panel (light-themed by default to match the app, via the `?theme=`
134
+ session override), injects pinned/focused AX context on prompt submission, and
134
135
  exposes actions for focus, session steering, work items, approval gates,
135
136
  review annotations, the AX timeline, and host-capability reporting. Install it
136
137
  into any repo with `pmx-canvas copilot install-extension` (`--dry-run` to
@@ -142,17 +143,26 @@ surfaces (the core never imports a host SDK).
142
143
  (MCP + in-app Browser) are exactly what the canvas requires.
143
144
  - **Claude Code desktop app** — the workbench runs in the desktop app's
144
145
  built-in browser pane alongside the PMX MCP server, same zero-adapter recipe
145
- as Codex: open `http://localhost:4313/workbench` in the app's browser and the
146
- live board, pins, and Ghost Cursor all work in-panel.
147
- - **Amp orb portals** (and other buffering proxies / nested-iframe embeds) —
148
- the canvas detects when the live event stream is being buffered by the proxy
149
- and automatically switches to a polling transport, so the board boots and
150
- stays live through the portal with no proxy shims
151
- (`/workbench?transport=poll` forces it). It also detects when the nested
152
- iframe embed blocks `src`-URL iframes entirely and renders HTML, graph,
153
- json-render, and app surfaces inline via `srcdoc` instead
154
- (`?iframe-mode=srcdoc` forces it), so iframe-backed nodes work in the portal
155
- too.
146
+ as Codex: open `http://localhost:4313/workbench?theme=light` (or
147
+ `?theme=auto` to follow the app's appearance) in the app's browser and the
148
+ live board, pins, and Ghost Cursor all work in-panel — themed to match the
149
+ pane without changing what other clients see.
150
+ - **AMPCode orbs + portal** — run the canvas as an orb service and view the
151
+ live workbench through the Amp portal (the `*.onamp.dev` URL ampcode.com
152
+ embeds for the thread). Zero configuration: orbs set `AMP_ORB` in the
153
+ service environment, and the canvas auto-adapts to the portal's constraints
154
+ — it defaults straight to the proxy-safe polling transport (the portal
155
+ proxy buffers the live event stream), and renders HTML, graph, json-render,
156
+ and app surfaces inline via `srcdoc` because the nested-iframe embed blocks
157
+ `src`-URL child iframes (`?transport=sse` and `?iframe-mode=src` override
158
+ either behavior when diagnosing). Non-orb buffering proxies get the same
159
+ protections via runtime detection: an SSE watchdog that falls back to
160
+ polling, and the boot-time iframe probe.
161
+ <!-- screenshot: docs/screenshots/amp-orb-portal.png — PMX Canvas live inside the Amp portal (to be added) -->
162
+
163
+ Any other app browser (the ChatGPT desktop app, an IDE webview, …) works the
164
+ same zero-adapter way: open the workbench URL, and add `?theme=<name|auto>` to
165
+ give that panel its own session-local default theme.
156
166
 
157
167
  The contract is host-agnostic, so a new host plugs in the same way: map its
158
168
  hooks, canvas, and session APIs onto PMX's AX primitives — no core changes.
@@ -239,6 +249,19 @@ Agents read pinned/focused context from `canvas://ax-context` /
239
249
  [`codex-app-adapter.md`](skills/pmx-canvas/references/codex-app-adapter.md) for
240
250
  the full workflow and live-test checklist.
241
251
 
252
+ ### Use inside AMPCode orbs and the Amp portal
253
+
254
+ Amp needs no adapter either. Run `pmx-canvas` as an orb service (the orb sets
255
+ `AMP_ORB` in the environment automatically) and open the workbench through the
256
+ thread's portal URL. The canvas detects the orb and the portal's nested-iframe
257
+ embed on its own: live updates arrive over the proxy-safe polling transport,
258
+ and iframe-backed nodes (HTML, graph, json-render, web artifacts) render
259
+ inline via `srcdoc` with their theme styling included. Cross-origin hosted
260
+ apps (e.g. the hosted Excalidraw MCP app) cannot be inlined and may stay
261
+ blocked by the portal embed — everything served from the canvas itself works.
262
+ Debug overrides: `/workbench?transport=poll|sse` and
263
+ `?iframe-mode=srcdoc|src`.
264
+
242
265
  ### Install the agent skill (recommended)
243
266
 
244
267
  The fastest way to get a working canvas is to install the `pmx-canvas` agent
@@ -337,16 +337,16 @@
337
337
  }
338
338
 
339
339
  :root[data-theme="ember"] {
340
- /* ── Core palette (ember · charcoal + vivid warm orange) ─── */
341
- --c-bg: #0E0E11;
342
- --c-panel: #17181D;
343
- --c-panel-soft: #121317;
344
- --c-line: #2A2C34;
345
- --c-text: #ECEDF0;
346
- --c-text-soft: #C9CCD4;
347
- --c-muted: #8E939F;
348
- --c-dim: #5F646E;
349
- --c-accent: #FF5C2E;
340
+ /* ── Core palette (ember · neutral charcoal + vivid orange-red) ─── */
341
+ --c-bg: #0B0B0C;
342
+ --c-panel: #161618;
343
+ --c-panel-soft: #101012;
344
+ --c-line: #29292C;
345
+ --c-text: #EDEBE6;
346
+ --c-text-soft: #CBC9C2;
347
+ --c-muted: #96948D;
348
+ --c-dim: #66655F;
349
+ --c-accent: #F34E1C;
350
350
  --c-ok: #4CC38A;
351
351
  --c-warn: #F5B944;
352
352
  --c-warn-alt: #FFB300;
@@ -354,23 +354,23 @@
354
354
  --c-purple: #A78BFA;
355
355
  --c-thinking: #A78BFA;
356
356
  --c-subagent: #38BDF8;
357
- --c-input-bg: #121317;
358
- --c-dot-grid: rgba(255, 92, 46, 0.08);
359
- --c-panel-overlay: rgba(14, 14, 17, 0.6);
360
- --c-panel-glass: rgba(23, 24, 29, 0.92);
357
+ --c-input-bg: #101012;
358
+ --c-dot-grid: rgba(243, 78, 28, 0.08);
359
+ --c-panel-overlay: rgba(11, 11, 12, 0.6);
360
+ --c-panel-glass: rgba(22, 22, 24, 0.92);
361
361
  --c-shadow: rgba(0, 0, 0, 0.3);
362
362
  --c-shadow-heavy: rgba(0, 0, 0, 0.5);
363
- --c-glow-accent: rgba(255, 92, 46, 0.15);
363
+ --c-glow-accent: rgba(243, 78, 28, 0.15);
364
364
  --c-surface-hover: rgba(255, 255, 255, 0.06);
365
365
  --c-surface-subtle: rgba(255, 255, 255, 0.03);
366
366
  --c-surface-th: rgba(255, 255, 255, 0.04);
367
- --c-accent-8: rgba(255, 92, 46, 0.08);
368
- --c-accent-10: rgba(255, 92, 46, 0.1);
369
- --c-accent-12: rgba(255, 92, 46, 0.12);
370
- --c-accent-15: rgba(255, 92, 46, 0.15);
371
- --c-accent-25: rgba(255, 92, 46, 0.25);
372
- --c-accent-30: rgba(255, 92, 46, 0.3);
373
- --c-accent-40: rgba(255, 92, 46, 0.4);
367
+ --c-accent-8: rgba(243, 78, 28, 0.08);
368
+ --c-accent-10: rgba(243, 78, 28, 0.1);
369
+ --c-accent-12: rgba(243, 78, 28, 0.12);
370
+ --c-accent-15: rgba(243, 78, 28, 0.15);
371
+ --c-accent-25: rgba(243, 78, 28, 0.25);
372
+ --c-accent-30: rgba(243, 78, 28, 0.3);
373
+ --c-accent-40: rgba(243, 78, 28, 0.4);
374
374
  --c-ok-10: rgba(76, 195, 138, 0.1);
375
375
  --c-ok-12: rgba(76, 195, 138, 0.12);
376
376
  --c-ok-20: rgba(76, 195, 138, 0.2);
@@ -383,12 +383,12 @@
383
383
  --c-warn-30: rgba(245, 185, 68, 0.3);
384
384
  --c-warn-60: rgba(245, 185, 68, 0.6);
385
385
  --c-danger-12: rgba(240, 86, 106, 0.12);
386
- --c-line-30: rgba(42, 44, 52, 0.3);
387
- --c-contrast-fg: #0E0E11;
388
- --c-accent-hover: #FF7A52;
386
+ --c-line-30: rgba(41, 41, 44, 0.3);
387
+ --c-contrast-fg: #0B0B0C;
388
+ --c-accent-hover: #FF6B3A;
389
389
  --c-warn-hover: #F7C868;
390
390
  --c-canvas-wash: linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(0, 0, 0, 0));
391
- --c-annotation: #F2F2F4;
391
+ --c-annotation: #F2F0EB;
392
392
  }
393
393
 
394
394
  :root[data-theme="forest"] {
@@ -446,6 +446,61 @@
446
446
  --c-annotation: #EDF5EF;
447
447
  }
448
448
 
449
+ :root[data-theme="volt"] {
450
+ /* ── Core palette (volt · green-tinted charcoal + coral) ── */
451
+ --c-bg: #0B0D0B;
452
+ --c-panel: #171A17;
453
+ --c-panel-soft: #111311;
454
+ --c-line: #272A27;
455
+ --c-text: #F6FFF5;
456
+ --c-text-soft: #CBD3CA;
457
+ --c-muted: #9CA49C;
458
+ --c-dim: #626962;
459
+ --c-accent: #F34E3F;
460
+ --c-ok: #52C880;
461
+ --c-warn: #E4C465;
462
+ --c-warn-alt: #FFB300;
463
+ --c-danger: #D67670;
464
+ --c-purple: #BC8FB4;
465
+ --c-thinking: #BC8FB4;
466
+ --c-subagent: #6CA0E0;
467
+ --c-input-bg: #111311;
468
+ --c-dot-grid: rgba(243, 78, 63, 0.08);
469
+ --c-panel-overlay: rgba(11, 13, 11, 0.6);
470
+ --c-panel-glass: rgba(23, 26, 23, 0.92);
471
+ --c-shadow: rgba(0, 0, 0, 0.3);
472
+ --c-shadow-heavy: rgba(0, 0, 0, 0.5);
473
+ --c-glow-accent: rgba(243, 78, 63, 0.15);
474
+ --c-surface-hover: rgba(255, 255, 255, 0.06);
475
+ --c-surface-subtle: rgba(255, 255, 255, 0.03);
476
+ --c-surface-th: rgba(255, 255, 255, 0.04);
477
+ --c-accent-8: rgba(243, 78, 63, 0.08);
478
+ --c-accent-10: rgba(243, 78, 63, 0.1);
479
+ --c-accent-12: rgba(243, 78, 63, 0.12);
480
+ --c-accent-15: rgba(243, 78, 63, 0.15);
481
+ --c-accent-25: rgba(243, 78, 63, 0.25);
482
+ --c-accent-30: rgba(243, 78, 63, 0.3);
483
+ --c-accent-40: rgba(243, 78, 63, 0.4);
484
+ --c-ok-10: rgba(82, 200, 128, 0.1);
485
+ --c-ok-12: rgba(82, 200, 128, 0.12);
486
+ --c-ok-20: rgba(82, 200, 128, 0.2);
487
+ --c-ok-25: rgba(82, 200, 128, 0.25);
488
+ --c-warn-10: rgba(228, 196, 101, 0.1);
489
+ --c-warn-12: rgba(228, 196, 101, 0.12);
490
+ --c-warn-15: rgba(228, 196, 101, 0.15);
491
+ --c-warn-20: rgba(228, 196, 101, 0.2);
492
+ --c-warn-22: rgba(228, 196, 101, 0.22);
493
+ --c-warn-30: rgba(228, 196, 101, 0.3);
494
+ --c-warn-60: rgba(228, 196, 101, 0.6);
495
+ --c-danger-12: rgba(214, 118, 112, 0.12);
496
+ --c-line-30: rgba(39, 42, 39, 0.3);
497
+ --c-contrast-fg: #0B0D0B;
498
+ --c-accent-hover: #F8695B;
499
+ --c-warn-hover: #EBD087;
500
+ --c-canvas-wash: linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(0, 0, 0, 0));
501
+ --c-annotation: #F1F7F0;
502
+ }
503
+
449
504
  * {
450
505
  box-sizing: border-box;
451
506
  margin: 0;
@@ -944,7 +999,6 @@ body,
944
999
  }
945
1000
 
946
1001
  .toolbar-group {
947
- position: relative;
948
1002
  display: flex;
949
1003
  flex-wrap: wrap;
950
1004
  align-items: center;
@@ -953,6 +1007,14 @@ body,
953
1007
  max-width: 100%;
954
1008
  }
955
1009
 
1010
+ /* Positioning parent for a toolbar popover menu. Every menu MUST live inside
1011
+ one of these wrapped around its trigger — anchoring to a wider ancestor
1012
+ opens the menu far from the button (0.4.2 report Finding T). */
1013
+ .toolbar-menu-anchor {
1014
+ position: relative;
1015
+ display: inline-flex;
1016
+ }
1017
+
956
1018
  .canvas-toolbar button svg {
957
1019
  display: block;
958
1020
  }
@@ -1074,7 +1136,9 @@ body,
1074
1136
  }
1075
1137
 
1076
1138
  .toolbar-more {
1077
- display: contents;
1139
+ /* inline-flex (NOT contents): this span is also the positioning anchor
1140
+ for the More menu — display:contents cannot anchor absolute children. */
1141
+ display: inline-flex;
1078
1142
  }
1079
1143
  }
1080
1144