pmx-canvas 0.11.0 → 0.12.1

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 (300) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +4 -1
  2. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  3. package/.github/extensions/pmx-canvas/tools.json +440 -18
  4. package/CHANGELOG.md +51 -0
  5. package/dist/amp-plugin/pmx-canvas/LICENSE +21 -0
  6. package/dist/amp-plugin/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
  7. package/dist/amp-plugin/pmx-canvas/index.js +5445 -0
  8. package/dist/amp-plugin/pmx-canvas/runtime.json +3 -0
  9. package/dist/amp-plugin/pmx-canvas/shared-runtime.mjs +194 -0
  10. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/SKILL.md +267 -0
  11. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +1168 -0
  12. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/jwt.ts +17 -0
  13. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/login.ts +12 -0
  14. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/middleware.ts +13 -0
  15. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/routes/auth.ts +13 -0
  16. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/investigation-board/src/handlers/users.ts +27 -0
  17. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/mcp.json +36 -0
  18. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/ax-html-control-surface.md +101 -0
  19. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/codex-app-adapter.md +151 -0
  20. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +150 -0
  21. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/full-reference.md +1839 -0
  22. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/github-copilot-app-adapter.md +223 -0
  23. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/html-primitives.md +139 -0
  24. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/importing-context.md +132 -0
  25. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/installing-pmx-canvas.md +301 -0
  26. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +117 -0
  27. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +908 -0
  28. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-orchestration/SKILL.md +141 -0
  29. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-testing/SKILL.md +266 -0
  30. package/dist/amp-plugin/pmx-canvas/start-mcp.mjs +14857 -0
  31. package/dist/amp-plugin/pmx-canvas/tools.json +10963 -0
  32. package/dist/canvas/chunks/{abnfDiagram-N423BO3Z-p8dtp6va.js → abnfDiagram-N423BO3Z-cwtsds5d.js} +1 -1
  33. package/dist/canvas/chunks/architecture-TIHT7OUA-x63cwejs.js +1 -0
  34. package/dist/canvas/chunks/{architectureDiagram-T3A2C74G-5jhmxrzf.js → architectureDiagram-T3A2C74G-b4xc9tpz.js} +3 -3
  35. package/dist/canvas/chunks/{blockDiagram-VBNYF7ZC-pw1fa9yb.js → blockDiagram-VBNYF7ZC-ahp3yxxs.js} +7 -7
  36. package/dist/canvas/chunks/{c4Diagram-5PPSVZJV-x07nq0tt.js → c4Diagram-5PPSVZJV-nappnay7.js} +4 -4
  37. package/dist/canvas/chunks/classDiagram-JCYQIIEL-k1j2xc1t.js +1 -0
  38. package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-33x08xf2.js +1 -0
  39. package/dist/canvas/chunks/{cose-bilkent-JH36ORCC-nm39npq0.js → cose-bilkent-JH36ORCC-a428syda.js} +1 -1
  40. package/dist/canvas/chunks/cynefin-VYW2F7L2-8v6jxfz8.js +1 -0
  41. package/dist/canvas/chunks/{cynefinDiagram-MW4NZA55-vdxrwahz.js → cynefinDiagram-MW4NZA55-nspsqebj.js} +1 -1
  42. package/dist/canvas/chunks/{dagre-VZM6K2ZE-bqdm7wpd.js → dagre-VZM6K2ZE-8y6177fh.js} +2 -2
  43. package/dist/canvas/chunks/{diagram-7IWD3JNH-gs5gpr93.js → diagram-7IWD3JNH-vym028wn.js} +4 -4
  44. package/dist/canvas/chunks/{diagram-B4RE2ZJO-cgwjg47s.js → diagram-B4RE2ZJO-jddqb8sy.js} +1 -1
  45. package/dist/canvas/chunks/{diagram-LBJQPF4R-w3vahpav.js → diagram-LBJQPF4R-bjwbpkrn.js} +1 -1
  46. package/dist/canvas/chunks/{diagram-Q27KOJAE-42837z6h.js → diagram-Q27KOJAE-j6ph6hcx.js} +2 -2
  47. package/dist/canvas/chunks/{diagram-UB23O5K3-4fz83q59.js → diagram-UB23O5K3-p09w8jej.js} +1 -1
  48. package/dist/canvas/chunks/{ebnfDiagram-BXEA7PRR-zphtgegj.js → ebnfDiagram-BXEA7PRR-j975ycq5.js} +1 -1
  49. package/dist/canvas/chunks/{erDiagram-JOGREHBK-zvbbdxp4.js → erDiagram-JOGREHBK-sxp6hjn8.js} +5 -5
  50. package/dist/canvas/chunks/eventmodeling-45OFAUF4-xzfgn6xg.js +1 -0
  51. package/dist/canvas/chunks/flowDiagram-UKHOOZJN-2nsmkskb.js +1 -0
  52. package/dist/canvas/chunks/{ganttDiagram-PKOTCBZU-282jzycm.js → ganttDiagram-PKOTCBZU-4tv230y2.js} +2 -2
  53. package/dist/canvas/chunks/gitGraph-TEB2WS4Q-macbhand.js +1 -0
  54. package/dist/canvas/chunks/{gitGraphDiagram-DS77QQ5N-vqqy6p1b.js → gitGraphDiagram-DS77QQ5N-mqgmnw9g.js} +1 -1
  55. package/dist/canvas/chunks/{index-wgv7en21.js → index-037z5dz2.js} +2 -2
  56. package/dist/canvas/chunks/{index-w2mra17t.js → index-081n3har.js} +1 -1
  57. package/dist/canvas/chunks/{index-353m7b0p.js → index-5n03dmb9.js} +1 -1
  58. package/dist/canvas/chunks/{index-yw8h9vha.js → index-60pgzdqe.js} +1 -1
  59. package/dist/canvas/chunks/index-8g1vb57n.js +2 -0
  60. package/dist/canvas/chunks/{index-vv56h019.js → index-8rjes9r6.js} +1 -1
  61. package/dist/canvas/chunks/{index-nprm96xp.js → index-9022d1n9.js} +1 -1
  62. package/dist/canvas/chunks/{index-194m3hc3.js → index-9b02042t.js} +1 -1
  63. package/dist/canvas/chunks/{index-0rk4gp1g.js → index-ap4q4vez.js} +1 -1
  64. package/dist/canvas/chunks/{index-rh7nv68n.js → index-dyp2nhd3.js} +1 -1
  65. package/dist/canvas/chunks/index-frvta4tm.js +2 -0
  66. package/dist/canvas/chunks/{index-s4j678gj.js → index-h9ycj8n2.js} +1 -1
  67. package/dist/canvas/chunks/{index-fjtdggjn.js → index-mergmj97.js} +2 -2
  68. package/dist/canvas/chunks/index-nwsya6dj.js +301 -0
  69. package/dist/canvas/chunks/{index-3cy1bzwh.js → index-nz8qbd1p.js} +1 -1
  70. package/dist/canvas/chunks/{index-d3b3gqe9.js → index-qhfrazv3.js} +1 -1
  71. package/dist/canvas/chunks/{index-6qmsnxwy.js → index-v119nznk.js} +4 -4
  72. package/dist/canvas/chunks/{index-r2fyc9w1.js → index-vpt37m3d.js} +6 -6
  73. package/dist/canvas/chunks/{index-qj8fxwsf.js → index-y9ma6azy.js} +1 -1
  74. package/dist/canvas/chunks/{index-tb1wf525.js → index-zx0j3a2z.js} +26 -26
  75. package/dist/canvas/chunks/info-DKCQHKI2-a0ykzs9z.js +1 -0
  76. package/dist/canvas/chunks/{infoDiagram-6WML65LV-v1yvggqj.js → infoDiagram-6WML65LV-qv97qq6a.js} +2 -2
  77. package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-p9306gh0.js +70 -0
  78. package/dist/canvas/chunks/{journeyDiagram-NVQOT4AX-zjcffveh.js → journeyDiagram-NVQOT4AX-sv67zats.js} +5 -5
  79. package/dist/canvas/chunks/{kanban-definition-27J2QSJJ-dakqtfp1.js → kanban-definition-27J2QSJJ-k2pfam6p.js} +1 -1
  80. package/dist/canvas/chunks/{mermaid.core-j2geswrd.js → mermaid.core-83r0fdce.js} +4 -4
  81. package/dist/canvas/chunks/{mindmap-definition-FAOFIHXS-142xhr3k.js → mindmap-definition-FAOFIHXS-vg2newm1.js} +1 -1
  82. package/dist/canvas/chunks/packet-7NZHBO7P-tqnabcdv.js +1 -0
  83. package/dist/canvas/chunks/{pegDiagram-VL7TDLO6-5wvk7vsd.js → pegDiagram-VL7TDLO6-k2h7zc5n.js} +1 -1
  84. package/dist/canvas/chunks/pie-RZYD4A2V-wke6gyhe.js +1 -0
  85. package/dist/canvas/chunks/{pieDiagram-7S7Q4E2Y-ztdhwycc.js → pieDiagram-7S7Q4E2Y-hg169p1v.js} +2 -2
  86. package/dist/canvas/chunks/{quadrantDiagram-CIZ2JOQS-c5p9gmg6.js → quadrantDiagram-CIZ2JOQS-bxtvsq0b.js} +5 -5
  87. package/dist/canvas/chunks/radar-I7S5WNFK-w7ka0drw.js +1 -0
  88. package/dist/canvas/chunks/railroad-3IZDKUUU-y3vg318x.js +1 -0
  89. package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-6hqvcz3c.js +1 -0
  90. package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-vcae260m.js +1 -0
  91. package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-0dbyme23.js +1 -0
  92. package/dist/canvas/chunks/{railroadDiagram-AXF67PYL-xkmqkjwf.js → railroadDiagram-AXF67PYL-njyhcp4p.js} +1 -1
  93. package/dist/canvas/chunks/{requirementDiagram-LRYGKXZP-9wm2dyaw.js → requirementDiagram-LRYGKXZP-2z6v09gm.js} +4 -4
  94. package/dist/canvas/chunks/{sankeyDiagram-W5VNT64P-2w19etgb.js → sankeyDiagram-W5VNT64P-15mzam0e.js} +1 -1
  95. package/dist/canvas/chunks/{sequenceDiagram-SI44F4Z6-0jn0vwmz.js → sequenceDiagram-SI44F4Z6-rbshyd2c.js} +12 -12
  96. package/dist/canvas/chunks/{sizeCapture-X5ZJPWSS-x75epwdq.js → sizeCapture-X5ZJPWSS-sg26xwts.js} +1 -1
  97. package/dist/canvas/chunks/{stateDiagram-OKZ733FA-schjpy6d.js → stateDiagram-OKZ733FA-6eahp98e.js} +1 -1
  98. package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-tty0ggn5.js +1 -0
  99. package/dist/canvas/chunks/{swimlanes-SLNWSIFB-gkw4xce3.js → swimlanes-SLNWSIFB-ze5db10t.js} +1 -1
  100. package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-thze75gq.js +8 -0
  101. package/dist/canvas/chunks/{timeline-definition-Z64GVDOM-x7nkcy01.js → timeline-definition-Z64GVDOM-vnp7jyy8.js} +3 -3
  102. package/dist/canvas/chunks/treeView-QDETBFTQ-6ysffnm6.js +1 -0
  103. package/dist/canvas/chunks/treemap-6X3UGDF4-m3h9p066.js +1 -0
  104. package/dist/canvas/chunks/vennDiagram-T6HMQDX7-0qrnb7mj.js +34 -0
  105. package/dist/canvas/chunks/wardley-OPB4EBWU-dp37drwx.js +1 -0
  106. package/dist/canvas/chunks/{wardleyDiagram-T6FBY63Y-t1j52t9r.js → wardleyDiagram-T6FBY63Y-8nxg8wqt.js} +4 -4
  107. package/dist/canvas/chunks/{xychartDiagram-ELKLHX3M-38t16pme.js → xychartDiagram-ELKLHX3M-nj921e7s.js} +5 -5
  108. package/dist/canvas/global.css +94 -1
  109. package/dist/canvas/index.js +154 -145
  110. package/dist/canvas/mcp-app.html +600 -498
  111. package/dist/json-render/index.css +1 -1
  112. package/dist/types/client/canvas/BoardMoveDialog.d.ts +2 -0
  113. package/dist/types/client/canvas/EdgeEditor.d.ts +19 -0
  114. package/dist/types/client/canvas/GroupNavigation.d.ts +2 -0
  115. package/dist/types/client/canvas/TextPrompt.d.ts +4 -0
  116. package/dist/types/client/nodes/PromptNode.d.ts +1 -0
  117. package/dist/types/client/nodes/ViewerFrame.d.ts +1 -1
  118. package/dist/types/client/nodes/WorkCard.d.ts +7 -0
  119. package/dist/types/client/nodes/use-ax-surface-bridge.d.ts +1 -0
  120. package/dist/types/client/state/canvas-store.d.ts +15 -0
  121. package/dist/types/client/state/human-store.d.ts +2 -0
  122. package/dist/types/client/state/intent-bridge.d.ts +4 -2
  123. package/dist/types/client/state/relation-store.d.ts +11 -0
  124. package/dist/types/client/types.d.ts +1 -1
  125. package/dist/types/json-render/catalog.d.ts +2 -2
  126. package/dist/types/server/agent-presence.d.ts +1 -0
  127. package/dist/types/server/ax-interaction.d.ts +1 -0
  128. package/dist/types/server/ax-state-manager.d.ts +10 -2
  129. package/dist/types/server/board-local.d.ts +4 -0
  130. package/dist/types/server/board-move.d.ts +38 -0
  131. package/dist/types/server/canvas-db.d.ts +12 -2
  132. package/dist/types/server/canvas-operations.d.ts +5 -6
  133. package/dist/types/server/canvas-provenance.d.ts +1 -1
  134. package/dist/types/server/canvas-serialization.d.ts +1 -0
  135. package/dist/types/server/canvas-state.d.ts +84 -20
  136. package/dist/types/server/card-relocation.d.ts +6 -0
  137. package/dist/types/server/context-reads.d.ts +3 -1
  138. package/dist/types/server/human-presence.d.ts +1 -1
  139. package/dist/types/server/index.d.ts +16 -5
  140. package/dist/types/server/intent-registry.d.ts +8 -4
  141. package/dist/types/server/knowledge-lifecycle.d.ts +4 -2
  142. package/dist/types/server/mutation-history.d.ts +7 -5
  143. package/dist/types/server/operations/registry.d.ts +2 -0
  144. package/dist/types/server/trace-manager.d.ts +1 -0
  145. package/dist/types/shared/ax-kinds.d.ts +6 -0
  146. package/dist/types/shared/ax-surface-protocol.d.ts +8 -0
  147. package/dist/types/shared/human-presence.d.ts +1 -0
  148. package/dist/types/shared/lifecycle-status.d.ts +2 -0
  149. package/docs/RELEASE.md +21 -14
  150. package/docs/agent-plugin.md +22 -2
  151. package/docs/amp-plugin.md +270 -0
  152. package/docs/cli.md +42 -3
  153. package/docs/design/GuestName.dc.html +50 -0
  154. package/docs/design/PluginLogo.svg +7 -0
  155. package/docs/design/WorkCards.dc.html +39 -0
  156. package/docs/design/canvas.json +16 -0
  157. package/docs/design/plugin-logo.png +0 -0
  158. package/docs/design.md +111 -8
  159. package/docs/evals/agents-md-sections.md +38 -0
  160. package/docs/http-api.md +128 -10
  161. package/docs/mcp.md +61 -9
  162. package/docs/node-types.md +71 -1
  163. package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
  164. package/docs/plans/plan-019-post-0.9-roadmap.md +44 -20
  165. package/docs/plans/plan-020-wave-5-work-planning.md +553 -0
  166. package/docs/product-vision-2026-09.md +33 -17
  167. package/docs/sdk.md +18 -0
  168. package/package.json +6 -3
  169. package/plugins/pmx-canvas/assets/preview.png +0 -0
  170. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +4 -1
  171. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  172. package/plugins/pmx-canvas/plugin.json +2 -2
  173. package/plugins/pmx-canvas/runtime.json +1 -1
  174. package/plugins/pmx-canvas/tools.json +440 -18
  175. package/skills/data-analysis/SKILL.md +13 -26
  176. package/skills/frontend-design/LICENSE.txt +177 -0
  177. package/skills/frontend-design/SKILL.md +3 -0
  178. package/skills/pmx-canvas/SKILL.md +248 -842
  179. package/skills/pmx-canvas/evals/evals.json +110 -5
  180. package/skills/pmx-canvas/references/full-reference.md +142 -28
  181. package/skills/pmx-canvas/references/html-primitives.md +7 -0
  182. package/skills/pmx-canvas/references/installing-pmx-canvas.md +61 -11
  183. package/skills/pmx-canvas/references/tours-and-recording.md +39 -6
  184. package/skills/pmx-canvas/references/workbench-workflows.md +908 -0
  185. package/skills/pmx-canvas-testing/SKILL.md +7 -5
  186. package/skills/published-consumer-e2e/SKILL.md +25 -8
  187. package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +51 -75
  188. package/skills/web-artifacts-builder/LICENSE.txt +202 -0
  189. package/skills/web-artifacts-builder/SKILL.md +6 -0
  190. package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +2 -2
  191. package/skills/web-artifacts-builder/scripts/init-artifact.sh +8 -8
  192. package/src/cli/commands/apps.ts +1 -0
  193. package/src/cli/commands/boards.ts +30 -0
  194. package/src/client/App.tsx +20 -4
  195. package/src/client/canvas/AttentionHistory.tsx +10 -2
  196. package/src/client/canvas/BoardMoveDialog.tsx +225 -0
  197. package/src/client/canvas/CanvasNode.tsx +45 -2
  198. package/src/client/canvas/CanvasViewport.tsx +38 -9
  199. package/src/client/canvas/ContextMenu.tsx +34 -12
  200. package/src/client/canvas/EdgeEditor.tsx +149 -0
  201. package/src/client/canvas/EdgeLayer.tsx +87 -16
  202. package/src/client/canvas/ExpandedNodeOverlay.tsx +4 -0
  203. package/src/client/canvas/GroupNavigation.tsx +85 -0
  204. package/src/client/canvas/RelationsPanel.tsx +9 -0
  205. package/src/client/canvas/SelectionBar.tsx +21 -0
  206. package/src/client/canvas/SessionPanel.tsx +35 -1
  207. package/src/client/canvas/TextPrompt.tsx +18 -1
  208. package/src/client/canvas/TopBar.tsx +30 -0
  209. package/src/client/canvas/canvas-area.ts +1 -1
  210. package/src/client/nodes/ExtAppFrame.tsx +26 -10
  211. package/src/client/nodes/MarkdownNode.tsx +6 -7
  212. package/src/client/nodes/PromptNode.tsx +32 -3
  213. package/src/client/nodes/StatusNode.tsx +12 -0
  214. package/src/client/nodes/StatusSummary.tsx +11 -0
  215. package/src/client/nodes/ViewerFrame.tsx +21 -10
  216. package/src/client/nodes/WorkCard.tsx +175 -0
  217. package/src/client/nodes/use-ax-surface-bridge.ts +18 -14
  218. package/src/client/state/attention-bridge.ts +5 -0
  219. package/src/client/state/attention-store.ts +1 -0
  220. package/src/client/state/canvas-store.ts +185 -15
  221. package/src/client/state/human-store.ts +22 -2
  222. package/src/client/state/intent-bridge.ts +19 -5
  223. package/src/client/state/relation-store.ts +127 -1
  224. package/src/client/theme/global.css +94 -1
  225. package/src/client/types.ts +1 -0
  226. package/src/mcp/server.ts +13 -3
  227. package/src/server/agent-presence.ts +43 -23
  228. package/src/server/ax-flow-loop.ts +6 -0
  229. package/src/server/ax-gate-ttl.ts +8 -1
  230. package/src/server/ax-interaction.ts +16 -10
  231. package/src/server/ax-state-manager.ts +17 -4
  232. package/src/server/ax-wait.ts +20 -6
  233. package/src/server/board-export.ts +19 -6
  234. package/src/server/board-local.ts +20 -0
  235. package/src/server/board-move.ts +297 -0
  236. package/src/server/canvas-db.ts +262 -160
  237. package/src/server/canvas-operations.ts +139 -109
  238. package/src/server/canvas-schema.ts +21 -0
  239. package/src/server/canvas-serialization.ts +2 -0
  240. package/src/server/canvas-state.ts +792 -159
  241. package/src/server/card-relocation.ts +53 -0
  242. package/src/server/code-graph.ts +4 -3
  243. package/src/server/context-reads.ts +83 -55
  244. package/src/server/demo-state.json +35 -15
  245. package/src/server/file-watcher.ts +30 -10
  246. package/src/server/human-presence.ts +18 -5
  247. package/src/server/index.ts +35 -15
  248. package/src/server/intent-registry.ts +43 -5
  249. package/src/server/knowledge-lifecycle.ts +32 -9
  250. package/src/server/lifecycle-status.ts +29 -1
  251. package/src/server/mcp-app-runtime.ts +26 -7
  252. package/src/server/mutation-history.ts +37 -8
  253. package/src/server/operations/composites.ts +6 -3
  254. package/src/server/operations/mcp-contracts.ts +15 -0
  255. package/src/server/operations/mcp.ts +36 -9
  256. package/src/server/operations/ops/annotation.ts +22 -1
  257. package/src/server/operations/ops/ax-await.ts +1 -0
  258. package/src/server/operations/ops/ax-timeline.ts +28 -2
  259. package/src/server/operations/ops/batch.ts +25 -11
  260. package/src/server/operations/ops/boards.ts +44 -3
  261. package/src/server/operations/ops/nodes.ts +70 -0
  262. package/src/server/operations/ops/query.ts +32 -3
  263. package/src/server/operations/ops/snapshots.ts +1 -0
  264. package/src/server/operations/registry.ts +49 -4
  265. package/src/server/server.ts +62 -16
  266. package/src/server/tour-control.ts +37 -12
  267. package/src/server/trace-manager.ts +4 -0
  268. package/src/server/web-artifacts/scripts/bundle-artifact.sh +2 -2
  269. package/src/server/web-artifacts/scripts/init-artifact.sh +8 -8
  270. package/src/shared/agent-context.ts +7 -1
  271. package/src/shared/auto-arrange.ts +5 -4
  272. package/src/shared/ax-kinds.ts +13 -0
  273. package/src/shared/ax-surface-protocol.ts +22 -0
  274. package/src/shared/human-presence.ts +1 -0
  275. package/src/shared/lifecycle-status.ts +2 -0
  276. package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +0 -1
  277. package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +0 -1
  278. package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +0 -1
  279. package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +0 -1
  280. package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +0 -1
  281. package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +0 -1
  282. package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +0 -1
  283. package/dist/canvas/chunks/index-1r72hdzb.js +0 -301
  284. package/dist/canvas/chunks/index-g3pwnqeb.js +0 -2
  285. package/dist/canvas/chunks/index-rft2hcz5.js +0 -2
  286. package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +0 -1
  287. package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +0 -70
  288. package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +0 -1
  289. package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +0 -1
  290. package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +0 -1
  291. package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +0 -1
  292. package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +0 -1
  293. package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +0 -1
  294. package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +0 -1
  295. package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +0 -1
  296. package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +0 -8
  297. package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +0 -1
  298. package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +0 -1
  299. package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +0 -34
  300. package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +0 -1
