pmx-canvas 0.10.0 → 0.11.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.
Files changed (201) hide show
  1. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  2. package/.github/extensions/pmx-canvas/tools.json +246 -108
  3. package/CHANGELOG.md +32 -0
  4. package/dist/canvas/chunks/abnfDiagram-N423BO3Z-p8dtp6va.js +1 -0
  5. package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +1 -0
  6. package/dist/canvas/chunks/architectureDiagram-T3A2C74G-5jhmxrzf.js +36 -0
  7. package/dist/canvas/chunks/blockDiagram-VBNYF7ZC-pw1fa9yb.js +132 -0
  8. package/dist/canvas/chunks/c4Diagram-5PPSVZJV-x07nq0tt.js +10 -0
  9. package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +1 -0
  10. package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +1 -0
  11. package/dist/canvas/chunks/cose-bilkent-JH36ORCC-nm39npq0.js +1 -0
  12. package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +1 -0
  13. package/dist/canvas/chunks/cynefinDiagram-MW4NZA55-vdxrwahz.js +62 -0
  14. package/dist/canvas/chunks/dagre-VZM6K2ZE-bqdm7wpd.js +4 -0
  15. package/dist/canvas/chunks/diagram-7IWD3JNH-gs5gpr93.js +30 -0
  16. package/dist/canvas/chunks/diagram-B4RE2ZJO-cgwjg47s.js +3 -0
  17. package/dist/canvas/chunks/diagram-LBJQPF4R-w3vahpav.js +24 -0
  18. package/dist/canvas/chunks/diagram-Q27KOJAE-42837z6h.js +24 -0
  19. package/dist/canvas/chunks/diagram-UB23O5K3-4fz83q59.js +41 -0
  20. package/dist/canvas/chunks/ebnfDiagram-BXEA7PRR-zphtgegj.js +1 -0
  21. package/dist/canvas/chunks/erDiagram-JOGREHBK-zvbbdxp4.js +85 -0
  22. package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +1 -0
  23. package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +1 -0
  24. package/dist/canvas/chunks/ganttDiagram-PKOTCBZU-282jzycm.js +292 -0
  25. package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +1 -0
  26. package/dist/canvas/chunks/gitGraphDiagram-DS77QQ5N-vqqy6p1b.js +106 -0
  27. package/dist/canvas/chunks/index-0rk4gp1g.js +2 -0
  28. package/dist/canvas/chunks/index-194m3hc3.js +2 -0
  29. package/dist/canvas/chunks/index-1r72hdzb.js +301 -0
  30. package/dist/canvas/chunks/index-353m7b0p.js +2 -0
  31. package/dist/canvas/chunks/index-3cy1bzwh.js +2 -0
  32. package/dist/canvas/chunks/index-5tv8v1hk.js +1 -0
  33. package/dist/canvas/chunks/index-6ceya556.js +332 -0
  34. package/dist/canvas/chunks/index-6qmsnxwy.js +4 -0
  35. package/dist/canvas/chunks/index-d3b3gqe9.js +2 -0
  36. package/dist/canvas/chunks/index-fjtdggjn.js +2 -0
  37. package/dist/canvas/chunks/index-g3pwnqeb.js +2 -0
  38. package/dist/canvas/chunks/index-gf4e0et4.js +2 -0
  39. package/dist/canvas/chunks/index-hj99nz1f.js +1 -0
  40. package/dist/canvas/chunks/index-nprm96xp.js +16 -0
  41. package/dist/canvas/chunks/index-qj8fxwsf.js +2 -0
  42. package/dist/canvas/chunks/index-r2fyc9w1.js +89 -0
  43. package/dist/canvas/chunks/index-rft2hcz5.js +2 -0
  44. package/dist/canvas/chunks/index-rh7nv68n.js +2 -0
  45. package/dist/canvas/chunks/index-s4j678gj.js +2 -0
  46. package/dist/canvas/chunks/index-tb1wf525.js +179 -0
  47. package/dist/canvas/chunks/index-vv56h019.js +2 -0
  48. package/dist/canvas/chunks/index-w2mra17t.js +207 -0
  49. package/dist/canvas/chunks/index-wgv7en21.js +232 -0
  50. package/dist/canvas/chunks/index-yw8h9vha.js +157 -0
  51. package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +1 -0
  52. package/dist/canvas/chunks/infoDiagram-6WML65LV-v1yvggqj.js +2 -0
  53. package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +70 -0
  54. package/dist/canvas/chunks/journeyDiagram-NVQOT4AX-zjcffveh.js +139 -0
  55. package/dist/canvas/chunks/kanban-definition-27J2QSJJ-dakqtfp1.js +89 -0
  56. package/dist/canvas/chunks/katex-dmg8k4zm.js +257 -0
  57. package/dist/canvas/chunks/mermaid.core-j2geswrd.js +12 -0
  58. package/dist/canvas/chunks/mindmap-definition-FAOFIHXS-142xhr3k.js +96 -0
  59. package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +1 -0
  60. package/dist/canvas/chunks/pegDiagram-VL7TDLO6-5wvk7vsd.js +1 -0
  61. package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +1 -0
  62. package/dist/canvas/chunks/pieDiagram-7S7Q4E2Y-ztdhwycc.js +39 -0
  63. package/dist/canvas/chunks/quadrantDiagram-CIZ2JOQS-c5p9gmg6.js +7 -0
  64. package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +1 -0
  65. package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +1 -0
  66. package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +1 -0
  67. package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +1 -0
  68. package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +1 -0
  69. package/dist/canvas/chunks/railroadDiagram-AXF67PYL-xkmqkjwf.js +1 -0
  70. package/dist/canvas/chunks/requirementDiagram-LRYGKXZP-9wm2dyaw.js +84 -0
  71. package/dist/canvas/chunks/sankeyDiagram-W5VNT64P-2w19etgb.js +40 -0
  72. package/dist/canvas/chunks/sequenceDiagram-SI44F4Z6-0jn0vwmz.js +162 -0
  73. package/dist/canvas/chunks/sizeCapture-X5ZJPWSS-x75epwdq.js +1 -0
  74. package/dist/canvas/chunks/stateDiagram-OKZ733FA-schjpy6d.js +1 -0
  75. package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +1 -0
  76. package/dist/canvas/chunks/swimlanes-SLNWSIFB-gkw4xce3.js +1 -0
  77. package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +8 -0
  78. package/dist/canvas/chunks/timeline-definition-Z64GVDOM-x7nkcy01.js +120 -0
  79. package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +1 -0
  80. package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +1 -0
  81. package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +34 -0
  82. package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +1 -0
  83. package/dist/canvas/chunks/wardleyDiagram-T6FBY63Y-t1j52t9r.js +78 -0
  84. package/dist/canvas/chunks/xychartDiagram-ELKLHX3M-38t16pme.js +7 -0
  85. package/dist/canvas/global.css +389 -229
  86. package/dist/canvas/index.js +141 -211
  87. package/dist/canvas/mcp-app.html +4205 -554
  88. package/dist/canvas/mermaid-entry.js +338 -338
  89. package/dist/canvas/surface-theme.css +6 -6
  90. package/dist/json-render/index.css +1 -1
  91. package/dist/json-render/index.js +49 -49
  92. package/dist/types/client/canvas/BarHint.d.ts +5 -3
  93. package/dist/types/client/canvas/CanvasNode.d.ts +0 -11
  94. package/dist/types/client/canvas/EdgeLayer.d.ts +1 -2
  95. package/dist/types/client/canvas/auto-fit.d.ts +4 -5
  96. package/dist/types/client/mermaid-entry.d.ts +1 -2
  97. package/dist/types/client/mermaid-render.d.ts +1 -0
  98. package/dist/types/client/nodes/BoardNode.d.ts +1 -1
  99. package/dist/types/client/nodes/ExtAppFrame.d.ts +7 -90
  100. package/dist/types/client/nodes/McpAppNode.d.ts +1 -25
  101. package/dist/types/client/nodes/MermaidNode.d.ts +1 -8
  102. package/dist/types/client/nodes/ViewerFrame.d.ts +29 -0
  103. package/dist/types/client/nodes/ViewerHost.d.ts +6 -0
  104. package/dist/types/client/nodes/iframe-document-url.d.ts +3 -9
  105. package/dist/types/client/nodes/primitive-copy.d.ts +5 -0
  106. package/dist/types/client/nodes/use-iframe-content-height.d.ts +1 -1
  107. package/dist/types/client/nodes/use-surface-frame.d.ts +4 -6
  108. package/dist/types/client/nodes/viewer-runtime-state.d.ts +2 -1
  109. package/dist/types/client/state/canvas-store.d.ts +3 -1
  110. package/dist/types/client/state/intent-bridge.d.ts +1 -0
  111. package/dist/types/client/state/presentation.d.ts +17 -6
  112. package/dist/types/json-render/server.d.ts +2 -1
  113. package/dist/types/server/canvas-state.d.ts +2 -0
  114. package/dist/types/server/index.d.ts +10 -5
  115. package/dist/types/server/operations/ops/boards.d.ts +0 -1
  116. package/dist/types/server/tour-control.d.ts +13 -3
  117. package/dist/types/shared/ax-surface-protocol.d.ts +0 -9
  118. package/dist/types/shared/tour.d.ts +8 -0
  119. package/docs/design/Chrome.dc.html +32 -6
  120. package/docs/design/Types1.dc.html +3 -2
  121. package/docs/design/Types2.dc.html +1 -1
  122. package/docs/design/Types3.dc.html +1 -1
  123. package/docs/design/Zoom.dc.html +37 -60
  124. package/docs/design/canvas.json +1 -1
  125. package/docs/design.md +169 -4
  126. package/docs/http-api.md +51 -1
  127. package/docs/mcp.md +8 -0
  128. package/docs/node-types.md +38 -3
  129. package/docs/plans/plan-009-tech-debt-backlog.md +2 -2
  130. package/docs/plans/plan-019-post-0.9-roadmap.md +283 -11
  131. package/docs/product-vision-2026-09.md +9 -0
  132. package/package.json +2 -2
  133. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  134. package/plugins/pmx-canvas/plugin.json +1 -1
  135. package/plugins/pmx-canvas/runtime.json +1 -1
  136. package/plugins/pmx-canvas/tools.json +246 -108
  137. package/skills/pmx-canvas/SKILL.md +11 -18
  138. package/skills/pmx-canvas/references/full-reference.md +9 -4
  139. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  140. package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +3 -5
  141. package/src/client/canvas/BarHint.tsx +6 -3
  142. package/src/client/canvas/CanvasNode.tsx +34 -47
  143. package/src/client/canvas/CanvasViewport.tsx +6 -7
  144. package/src/client/canvas/ContextMenu.tsx +63 -3
  145. package/src/client/canvas/EdgeLayer.tsx +365 -169
  146. package/src/client/canvas/ExpandedNodeOverlay.tsx +1 -2
  147. package/src/client/canvas/Presentation.tsx +53 -3
  148. package/src/client/canvas/TopBar.tsx +24 -9
  149. package/src/client/canvas/auto-fit.ts +9 -11
  150. package/src/client/canvas/canvas-area.ts +1 -1
  151. package/src/client/canvas/use-focus-trap.ts +6 -1
  152. package/src/client/mermaid-entry.ts +6 -20
  153. package/src/client/mermaid-render.ts +39 -0
  154. package/src/client/nodes/AxStepControls.tsx +45 -69
  155. package/src/client/nodes/BoardNode.tsx +65 -9
  156. package/src/client/nodes/ContextNode.tsx +88 -396
  157. package/src/client/nodes/ExtAppFrame.tsx +283 -1462
  158. package/src/client/nodes/FileNode.tsx +92 -81
  159. package/src/client/nodes/HtmlNode.tsx +8 -36
  160. package/src/client/nodes/LedgerNode.tsx +31 -17
  161. package/src/client/nodes/MarkdownNode.tsx +8 -17
  162. package/src/client/nodes/McpAppNode.tsx +48 -154
  163. package/src/client/nodes/MermaidNode.tsx +80 -69
  164. package/src/client/nodes/PromptNode.tsx +12 -14
  165. package/src/client/nodes/ResponseNode.tsx +8 -6
  166. package/src/client/nodes/StatusNode.tsx +22 -7
  167. package/src/client/nodes/StatusSummary.tsx +5 -3
  168. package/src/client/nodes/TraceNode.tsx +8 -8
  169. package/src/client/nodes/ViewerFrame.tsx +262 -0
  170. package/src/client/nodes/ViewerHost.tsx +41 -0
  171. package/src/client/nodes/WebpageNode.tsx +22 -24
  172. package/src/client/nodes/iframe-document-url.ts +20 -27
  173. package/src/client/nodes/primitive-copy.ts +97 -0
  174. package/src/client/nodes/use-iframe-content-height.ts +34 -10
  175. package/src/client/nodes/use-surface-frame.ts +22 -11
  176. package/src/client/nodes/viewer-runtime-state.ts +41 -35
  177. package/src/client/state/canvas-store.ts +18 -12
  178. package/src/client/state/intent-bridge.ts +1 -0
  179. package/src/client/state/presentation.ts +135 -15
  180. package/src/client/state/sse-bridge.ts +18 -19
  181. package/src/client/theme/global.css +389 -229
  182. package/src/client/theme/surface-theme.css +6 -6
  183. package/src/json-render/charts/tufte-components.tsx +44 -2
  184. package/src/json-render/renderer/index.css +62 -2
  185. package/src/json-render/renderer/index.tsx +31 -7
  186. package/src/json-render/server.ts +3 -2
  187. package/src/server/board-export.ts +35 -24
  188. package/src/server/canvas-geometry.ts +1 -0
  189. package/src/server/canvas-state.ts +5 -0
  190. package/src/server/demo-state.json +106 -100
  191. package/src/server/html-primitives.ts +3 -9
  192. package/src/server/html-surface.ts +8 -0
  193. package/src/server/index.ts +19 -9
  194. package/src/server/operations/mcp-contracts.ts +17 -2
  195. package/src/server/operations/ops/boards.ts +1 -3
  196. package/src/server/operations/ops/viewport.ts +11 -9
  197. package/src/server/server.ts +7 -59
  198. package/src/server/tour-control.ts +66 -6
  199. package/src/shared/ax-surface-protocol.ts +0 -11
  200. package/src/shared/content-height-reporter.ts +4 -3
  201. package/src/shared/tour.ts +8 -0
