pmx-canvas 0.11.0 → 0.12.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 (298) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +3 -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 +37 -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 +251 -0
  11. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +1086 -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 +1785 -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 +251 -0
  26. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +111 -0
  27. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +900 -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 +16 -2
  151. package/docs/amp-plugin.md +271 -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 +3 -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 +232 -842
  179. package/skills/pmx-canvas/evals/evals.json +26 -3
  180. package/skills/pmx-canvas/references/full-reference.md +80 -20
  181. package/skills/pmx-canvas/references/html-primitives.md +7 -0
  182. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  183. package/skills/pmx-canvas/references/tours-and-recording.md +29 -2
  184. package/skills/pmx-canvas/references/workbench-workflows.md +900 -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/init-artifact.sh +2 -2
  191. package/src/cli/commands/apps.ts +1 -0
  192. package/src/cli/commands/boards.ts +30 -0
  193. package/src/client/App.tsx +20 -4
  194. package/src/client/canvas/AttentionHistory.tsx +10 -2
  195. package/src/client/canvas/BoardMoveDialog.tsx +225 -0
  196. package/src/client/canvas/CanvasNode.tsx +45 -2
  197. package/src/client/canvas/CanvasViewport.tsx +38 -9
  198. package/src/client/canvas/ContextMenu.tsx +34 -12
  199. package/src/client/canvas/EdgeEditor.tsx +149 -0
  200. package/src/client/canvas/EdgeLayer.tsx +87 -16
  201. package/src/client/canvas/ExpandedNodeOverlay.tsx +4 -0
  202. package/src/client/canvas/GroupNavigation.tsx +85 -0
  203. package/src/client/canvas/RelationsPanel.tsx +9 -0
  204. package/src/client/canvas/SelectionBar.tsx +21 -0
  205. package/src/client/canvas/SessionPanel.tsx +35 -1
  206. package/src/client/canvas/TextPrompt.tsx +18 -1
  207. package/src/client/canvas/TopBar.tsx +30 -0
  208. package/src/client/canvas/canvas-area.ts +1 -1
  209. package/src/client/nodes/ExtAppFrame.tsx +26 -10
  210. package/src/client/nodes/MarkdownNode.tsx +6 -7
  211. package/src/client/nodes/PromptNode.tsx +32 -3
  212. package/src/client/nodes/StatusNode.tsx +12 -0
  213. package/src/client/nodes/StatusSummary.tsx +11 -0
  214. package/src/client/nodes/ViewerFrame.tsx +21 -10
  215. package/src/client/nodes/WorkCard.tsx +175 -0
  216. package/src/client/nodes/use-ax-surface-bridge.ts +18 -14
  217. package/src/client/state/attention-bridge.ts +5 -0
  218. package/src/client/state/attention-store.ts +1 -0
  219. package/src/client/state/canvas-store.ts +185 -15
  220. package/src/client/state/human-store.ts +22 -2
  221. package/src/client/state/intent-bridge.ts +19 -5
  222. package/src/client/state/relation-store.ts +127 -1
  223. package/src/client/theme/global.css +94 -1
  224. package/src/client/types.ts +1 -0
  225. package/src/mcp/server.ts +13 -3
  226. package/src/server/agent-presence.ts +43 -23
  227. package/src/server/ax-flow-loop.ts +6 -0
  228. package/src/server/ax-gate-ttl.ts +8 -1
  229. package/src/server/ax-interaction.ts +16 -10
  230. package/src/server/ax-state-manager.ts +17 -4
  231. package/src/server/ax-wait.ts +20 -6
  232. package/src/server/board-export.ts +19 -6
  233. package/src/server/board-local.ts +20 -0
  234. package/src/server/board-move.ts +297 -0
  235. package/src/server/canvas-db.ts +249 -158
  236. package/src/server/canvas-operations.ts +139 -109
  237. package/src/server/canvas-schema.ts +21 -0
  238. package/src/server/canvas-serialization.ts +2 -0
  239. package/src/server/canvas-state.ts +792 -159
  240. package/src/server/card-relocation.ts +53 -0
  241. package/src/server/code-graph.ts +4 -3
  242. package/src/server/context-reads.ts +83 -55
  243. package/src/server/demo-state.json +35 -15
  244. package/src/server/file-watcher.ts +30 -10
  245. package/src/server/human-presence.ts +18 -5
  246. package/src/server/index.ts +35 -15
  247. package/src/server/intent-registry.ts +43 -5
  248. package/src/server/knowledge-lifecycle.ts +32 -9
  249. package/src/server/lifecycle-status.ts +29 -1
  250. package/src/server/mcp-app-runtime.ts +26 -7
  251. package/src/server/mutation-history.ts +37 -8
  252. package/src/server/operations/composites.ts +6 -3
  253. package/src/server/operations/mcp-contracts.ts +15 -0
  254. package/src/server/operations/mcp.ts +36 -9
  255. package/src/server/operations/ops/annotation.ts +22 -1
  256. package/src/server/operations/ops/ax-await.ts +1 -0
  257. package/src/server/operations/ops/ax-timeline.ts +28 -2
  258. package/src/server/operations/ops/batch.ts +25 -11
  259. package/src/server/operations/ops/boards.ts +44 -3
  260. package/src/server/operations/ops/nodes.ts +70 -0
  261. package/src/server/operations/ops/query.ts +32 -3
  262. package/src/server/operations/ops/snapshots.ts +1 -0
  263. package/src/server/operations/registry.ts +49 -4
  264. package/src/server/server.ts +62 -16
  265. package/src/server/tour-control.ts +37 -12
  266. package/src/server/trace-manager.ts +4 -0
  267. package/src/server/web-artifacts/scripts/init-artifact.sh +2 -2
  268. package/src/shared/agent-context.ts +7 -1
  269. package/src/shared/auto-arrange.ts +5 -4
  270. package/src/shared/ax-kinds.ts +13 -0
  271. package/src/shared/ax-surface-protocol.ts +22 -0
  272. package/src/shared/human-presence.ts +1 -0
  273. package/src/shared/lifecycle-status.ts +2 -0
  274. package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +0 -1
  275. package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +0 -1
  276. package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +0 -1
  277. package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +0 -1
  278. package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +0 -1
  279. package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +0 -1
  280. package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +0 -1
  281. package/dist/canvas/chunks/index-1r72hdzb.js +0 -301
  282. package/dist/canvas/chunks/index-g3pwnqeb.js +0 -2
  283. package/dist/canvas/chunks/index-rft2hcz5.js +0 -2
  284. package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +0 -1
  285. package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +0 -70
  286. package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +0 -1
  287. package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +0 -1
  288. package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +0 -1
  289. package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +0 -1
  290. package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +0 -1
  291. package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +0 -1
  292. package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +0 -1
  293. package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +0 -1
  294. package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +0 -8
  295. package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +0 -1
  296. package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +0 -1
  297. package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +0 -34
  298. package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +0 -1
