pmx-canvas 0.7.2 → 0.7.3

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 (36) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/Readme.md +25 -11
  3. package/dist/canvas/global.css +123 -40
  4. package/dist/canvas/index.js +84 -84
  5. package/dist/json-render/index.js +55 -55
  6. package/dist/types/client/canvas/ChoiceMenu.d.ts +13 -0
  7. package/dist/types/client/nodes/McpAppNode.d.ts +2 -1
  8. package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -0
  9. package/dist/types/client/state/canvas-store.d.ts +2 -0
  10. package/dist/types/client/state/intent-bridge.d.ts +1 -1
  11. package/dist/types/json-render/renderer/index.d.ts +2 -1
  12. package/dist/types/json-render/server.d.ts +2 -0
  13. package/dist/types/shared/ax-surface-protocol.d.ts +2 -0
  14. package/docs/ax-host-adapter-contract.md +13 -2
  15. package/package.json +1 -1
  16. package/skills/pmx-canvas/SKILL.md +23 -11
  17. package/skills/pmx-canvas/evals/evals.json +69 -0
  18. package/skills/pmx-canvas/references/full-reference.md +3 -3
  19. package/skills/pmx-canvas/references/installing-pmx-canvas.md +76 -4
  20. package/skills/pmx-canvas-testing/SKILL.md +49 -0
  21. package/src/client/canvas/CanvasNode.tsx +200 -195
  22. package/src/client/canvas/CanvasViewport.tsx +6 -0
  23. package/src/client/canvas/ChoiceMenu.tsx +122 -0
  24. package/src/client/canvas/CommandBar.tsx +25 -23
  25. package/src/client/canvas/FeedbackDialog.tsx +10 -8
  26. package/src/client/canvas/ToolRail.tsx +46 -99
  27. package/src/client/nodes/McpAppNode.tsx +16 -0
  28. package/src/client/nodes/use-ax-surface-bridge.ts +4 -2
  29. package/src/client/nodes/viewer-runtime-state.ts +78 -0
  30. package/src/client/state/canvas-store.ts +3 -0
  31. package/src/client/state/intent-bridge.ts +14 -2
  32. package/src/client/theme/global.css +123 -40
  33. package/src/json-render/renderer/index.tsx +58 -38
  34. package/src/json-render/server.ts +8 -1
  35. package/src/server/server.ts +3 -1
  36. package/src/shared/ax-surface-protocol.ts +3 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
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.7.3] - 2026-10-01
7
+
8
+ ### Fixed
9
+
10
+ - JSON form values and dynamic lists survive expanding and closing focus mode without silently saving drafts to the server; authored spec updates still reset runtime state.
11
+ - Interactive surfaces register their action bridge before first paint.
12
+ - Bundled skill quick references match live MCP discovery, including board opening and workboards, and distinguish local JSON runtime state from durable data.
13
+ - Amp setup guidance covers workspace trust approval, portal targeting and native steering verification; testing guidance preserves first-failure evidence and distinguishes host coverage gaps.
14
+ - The main toolbar keeps Markdown, Attach files, Webpage and Group; one file picker replaces separate File, Image and HTML buttons, with supported formats explained in its tooltip.
15
+ - Draw, Text note and Eraser are separate toolbar buttons for quick board feedback.
16
+ - Connection handles are easier to grab at any zoom, and saved connections appear without waiting for the next background update.
17
+ - Feedback and agent selectors use themed menus, and Cancel actions have clearer button styling.
18
+
6
19
  ## [0.7.2] - 2026-09-30
7
20
 
8
21
  ### Added
package/Readme.md CHANGED
@@ -397,20 +397,33 @@ merge this into the workspace's `.amp/settings.json` (not `.mcp.json`):
397
397
  "amp.mcpServers": {
398
398
  "canvas": {
399
399
  "command": "bunx",
400
- "args": ["pmx-canvas@0.7.2", "--mcp"]
400
+ "args": ["pmx-canvas@0.7.3", "--mcp"],
401
+ "includeTools": ["canvas_*"],
402
+ "env": {
403
+ "PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
404
+ "PMX_CANVAS_PORT": "14313",
405
+ "PMX_CANVAS_URL": "",
406
+ "PMX_CANVAS_AGENT_SOURCE": "amp"
407
+ }
401
408
  }
402
409
  }
403
410
  }