@@ -13,7 +13,7 @@
13
13
  body{margin:0}
14
14
  </style>
15
15
  </helmet>
16
- <div style="width: 1440px; height: 1000px; box-sizing: border-box; padding: 40px 48px; background: #081524; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: grid; grid-template-columns: 500px repeat(2, minmax(0, 1fr)); gap: 40px">
16
+ <div style="width: 1440px; height: 1360px; box-sizing: border-box; padding: 40px 48px; background: #081524; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: grid; grid-template-columns: 500px repeat(2, minmax(0, 1fr)); gap: 40px">
17
17
 
18
18
  <section style="display: flex; flex-direction: column; gap: 14px">
19
19
  <div><h2 style="margin: 0; font-size: 15px; font-weight: 600">7 · Tool rail in a 690px-tall window</h2><p style="margin: 2px 0 0; font-size: 12px; color: #8ea3bd">20 buttons need 850px. Settings-type tools move into one menu.</p></div>
@@ -62,7 +62,7 @@ body{margin:0}
62
62
  </section>
63
63
 
64
64
  <section style="display: flex; flex-direction: column; gap: 14px">
65
- <div><h2 style="margin: 0; font-size: 15px; font-weight: 600">8 · Node header</h2><p style="margin: 2px 0 0; font-size: 12px; color: #8ea3bd">Expand and the context pin always show; ⋯ on hover opens the existing node menu (now with Open in new tab), and Set as README stays in the markdown footer.</p></div>
65
+ <div><h2 style="margin: 0; font-size: 15px; font-weight: 600">8 · Node header</h2><p style="margin: 2px 0 0; font-size: 12px; color: #8ea3bd">Expand and the context pin always show; ⋯ on hover or keyboard focus opens the node menu. Set as README lives here, becoming Remove as README on the current README. Edit stays visible below Markdown.</p></div>
66
66
 
67
67
  <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Today</span>
68
68
  <div style="border-radius: 12px 12px 8px 8px; border: 1px solid #1b2c44; background: #0f1d31; box-shadow: 0 0 0 1px rgba(244,197,66,0.3), 0 8px 24px rgba(244,197,66,0.1); overflow: hidden">
@@ -78,7 +78,8 @@ body{margin:0}
78
78
  <div style="border-radius: 10px; border: 1px solid #1b2c44; background: #0f1d31; box-shadow: 0 1px 2px rgba(0,0,0,0.4), 0 6px 20px rgba(0,0,0,0.24); overflow: hidden">