@@ -1,3 +1,4 @@
1
+ import { useLayoutEffect, useRef } from 'preact/hooks';
1
2
  import {
2
3
  attentionHistory,
3
4
  attentionHistoryOpen,
@@ -20,13 +21,19 @@ function handleOpenUpdates(): void {
20
21
 
21
22
  export function AttentionHistory() {
22
23
  const entries = attentionHistory.value;
23
- if (entries.length === 0) return null;
24
-
25
24
  const isOpen = attentionHistoryOpen.value;
26
25
  const unread = attentionHistoryUnread.value;
26
+ const toggleRef = useRef<HTMLButtonElement>(null);
27
+ const wasOpen = useRef(isOpen);
28
+ useLayoutEffect(() => {
29
+ if (isOpen !== wasOpen.current) toggleRef.current?.focus({ preventScroll: true });
30
+ wasOpen.current = isOpen;
31
+ }, [isOpen]);
32
+ if (entries.length === 0) return null;
27
33
  if (!isOpen) {
28
34
  return (
29
35
  <button
36
+ ref={toggleRef}
30
37
  type="button"
31
38
  class="attention-history-tab"
32
39
  onClick={handleOpenUpdates}
@@ -67,6 +74,7 @@ export function AttentionHistory() {
67
74
  <span class="attention-history-subtitle">Focus and meaning shifts</span>
68
75
  </div>
69
76
  <button
77
+ ref={toggleRef}
70
78
  type="button"
71
79
  class="attention-history-close"
72
80
  onClick={closeAttentionHistory}
@@ -0,0 +1,225 @@
1
+ import { signal } from '@preact/signals';
2
+ import { useRef, useState } from 'preact/hooks';
3
+ import type { BoardMoveInput, BoardMovePreview, BoardMoveResult } from '../../server/board-move.js';
4
+ import { activeBoardId, boardIncarnation, claimNavigation, openBoard } from '../state/boards-store';
5
+ import { nodes } from '../state/canvas-store';
6
+ import { requestJson } from '../state/intent-bridge';
7
+ import { hasUnsavedMarkdown } from '../nodes/MarkdownNode';
8
+ import { hasUnsavedPrompt } from '../nodes/PromptNode';
9
+ import { hasUnsavedWorkCard } from '../nodes/WorkCard';
10
+ import { useFocusTrap } from './use-focus-trap';
11
+ import { submitWithoutNavigation } from './submit-without-navigation';
12
+
13
+ type MoveSelection = {
14
+ sourceBoardId: string;
15
+ nodeIds: string[];
16
+ requestId: string;
17
+ incarnation: number;
18
+ canNavigate: () => boolean;
19
+ };
20
+ const selection = signal<MoveSelection | null>(null);
21
+
22
+ export function openBoardMove(nodeIds: string[]): void {
23
+ if (!activeBoardId.value || !nodeIds.length) return;
24
+ selection.value = {
25
+ sourceBoardId: activeBoardId.value,
26
+ nodeIds: [...nodeIds],
27
+ requestId: crypto.randomUUID(),
28
+ incarnation: boardIncarnation,
29
+ canNavigate: claimNavigation(),
30
+ };
31
+ }
32
+
33
+ export function BoardMoveDialog() {
34
+ const current = selection.value;
35
+ return current ? <MoveForm key={current.requestId} selected={current} /> : null;
36
+ }
37
+
38
+ function MoveForm({ selected }: { selected: MoveSelection }) {
39
+ const dialogRef = useRef<HTMLDivElement>(null);
40
+ const nameRef = useRef<HTMLInputElement>(null);
41
+ const [name, setName] = useState('New board');
42
+ const [preview, setPreview] = useState<BoardMovePreview | null>(null);
43
+ const [input, setInput] = useState<BoardMoveInput | null>(null);
44
+ const [busy, setBusy] = useState(false);
45
+ const [attempted, setAttempted] = useState(false);
46
+ const [refused, setRefused] = useState(false);
47
+ const [error, setError] = useState('');
48
+ const close = () => {
49
+ if (selection.value === selected) selection.value = null;
50
+ };
51
+ const current = () =>
52
+ selection.value === selected &&
53
+ selected.incarnation === boardIncarnation &&
54
+ activeBoardId.value === selected.sourceBoardId;
55
+ useFocusTrap(dialogRef, true, { initial: nameRef, onEscape: close });
56
+
57
+ const unsaved = (ids: string[]) =>
58
+ ids.filter(
59
+ (id) =>
60
+ hasUnsavedMarkdown(selected.sourceBoardId, id) ||
61
+ hasUnsavedWorkCard(selected.sourceBoardId, id) ||
62
+ hasUnsavedPrompt(selected.sourceBoardId, id),
63
+ );
64
+ const submit = async (event: Event) => {
65
+ event.preventDefault();
66
+ if (busy || refused || !name.trim()) return;
67
+ if (!current()) return setError('The board changed. Close this dialog and select the cards again.');
68
+ setBusy(true);
69
+ setError('');
70
+ if (!preview) {
71
+ const board = await requestJson<{ ok: boolean; contentRevision: number } | null>(
72
+ 'moveRevision',
73
+ `/api/canvas/boards/${encodeURIComponent(selected.sourceBoardId)}`,
74
+ null,
75
+ );
76
+ if (!current()) {
77
+ setBusy(false);
78
+ return;
79
+ }
80
+ if (!board?.ok) {
81
+ setError('Could not read the source board. Try again.');
82
+ setBusy(false);
83
+ return;
84
+ }
85
+ const next = {
86
+ sourceBoardId: selected.sourceBoardId,
87
+ nodeIds: selected.nodeIds,
88
+ requestId: selected.requestId,
89
+ name: name.trim(),
90
+ expectedRevision: board.contentRevision,
91
+ };
92
+ const result = await requestJson<{ ok: boolean; preview?: BoardMovePreview; error?: string } | null>(
93
+ 'previewMove',
94
+ '/api/canvas/boards/move-selection',
95
+ null,
96
+ {
97
+ method: 'POST',
98
+ headers: { 'Content-Type': 'application/json' },
99
+ body: JSON.stringify({ ...next, preview: true }),
100
+ },
101
+ );
102
+ if (current()) {
103
+ if (result?.ok && result.preview) {
104
+ setInput(next);
105
+ setPreview(result.preview);
106
+ if (unsaved(result.preview.nodeIds).length)
107
+ setError('Save or clear the selected cards’ drafts before moving. Close and preview again.');
108
+ } else setError(result?.error ?? 'Could not preview the move. Try again.');
109
+ }
110
+ } else if (input && !preview.blockers.length) {
111
+ if (unsaved(preview.nodeIds).length) {
112
+ setBusy(false);
113
+ return setError('Save or clear the selected cards’ drafts before moving. Close and preview again.');
114
+ }
115
+ setAttempted(true);
116
+ const result = await requestJson<
117
+ ({ ok: true } & BoardMoveResult) | { ok: false; error?: string; blockers?: string[] } | null
118
+ >('moveSelection', '/api/canvas/boards/move-selection', null, {
119
+ method: 'POST',
120
+ headers: { 'Content-Type': 'application/json' },
121
+ body: JSON.stringify(input),
122
+ });
123
+ if (result?.ok) {
124
+ const navigate = current() && selected.canNavigate();
125
+ close();
126
+ if (navigate) await openBoard(result.destinationBoardId);
127
+ } else if (selection.value === selected) {
128
+ if (result) {
129
+ setRefused(true);
130
+ setError(`${result.error ?? 'Move refused.'} ${result.blockers?.join(' ') ?? ''} Close and preview again.`);
131
+ } else {
132
+ setError('No reply received. Retry uses the same request and cannot create a second board.');
133
+ }
134
+ }
135
+ }
136
+ setBusy(false);
137
+ };
138
+
139
+ const blocked = !!preview?.blockers.length;
140
+ return (
141
+ <div class="text-prompt-backdrop board-move-backdrop" onMouseDown={close}>
142
+ <div
143
+ ref={dialogRef}
144
+ class="text-prompt board-move-dialog"
145
+ role="dialog"
146
+ aria-modal="true"
147
+ aria-labelledby="board-move-title"
148
+ onMouseDown={(event) => event.stopPropagation()}
149
+ onKeyDown={(event) => {
150
+ event.stopPropagation();
151
+ if (event.key === 'Escape') {
152
+ event.preventDefault();
153
+ close();
154
+ }
155
+ }}
156
+ >
157
+ <form onClick={submitWithoutNavigation} onSubmit={submit}>
158
+ <h2 id="board-move-title">Move to new board</h2>
159
+ <p>Move the original cards, including group contents. Leave a portal on this board.</p>
160
+ <label class="board-copy-field">
161
+ New board name
162
+ <input
163
+ ref={nameRef}
164
+ class="text-prompt-input"
165
+ maxLength={120}
166
+ required
167
+ value={name}
168
+ readOnly={!!preview || busy}
169
+ onInput={(event) => setName(event.currentTarget.value)}
170
+ />
171
+ </label>
172
+ {preview && (
173
+ <>
174
+ <p>
175
+ {preview.nodeIds.length} cards · {preview.pinnedNodeIds.length} pinned ·{' '}
176
+ {preview.affectedBoardIds.length + 1} affected boards
177
+ </p>
178
+ <ul class="board-move-cards">
179
+ {preview.nodeIds.map((id) => (
180
+ <li key={id}>{String(nodes.value.get(id)?.data.title ?? id)}</li>
181
+ ))}
182
+ </ul>
183
+ {preview.readmeNodeId && <p>The README moves with the cards.</p>}
184
+ {!!preview.liveTaskNodeIds.length && (
185
+ <p>{preview.liveTaskNodeIds.length} live task cards keep their original task owner.</p>
186
+ )}
187
+ <p>Content, relations and review history are preserved. Moving does not validate or read content.</p>
188
+ <p class="board-move-warning">
189
+ ⚠ History boundary: pre-move Undo/Redo is cleared on affected boards. Older snapshots remain
190
+ inspectable, but cannot be restored across the move.
191
+ </p>
192
+ <p>Board-space annotations stay here. The new board opens after the move.</p>
193
+ {blocked && (
194
+ <div role="alert" class="board-move-warning">
195
+ <strong>⚠ Cannot move this selection</strong>
196
+ <ul>
197
+ {preview.blockers.map((reason) => (
198
+ <li key={reason}>{reason}</li>
199
+ ))}
200
+ </ul>
201
+ <p>No cards moved. Close, resolve the listed bindings and preview again.</p>
202
+ </div>
203
+ )}
204
+ </>
205
+ )}
206
+ {error && (
207
+ <p role="alert" class="board-move-warning">
208
+ ⚠ {error}
209
+ </p>
210
+ )}
211
+ <div class="text-prompt-actions">
212
+ <button type="button" class="text-prompt-cancel" onClick={close}>
213
+ {blocked || refused ? 'Close' : 'Cancel'}
214
+ </button>
215
+ {!blocked && !refused && (
216
+ <button type="submit" class="text-prompt-confirm" disabled={busy || !name.trim()}>
217
+ {busy ? 'Working…' : attempted ? 'Retry move' : preview ? 'Move and open' : 'Preview move'}
218
+ </button>
219
+ )}
220
+ </div>
221
+ </form>
222
+ </div>
223
+ </div>
224
+ );
225
+ }
@@ -4,6 +4,7 @@ import { knowledgeLifecycleEnabled } from '../state/workspace-settings';
4
4
  import { relationHighlight } from '../state/relation-store';
5
5
  import { LifecycleFooter } from './LifecycleChrome';
6
6
  import { BarHint } from './BarHint';
7
+ import { openBoardMove } from './BoardMoveDialog';
7
8
  import { CrossBoardReferences } from './CrossBoardReferences';
8
9
  import { BoardPinButton } from './BoardPinButton';
9
10
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks';
@@ -36,6 +37,8 @@ import {
36
37
  updateNodeData,
37
38
  viewport,
38
39
  canvasTool,
40
+ groupContents,
41
+ openGroup,
39
42
  } from '../state/canvas-store';
40
43
  import {
41
44
  addToGroupFromClient,
@@ -275,7 +278,10 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
275
278
  reportHumanGrab(node.id);
276
279
  noteNearAtGrab(node.id);
277
280
  takeOverNode(node.id, typeof node.data.title === 'string' && node.data.title ? node.data.title : node.type);
278
- buildSnapCache(node.id, nodes.value.values());
281
+ buildSnapCache(
282
+ node.id,
283
+ [...nodes.value.values()].filter((candidate) => !groupContents.value || groupContents.value.has(candidate.id)),
284
+ );
279
285
  if (node.type !== 'group') {
280
286
  const onKey = (ev: KeyboardEvent) => {
281
287
  if (ev.key === 'Escape') suppressDropForDrag();
@@ -333,12 +339,21 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
333
339
  const dir = ARROW_DIRS[e.key];
334
340
  if (dir) {
335
341
  e.preventDefault();
336
- const next = nearestNodeInDirection(node, dir, nodes.value.values());
342
+ const next = nearestNodeInDirection(
343
+ node,
344
+ dir,
345
+ [...nodes.value.values()].filter(
346
+ (candidate) => !groupContents.value || groupContents.value.has(candidate.id),
347
+ ),
348
+ );
337
349
  if (!next) return;
338
350
  bringToFront(next.id);
339
351
  requestAnimationFrame(() => {
340
352
  document.querySelector<HTMLElement>(`.canvas-node[data-node-id="${CSS.escape(next.id)}"]`)?.focus();
341
353
  });
354
+ } else if (e.key === 'Enter' && e.target === e.currentTarget && node.type === 'group') {
355
+ e.preventDefault();
356
+ openGroup(node.id);
342
357
  } else if (e.key === 'Enter' && e.target === e.currentTarget && EXPANDABLE_TYPES.has(node.type)) {
343
358
  e.preventDefault();
344
359
  expandNode(node.id);
@@ -380,6 +395,8 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
380
395
  if (!body) return;
381
396
 
382
397
  const observer = new ResizeObserver(() => {
398
+ // Focus-mode return can remount an open work draft. Never save its temporary form height.
399
+ if (body.querySelector('.work-card-editor')) hasAutoFit.current = true;
383
400
  if (hasAutoFit.current) {
384
401
  observer.disconnect();
385
402
  return;
@@ -492,8 +509,12 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
492
509
  // Collapsed group: a compact chip. Children keep their positions for restore.
493
510
  return (
494
511
  <div
512
+ ref={shellRef}
495
513
  class={`${nodeClass} group-chip`}
496
514
  data-node-type={node.type}
515
+ data-node-id={node.id}
516
+ tabIndex={isActive ? 0 : -1}
517
+ onKeyDown={handleNodeKeyDown}
497
518
  style={{ ...nodeStyle, width: 'auto', height: 'auto' }}
498
519
  onPointerDown={handlePointerDown}
499
520
  onDblClick={handleDblClick}
@@ -671,6 +692,28 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
671
692
  </button>
672
693
  {groupMenuOpen && (
673
694
  <div class="group-menu" role="menu" onPointerDown={(e) => e.stopPropagation()}>
695
+ <button
696
+ type="button"
697
+ role="menuitem"
698
+ onClick={(e) => {
699
+ e.stopPropagation();
700
+ setGroupMenuOpen(false);
701
+ openGroup(node.id);
702
+ }}
703
+ >
704
+ Open group
705
+ </button>
706
+ <button
707
+ type="button"
708
+ role="menuitem"
709
+ onClick={(e) => {
710
+ e.stopPropagation();
711
+ setGroupMenuOpen(false);
712
+ openBoardMove([node.id]);
713
+ }}
714
+ >
715
+ Move to new board
716
+ </button>
674
717
  <button
675
718
  type="button"
676
719
  role="menuitem"
@@ -37,6 +37,9 @@ import {
37
37
  dragDropTarget,
38
38
  hiddenByCollapsedGroup,
39
39
  canvasTool,
40
+ groupContents,
41
+ groupViewNodes,
42
+ canvasInteractionEpoch,
40
43
  } from '../state/canvas-store';
41
44
  import { createEdgeFromClient, createNodeFromClient } from '../state/intent-bridge';
42
45
  import type { AnnotationTool, CanvasAnnotation, CanvasNodeState } from '../types';
@@ -50,6 +53,7 @@ import { ScopeFenceLayer } from './ScopeFenceLayer';
50
53
  import { CanvasNode } from './CanvasNode';
51
54
  import { BoardNode } from '../nodes/BoardNode';
52
55
  import { EdgeLayer } from './EdgeLayer';
56
+ import { GroupOutsideLinks } from './GroupNavigation';
53
57
  import { AnnotationLayer } from './AnnotationLayer';
54
58
  import { activeGuides } from './snap-guides';
55
59
  import { usePanZoom } from './use-pan-zoom';
@@ -104,19 +108,30 @@ function renderNodeContent(node: CanvasNodeState) {
104
108
  */
105
109
  class WorldNode extends Component<{
106
110
  node: CanvasNodeState;
111
+ hidden: boolean;
107
112
  onContextMenu?: (e: MouseEvent, nodeId: string) => void;
108
113
  }> {
109
- shouldComponentUpdate(next: { node: CanvasNodeState; onContextMenu?: (e: MouseEvent, nodeId: string) => void }) {
110
- return next.node !== this.props.node || next.onContextMenu !== this.props.onContextMenu;
114
+ shouldComponentUpdate(next: {
115
+ node: CanvasNodeState;
116
+ hidden: boolean;
117
+ onContextMenu?: (e: MouseEvent, nodeId: string) => void;
118
+ }) {
119
+ return (
120
+ next.node !== this.props.node ||
121
+ next.hidden !== this.props.hidden ||
122
+ next.onContextMenu !== this.props.onContextMenu
123
+ );
111
124
  }
112
125
 
113
126
  render() {
114
- const { node, onContextMenu } = this.props;
127
+ const { node, hidden, onContextMenu } = this.props;
115
128
  const viewer = ['html', 'mcp-app', 'json-render', 'graph', 'webpage', 'mermaid'].includes(node.type);
116
129
  return (
117
- <CanvasNode node={node} onContextMenu={onContextMenu}>
118
- {viewer ? <ViewerHost node={node}>{renderNodeContent(node)}</ViewerHost> : renderNodeContent(node)}
119
- </CanvasNode>
130
+ <div style={{ display: hidden ? 'none' : 'contents' }}>
131
+ <CanvasNode node={node} onContextMenu={onContextMenu}>
132
+ {viewer ? <ViewerHost node={node}>{renderNodeContent(node)}</ViewerHost> : renderNodeContent(node)}
133
+ </CanvasNode>
134
+ </div>
120
135
  );
121
136
  }
122
137
  }
@@ -572,6 +587,7 @@ export function CanvasViewport({
572
587
  // Find intersecting nodes (AABB intersection)
573
588
  const hits: string[] = [];
574
589
  for (const node of nodes.value.values()) {
590
+ if (groupContents.value && !groupContents.value.has(node.id)) continue;
575
591
  const nx = node.position.x;
576
592
  const ny = node.position.y;
577
593
  if (nx + node.size.width > worldMinX && nx < worldMaxX && ny + node.size.height > worldMinY && ny < worldMaxY) {
@@ -622,6 +638,7 @@ export function CanvasViewport({
622
638
  let hit: string | null = null;
623
639
  for (const node of nodes.value.values()) {
624
640
  if (node.id === exceptId || node.type === 'group') continue;
641
+ if (groupContents.value && !groupContents.value.has(node.id)) continue;
625
642
  if (
626
643
  wx >= node.position.x &&
627
644
  wx <= node.position.x + node.size.width &&
@@ -807,11 +824,17 @@ export function CanvasViewport({
807
824
  // reorder DOM children when bringToFront() changes zIndex, causing browsers to
808
825
  // detach/reattach iframe elements (which forces them to reload/reconnect).
809
826
  // Group nodes render first (behind) so they serve as visual containers.
827
+ const mounted = useRef({ epoch: canvasInteractionEpoch.value, ids: new Set<string>() });
828
+ if (mounted.current.epoch !== canvasInteractionEpoch.value)
829
+ mounted.current = { epoch: canvasInteractionEpoch.value, ids: new Set() };
830
+ for (const id of mounted.current.ids) if (!nodes.value.has(id)) mounted.current.ids.delete(id);
831
+ const collapsed = hiddenByCollapsedGroup.value;
810
832
  const worldNodes = getRenderableWorldNodes(
811
833
  nodes.value.values(),
812
834
  expandedNodeId.value,
813
- new Set(hiddenByCollapsedGroup.value.keys()),
835
+ new Set([...collapsed.keys()].filter((id) => !mounted.current.ids.has(id))),
814
836
  );
837
+ for (const node of worldNodes) mounted.current.ids.add(node.id);
815
838
  const lowestZ = worldNodes.reduce((lowest, node) => Math.min(lowest, Math.floor(node.zIndex)), 0);
816
839
  const highestZ = worldNodes.reduce((highest, node) => Math.max(highest, Math.ceil(node.zIndex)), 0);
817
840
  // Keep group-body hit order without changing the frames' visual stacking.
@@ -895,7 +918,8 @@ export function CanvasViewport({
895
918
  <IntentLayer />
896
919
  <AgentPresenceLayer />
897
920
  <HumanPresenceLayer />
898
- <EdgeLayer nodes={nodes} edges={edges} />
921
+ <EdgeLayer nodes={groupViewNodes} edges={edges} />
922
+ <GroupOutsideLinks />
899
923
  {draggingEdge.value && (
900
924
  <div
901
925
  class="edge-hint-pill"
@@ -930,7 +954,12 @@ export function CanvasViewport({
930
954
  <AnnotationLayer annotations={Array.from(annotations.value.values())} />
931
955
  {draftAnnotation && draftAnnotation.points.length >= 2 && <AnnotationLayer annotations={[draftAnnotation]} />}
932
956
  {worldNodes.map((node) => (
933
- <WorldNode key={node.id} node={node} onContextMenu={onNodeContextMenu} />
957
+ <WorldNode
958
+ key={node.id}
959
+ node={node}
960
+ hidden={collapsed.has(node.id) || (groupContents.value !== null && !groupContents.value.has(node.id))}
961
+ onContextMenu={onNodeContextMenu}
962
+ />
934
963
  ))}
935
964
  {/* Snap alignment guide lines */}
936
965
  {activeGuides.value && (
@@ -7,6 +7,7 @@ import {
7
7
  expandNode,
8
8
  focusNode,
9
9
  nodes,
10
+ openGroup,
10
11
  pendingConnection,
11
12
  persistLayout,
12
13
  removeNode,
@@ -30,6 +31,7 @@ import {
30
31
  import { canOpenAsSite, openNodeAsSite } from '../nodes/surface-url';
31
32
  import { canCopyPrimitiveJson, copyPrimitiveJson } from '../nodes/primitive-copy';
32
33
  import { askText } from './TextPrompt';
34
+ import { openBoardMove } from './BoardMoveDialog';
33
35
  import { activeBoard, boardIncarnation, setBoardReadme } from '../state/boards-store';
34
36
  import { openLifecycle } from '../state/knowledge-store';
35
37
  import { RELATION_KINDS, relationKind } from '../../shared/relations.js';
@@ -468,6 +470,23 @@ async function createPromptedNode(opts: {
468
470
 
469
471
  function buildCanvasMenuItems(canvasX: number, canvasY: number): MenuItem[] {
470
472
  return [
473
+ {
474
+ label: 'New work card',
475
+ action: () => {
476
+ const width = 360;
477
+ const height = 200;
478
+ const position = centeredPosition(canvasX, canvasY, width, height);
479
+ void createNodeFromClient({
480
+ type: 'status',
481
+ title: 'New work card',
482
+ data: { workState: 'todo' },
483
+ x: position.x,
484
+ y: position.y,
485
+ width,
486
+ height,
487
+ });
488
+ },
489
+ },
471
490
  {
472
491
  label: 'New note',
473
492
  action: () => {
@@ -567,18 +586,19 @@ function buildNodeMenuItems(node: CanvasNodeState): MenuItem[] {
567
586
  action: () => focusNode(node.id),
568
587
  });
569
588
 
570
- items.push({
571
- label: 'Rename…',
572
- action: () => {
573
- void askText('Rename node', 'Node title', {
574
- initial: typeof node.data.title === 'string' ? node.data.title : '',
575
- confirm: 'Save',
576
- }).then((title) => {
577
- if (!title?.trim()) return;
578
- void updateNodeFromClient(node.id, { title: title.trim() });
579
- });
580
- },
581
- });
589
+ if (!(node.type === 'status' && typeof node.data.workItemId === 'string'))
590
+ items.push({
591
+ label: 'Rename…',
592
+ action: () => {
593
+ void askText('Rename node', 'Node title', {
594
+ initial: typeof node.data.title === 'string' ? node.data.title : '',
595
+ confirm: 'Save',
596
+ }).then((title) => {
597
+ if (!title?.trim()) return;
598
+ void updateNodeFromClient(node.id, { title: title.trim() });
599
+ });
600
+ },
601
+ });
582
602
 
583
603
  // Expand into full-viewport overlay for focused work
584
604
  if (EXPANDABLE_TYPES.has(node.type)) {
@@ -757,6 +777,8 @@ function buildNodeMenuItems(node: CanvasNodeState): MenuItem[] {
757
777
  if (node.type === 'group') {
758
778
  const childIds = (node.data.children as string[]) ?? [];
759
779
  items.push({ separator: true });
780
+ items.push({ label: 'Open group', action: () => openGroup(node.id) });
781
+ items.push({ label: 'Move to new board', action: () => openBoardMove([node.id]) });
760
782
  items.push({
761
783
  render: (onClose) => renderGroupColorSection(node, onClose),
762
784
  });