404
411
  ```
405
412
 
413
+ Replace the root and example port with the portal service's actual workspace and local
414
+ listening port, including an orb-assigned port. Configure this inside the orb/runner;
415
+ settings on your laptop are not automatically available there.
406
416
  In the PMX product checkout, use `"command": "bun"` and
407
417
  `"args": ["run", "src/mcp/server.ts"]` to test the checked-out source instead.
408
- Reload MCP connections and complete any host trust prompt. Confirm Canvas tools
409
- are discoverable and the target `/health.workspace` and version match before
410
- mutating a board. If discovery is empty, inspect Amp's MCP connection status;
411
- an open portal or a successful standalone MCP handshake does not prove a native
412
- connection, skill activation, or steering delivery. See
413
- [Amp MCP configuration](https://ampcode.com/docs/customize/mcp).
418
+ Run `amp mcp doctor` in that workspace. If it reports `awaiting approval`, the user
419
+ must review the command and run `amp mcp approve canvas` there; the agent must not
420
+ self-approve or bypass workspace trust. Then reload MCP connections and confirm
421
+ `connected`, Canvas tool discovery, and matching `/health.workspace`, version and
422
+ active board before writes. An open portal or standalone MCP handshake does not prove
423
+ native discovery. PMX ships no dedicated Amp lifecycle adapter: an active agent can
424
+ poll steering, but MCP setup alone does not wake an idle thread or activate skills.
425
+ See the bundled [Amp setup and verification guide](skills/pmx-canvas/references/installing-pmx-canvas.md#amp-connect-the-agent-separately-from-the-portal)
426
+ and [Amp MCP configuration](https://ampcode.com/docs/customize/mcp).
414
427
 
415
428
  For the visible workbench, run `pmx-canvas` as an orb service (the orb sets
416
429
  `AMP_ORB` in the environment automatically) and open the workbench through the
@@ -428,7 +441,7 @@ services:
428
441
  ```bash
429
442
  # .agents/setup — install the CLI (pin the exact version: a fresh orb running
430
443
  # @latest can silently pick up a newer release than the one you validated)