79
79
  <div style="display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 8px 0 12px; border-bottom: 1px solid #1b2c44">
80
80
  <svg width="16" height="16" viewBox="0 0 24 24" style="color: #8ea3bd; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round"><path d="M6 5h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2zM8 10h8M8 14h5"></path></svg>
81
- <span style="flex: 1; font-size: 12px; font-weight: 600">PMX Canvas — guided tour</span>
81
+ <span style="flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 12px; font-weight: 600">PMX Canvas — guided tour</span>
82
+ <span style="flex-shrink: 0; font-size: 11px; color: #8ea3bd">README</span>
82
83
  <button aria-label="Expand (Enter)" style="width: 22px; height: 22px; border: 0; border-radius: 6px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="13" height="13" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round"><path d="M14 4h6v6M20 4l-7 7M10 20H4v-6M4 20l7-7"></path></svg></button>
83
84
  <button aria-label="Add to context (P)" style="width: 18px; height: 18px; box-sizing: border-box; border: 1.5px solid #6b7d94; border-radius: 999px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="10" height="10" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round"><path d="M9 4h6l-.8 5.5 2.8 3H7l2.8-3zM12 12.5V20"></path></svg></button>
84
85
  <!-- Reserve the hover-action slots: revealing More/Close must not move the pin. -->
@@ -111,7 +112,8 @@ body{margin:0}
111
112
  <div style="height: 236px; display: flex; flex-direction: column; border: 1px solid #1b2c44; border-radius: 10px; overflow: hidden; background: #0f1d31">
112
113
  <div style="flex-shrink: 0; height: 36px; display: flex; align-items: center; padding: 0 12px; border-bottom: 1px solid #1b2c44; font-size: 12px; font-weight: 600">Research index</div>
113
114
  <div style="flex: 1; min-height: 0; display: flex; flex-direction: column">
114
- <div style="flex: 1; min-height: 0; padding: 10px 12px; overflow: auto; font-size: 13px; color: #b9c6d6">Read the source, then follow <a href="#reference-footer" style="color: #e6eef7">the evidence</a>.<div style="margin-top: 20px; font-size: 12px; color: #8ea3bd">Set as README <span style="float: right">Edit</span></div></div>
115
+ <div style="flex: 1; min-height: 0; padding: 10px 12px; overflow: auto; font-size: 13px; color: #b9c6d6">Read the source, then follow <a href="#reference-footer" style="color: #e6eef7">the evidence</a>.</div>
116
+ <div style="flex-shrink: 0; border-top: 1px solid #1b2c44; padding: 6px 36px 6px 8px; text-align: right"><button style="border: 1px solid transparent; border-radius: 6px; padding: 4px 8px; background: transparent; color: #8ea3bd; font: 500 11px 'IBM Plex Sans', sans-serif">Edit</button></div>
115
117
  <div id="reference-footer" role="region" aria-label="Cross-board references" tabindex="0" style="flex-shrink: 0; max-height: min(40%, 132px); overflow: auto; overscroll-behavior: contain">
116
118
  <div style="padding: 6px 12px; border-top: 1px solid #1b2c44; font-size: 12px; color: #b9c6d6">↗ references: Research / Interview notes</div>
117
119
  <div style="padding: 6px 12px; border-top: 1px solid #1b2c44; font-size: 12px; color: #b9c6d6">↗ references: Research / Findings</div>
@@ -174,11 +176,35 @@ body{margin:0}
174
176
  <span style="font-size: 11px; color: #8ea3bd">Fit and focus keep a lane clear for floating chrome.</span>
175
177
  </div>
176
178
  </div>
179
+
180
+ <div style="display: flex; flex-direction: column; gap: 8px">
181
+ <h2 style="margin: 0; font-size: 15px; font-weight: 600">Present · hover / keyboard focus</h2>
182
+ <p style="margin: 0; font-size: 11px; color: #8ea3bd">2026-10-25: the button describes this tooltip with aria-describedby. In the narrow ⋯ menu, the same text appears in flow below Present so the scrolling menu cannot clip it.</p>
183
+ <div role="tooltip" style="max-width: 240px; padding: 10px 12px; border: 1px solid #2a4566; border-radius: 10px; background: #0f1d31; color: #b9c6d6; font-size: 12px; line-height: 1.35">
184
+ <strong style="color: #e6eef7">Present board</strong>
185
+ <p>A presentation follows either a saved tour or the board’s groups.</p>
186
+ <p>Saved tour: ask your agent to define the stops and their order. Each stop can focus on a card, a group, a rectangular area, or a specific camera position.</p>
187
+ <p style="margin-bottom: 0">No saved tour: every group becomes a stop, ordered top-to-bottom, then left-to-right. It doesn’t automatically visit every card.</p>
188
+ </div>
189
+ </div>
190
+ <div style="display: flex; flex-direction: column; gap: 8px">
191
+ <h2 style="margin: 0; font-size: 15px; font-weight: 600">Presentation · navigation</h2>
192
+ <p style="margin: 0; font-size: 11px; color: #8ea3bd">Owner decision, 2026-10-25: Back and Next sit beside Exit at the top-right. Instructions appear only in hover/focus tooltips, never in a corner panel. End buttons stay focusable but unavailable; their descriptions remain accessible.</p>
193
+ <div style="position: relative; height: 180px; border: 1px solid #1b2c44; border-radius: 10px; background: #081524">
194
+ <div style="position: absolute; top: 16px; right: 16px; display: flex; gap: 8px">
195
+ <button style="padding: 8px 14px; min-height: 38px; border: 1px solid #8ea3bd; border-radius: 8px; background: #0f1d31; color: #e6eef7; font: inherit; font-weight: 600; opacity: .5" aria-disabled="true">Back</button>
196
+ <button style="padding: 8px 14px; min-height: 38px; border: 1px solid #8ea3bd; border-radius: 8px; background: #0f1d31; color: #e6eef7; font: inherit; font-weight: 600">Next</button>
197
+ <button style="padding: 8px 14px; min-height: 38px; border: 1px solid #8ea3bd; border-radius: 8px; background: #0f1d31; color: #e6eef7; font: inherit; font-weight: 600">Exit presentation</button>
198
+ </div>
199
+ <div style="position: absolute; top: 66px; right: 16px; width: 168px; padding: 10px 12px; border: 1px solid #2a4566; border-radius: 10px; background: #0f1d31; box-shadow: 0 14px 36px #0006; font-size: 12px"><strong>Next stop</strong><br><span style="color: #8ea3bd">→ / ↓ / Space</span></div>
200
+ </div>
201
+ <p style="margin: 0; font-size: 11px; color: #8ea3bd">Back: Previous stop, ← / ↑. Next: Next stop, → / ↓ / Space. Exit: Exit presentation, Esc. Fit clears the top controls; exact camera targets and recording remain exact.</p>
202
+ </div>
177
203
  </section>
178
204
 
179
205
  </div>
180
206
  </x-dc>