@@ -0,0 +1,17 @@
1
+ const SECRET = process.env.JWT_SECRET ?? 'dev-secret';
2
+
3
+ export interface JwtClaims {
4
+ sub: string;
5
+ exp: number;
6
+ }
7
+
8
+ export function signJwt(claims: JwtClaims): string {
9
+ const payload = Buffer.from(JSON.stringify(claims)).toString('base64url');
10
+ return `${payload}.${SECRET}`;
11
+ }
12
+
13
+ export function verifyJwt(token: string): JwtClaims | null {
14
+ const [payload, signature] = token.split('.');
15
+ if (signature !== SECRET || !payload) return null;
16
+ return JSON.parse(Buffer.from(payload, 'base64url').toString()) as JwtClaims;
17
+ }
@@ -0,0 +1,12 @@
1
+ import { signJwt, type JwtClaims } from './jwt';
2
+
3
+ export async function login(username: string, password: string): Promise<string | null> {
4
+ const ok = await checkCredentials(username, password);
5
+ if (!ok) return null;
6
+ const claims: JwtClaims = { sub: username, exp: Date.now() + 3_600_000 };
7
+ return signJwt(claims);
8
+ }
9
+
10
+ async function checkCredentials(username: string, password: string): Promise<boolean> {
11
+ return Boolean(username) && password.length >= 8;
12
+ }
@@ -0,0 +1,13 @@
1
+ import { verifyJwt } from './jwt';
2
+
3
+ export interface AuthedRequest {
4
+ headers: Record<string, string>;
5
+ userId?: string;
6
+ }
7
+
8
+ export function authMiddleware(req: AuthedRequest, next: () => void): void {
9
+ const token = (req.headers.authorization ?? '').replace(/^Bearer /, '');
10
+ const claims = verifyJwt(token);
11
+ if (claims) req.userId = claims.sub;
12
+ next();
13
+ }
@@ -0,0 +1,13 @@
1
+ import { login } from '../auth/login';
2
+ import { authMiddleware, type AuthedRequest } from '../auth/middleware';
3
+
4
+ export function registerAuthRoutes(router: {
5
+ post(path: string, handler: (req: AuthedRequest) => Promise<unknown>): void;
6
+ use(handler: (req: AuthedRequest, next: () => void) => void): void;
7
+ }): void {
8
+ router.use(authMiddleware);
9
+ router.post('/login', async (req) => {
10
+ const token = await login(req.headers.username ?? '', req.headers.password ?? '');
11
+ return token ? { token } : { error: 'invalid credentials' };
12
+ });
13
+ }
@@ -0,0 +1,27 @@
1
+ import { EventEmitter } from 'node:events';
2
+
3
+ const refreshBus = new EventEmitter();
4
+
5
+ interface UserRequest {
6
+ query: Record<string, string>;
7
+ headers: Record<string, string>;
8
+ }
9
+
10
+ interface UserResponse {
11
+ json(body: unknown): void;
12
+ }
13
+
14
+ /**
15
+ * GET /api/users
16
+ *
17
+ * Memory leak: every request registers a `refresh` listener whose closure
18
+ * captures the entire `req` object and is never removed. The heap retains one
19
+ * request per call and the EventEmitter listener count grows without bound.
20
+ */
21
+ export function getUsers(req: UserRequest, res: UserResponse): void {
22
+ refreshBus.on('refresh', () => {
23
+ // Captures `req` for the lifetime of the process — the leak.
24
+ void req.headers;
25
+ });
26
+ res.json({ users: [] });
27
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "pmx-canvas": {
3
+ "command": "bun",
4
+ "args": [
5
+ "${AMP_SKILL_DIR}/../../start-mcp.mjs"
6
+ ],
7
+ "includeTools": [
8
+ "canvas_connect_workspace",
9
+ "canvas_help",
10
+ "canvas_pin_nodes",
11
+ "canvas_invoke_command",
12
+ "canvas_batch",
13
+ "canvas_import",
14
+ "canvas_snapshot",
15
+ "canvas_board",
16
+ "canvas_node",
17
+ "canvas_render",
18
+ "canvas_edge",
19
+ "canvas_group",
20
+ "canvas_history",
21
+ "canvas_view",
22
+ "canvas_query",
23
+ "canvas_app",
24
+ "canvas_webview",
25
+ "canvas_ax_state",
26
+ "canvas_ax_work",
27
+ "canvas_ax_gate",
28
+ "canvas_ax_timeline",
29
+ "canvas_ax_delivery",
30
+ "canvas_intent",
31
+ "canvas_ax_interaction",
32
+ "canvas_ingest_activity",
33
+ "canvas_screenshot"
34
+ ]
35
+ }
36
+ }
@@ -0,0 +1,101 @@
1
+ # AX HTML Control Surface — a blessed, copy-paste-safe recipe
2
+
3
+ A canvas `html` node can be a live **AX control surface**: it emits AX interactions
4
+ (create work, request approval, steer the agent, …) and reflects the current AX board
5
+ back, all from inside the sandboxed iframe. The bridge is already injected for you —
6
+ you do **not** need to read `axToken` from the URL or hand-post `pmx-canvas-ax`
7
+ messages. Just opt the node in and use `window.PMX_AX`.
8
+
9
+ ## Opt in
10
+
11
+ ```js
12
+ // MCP / SDK
13
+ canvas_node({
14
+ action: "add",
15
+ type: "html",
16
+ title: "AX Control Room",
17
+ html: "<!-- see below -->",
18
+ axCapabilities: { enabled: true, allowed: ["ax.work.create", "ax.steer"] },
19
+ });
20
+ // HTTP: POST /api/canvas/node { type:"html", title, html, axCapabilities } (top-level html + axCapabilities both accepted)
21
+ ```
22
+
23
+ Without `axCapabilities.enabled = true`, `window.PMX_AX` is **not** injected — the node
24
+ renders but can't emit. `allowed` narrows what it may emit (never escalates the type's
25
+ ceiling). Flip an existing node on with `canvas_node({ action: "update", id, axCapabilities: { enabled: true, allowed: [...] } })`.
26
+
27
+ Changing effective capabilities reloads the mounted HTML document in both `src` and
28
+ `srcdoc` modes, including disable/re-enable and allowed-action changes. Unsaved iframe-local
29
+ drafts reset: save important state through durable AX primitives before changing capabilities.
30
+ Wait for the new document, invoke the action, and verify both its acknowledgement and persisted
31
+ result. On 0.8.0, late enabling required a deliberate remount; upgrading fixes that missing bridge.
32
+
33
+ ## Three footguns (this is why a hand-rolled node looks "inert")
34
+
35
+ 1. **The iframe is sandboxed opaque-origin** (no `allow-same-origin`). `localStorage`,
36
+ `sessionStorage`, and `document.cookie` **throw** — and an uncaught throw at script
37
+ start aborts the whole script, so the node renders blank/inert. Keep state in plain
38
+ JS variables (or `window.PMX_AX.state`); if you must touch storage, wrap it in
39
+ `try/catch`.
40
+ 2. **`window.PMX_AX.emit(type, payload)` is async.** It returns a Promise that resolves
41
+ with the result once the canvas acks it. `await` it (or `.then` / `window.PMX_AX.on('ack', cb)`).
42
+ Reading a result synchronously gets you `undefined`.
43
+ 3. **`ax.steer` is recorded, not delivered.** A successful emit (`{ ok: true }`) means
44
+ the steer was **queued** on the timeline — it does **not** wake or notify the active
45
+ agent. A cooperating host adapter must drain the delivery queue and call its native
46
+ send to create a visible turn (host-owned). Otherwise the steer is picked up on the
47
+ human's next turn. Label steering buttons honestly ("Queued for the agent's next turn").
48
+
49
+ ## Drop-in template (work + steer, with ack + live reflection)
50
+
51
+ ```html
52
+ <style>
53
+ body { font: 13px system-ui; margin: 0; padding: 12px; }
54
+ button { cursor: pointer; }
55
+ #s { margin-left: 8px; color: #6b7280; }
56
+ ul { padding-left: 18px; }
57
+ </style>
58
+ <button id="add">+ Work item</button>
59
+ <button id="steer">Steer agent</button>
60
+ <span id="s"></span>
61
+ <ul id="q"></ul>
62
+ <script>
63
+ // In-memory only — NO localStorage/sessionStorage/cookies (sandboxed: they throw).
64
+ const $ = (id) => document.getElementById(id);
65
+ const flash = (msg) => { $('s').textContent = msg; setTimeout(() => { $('s').textContent = ''; }, 1500); };
66
+
67
+ function render(state) {
68
+ const items = (state && state.workItems) || [];
69
+ $('q').innerHTML = items.map((w) => '<li>[' + w.status + '] ' + w.title + '</li>').join('');
70
+ }
71
+
72
+ $('add').onclick = async () => {
73
+ const r = await window.PMX_AX.emit('ax.work.create', { title: 'New task' });
74
+ flash(r && r.ok ? 'queued ✓' : ('failed: ' + (r && (r.error || r.code))));
75
+ };
76
+ $('steer').onclick = async () => {
77
+ const r = await window.PMX_AX.emit('ax.steer', { message: 'Prioritize the auth refactor' });
78
+ // Honest: recorded/queued, NOT delivered to the live agent.
79
+ flash(r && r.ok ? 'queued for next turn ✓' : 'failed');
80
+ };
81
+
82
+ // Reflect: seeded once at load, then live via the pmx-ax-update event.
83
+ render(window.PMX_AX && window.PMX_AX.state);
84
+ window.addEventListener('pmx-ax-update', (e) => render(e.detail));
85
+ </script>
86
+ ```
87
+
88
+ ## API surface (injected by PMX when opted in)
89
+
90
+ - `window.PMX_AX.emit(type, payload) → Promise<{ ok, primitive?, status?, code?, error? }>`
91
+ — `ok:true` on accept; `ok:false` with `code`/`error` on reject; falls back to an
92
+ `ax-ack-timeout` result after 10s so `await` never hangs.
93
+ - `window.PMX_AX.on('ack', (result, interaction) => …)` — also fires a `pmx-ax-ack`
94
+ CustomEvent; use instead of `await` if you prefer a listener.
95
+ - `window.PMX_AX.state` — compact board snapshot `{ focus, workItems, approvalGates,
96
+ reviewAnnotations, elicitations, modeRequests, policy }` (human review text redacted).
97
+ - `pmx-ax-update` window event — fires with the fresh snapshot on every AX change.
98
+
99
+ Allowed `type`s are gated per node capability (see the node-capability matrix in
100
+ `SKILL.md`). Emits are clamped to the surface's own node; the server re-validates every
101
+ interaction — the bridge is convenience, not a trust boundary.
@@ -0,0 +1,151 @@
1
+ # Codex App Adapter
2
+
3
+ Use this reference when PMX Canvas is running natively in the Codex app. The adapter is intentionally
4
+ thin: PMX Canvas remains the state owner, Codex uses MCP for agent operations/context, and the Codex
5
+ in-app Browser renders the live PMX workbench.
6
+
7
+ ## Adapter Identity
8
+
9
+ - Host: Codex app
10
+ - Visual surface: Codex in-app Browser opened to the PMX `/workbench` URL
11
+ - Agent surface: PMX Canvas MCP server (`pmx-canvas --mcp`)
12
+ - Fallback surface: PMX Canvas CLI for manual scripts when MCP is unavailable
13
+ - AX source label: `codex`
14
+
15
+ This is not a separate PMX renderer and does not require a Codex extension API. Codex already has
16
+ the two native surfaces PMX needs: MCP resources/tools for the agent, and the in-app Browser for the
17
+ human-visible canvas. Prefer MCP over the CLI for Codex-native use because MCP exposes structured
18
+ tools, resources, and `canvas://ax-context`; use the CLI only as a fallback or for shell scripts.
19
+
20
+ ## What The Adapter Does
21
+
22
+ - Opens the live PMX workbench in the Codex in-app Browser.
23
+ - Uses MCP tools/resources for all agent-side operations.
24
+ - Reads `canvas://ax-context` or `canvas_ax_state { action: "get" }` for pinned and focused context.
25
+ - Sets AX focus through `canvas_ax_state { action: "set-focus" }` with `source: "codex"` when the
26
+ focus change comes from Codex-hosted steering.
27
+ - Keeps all persistent PMX state in `.pmx-canvas/canvas.db`; Codex does not own canvas state.
28
+
29
+ ## Setup
30
+
31
+ Use MCP as the primary Codex adapter path.
32
+
33
+ Add the PMX MCP server to the Codex workspace config:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "canvas": {
39
+ "command": "bunx",
40
+ "args": ["pmx-canvas", "--mcp"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ For local repo development, use the source entrypoint instead:
47
+
48
+ ```json
49
+ {
50
+ "mcpServers": {
51
+ "canvas": {
52
+ "command": "bun",
53
+ "args": ["run", "src/mcp/server.ts"]
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ The MCP server auto-starts the HTTP workbench on first tool call. Open the returned workbench URL
60
+ in the Codex in-app Browser, usually `http://127.0.0.1:4313/workbench` or
61
+ `http://localhost:4313/workbench`.
62
+
63
+ ### Agent presence (cursor + phase chip)
64
+
65
+ Name the agent on the MCP server so its writes, its session, and its cursor share one identity:
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "canvas": {
71
+ "command": "bunx",
72
+ "args": ["pmx-canvas", "--mcp"],
73
+ "env": { "PMX_CANVAS_AGENT_SOURCE": "codex" }
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ Then attach at the start of a task and detach at the end:
80
+
81
+ ```json
82
+ { "tool": "canvas_ax_state", "action": "set-presence", "attached": true, "label": "Codex", "phase": "idle" }
83
+ { "tool": "canvas_ax_state", "action": "set-presence", "attached": false }
84
+ ```
85
+
86
+ While attached the workbench shows the Codex cursor on the node it last touched and a phase chip
87
+ (`thinking` · `tooling` · `waiting-approval` · `idle`). `tooling` and the cursor position are
88
+ derived from Codex's own MCP writes — nothing per call. Even without `PMX_CANVAS_AGENT_SOURCE`,
89
+ writes arriving as plain `mcp` are attributed to the single attached session.
90
+
91
+ ## Codex-Native Workflow
92
+
93
+ 1. Open `/workbench` in the Codex in-app Browser as the first visible action. If PMX is not running
94
+ yet, start/connect the MCP server first only long enough to get the workbench URL, then open the
95
+ browser before mutating the board.
96
+ 2. Start or keep using the PMX MCP server for agent operations.
97
+ 3. Use the browser canvas for human spatial curation: pin nodes, move nodes, group nodes, and
98
+ inspect rendered artifacts.
99
+ 4. Use MCP tools for agent operations: create/update nodes, pin nodes, read layout, and read AX
100
+ context.
101
+ 5. When Codex wants to mark the current attention target, call
102
+ `canvas_ax_state { action: "set-focus", … }` with:
103
+
104
+ ```json
105
+ {
106
+ "nodeIds": ["node-123"],
107
+ "source": "codex"
108
+ }
109
+ ```
110
+
111
+ ## Context Contract
112
+
113
+ Codex agents should treat PMX AX context as host-native working context:
114
+
115
+ - `canvas://pinned-context` is the explicit human-curated node set.
116
+ - `canvas://ax-context` combines pins, focus, and surface metadata, plus a compact
117
+ loop-safe `delivery: { pendingSteering, pendingActivity }` lead block
118
+ (`GET /api/canvas/ax/context?consumer=codex` filters out Codex-originated items).
119
+ - `canvas_ax_state { action: "get" }` returns both persisted AX state and agent-ready context.
120
+ - Focus is a current attention target, not a command to ignore the rest of the repository.
121
+
122
+ The adapterless MCP+Browser path is poll-based: there is no automatic prompt injection,
123
+ so a board click does not wake the current turn. Codex agents poll
124
+ `canvas_ax_delivery { action: "claim" }` (steering + `pendingActivity`) and act/ack explicitly. The
125
+ loop-closing surfaces work over MCP today even without a dedicated extension:
126
+
127
+ - **Self-report work** with `canvas_ingest_activity` (the board auto-reacts: a failed
128
+ tool → a blocked work item + review + evidence). Automatic forwarding of Codex's own
129
+ tool hooks would need a Codex adapter; manual ingestion works now.
130
+ - **Block on a decision** with `canvas_ax_gate { kind, action: "await", id }` (it long-polls PMX
131
+ until the human resolves the gate in the Browser or the timeout elapses) instead of looping on
132
+ `canvas_ax_state { action: "get" }`.
133
+
134
+ ## Live-Test Checklist
135
+
136
+ 1. Open `http://127.0.0.1:4313/workbench` in the Codex in-app Browser first so the user can see
137
+ all later canvas mutations.
138
+ 2. Confirm the PMX MCP server is configured for the workspace.
139
+ 3. Call `canvas_ax_state { action: "get" }` and confirm it returns `ok: true`.
140
+ 4. Add or reuse a node, then pin it from the browser or with `canvas_pin_nodes`.
141
+ 5. Read `canvas://ax-context` and confirm the pinned node appears.
142
+ 6. Call `canvas_ax_state { action: "set-focus", source: "codex" }` with a real node ID.
143
+ 7. Read `canvas_ax_state { action: "get" }` again and confirm `state.focus.source` is `codex`.
144
+ 8. Refresh the browser and confirm the workbench still shows the same state.
145
+
146
+ ## Adapter Boundary
147
+
148
+ Do not add Codex-specific APIs to PMX Canvas core. Core owns neutral AX primitives; Codex maps them
149
+ through MCP and the in-app Browser. If Codex later exposes a dedicated extension or prompt-injection
150
+ hook, implement that as a separate adapter layer that reads the same `/api/canvas/ax/context` or
151
+ `canvas://ax-context` contract.
@@ -0,0 +1,150 @@
1
+ # Excalidraw Diagram Authoring
2
+
3
+ Use this guide when creating diagrams through PMX Canvas with `canvas_app { action: "diagram" }` or
4
+ `pmx-canvas external-app add --kind excalidraw`.
5
+
6
+ Request `width: 720, height: 500` when opening the standard landscape diagram tile. Treat those as
7
+ requested preset dimensions, not authoritative geometry: after the app mounts, query
8
+ `canvas_query { action: "layout" }` and use the node's persisted width and height for placement,
9
+ overlap checks, and any later layout. A hosted app can resize during mount.
10
+
11
+ ## Why Text Can Still Drift
12
+
13
+ PMX normalizes canonical Excalidraw bound text (`containerId` / `boundElements`) into the hosted
14
+ app's supported shape-level `label` format before calling Excalidraw. That fixes the payload
15
+ format mismatch, but it does not fix poor diagram geometry.
16
+
17
+ Text can still appear clipped or misplaced when:
18
+
19
+ - A label is too long for its shape.
20
+ - A diamond or ellipse is too small for the label's usable center area.
21
+ - The `cameraUpdate` viewport is too tight or not 4:3.
22
+ - Title, footer, or notes are placed near the camera edge.
23
+ - The caller bypasses PMX and sends raw elements directly to Excalidraw MCP.
24
+
25
+ ## Text Format Rules
26
+
27
+ For text inside a rectangle, ellipse, or diamond, use shape-level `label`:
28
+
29
+ ```json
30
+ {
31
+ "type": "rectangle",
32
+ "id": "step-a",
33
+ "x": 100,
34
+ "y": 100,
35
+ "width": 260,
36
+ "height": 90,
37
+ "label": { "text": "Step A", "fontSize": 18 }
38
+ }
39
+ ```
40
+
41
+ Do not create separate centered text elements for shape labels:
42
+
43
+ ```json
44
+ {
45
+ "type": "text",
46
+ "containerId": "step-a",
47
+ "text": "Step A"
48
+ }
49
+ ```
50
+
51
+ Standalone text is fine for titles, notes, captions, and free-floating annotations. For standalone
52
+ text, `x` is the left edge; `textAlign` does not center it on a point.
53
+
54
+ ## Label Length Rules
55
+
56
+ - Use 1-4 words inside shapes.
57
+ - Put detailed explanations in nearby standalone text annotations.
58
+ - Prefer `Bound Text` over `Pattern B: containerId+boundElements only`.
59
+ - If a label needs more than 4 words, either widen the shape or split the idea into a label plus an annotation.
60
+
61
+ ## Shape Sizing Rules
62
+
63
+ - Minimum labeled rectangle or ellipse: `180x80`.
64
+ - For 3-5 word labels: `240x90` or larger.
65
+ - For long labels: `320+` width or use an external annotation.
66
+ - Diamonds need more room than rectangles because the usable center area is smaller.
67
+ - Leave at least `30px` gap between shapes and labels/arrows.
68
+
69
+ ## Camera Rules
70
+
71
+ Always start with a `cameraUpdate` as the first element.
72
+
73
+ Use 4:3 camera sizes only:
74
+
75
+ - `400x300`
76
+ - `600x450`
77
+ - `800x600`
78
+ - `1200x900`
79
+ - `1600x1200`
80
+
81
+ Camera bounds must include the full diagram plus padding. Leave at least `80px` padding around all
82
+ visible content.
83
+
84
+ Example:
85
+
86
+ ```json
87
+ { "type": "cameraUpdate", "x": 20, "y": 0, "width": 1200, "height": 900 }
88
+ ```
89
+
90
+ If a title, footer, or rightmost label is clipped, the camera is wrong even if the elements are valid.
91
+
92
+ ## Good Pattern
93
+
94
+ ```json
95
+ [
96
+ { "type": "cameraUpdate", "x": 20, "y": 0, "width": 1200, "height": 900 },
97
+ {
98
+ "type": "rectangle",
99
+ "id": "a",
100
+ "x": 120,
101
+ "y": 160,
102
+ "width": 260,
103
+ "height": 90,
104
+ "backgroundColor": "#a5d8ff",
105
+ "fillStyle": "solid",
106
+ "label": { "text": "Short Label", "fontSize": 18 }
107
+ },
108
+ {
109
+ "type": "rectangle",
110
+ "id": "b",
111
+ "x": 520,
112
+ "y": 160,
113
+ "width": 280,
114
+ "height": 90,
115
+ "backgroundColor": "#b2f2bb",
116
+ "fillStyle": "solid",
117
+ "label": { "text": "Next Step", "fontSize": 18 }
118
+ },
119
+ {
120
+ "type": "arrow",
121
+ "id": "a-to-b",
122
+ "x": 390,
123
+ "y": 205,
124
+ "width": 110,
125
+ "height": 0,
126
+ "points": [[0, 0], [110, 0]],
127
+ "endArrowhead": "arrow",
128
+ "label": { "text": "then", "fontSize": 14 }
129
+ },
130
+ {
131
+ "type": "text",
132
+ "id": "note",
133
+ "x": 120,
134
+ "y": 290,
135
+ "text": "Longer explanation goes here, outside the shape.",
136
+ "fontSize": 16
137
+ }
138
+ ]
139
+ ```
140
+
141
+ ## Preflight Checklist
142
+
143
+ - Shape text uses `label`, not separate `text` elements.
144
+ - Shape labels are short enough to fit.
145
+ - Long explanations are outside shapes.
146
+ - The first element is a 4:3 `cameraUpdate`.
147
+ - Camera has at least `80px` padding around all visible content.
148
+ - Titles and footers are not near the camera edge.
149
+ - Arrows have explicit `points` and enough space for labels.
150
+ - Calls go through PMX (`canvas_app { action: "diagram" }` or `external-app add --kind excalidraw`) unless you manually apply these rules to raw Excalidraw MCP input.