431
- npm install -g pmx-canvas@0.7.2
444
+ npm install -g pmx-canvas@0.7.3
432
445
  ```
433
446
 
434
447
  The server binds the portal-assigned `$PORT` automatically (gated on the
@@ -444,9 +457,10 @@ be inlined and stay blocked by the portal embed: cross-origin hosted apps (e.g.
444
457
  the hosted Excalidraw MCP app), and PDF file nodes — a PDF node offers an
445
458
  "Open PDF" link there instead of a preview.
446
459
  Debug overrides: `/workbench?transport=poll|sse` and
447
- `?iframe-mode=srcdoc|src`. Once the service is up, `pmx-canvas smoke` verifies
448
- the whole stack (health, versions, MCP handshake, node lifecycle, validation)
449
- in one command.
460
+ `?iframe-mode=srcdoc|src`. After verifying the workspace, `pmx-canvas smoke --port=<actual-port>`
461
+ checks health, versions, a standalone MCP handshake, node lifecycle and validation.
462
+ Use a disposable board: it performs temporary writes and leaves history. This does
463
+ not verify native Amp discovery or browser rendering.
450
464
 
451
465
  ### Install the agent skill (recommended)
452
466
 
@@ -572,9 +572,23 @@ body,
572
572
  box-shadow: 0 4px 24px var(--c-shadow);
573
573
  display: flex;
574
574
  flex-direction: column;
575
- overflow: hidden;
575
+ overflow: visible;
576
576
  transition: box-shadow 0.15s ease;
577
- contain: layout style paint;
577
+ contain: layout style;
578
+ }
579
+
580
+ /* Clip card content, not the connection handles centered on its border. */
581
+ .node-content {
582
+ display: flex;
583
+ flex-direction: column;
584
+ flex: 1;
585
+ min-height: 0;
586
+ border-radius: inherit;
587
+ overflow: hidden;
588
+ contain: paint;
589
+ }
590
+ .group-node > .node-content {
591
+ display: contents;
578
592
  }
579
593
 
580
594
  .canvas-node:hover {
@@ -610,34 +624,42 @@ body,
610
624
  /* ── Connection port handles (drag-to-connect) ───────────── */
611
625
  .node-port {
612
626
  position: absolute;
613
- width: 10px;
614
- height: 10px;
615
- border-radius: 50%;
616
- background: var(--c-accent);
617
- border: 2px solid var(--c-panel);
627
+ /* The visible dot stays compact; its grab area stays usable at every zoom. */
628
+ width: calc(32px * var(--node-port-scale, 1));
629
+ height: calc(32px * var(--node-port-scale, 1));
630
+ display: grid;
631
+ place-items: center;
618
632
  opacity: 0;
619
633
  cursor: crosshair;
620
634
  z-index: 10;
621
- transition: opacity 0.15s, transform 0.15s;
622
- transform: translate(-50%, -50%) scale(0.6);
635
+ touch-action: none;
636
+ transition: opacity 0.15s;
623
637
  pointer-events: auto;
624
638
  }
625
- .canvas-node:hover .node-port {
639
+ .node-port::after {
640
+ content: '';
641
+ width: calc(6px * var(--node-port-scale, 1));
642
+ height: calc(6px * var(--node-port-scale, 1));
643
+ border-radius: 50%;
644
+ background: var(--c-accent);
645
+ border: calc(2px * var(--node-port-scale, 1)) solid var(--c-panel);
646
+ box-shadow: 0 0 0 calc(1px * var(--node-port-scale, 1)) var(--c-accent);
647
+ }
648
+ .canvas-node:hover .node-port,
649
+ .canvas-node.selected .node-port,
650
+ .canvas-node:focus-within .node-port {
626
651
  opacity: 1;
627
- transform: translate(-50%, -50%) scale(1);
628
652
  }
629
- .node-port:hover {
653
+ .node-port:hover::after {
630
654
  background: var(--c-accent-hover);
631
- transform: translate(-50%, -50%) scale(1.3);
632
655
  }
633
- .node-port-top { top: 0; left: 50%; }
634
- .node-port-bottom { bottom: 0; left: 50%; transform: translate(-50%, 50%) scale(0.6); }
635
- .node-port-left { left: 0; top: 50%; }
636
- .node-port-right { right: 0; top: 50%; transform: translate(50%, -50%) scale(0.6); }
637
- .canvas-node:hover .node-port-bottom { transform: translate(-50%, 50%) scale(1); }
638
- .canvas-node:hover .node-port-right { transform: translate(50%, -50%) scale(1); }
639
- .node-port-bottom:hover { transform: translate(-50%, 50%) scale(1.3); }
640
- .node-port-right:hover { transform: translate(50%, -50%) scale(1.3); }
656
+ /* Bias vertical grab areas outward so they do not steal title/footer clicks. */
657
+ .node-port-top { top: 0; left: 50%; transform: translate(-50%, -75%); }
658
+ .node-port-bottom { bottom: 0; left: 50%; transform: translate(-50%, 75%); }
659
+ .node-port-top::after { transform: translateY(calc(8px * var(--node-port-scale, 1))); }
660
+ .node-port-bottom::after { transform: translateY(calc(-8px * var(--node-port-scale, 1))); }
661
+ .node-port-left { left: 0; top: 50%; transform: translate(-50%, -50%); }
662
+ .node-port-right { right: 0; top: 50%; transform: translate(50%, -50%); }
641
663
 
642
664
  .canvas-node .node-titlebar {
643
665
  display: flex;
@@ -2001,6 +2023,7 @@ body,
2001
2023
  color: var(--c-dim);
2002
2024
  }
2003
2025
  .command-bar-composer {
2026
+ position: relative;
2004
2027
  display: flex;
2005
2028
  align-items: center;
2006
2029
  gap: 10px;
@@ -2012,24 +2035,72 @@ body,
2012
2035
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
2013
2036
  }
2014
2037
  .command-bar-target {
2015
- appearance: none;
2016
- -webkit-appearance: none;
2017
2038
  max-width: 148px;
2018
- padding: 4px 18px 4px 8px;
2019
- border: 1px solid var(--c-line);
2020
- border-radius: 7px;
2021
- background: color-mix(in srgb, var(--c-panel) 80%, transparent)
2022
- url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='M1 1l3 3 3-3' fill='none' stroke='%237a8699' stroke-width='1.4'/%3E%3C/svg%3E")
2023
- no-repeat right 6px center;
2024
- color: var(--c-text-soft);
2025
- font: 12px/1.3 var(--mono);
2026
- cursor: pointer;
2027
2039
  flex-shrink: 0;
2028
2040
  }
2029
- .command-bar-target:hover,
2030
- .command-bar-target:focus-visible {
2031
- border-color: var(--c-accent-25);
2041
+ .choice-menu {
2042
+ position: relative;
2043
+ min-width: 0;
2044
+ }
2045
+ .choice-menu-trigger {
2046
+ display: flex;
2047
+ align-items: center;
2048
+ justify-content: space-between;
2049
+ gap: 10px;
2050
+ width: 100%;
2051
+ min-height: 36px;
2052
+ padding: 8px 10px;
2053
+ border: 1px solid color-mix(in srgb, var(--c-line) 65%, var(--c-muted));
2054
+ border-radius: 8px;
2055
+ background: var(--c-panel-overlay);
2032
2056
  color: var(--c-text);
2057
+ font: 12px/1.35 var(--font);
2058
+ cursor: pointer;
2059
+ }
2060
+ .choice-menu-trigger > span {
2061
+ overflow: hidden;
2062
+ text-overflow: ellipsis;
2063
+ white-space: nowrap;
2064
+ }
2065
+ .choice-menu-trigger svg { flex-shrink: 0; }
2066
+ .choice-menu-trigger:hover,
2067
+ .choice-menu-trigger[aria-expanded='true'] { border-color: var(--c-accent); }
2068
+ .choice-menu-trigger:focus-visible,
2069
+ .choice-menu-options button:focus-visible {
2070
+ outline: 2px solid var(--c-accent);
2071
+ outline-offset: 2px;
2072
+ }
2073
+ .toolbar-menu.choice-menu-options {
2074
+ top: calc(100% + 6px);
2075
+ left: 0;
2076
+ right: auto;
2077
+ min-width: 100%;
2078
+ width: max-content;
2079
+ max-width: min(320px, calc(100vw - 64px));
2080
+ max-height: min(280px, 40vh);
2081
+ overflow-y: auto;
2082
+ z-index: 20;
2083
+ }
2084
+ .toolbar-menu.choice-menu-options.opens-above {
2085
+ top: auto;
2086
+ bottom: calc(100% + 6px);
2087
+ }
2088
+ .command-bar-target.choice-menu {
2089
+ position: static;
2090
+ }
2091
+ .command-bar-target .choice-menu-options {
2092
+ /* Anchor to the composer, not its inset trigger, to clear the session rail. */
2093
+ min-width: min(200px, 100%);
2094
+ max-width: 100%;
2095
+ }
2096
+ .choice-menu-options .toolbar-menu-item {
2097
+ min-height: 36px;
2098
+ white-space: normal;
2099
+ overflow-wrap: anywhere;
2100
+ }
2101
+ .choice-menu-options button:disabled {
2102
+ opacity: 0.5;
2103
+ cursor: not-allowed;
2033
2104
  }
2034
2105
 
2035
2106
  .command-bar-icon {
@@ -4498,13 +4569,24 @@ html.is-node-resizing .ext-app-preview-catcher {
4498
4569
  }
4499
4570
  .text-prompt-cancel,
4500
4571
  .text-prompt-confirm {
4501
- padding: 5px 12px;
4572
+ min-height: 36px;
4573
+ padding: 8px 14px;
4502
4574
  border-radius: 7px;
4503
- border: 1px solid var(--c-line);
4504
- background: none;
4505
- color: var(--c-text-soft);
4575
+ border: 1px solid color-mix(in srgb, var(--c-line) 65%, var(--c-muted));
4576
+ background: var(--c-panel-overlay);
4577
+ color: var(--c-text);
4506
4578
  cursor: pointer;
4507
4579
  font-size: 12px;
4580
+ font-weight: 600;
4581
+ }
4582
+ .text-prompt-cancel:hover {
4583
+ background: var(--c-accent-10);
4584
+ border-color: var(--c-muted);
4585
+ }
4586
+ .text-prompt-cancel:focus-visible,
4587
+ .text-prompt-confirm:focus-visible {
4588
+ outline: 2px solid var(--c-accent);
4589
+ outline-offset: 2px;
4508
4590
  }
4509
4591
  .text-prompt-confirm {
4510
4592
  background: var(--c-accent);
@@ -4524,7 +4606,8 @@ html.is-node-resizing .ext-app-preview-catcher {
4524
4606
  overflow-y: auto;
4525
4607
  color: var(--c-text);
4526
4608
  }
4527
- .feedback-dialog label {
4609
+ .feedback-dialog label,
4610
+ .feedback-field {
4528
4611
  display: flex;
4529
4612
  flex-direction: column;
4530
4613
  gap: 6px;