181
- <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":1000}}'>
207
+ <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":1360}}'>
182
208
  class Component extends DCLogic {
183
209
  renderVals() {
184
210
  var P = {
@@ -245,7 +271,7 @@ class Component extends DCLogic {
245
271
  optIn('Trace', 'Show execution traces. Turning off keeps existing traces and never starts an agent.', false, false),
246
272
  setting('Send feedback', '', 'Share feedback about PMX Canvas.')
247
273
  ];
248
- var nodeMenu = [m('Focus', ''), m('Rename…', ''), m('Collapse', ''), m('Open in new tab', '', true), msep, m('Delete', '⌫')];
274
+ var nodeMenu = [m('Focus', ''), m('Rename…', ''), m('Collapse', ''), m('Open in new tab', ''), msep, m('Set as README', '', true), msep, m('Delete', '⌫')];
249
275
  var oldIcons = [{ d: P.ctx }, { d: P.ask }, { d: P.open }, { d: P.expand }, { d: P.chevron }, { d: P.close }];
250
276
  return { railToday: railToday, railNew: railNew, settings: settings, nodeMenu: nodeMenu, oldIcons: oldIcons };
251
277
  }
@@ -22,7 +22,7 @@ body{margin:0}
22
22
  </div>
23
23
 
24
24
  <div style="{{s.row}}">
25
- <div><div style="{{s.type}}">markdown</div><p style="{{s.what}}">Body 11 → 12px. “Set as README / Edit” footer moves into ⋯; double-click still edits.</p></div>
25
+ <div><div style="{{s.type}}">markdown</div><p style="{{s.what}}">Body 11 → 12px. Set as README moves into ⋯, becoming Remove as README when assigned. A quiet README header word stays visible on the designated card, including heading display (Chrome §8). Edit stays visible; double-click still edits. Heading display has no footer.</p></div>
26
26
  <div style="{{o.node}}">
27
27
  <div style="{{o.head}}"><svg width="16" height="16" viewBox="0 0 24 24" style="{{o.icon}} color: #4BBCFF"><path d="{{i.note}}"></path></svg><span style="{{o.title}}">PMX Canvas — guided tour</span><span style="{{o.ctl}}">⤢⌄×</span></div>
28
28
  <div style="padding: 10px 14px; display: flex; flex-direction: column; gap: 6px">
@@ -39,6 +39,7 @@ body{margin:0}
39
39
  <div style="color: #b9c6d6">An infinite spatial canvas that agents drive over MCP, HTTP or the Bun SDK — and that humans steer by pinning nodes.</div>
40
40
  <div style="color: #b9c6d6">• Pin a node in the browser<br>• The agent reads it from <code style="{{n.code}}">canvas://pinned-context</code></div>
41
41
  </div>
42
+ <div style="border-top: 1px solid #1b2c44; padding: 6px 36px 6px 8px; text-align: right"><button style="border: 1px solid transparent; border-radius: 6px; padding: 4px 8px; background: transparent; color: #8ea3bd; font: 500 11px 'IBM Plex Sans', sans-serif">Edit</button></div>
42
43
  </div>
43
44
  </div>
44
45
 
@@ -120,7 +121,7 @@ body{margin:0}
120
121
  </div>
121
122
 
122
123
  <div style="{{s.row}}">
123
- <div><div style="{{s.type}}">file</div><p style="{{s.what}}">The extra toolbar row folds into the header (path · lines). Code 10 → 12px with dim line numbers.</p></div>
124
+ <div><div style="{{s.type}}">file</div><p style="{{s.what}}">The extra toolbar row folds into the header (path · lines). Only metadata clips as space tightens; Reload and AX evidence hints remain visible on hover/focus. Code 10 → 12px with dim line numbers.</p></div>
124
125
  <div style="{{o.node}}">
125
126
  <div style="{{o.head}}"><svg width="16" height="16" viewBox="0 0 24 24" style="{{o.icon}} color: #4BBCFF"><path d="{{i.file}}"></path></svg><span style="{{o.title}}">node-sizes.ts</span><span style="{{o.ctl}}">⤢⌄×</span></div>
126
127
  <div style="display: flex; align-items: center; gap: 6px; padding: 4px 12px; border-bottom: 1px solid #1b2c44; font-family: 'IBM Plex Mono', monospace; font-size: 9px; color: #8ea3bd"><span style="padding: 1px 4px; border-radius: 3px; background: #13243b; color: #4BBCFF">TS</span><span>46 lines</span><span style="color: #5c6b80">src/shared/node-sizes.ts</span></div>
@@ -90,7 +90,7 @@ body{margin:0}
90
90
  </div>
91
91
 
92
92
  <div style="{{s.row}}">
93
- <div><div style="{{s.type}}">html</div><p style="{{s.what}}">The white outline around the frame goes. The page gets the canvas tokens, so it sits on the node surface.</p></div>
93
+ <div><div style="{{s.type}}">html</div><p style="{{s.what}}">The white outline around the frame goes. The page gets the canvas tokens, so it sits on the node surface. Owner decision, 2026-10-25: generated primitives have no shared header copy buttons. Copy JSON lives in the card's ⋯ menu and includes live edits. Copy prompt is removed; primitive-specific copy controls remain.</p></div>
94
94
  <div style="{{o.node}}">
95
95
  <div style="{{o.head}}"><svg width="16" height="16" viewBox="0 0 24 24" style="{{o.icon}} color: #4BBCFF"><path d="{{i.html}}"></path></svg><span style="{{o.title}}">Release gates</span><span style="{{o.ctl}}">↗⤢⌄×</span></div>
96
96
  <div style="flex: 1; margin: 8px; border: 1px solid #f2f2f2; border-radius: 2px; padding: 10px 12px; display: flex; flex-direction: column; gap: 5px">
@@ -49,7 +49,7 @@ body{margin:0}
49
49
  </div>
50
50
 
51
51
  <div style="{{s.row}}">
52
- <div><div style="{{s.type}}">graph</div><p style="{{s.what}}">Chart sits on the node surface. The title isn't repeated inside; the chart type becomes header meta.</p></div>
52
+ <div><div style="{{s.type}}">graph</div><p style="{{s.what}}">Chart sits on the node surface. The title isn't repeated inside; the chart type becomes header meta. Close slopegraph labels separate by 14px when space permits; points stay at their values, with short same-ink leaders.</p><svg width="200" height="60" role="img" aria-label="Close slopegraph labels, values unchanged"><path d="M10 42L70 20M10 52L70 26L75 34" fill="none" stroke="#4BBCFF" stroke-width="1.5"></path><circle cx="70" cy="20" r="2.5" fill="#4BBCFF"></circle><circle cx="70" cy="26" r="2.5" fill="#4BBCFF"></circle><text x="78" y="20" dominant-baseline="central" fill="#e6eef7" font-size="11">59 cli</text><text x="78" y="34" dominant-baseline="central" fill="#e6eef7" font-size="11">58 client</text></svg></div>
53
53
  <div style="{{o.node}}">
54
54
  <div style="{{o.head}}"><svg width="16" height="16" viewBox="0 0 24 24" style="{{o.icon}} color: #f4c542"><path d="{{i.chart}}"></path></svg><span style="{{o.title}}">bar — merged PRs per week</span><span style="{{o.ctl}}">↗⤢⌄×</span></div>
55
55
  <div style="flex: 1; margin: 0 8px 8px; position: relative; background: #000000; border-radius: 8px; padding: 10px 12px; font-family: system-ui, sans-serif">
@@ -2,79 +2,56 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <title>Zoomed out — fit all</title>
5
+ <title>Native spatial zoom</title>
6
6
  <script src="./support.js"></script>
7
7
  </head>
8
8
  <body>
9
9
  <x-dc>
10
10
  <helmet>
11
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&amp;family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap">
12
- <style>
13
- body{margin:0}
14
- </style>
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap">
12
+ <style>body{margin:0}</style>
15
13
  </helmet>
16
- <div style="width: 1440px; height: 640px; box-sizing: border-box; padding: 40px 48px; background: #081524; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: flex; flex-direction: column; gap: 20px">
17
- <div><h2 style="margin: 0; font-size: 15px; font-weight: 600">11 · Fit all on the demo board lands at 8%</h2><p style="margin: 2px 0 0; font-size: 12px; color: #8ea3bd">41 of 70 nodes are embedded frames and render as empty cards. Below ~25%, draw each node as its kind and title at screen size.</p></div>
18
- <div style="flex: 1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px">
19
-
20
- <div style="display: flex; flex-direction: column; gap: 8px">
21
- <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Today</span>
22
- <div style="position: relative; flex: 1; border-radius: 10px; border: 1px solid #1b2c44; overflow: hidden; background: #081524">
23
- <sc-for list="{{sections}}" as="sct" hint-placeholder-count="2">
24
- <div style="position: absolute; left: 24px; top: {{sct.y}}px; width: 600px; height: 10px; border-radius: 3px; background: #0f1d31; border: 1px solid #1b2c44; box-shadow: 0 0 0 1px rgba(244,197,66,0.3)"></div>
14
+ <div style="width:1440px;height:640px;box-sizing:border-box;padding:40px 48px;background:#081524;color:#e6eef7;font-family:'IBM Plex Sans',system-ui,sans-serif;font-size:13px;line-height:1.5">
15
+ <h2 style="margin:0;font-size:20px;font-weight:600">11 · Keep the native board at every zoom</h2>
16
+ <p style="color:#8ea3bd">Maintainer decision, 2026-10-25. The same positions, sizes, groups, connections and contents scale together. No alternate grid or content enlargement.</p>
17
+ <div style="display:flex;gap:24px;margin-top:28px">
18
+ <sc-for list="{{views}}" as="view" hint-placeholder-count="3">
19
+ <section style="width:432px">
20
+ <h3 style="font-size:14px">{{view.label}}</h3>
21
+ <svg width="432" height="380" viewBox="0 0 432 380" style="border:1px solid #1b2c44;border-radius:10px;background:#081524">
22
+ <g transform="translate(16 44)">
23
+ <g transform="scale({{view.scale}})">
24
+ <rect x="0" y="0" width="396" height="308" rx="8" fill="none" stroke="#536c88"/>
25
+ <text x="12" y="22" fill="#e6eef7" font-size="16">Release evidence</text>
26
+ <path d="M182 128 L218 218" stroke="#6b7d94" stroke-width="2" fill="none"/>
27
+ <path d="M211 213 L218 218 L218 210" stroke="#6b7d94" stroke-width="2" fill="none"/>
28
+ <g transform="translate(12 36)">
29
+ <rect width="170" height="154" rx="8" fill="#0f1d31" stroke="#1b2c44"/>
30
+ <text x="12" y="22" fill="#e6eef7" font-size="{{view.titleSize}}">Build durations</text>
31
+ <path d="M14 126 L45 99 L77 110 L110 72 L153 84" fill="none" stroke="#62c4b8" stroke-width="3"/>
32
+ <path d="M14 44 V138 H156" fill="none" stroke="#536c88"/>
33
+ <text x="16" y="59" fill="#8ea3bd" font-size="11">37 → 19 minutes</text>
34
+ </g>
35
+ <g transform="translate(218 146)">
36
+ <rect width="166" height="146" rx="8" fill="#0f1d31" stroke="#1b2c44"/>
37
+ <text x="12" y="22" fill="#e6eef7" font-size="{{view.titleSize}}">Release gates</text>
38
+ <text x="12" y="56" fill="#e6eef7" font-size="13">✓ Tests passed</text>
39
+ <text x="12" y="82" fill="#e6eef7" font-size="13">✓ Review complete</text>
40
+ <text x="12" y="108" fill="#8ea3bd" font-size="12">Source: build report</text>
41
+ </g>
42
+ </g>
43
+ </g>
44
+ </svg>
45
+ </section>
25
46
  </sc-for>
26
- <sc-for list="{{tiles}}" as="t" hint-placeholder-count="20">
27
- <div style="position: absolute; left: {{t.x}}px; top: {{t.y}}px; width: 112px; height: 72px; box-sizing: border-box; border-radius: 4px; background: #0f1d31; border: 1px solid #1b2c44; overflow: hidden"><div style="height: 6px; background: #13243b"></div></div>
28
- </sc-for>
29
- <span style="position: absolute; right: 12px; bottom: 10px; font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: #5c6b80">8%</span>
30
- </div>
31
- </div>
32
-
33
- <div style="display: flex; flex-direction: column; gap: 8px">
34
- <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Proposed</span>
35
- <div style="position: relative; flex: 1; border-radius: 10px; border: 1px solid #1b2c44; overflow: hidden; background: #081524">
36
- <sc-for list="{{sections}}" as="sct" hint-placeholder-count="2">
37
- <div style="position: absolute; left: 24px; top: {{sct.yt}}px; font-size: 15px; font-weight: 600; line-height: 20px">{{sct.title}}</div>
38
- </sc-for>
39
- <sc-for list="{{tiles}}" as="t" hint-placeholder-count="20">
40
- <div style="position: absolute; left: {{t.x}}px; top: {{t.y}}px; width: 112px; height: 72px; box-sizing: border-box; padding: 8px 10px; border-radius: 6px; background: #0f1d31; border: 1px solid #1b2c44; display: flex; flex-direction: column; gap: 4px">
41
- <div style="display: flex; align-items: center; gap: 6px"><svg width="14" height="14" viewBox="0 0 24 24" style="flex-shrink: 0; color: #8ea3bd; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round"><path d="{{t.icon}}"></path></svg><span style="font-size: 11px; color: #8ea3bd">{{t.kind}}</span></div>
42
- <span style="font-size: 12px; font-weight: 600; line-height: 1.3">{{t.title}}</span>
43
- </div>
44
- </sc-for>
45
- <span style="position: absolute; right: 12px; bottom: 10px; font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: #6b7d94">8%</span>
46
- </div>
47
- </div>
48
-
49
47
  </div>
48
+ <p style="color:#8ea3bd">Automatic previews stay on the board. Zoom or expand to read details. Title text grows at most 1.2×; the header line and content space stay fixed.</p>
50
49
  </div>
51
50
  </x-dc>
52
51
  <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":640}}'>
53
52
  class Component extends DCLogic {
54
53
  renderVals() {
55
- var chart = 'M4 4v16h16M8 16v-4M12 16V8M16 16v-6';
56
- var html = 'M8 8l-4 4 4 4M16 8l4 4-4 4M13.5 5l-3 14';
57
- var charts = [
58
- ['line', 'p95 latency'], ['bar', 'Merged PRs'], ['area', 'Nodes created'], ['pie', 'Node types'], ['stacked', 'CI minutes'],
59
- ['composed', 'Throughput'], ['scatter', 'PR size'], ['radar', 'Coverage'], ['sparkline', 'Build time'], ['bullet', 'Release KPIs']
60
- ];
61
- var prims = [
62
- ['html', 'Choice grid'], ['html', 'Plan timeline'], ['html', 'Review sheet'], ['html', 'PR write-up'], ['html', 'System map'],
63
- ['html', 'Walkthrough'], ['html', 'Design sheet'], ['html', 'Component gallery'], ['html', 'Flowchart'], ['html', 'Triage board']
64
- ];
65
- var sections = [
66
- { y: 22, yt: 18, title: '④ Charts' },
67
- { y: 222, yt: 218, title: '⑦ HTML primitives' }
68
- ];
69
- var tiles = [];
70
- function place(list, top, icon) {
71
- list.forEach(function (it, i) {
72
- tiles.push({ x: 24 + (i % 5) * 122, y: top + Math.floor(i / 5) * 82, kind: it[0], title: it[1], icon: icon });
73
- });
74
- }
75
- place(charts, 50, chart);
76
- place(prims, 250, html);
77
- return { sections: sections, tiles: tiles };
54
+ return { views: [{ label: '100%', scale: 1, titleSize: 12 }, { label: '39%', scale: 0.39, titleSize: 14.4 }, { label: '20%', scale: 0.2, titleSize: 14.4 }] };
78
55
  }
79
56
  }
80
57
  </script>
@@ -38,7 +38,7 @@
38
38
  "y": 10260
39
39
  },
40
40
  "Chrome.dc.html": {
41
- "h": 1000,
41
+ "h": 1360,
42
42
  "title": "Rail, node header, section labels",
43
43
  "w": 1440,
44
44
  "x": 1520,
package/docs/design.md CHANGED
@@ -87,8 +87,8 @@ Components reference tokens only. Raw colour values live only in the theme files
87
87
  - Tints are derived — `color-mix(in srgb, var(--c-pin) 12%, transparent)` — so the 22 alpha
88
88
  copies, `--c-warn-alt` and `--c-glow-accent` go.
89
89
  - `--c-thinking` becomes `--c-agent`.
90
- - **Kinds carry no colour.** Type icons use `--c-muted`; the glyph tells the kind, plus the kind
91
- word on zoomed-out tiles (Zoom board). Minimap rects and group-chip kind dots are neutral, and
90
+ - **Kinds carry no colour.** Type icons use `--c-muted`; the glyph tells the kind.
91
+ Minimap rects and group-chip kind dots are neutral, and
92
92
  only meaning colours mark them (pinned, agent at work). `KIND_COLOR`, `--kind-accent` and
93
93
  `--c-purple` go. Nine kind hues cannot all stay clear of ten meaning colours in nine themes,
94
94
  and every hue a kind borrows is one a meaning loses.
@@ -141,7 +141,7 @@ the same change.
141
141
  | [`Flows.dc.html`](design/Flows.dc.html) | Data lineage with staleness, what a finding rests on, opening a group | 3, 5 |
142
142
  | [`Uses.dc.html`](design/Uses.dc.html) | One relation layer for research, dependencies, decisions and agent flows | 3 |
143
143
  | [`Types1.dc.html`](design/Types1.dc.html)–[`Types3.dc.html`](design/Types3.dc.html) | Every node type, today vs proposed | 0, 4 |
144
- | [`Zoom.dc.html`](design/Zoom.dc.html) | Fit-all readability when frames are unmounted | 4 |
144
+ | [`Zoom.dc.html`](design/Zoom.dc.html) | Native spatial scaling at every zoom, maintainer override 2026-10-25 | 4 |
145
145
  | [`Pane600.dc.html`](design/Pane600.dc.html) | Home and a board at the narrow end, 600 px | all |
146
146
  | [`Vision.dc.html`](design/Vision.dc.html) | How each design area maps to the vision, and the open decisions | — |
147
147
 
@@ -377,7 +377,7 @@ workflow. A demo of existing features must not be used to claim full sketch pari
377
377
  | **1. Context made visible** — its curation evaluation moved to the end of the plan (2026-10-19) | Part 1 bet, moves 0a, 2, 7 | Surface `context_reads` (already recorded since 0.7.0: who read, which nodes were delivered) per node and per board as read / not read / changed since read, using revisions; who pinned; activity lens and session receipt from presence activity and revisions; board pins (`board_pins`, tiered brief, `canvas_board pin/unpin`) and Home's pinned-boards section | What the agent did with context, Nodes in context, Board pins, Home (Folders) | From the board alone, a person can answer "did the agent read what I pinned, and is its copy current?" The evaluation in `docs/evals/` reads the same data |
378
378
  | **2. Connected memory views** — implemented, verification gates apply | Moves 0, 14 | One board/card projection for Home, links/backlinks, brief discovery and `canvas://graph{?focus,depth}`; saved wiki ID bindings, direct cross-board card edges, previous-board chains, persisted Map positions | Home Map, Home Graph (deviations below) | Map and Graph are generated from the library; orphans visible; works at 600 px and in a full window |
379
379
  | **3. Relations layer** | Moves 0, 1, 13 | Edge `reason` and an open `kind` with a per-board vocabulary; relations carried in the brief's text form; "derived from" with staleness from provenance and recipes; relation queries for the inspector | Relations, Lineage, One relation layer | Hovering a node explains each link; a changed source turns downstream nodes amber; the agent's brief includes relations |
380
- | **4. Frame host and viewers** | Moves 5, 9 | One frame host passing the full theme tokens; json-render defaults to the canvas theme with an opt-out for design experiments; zoomed-out cards for unmounted frames | Node types 1–3, Zoomed out | No viewer draws its own background; 20 portals mount no more frames than none; fit-all is readable |
380
+ | **4. Frame host and viewers** | Moves 5, 9 | One frame host passing the full theme tokens; json-render defaults to the canvas theme with an opt-out for design experiments; native proportional canvas at every zoom | Node types 1–3, Zoom | Viewers use the canvas palette and fill their authored space; approaching a viewer mounts it automatically and retains its state when panning away; no alternate grid or lifetime preview cap |
381
381
  | **5. Agent output and groups as pages** | Moves 3, 0 | Work items, gates and asks as nodes; make board / inline board; opening a group keeps its outside links | One relation layer (agent flow), Lineage (open a group) | Agent output is styled by the same shell and tokens; a group opened as a board shows its outside stubs |
382
382
  | **Later: hosted version** | Part 4 (moved from Part 3, 2026-10-19) | Read-only link, comments, second writer, accounts and roles; org → team → personal scopes with configurable categories and per-user pins; built once for the ChatGPT Sites and later Cloudflare editions | HomeTeam is the earlier two-root look ahead; updated scope/category drawings are required before building, retaining explicit publication and locked cards for unreadable boards | Exports and shared views inherit the tokens; a viewer sees the same meaning colours; nothing personal reaches a wider audience without explicit publication |
383
383
 
@@ -630,6 +630,171 @@ curation evaluation after its remaining features. Local categories and lifecycle
630
630
  without a model; Jev-style classification is optional longer-term assistance, not a dependency
631
631
  of Wave 3 or the org/team/personal structure.
632
632
 
633
+ ## 0.11 implementation notes
634
+
635
+ Chrome §9–10 and Types1–3 drive the section-heading, header, typography and viewer
636
+ pass. Fit and focus reserve a 44 px screen-space top lane. JSON viewers inherit the
637
+ named canvas palette; only duplicate root titles disappear. Mermaid renders inline.
638
+ File-header metadata may clip within its own text container, but Reload and AX
639
+ evidence actions and their hover/focus hints stay outside that clip (Types1).
640
+ The maintainer's 2026-10-25 decision supersedes the earlier reading-flow footer:
641
+ **Set as README** lives only in the card's ⋯ menu, becoming **Remove as README**
642
+ for the current README. The agent normally assigns it; this menu is the human override.
643
+ The designated card keeps a quiet, always-visible **README** word in its header,
644
+ including heading-display Markdown. It uses neutral metadata ink, not pin blue or
645
+ selection styling; the title yields before this identifier truncates.
646
+ Ordinary Markdown keeps its visible **Edit** action; heading display has no footer.
647
+ Chrome and Types1 reflect this decision. The menu takes keyboard focus, supports
648
+ arrows and Tab, and restores the opener on activation or Escape.
649
+ **Present** explains saved tours versus group-derived stops on hover and keyboard
650
+ focus, with an accessible description. The narrow ⋯ menu shows the same explanation
651
+ in flow below Present rather than clipping a floating tooltip. Chrome records the
652
+ owner's exact copy; no tour behavior changes. Its saved-tour instruction says
653
+ **ask your agent to define the stops**, not that a visual human tour editor exists.
654
+ During presentation, **Back** and **Next** sit beside **Exit presentation** at
655
+ the top-right, following the existing shared server cursor. The owner's final
656
+ placement decision puts instructions only in each button's hover/focus tooltip:
657
+ Previous stop (← / ↑), Next stop (→ / ↓ / Space), Exit presentation (Esc).
658
+ There is no lower-left instruction panel. Unavailable end buttons remain focusable
659
+ for their descriptions. Node/rectangle fitting reserves the controls' measured
660
+ height; explicit camera targets and deterministic recordings remain unchanged.
661
+ HTML primitives put **Copy JSON** in the card's ⋯ menu, not their inner header;
662
+ **Copy prompt** is removed. JSON comes from the current mounted document, including
663
+ unsaved interactive edits, through the nonce-bound sandbox bridge. Unloaded or
664
+ replaced documents and denied clipboard access report failure, not stale data or
665
+ success. Raw HTML and the custom presentation primitive have no JSON-export menu
666
+ action. Primitive-specific Copy diff, Copy SVG and similar controls stay in place.
667
+ The maintainer's 2026-10-25 correction rejects the experimental CSS zoom, content
668
+ enlargement and alternate overview. The original world matrix scales cards,
669
+ embedded contents, groups and connections proportionally at every zoom. Only title
670
+ text grows, up to 1.2× inside a fixed 22 px header line. Header controls, padding and
671
+ body space stay fixed in world pixels; enlarging the whole header would resize the
672
+ iframe viewport and reflow charts while zooming. Card bodies never counter-scale.
673
+ All cards share the same stacking and input boundary. A separate
674
+ ext-app portal was rejected in review because it covered higher cards, bypassed
675
+ pan gestures, and disappeared during presentation. The common world also owns
676
+ clipping when a dock resizes the canvas. Headed Linux WebKit reproduced CSS zoom
677
+ shrinking the iframe's internal layout and retaining stale dimensions through
678
+ zoom transitions. Restoring the matrix keeps inner widths stable in the same probe.
679
+ Native WKWebView compositor health remains unverified, not established by that result.
680
+ Automatic previews mount on first approach at any scale, then retain their
681
+ documents to preserve arbitrary app state. The rejected lifetime cap starved later
682
+ board regions. Memory now grows with visited viewers. Visible viewers start loading
683
+ automatically without a shared bootstrap queue or per-card clicks. A slow document
684
+ does not hold another card's loading slot.
685
+ Each loading replacement temporarily coexists with its painted document.
686
+
687
+ The demo gives ordinary standalone charts the full authored card height instead
688
+ of imposing a smaller plot-height cap. Compact chart types keep compact cards:
689
+ the 36 px sparkline sits in a 120 px card, four dot-plot rows in 240 px, three bullet
690
+ rows in 260 px, and the slopegraph in 300 px. These are demo authoring choices,
691
+ not enforced sizes on user cards. Explicit chart `height` still caps content,
692
+ independently of `nodeHeight`. Dashboard and form stacks explicitly use stretch
693
+ alignment; the catalog's start-aligned default is unchanged, as are compact action
694
+ buttons. Work-item controls must fit their authored cards without accidental
695
+ overflow or truncated status. Enlarging the demo to 440×300 did not meet that
696
+ requirement and is not acceptance evidence. The card body now scrolls only its
697
+ content when needed; step controls and Edit share one fixed footer row. The full
698
+ status word takes priority over the title. Steering input and send button wrap
699
+ together. Expanded documents keep their sticky footer. The demo returns to a
700
+ 240 px height, with 360 and 440 px widths exercised in both Chromium and WebKit.
701
+ Bodies still shrink proportionally.
702
+
703
+ Static exports use the same embedded chart layout and named palettes, retain
704
+ heading display, and match the live 37 px card header. Fonts and shared palette
705
+ CSS are embedded for offline use. Saved positions, sizes and specs are unchanged;
706
+ live work controls are intentionally absent. Export is a separate acceptance
707
+ surface, not established by live canvas checks. Close slopegraph labels separate
708
+ without moving data endpoints; short same-ink leaders retain their association
709
+ (Types3). An explicitly overcrowded chart still requires adequate authored height.
710
+
711
+ Relation labels keep their curve midpoint when clear. Otherwise the pill and its
712
+ hit area move together to nearby open space, avoiding cards, group headers,
713
+ connection handles and earlier labels. A dashed leader in the relation's ink
714
+ ties it back to the curve. Card positions, curve routing and stacking do not
715
+ change. Font measurements determine pill width. Dense boards can still require
716
+ panning to a displaced label; labels remain hidden below the existing 35% floor.
717
+
718
+ `ViewerHost` owns preview admission, without a second positioning or recovery path.
719
+ `RefreshingViewerFrame` owns document loading, the painted replacement handoff and
720
+ Retry. HTML readiness and the ext-app protocol use that host; JSON additionally
721
+ restores its viewer-local state before promotion. The ext-app adapter no longer
722
+ has a separate paint/recovery ladder. Document preparation failures do not fall
723
+ back to another transport that the embedding probe already rejected.
724
+ Ext-app protocol bootstrap runs once after initialization, or after the loaded
725
+ document's bounded notification-only bootstrap interval. The host waits for
726
+ input/result delivery and paint before declaring it ready; bootstrap errors use
727
+ the same Retry as other loading failures.
728
+ Webpages show cached text until an explicit live-preview request; Mermaid
729
+ and native board thumbnails render inline. Mermaid's inline
730
+ and standalone/export renderers share their palette, serialization and sanitizer.
731
+ Inline rendering uses Mermaid's strict security mode and DOMPurify, including
732
+ sanitized `foreignObject` labels, instead of the old diagram iframe sandbox.
733
+ Mermaid is the exception to the static-import rule: the SPA loads it on the first
734
+ diagram, using hashed chunks from the same server. A six-Markdown-card cold-boot
735
+ probe requested only the entry and its small shared chunk, not Mermaid's 3.46 MB.
736
+ With Bun 1.4.4, headed Chromium 145, 4× CPU throttling and five alternating fresh
737
+ contexts per variant, the baseline median was 1,423.9 ms and the candidate median
738
+ 1,349.1 ms to six populated cards plus two animation frames. Ranges overlap
739
+ (1,277.3–1,484.7 ms versus 1,315.2–1,510.1 ms), so this is not a speedup claim.
740
+ The baseline is 0.10.0 rebuilt with the same compiler; both variants use current
741
+ CSS and local intercepted responses, not a network or native-host benchmark.
742
+ Reproduce with `DISPLAY=:99 node scripts/benchmark-canvas-boot.mjs <disposable-server-url> <baseline-index.js>`.
743
+ The populated-board check uses `scripts/benchmark-populated-canvas.mjs`, the same
744
+ 70-card fixture and 18% camera, headed Chromium 145 at 1440×900, and three cold-cache
745
+ loads plus three warm reloads per build, with other browsers and tests stopped.
746
+ All 33 visible viewers rendered. The final comparison found baseline 0.10.0 at
747
+ 6.71–7.67 s cold and 7.17–7.32 s warm; the candidate took 7.59–8.19 s cold and
748
+ 8.00–8.39 s warm. This remains a loading cost, not an orb-only explanation or a
749
+ speedup. Main-page long tasks totalled 4.43–4.87 s on the candidate versus
750
+ 3.46–4.20 s on baseline. Each chart document contains about 1.49 MB decoded
751
+ (baseline 1.47 MB). Parent Resource Timing alone misses baseline iframe bytes
752
+ and can duplicate candidate navigation bytes; do not compare or sum those totals.
753
+ The script records frame resources separately. Removing the provisional four-document queue produced overlapping
754
+ completion ranges and a later first viewer; the simpler independent loading path
755
+ avoids cross-card blocking, not a claimed performance win. Warm-load aborted
756
+ presence requests are recorded separately from viewer failures; no viewer failed.
757
+ At the default 52% camera there are no visible iframe viewers. The candidate's
758
+ board appeared in 0.67–0.70 s cold and 0.52–0.65 s warm versus baseline's
759
+ 1.08–1.32 s and 1.06–1.36 s. That measures deferred offscreen work, not faster
760
+ viewer rendering. Local document responses were 2–6 ms. Portal-URL checks use
761
+ Amp's local hairpin forwarding inside this orb, so they do not measure external
762
+ portal transport or the user's connection. These are instrumented local readiness
763
+ timings, not native paint latency. Linux WebKit's full fit-all load exceeded this orb's memory limit
764
+ on both baseline and candidate; narrower populated-row checks are separate evidence.
765
+ The self-contained MCP App cannot fetch those chunks: its document includes
766
+ Mermaid and grows from about 1.32 MB to 4.78 MB uncompressed. This is an introduced delivery
767
+ cost, not eliminated debt or measured native performance. Keeping one renderer
768
+ and an offline-capable MCP document is the tradeoff; native boot remains unmeasured.
769
+ The bundle audit found one Mermaid core, one shared renderer and one DOMPurify,
770
+ not an embedded copy of the standalone viewer. The old standalone Mermaid bundle
771
+ already weighed 3,492,157 bytes; moving its renderer inline brings that dependency
772
+ tree into the self-contained MCP host. The dependency graph contains different
773
+ major versions of `marked`, `layout-base` and `cose-base` required by Mermaid's
774
+ renderers; forcing aliases would not be a safe deduplication. Inspect the graph
775
+ with `bun build src/client/mcp-app/index.tsx --target=browser --format=iife --minify --metafile=/tmp/mcp-meta.json --outfile=/tmp/mcp.js`.
776
+ Removing the payload from MCP startup requires a different script-delivery/CSP
777
+ contract or a reduced diagram catalog, rather than deleting a duplicate host.
778
+ Static export checks separately cover all twelve graph variants, compact plots,
779
+ all nine named themes and 600, 1024 and 1920 px widths in headed Chromium.
780
+ The populated 70-card export matches live saved geometry and renders offline
781
+ without HTTP(S) requests; bounded Linux WebKit checks cover the compact chart
782
+ section and dashboard. Linked boards and live MCP apps retain honest unavailable
783
+ placeholders. Arbitrary user HTML can still contain external requests.
784
+
785
+ There is no threshold-triggered grid or list. Zoom's revised drawing keeps the
786
+ same positions, sizes, groups, connections and native content at every scale.
787
+ Fit-all is for orientation; zoom or expand to read details. The earlier overview's
788
+ scrolling and overlay-clearance fixes are superseded by its complete removal.
789
+
790
+ Visual audit: Harbor headings use the drawing's 20 px semibold `#e6eef7` on
791
+ `#081524` (15.69:1). The initial dim capture had the existing relation-focus
792
+ highlight active, which applies 0.35 opacity to unrelated cards; Escape clears
793
+ that view state. A neighboring card outside a 600 px viewport is spatially
794
+ clipped, not internally truncated: focusing it restores the complete title.
795
+ Expanded pin hints remain while their button has keyboard `:focus-visible`, even
796
+ after pointer movement; Tab away dismisses the hint after its fade transition.
797
+
633
798
  ## Definition of done for any design work
634
799
 
635
800
  1. It is drawn in the proposal canvas first, at 600 px and at full-window width.
package/docs/http-api.md CHANGED
@@ -217,9 +217,15 @@ content auto-height or indirect parent-group reflow after editing a child.
217
217
  update and are included in snapshots/undo. They are board-wide writes under a scope fence.
218
218
  - `POST /api/canvas/tour/go` with `{ "stop": 2 }` or `{ "step": "next" | "previous" }`
219
219
  (plus optional `"present": false`) moves the shared tour cursor →
220
- `{ ok, index, total, stop }`; 400 when the stop does not exist or the board has no stops.
220
+ `{ ok, index, total, stop, catalogEpoch, boardGeneration, sequence }`;
221
+ 400 when the stop does not exist or the board has no stops.
221
222
  Broadcasts `canvas-tour-step`; idle workbenches start presenting unless `present` is false.
223
+ Response and event share the same monotonically increasing `sequence` within the
224
+ server's `catalogEpoch`. Viewers accept only newer steps for their current epoch
225
+ and board generation, so delayed echoes cannot rewind or replay the camera.
222
226
  - `POST /api/canvas/tour/exit` leaves presentation in every viewer (`canvas-tour-exit`).
227
+ Returns `{ ok, catalogEpoch, boardGeneration, sequence }`; the event shares that
228
+ identity and the step sequence, so an old exit cannot dismiss a newer presentation.
223
229
  - `POST /api/canvas/camera/move` with exactly one of `nodeId`, `rect: { x, y, width, height }`
224
230
  or `viewport: { x, y, scale }`, plus optional `duration`, `easing`, `padding`, `pullback`
225
231
  → `{ ok, stop, viewport }`. Broadcasts `canvas-camera-move`; browsers ease to the target in
@@ -229,6 +235,29 @@ content auto-height or indirect parent-group reflow after editing a child.
229
235
  Tour steps and camera moves are navigation: no undo entry, no agent-presence write, no
230
236
  scope-fence check.
231
237
 
238
+ `tour/go` and `tour/exit` accept optional `presenter: { clientId, intent,
239
+ catalogEpoch, boardGeneration }`. The workbench assigns each document a random ID
240
+ and each gesture an increasing positive safe-integer intent. The server rejects
241
+ an old activation or an intent no newer than that document's accepted watermark
242
+ with `{ ok: true, superseded: true }`, without moving the cursor or emitting an event.
243
+ A structurally valid newer command supersedes older ones even if its stop is missing.
244
+ This lets Present proceed while an earlier Exit request is stalled, without that
245
+ late Exit stopping the restart. Resume reads cannot outlive their originating intent.
246
+
247
+ This is latest-intent-wins, not FIFO: reversed relative Next requests can coalesce.
248
+ Different documents, including a reload, remain independent; unfenced agent calls
249
+ and other documents still act in server arrival order. Reload is not cancellation
250
+ of an old document's pending request. A board activation retains at most 4096
251
+ document IDs, refusing new IDs with 409 rather than evicting an ordering record.
252
+ Switching boards resets that limit; requests stamped with the old activation still
253
+ cannot apply. The SDK exposes the same fence through `goToTourStop(step,
254
+ { presenter })` and `exitTour(presenter)`; fenced go may return `{ superseded: true }`.
255
+
256
+ Presentation's Back/Next buttons share this cursor with keyboard and agent navigation.
257
+ They sit beside Exit at the top; hover/focus tooltips show the shortcuts. Node and
258
+ rectangle targets fit below those controls, while explicit viewport targets remain
259
+ exact. Presentation does not change the saved board viewport.
260
+
232
261
  The [tour model and recording walkthrough](cli.md#tour-and-presentation) describes
233
262
  targets, duration, easing, padding, and pullback. Open `/workbench?present=1` to hide
234
263
  chrome; camera movement is local to that viewer. Recording writes local files via
@@ -388,6 +417,14 @@ stored once per file. Live MCP apps become placeholders; local file contents
388
417
  are left out unless `includeFiles` is true. Files are written to `exports/`
389
418
  beside the database. Built-app files are only embedded when they are regular,
390
419
  bounded-size files inside the board's owned `.pmx-canvas/artifacts/` directory.
420
+ Exports retain saved card positions and sizes, heading display, and the board's
421
+ named theme. Charts and dashboards use embedded card layout without repeating
422
+ the shell title; explicit chart heights remain unchanged. Viewer bundles, palette
423
+ CSS and shell fonts are embedded. Read-only exports omit live work controls.
424
+ When running from source, run `bun run build` before exporting. Export requires
425
+ the complete built viewer assets, `surface-theme.css`, and the font files referenced
426
+ by `global.css`; the installed package includes them. A missing asset tree is not
427
+ a supported text-only export mode.
391
428
  The preview manifest lists card links, remote images, and statically visible
392
429
  network destinations in embedded HTML. Imported source-linked Markdown is also
393
430
  excluded unless `includeDerivedText=true`, independently of `includeFiles`;
@@ -462,6 +499,13 @@ opening that board or changing where writes go. This is supported by
462
499
  return 404. Omit `board` to read the active board. Live AX session/delivery reads
463
500
  and fixed MCP resources continue to describe the open board.
464
501
 
502
+ The workbench's `POST /api/canvas/update` accepts `{updates: [{id, position?, size?,
503
+ collapsed?, zIndex?}], expectedBoardId?, recordHistory?}`. `zIndex` is finite and
504
+ persists stacking without changing geometry. The workbench raises a group and its
505
+ children together, keeping the frame below its children. Title-bar gestures save
506
+ stacking and geometry together on release. Layout notifications and reloads retain
507
+ that order.
508
+
465
509
  ```bash
466
510
  # Get canvas state
467
511
  curl http://localhost:4313/api/canvas/state
@@ -613,6 +657,10 @@ hosted MCP App still refuses file-linked edits. This is not a crash-recovery dis
613
657
  Replacing a JSON-render node's `spec` preserves its current title; include `title`
614
658
  to rename it explicitly. A new authored spec still resets the viewer's local drafts.
615
659
 
660
+ Markdown nodes accept `data: { "display": "heading" | "card" }` on creation and
661
+ update. Heading display removes the card frame and uses its title as a section
662
+ label; it does not alter content, position, or pin membership.
663
+
616
664
  Mermaid nodes accept `data: { "fit": "contain" | "none" }` on both
617
665
  `POST /api/canvas/node` and `PATCH /api/canvas/node/:id`. The default, `contain`,
618
666
  shrinks overflowing diagrams without changing aspect ratio; `none` keeps 100%
@@ -1294,6 +1342,8 @@ all consume this stream. Auto-reconnect with exponential backoff.
1294
1342
 
1295
1343
  The direct workbench's `connected` snapshot renews its per-process
1296
1344
  `workbenchToken` (also embedded in boot HTML) over both SSE and polling.
1345
+ It is followed by `boards-changed` before the layout, so later tour commands
1346
+ already have a board identity even when the separate catalog HTTP read is delayed.
1297
1347
  A same-bundle daemon restart does not reload the page or discard an open draft.
1298
1348
  Registered writes and file saves marked as workbench calls with a supplied stale capability
1299
1349
  return 409 until reconnect renews it; they are never silently recorded as agent
package/docs/mcp.md CHANGED
@@ -146,6 +146,10 @@ tour: { stops: [...] } }` persists an ordered tour; `tour: null` resets it.
146
146
  presents a stop in the workbench, `"tour-exit"` leaves presentation, and
147
147
  `canvas_view { action: "move", nodeId | rect | viewport, duration, easing, padding, pullback }`
148
148
  eases the camera. None of these enter undo history.
149
+ `tour-go` and `tour-exit` also accept the optional document `presenter` fence
150
+ described in [HTTP board tours](http-api.md#board-tours). Superseded requests return
151
+ `{ ok: true, superseded: true }` with no delivery or event. Ordinary agent calls
152
+ omit it and retain shared arrival-order navigation.
149
153
  See the [tour model and CLI walkthrough](cli.md#tour-and-presentation) for stop
150
154
  fields. Recording is a local CLI operation; no remote recording/file-writing
151
155
  tool is added.
@@ -513,6 +517,10 @@ into `persistFile: true` with both preconditions and top-level `content` to save
513
517
  workspace `.md` file and card together. In this mode, `data` accepts only the `rendered` preview;
514
518
  refused saves change neither. See [the file-save contract](http-api.md#nodes).
515
519
 
520
+ For Markdown nodes, `canvas_node` actions `add` and `update` accept
521
+ `data: { display: "heading" | "card" }`. Heading display uses the existing title
522
+ and content as a frameless section label, without changing pin membership.
523
+
516
524
  For Mermaid nodes, `canvas_node` actions `add` and `update` accept
517
525
  `data: { fit: "contain" | "none" }`. Contain is the default, shrinking overflow
518
526
  uniformly; none keeps 100% with scrolling. Use Open as site for 100% without