pmx-canvas 0.6.5 → 0.7.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 (169) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +67 -3
  2. package/CHANGELOG.md +54 -0
  3. package/Readme.md +29 -5
  4. package/dist/canvas/global.css +440 -24
  5. package/dist/canvas/index.js +90 -75
  6. package/dist/types/cli/agent.d.ts +3 -0
  7. package/dist/types/cli/commands/backup.d.ts +1 -0
  8. package/dist/types/cli/commands/boards.d.ts +1 -0
  9. package/dist/types/cli/commands/export.d.ts +1 -0
  10. package/dist/types/client/canvas/BoardCopyDialog.d.ts +5 -0
  11. package/dist/types/client/canvas/BoardFolderDialog.d.ts +7 -0
  12. package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -0
  13. package/dist/types/client/canvas/BoardSwitcher.d.ts +8 -0
  14. package/dist/types/client/canvas/ExportDialog.d.ts +7 -0
  15. package/dist/types/client/canvas/HomeView.d.ts +9 -0
  16. package/dist/types/client/nodes/AttachmentNode.d.ts +6 -0
  17. package/dist/types/client/nodes/BoardNode.d.ts +5 -0
  18. package/dist/types/client/state/boards-store.d.ts +79 -0
  19. package/dist/types/client/state/canvas-store.d.ts +2 -0
  20. package/dist/types/client/state/intent-bridge.d.ts +1 -0
  21. package/dist/types/client/state/session-store.d.ts +7 -0
  22. package/dist/types/client/types.d.ts +2 -2
  23. package/dist/types/json-render/catalog.d.ts +1 -1
  24. package/dist/types/json-render/charts/definitions.d.ts +1 -1
  25. package/dist/types/json-render/server.d.ts +6 -0
  26. package/dist/types/mcp/canvas-access.d.ts +10 -3
  27. package/dist/types/mcp/context-uri-template.d.ts +6 -0
  28. package/dist/types/server/agent-presence.d.ts +1 -1
  29. package/dist/types/server/attribution.d.ts +11 -0
  30. package/dist/types/server/ax-context.d.ts +3 -1
  31. package/dist/types/server/ax-interaction.d.ts +2 -2
  32. package/dist/types/server/ax-state.d.ts +13 -3
  33. package/dist/types/server/backup.d.ts +36 -0
  34. package/dist/types/server/board-export.d.ts +45 -0
  35. package/dist/types/server/canvas-db.d.ts +63 -9
  36. package/dist/types/server/canvas-operations.d.ts +23 -0
  37. package/dist/types/server/canvas-provenance.d.ts +1 -1
  38. package/dist/types/server/canvas-state.d.ts +140 -3
  39. package/dist/types/server/context-brief.d.ts +111 -0
  40. package/dist/types/server/context-reads.d.ts +55 -0
  41. package/dist/types/server/document-import.d.ts +66 -0
  42. package/dist/types/server/index.d.ts +103 -4
  43. package/dist/types/server/mutation-history.d.ts +1 -0
  44. package/dist/types/server/operations/invoker.d.ts +2 -0
  45. package/dist/types/server/operations/mcp.d.ts +7 -1
  46. package/dist/types/server/operations/ops/ax-shared.d.ts +2 -2
  47. package/dist/types/server/operations/ops/backup.d.ts +2 -0
  48. package/dist/types/server/operations/ops/boards.d.ts +10 -0
  49. package/dist/types/server/operations/ops/export.d.ts +2 -0
  50. package/dist/types/server/operations/ops/imports.d.ts +2 -0
  51. package/dist/types/server/operations/ops/nodes.d.ts +1 -1
  52. package/dist/types/server/operations/ops/query.d.ts +2 -0
  53. package/dist/types/server/operations/registry.d.ts +9 -1
  54. package/dist/types/server/operations/types.d.ts +1 -1
  55. package/dist/types/server/server.d.ts +1 -0
  56. package/dist/types/server/trace-manager.d.ts +7 -0
  57. package/dist/types/server/webpage-node.d.ts +22 -1
  58. package/dist/types/server/workbench-auth.d.ts +3 -0
  59. package/dist/types/shared/boards.d.ts +6 -0
  60. package/docs/RELEASE.md +5 -0
  61. package/docs/cli.md +68 -0
  62. package/docs/http-api.md +198 -2
  63. package/docs/mcp.md +71 -6
  64. package/docs/node-types.md +26 -0
  65. package/docs/plans/plan-010-attributed-ask-answers.md +39 -0
  66. package/docs/plans/plan-011-context-read-instrumentation.md +54 -0
  67. package/docs/plans/plan-012-many-boards-stage-1.md +98 -0
  68. package/docs/plans/plan-013-static-board-export.md +47 -0
  69. package/docs/plans/plan-014-connected-board-memory.md +140 -0
  70. package/docs/product-vision-2026-09.md +118 -18
  71. package/docs/sdk.md +50 -0
  72. package/package.json +1 -1
  73. package/skills/pmx-canvas/SKILL.md +53 -4
  74. package/skills/pmx-canvas/references/full-reference.md +43 -7
  75. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +3 -0
  76. package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
  77. package/skills/pmx-canvas-orchestration/SKILL.md +5 -0
  78. package/skills/pmx-canvas-testing/SKILL.md +4 -0
  79. package/src/cli/agent.ts +4 -0
  80. package/src/cli/commands/ax.ts +13 -0
  81. package/src/cli/commands/backup.ts +66 -0
  82. package/src/cli/commands/boards.ts +71 -0
  83. package/src/cli/commands/export.ts +30 -0
  84. package/src/cli/commands/nodes.ts +4 -1
  85. package/src/cli/commands/pins.ts +6 -2
  86. package/src/cli/commands/pump.ts +3 -0
  87. package/src/cli/commands/query.ts +33 -7
  88. package/src/cli/commands/view.ts +7 -5
  89. package/src/cli/index.ts +19 -9
  90. package/src/client/App.tsx +21 -3
  91. package/src/client/canvas/BoardCopyDialog.tsx +153 -0
  92. package/src/client/canvas/BoardFolderDialog.tsx +120 -0
  93. package/src/client/canvas/BoardLinkDialog.tsx +93 -0
  94. package/src/client/canvas/BoardSwitcher.tsx +149 -0
  95. package/src/client/canvas/CanvasViewport.tsx +3 -0
  96. package/src/client/canvas/CommandPalette.tsx +107 -4
  97. package/src/client/canvas/ContextMenu.tsx +4 -0
  98. package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -0
  99. package/src/client/canvas/ExportDialog.tsx +197 -0
  100. package/src/client/canvas/HomeView.tsx +277 -0
  101. package/src/client/canvas/SessionPanel.tsx +28 -1
  102. package/src/client/canvas/ToolRail.tsx +38 -3
  103. package/src/client/canvas/TopBar.tsx +34 -18
  104. package/src/client/canvas/import-files.ts +19 -1
  105. package/src/client/canvas/kind-colors.ts +1 -0
  106. package/src/client/icons.tsx +2 -0
  107. package/src/client/nodes/AttachmentNode.tsx +148 -0
  108. package/src/client/nodes/BoardNode.tsx +35 -0
  109. package/src/client/nodes/FileNode.tsx +6 -0
  110. package/src/client/nodes/MarkdownNode.tsx +47 -19
  111. package/src/client/nodes/ax-node-actions.ts +2 -5
  112. package/src/client/nodes/use-ax-surface-bridge.ts +1 -1
  113. package/src/client/state/boards-store.ts +167 -0
  114. package/src/client/state/canvas-store.ts +22 -0
  115. package/src/client/state/intent-bridge.ts +30 -2
  116. package/src/client/state/node-factory.ts +1 -0
  117. package/src/client/state/session-store.ts +5 -0
  118. package/src/client/state/sse-bridge.ts +49 -5
  119. package/src/client/theme/global.css +440 -24
  120. package/src/client/types.ts +2 -0
  121. package/src/json-render/server.ts +16 -9
  122. package/src/mcp/canvas-access.ts +59 -22
  123. package/src/mcp/context-uri-template.ts +28 -0
  124. package/src/mcp/server.ts +122 -15
  125. package/src/server/agent-context.ts +116 -29
  126. package/src/server/attribution.ts +29 -0
  127. package/src/server/ax-context.ts +4 -2
  128. package/src/server/ax-interaction.ts +1 -0
  129. package/src/server/ax-state-manager.ts +72 -3
  130. package/src/server/ax-state.ts +41 -0
  131. package/src/server/backup.ts +178 -0
  132. package/src/server/board-export.ts +686 -0
  133. package/src/server/canvas-db.ts +599 -107
  134. package/src/server/canvas-operations.ts +68 -9
  135. package/src/server/canvas-provenance.ts +1 -0
  136. package/src/server/canvas-schema.ts +12 -0
  137. package/src/server/canvas-state.ts +774 -45
  138. package/src/server/context-brief.ts +372 -0
  139. package/src/server/context-reads.ts +238 -0
  140. package/src/server/demo-state.json +659 -2
  141. package/src/server/document-import.ts +223 -0
  142. package/src/server/index.ts +231 -5
  143. package/src/server/mutation-history.ts +10 -13
  144. package/src/server/operations/composites.ts +26 -4
  145. package/src/server/operations/http.ts +9 -3
  146. package/src/server/operations/index.ts +8 -0
  147. package/src/server/operations/invoker.ts +14 -3
  148. package/src/server/operations/mcp.ts +50 -11
  149. package/src/server/operations/ops/app.ts +5 -0
  150. package/src/server/operations/ops/ax-read.ts +10 -6
  151. package/src/server/operations/ops/ax-timeline.ts +75 -0
  152. package/src/server/operations/ops/backup.ts +134 -0
  153. package/src/server/operations/ops/batch.ts +8 -1
  154. package/src/server/operations/ops/boards.ts +351 -0
  155. package/src/server/operations/ops/export.ts +74 -0
  156. package/src/server/operations/ops/ext-app.ts +4 -0
  157. package/src/server/operations/ops/imports.ts +220 -0
  158. package/src/server/operations/ops/nodes.ts +57 -5
  159. package/src/server/operations/ops/query.ts +206 -9
  160. package/src/server/operations/registry.ts +123 -31
  161. package/src/server/operations/types.ts +1 -1
  162. package/src/server/server.ts +146 -2
  163. package/src/server/spatial-analysis.ts +9 -17
  164. package/src/server/trace-manager.ts +19 -0
  165. package/src/server/web-artifacts.ts +2 -1
  166. package/src/server/webpage-node.ts +173 -13
  167. package/src/server/workbench-auth.ts +11 -0
  168. package/src/shared/boards.ts +18 -0
  169. package/src/shared/node-sizes.ts +1 -0
@@ -5,6 +5,7 @@ import { recomputeCodeGraph } from './code-graph.js';
5
5
  import { diffLayouts } from './mutation-history.js';
6
6
  import {
7
7
  canvasState,
8
+ defaultBoardName,
8
9
  type CanvasEdge,
9
10
  type CanvasNodeState,
10
11
  type CanvasNodeUpdate,
@@ -20,6 +21,7 @@ import {
20
21
  type ExternalMcpTransportConfig,
21
22
  } from './mcp-app-runtime.js';
22
23
  import { mutationHistory } from './mutation-history.js';
24
+ import { intentRegistry } from './intent-registry.js';
23
25
  import { clampCreateNodeSize } from './canvas-validation.js';
24
26
  import { computeGroupBounds, findOpenCanvasPosition } from './placement.js';
25
27
  import { searchNodes } from './spatial-analysis.js';
@@ -645,6 +647,7 @@ export function primeCanvasRuntimeBackends(options: { forceRehydrateExtApps?: bo
645
647
  export async function syncCanvasRuntimeBackends(
646
648
  options: { forceRehydrateExtApps?: boolean; alreadyPrimed?: boolean } = {},
647
649
  ): Promise<{ rehydrated: number; failed: number }> {
650
+ const boardGeneration = canvasState.boardGeneration;
648
651
  const targetIds =
649
652
  options.alreadyPrimed === true
650
653
  ? canvasState
@@ -656,6 +659,7 @@ export async function syncCanvasRuntimeBackends(
656
659
  let failed = 0;
657
660
 
658
661
  for (const nodeId of targetIds) {
662
+ if (canvasState.boardGeneration !== boardGeneration) break;
659
663
  const current = canvasState.getNode(nodeId);
660
664
  if (!isExtAppNode(current)) continue;
661
665
 
@@ -683,6 +687,10 @@ export async function syncCanvasRuntimeBackends(
683
687
  ? { serverName: current.data.serverName.trim() }
684
688
  : {}),
685
689
  });
690
+ if (canvasState.boardGeneration !== boardGeneration) {
691
+ closeMcpAppSession(opened.sessionId);
692
+ continue;
693
+ }
686
694
  const toolInput = resolveExtAppRehydratedToolInput(current, opened.toolInput);
687
695
  const storedCheckpointId = getStoredExcalidrawCheckpointId(current);
688
696
  const toolResult = isExcalidrawCreateView(opened.serverName, opened.toolName)
@@ -707,6 +715,7 @@ export async function syncCanvasRuntimeBackends(
707
715
  });
708
716
  rehydrated++;
709
717
  } catch (error) {
718
+ if (canvasState.boardGeneration !== boardGeneration) continue;
710
719
  canvasState.withSuppressedRecording(() => {
711
720
  setExtAppRuntimeState(nodeId, {
712
721
  appSessionId: null,
@@ -1060,6 +1069,7 @@ export async function refreshCanvasWebpageNode(
1060
1069
  }
1061
1070
 
1062
1071
  const currentData = existing.data;
1072
+ const boardGeneration = canvasState.boardGeneration;
1063
1073
  const configuredUrl =
1064
1074
  typeof options.url === 'string' && options.url.trim().length > 0
1065
1075
  ? options.url
@@ -1085,16 +1095,11 @@ export async function refreshCanvasWebpageNode(
1085
1095
  };
1086
1096
  }
1087
1097
 
1088
- const fetchingData: Record<string, unknown> = {
1089
- ...currentData,
1090
- url: normalizedUrl,
1091
- status: 'fetching',
1092
- };
1093
- delete fetchingData.error;
1094
- canvasState.updateNode(id, { data: fetchingData });
1095
-
1096
1098
  try {
1097
1099
  const snapshot = await fetchWebpageSnapshot(normalizedUrl);
1100
+ if (canvasState.boardGeneration !== boardGeneration) {
1101
+ return { ok: false, id, error: 'The board changed during webpage refresh.' };
1102
+ }
1098
1103
  const latest = canvasState.getNode(id);
1099
1104
  if (!latest || latest.type !== 'webpage') {
1100
1105
  return { ok: false, id, error: `Webpage node "${id}" disappeared during refresh.` };
@@ -1128,6 +1133,9 @@ export async function refreshCanvasWebpageNode(
1128
1133
  canvasState.updateNode(id, { data: nextData });
1129
1134
  return { ok: true, id };
1130
1135
  } catch (error) {
1136
+ if (canvasState.boardGeneration !== boardGeneration) {
1137
+ return { ok: false, id, error: 'The board changed during webpage refresh.' };
1138
+ }
1131
1139
  const details = getWebpageFetchErrorDetails(error);
1132
1140
  const latest = canvasState.getNode(id);
1133
1141
  if (latest?.type === 'webpage') {
@@ -1462,14 +1470,65 @@ export async function restoreCanvasSnapshot(idOrName: string): Promise<{ ok: boo
1462
1470
  const ok = canvasState.restoreSnapshot(idOrName);
1463
1471
  if (ok) {
1464
1472
  primeCanvasRuntimeBackends({ forceRehydrateExtApps: true });
1473
+ const boardGeneration = canvasState.boardGeneration;
1465
1474
  void syncCanvasRuntimeBackends({ forceRehydrateExtApps: true, alreadyPrimed: true }).finally(() => {
1466
- emitCanvasLayoutUpdate();
1475
+ if (canvasState.boardGeneration === boardGeneration) emitCanvasLayoutUpdate();
1467
1476
  });
1468
1477
  canvasState.flushToDisk();
1469
1478
  }
1470
1479
  return { ok };
1471
1480
  }
1472
1481
 
1482
+ /**
1483
+ * Open a board (or Home for null): the state layer saves the current board and
1484
+ * loads the next, and everything else that holds the old board in memory is
1485
+ * reset — undo closures, pending intents, file watchers, app sessions and the
1486
+ * code graph (the trace chain restarts itself when its last node is gone).
1487
+ * Agent and human presence are kept: they belong to the people and agents,
1488
+ * not to a board.
1489
+ */
1490
+ export function openCanvasBoard(id: string | null): { ok: boolean } {
1491
+ if (!canvasState.switchBoard(id)) return { ok: false };
1492
+ mutationHistory.reset();
1493
+ intentRegistry.reset();
1494
+ startBoardRuntime();
1495
+ return { ok: true };
1496
+ }
1497
+
1498
+ function startBoardRuntime(): void {
1499
+ primeCanvasRuntimeBackends({ forceRehydrateExtApps: true });
1500
+ scheduleCodeGraphRecompute();
1501
+ emitCanvasLayoutUpdate();
1502
+ // App rehydration can take a network round trip; it finishes in the background.
1503
+ const boardGeneration = canvasState.boardGeneration;
1504
+ void syncCanvasRuntimeBackends({ forceRehydrateExtApps: true, alreadyPrimed: true }).finally(() => {
1505
+ if (canvasState.boardGeneration === boardGeneration) emitCanvasLayoutUpdate();
1506
+ });
1507
+ }
1508
+
1509
+ /**
1510
+ * Restore the whole library from a backup file (the current file is kept as
1511
+ * `<db>.before-restore`); the restored library opens on its last open board.
1512
+ */
1513
+ export function restoreCanvasLibrary(file: string): void {
1514
+ canvasState.replaceDatabase(file);
1515
+ mutationHistory.reset();
1516
+ intentRegistry.reset();
1517
+ startBoardRuntime();
1518
+ }
1519
+
1520
+ /**
1521
+ * A write that arrives while no board is open (Home) lands on the most recently
1522
+ * opened board, or on a new board when the workspace has none — nothing on
1523
+ * Home is displaced, because Home holds nothing. Intents and undo history are
1524
+ * kept: whatever was signalled on Home was aimed at the board this opens.
1525
+ */
1526
+ export function openBoardForWrite(): void {
1527
+ if (canvasState.activeBoardId) return;
1528
+ const recent = canvasState.listBoards()[0] ?? canvasState.createBoard(defaultBoardName());
1529
+ if (recent && canvasState.switchBoard(recent.id)) startBoardRuntime();
1530
+ }
1531
+
1473
1532
  export function deleteCanvasSnapshot(id: string): { ok: boolean } {
1474
1533
  return { ok: canvasState.deleteSnapshot(id) };
1475
1534
  }
@@ -7,6 +7,7 @@ export type CanvasNodeType =
7
7
  | 'webpage'
8
8
  | 'json-render'
9
9
  | 'graph'
10
+ | 'board'
10
11
  | 'prompt'
11
12
  | 'response'
12
13
  | 'status'
@@ -107,6 +107,18 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
107
107
  'When a markdown node is path-backed (`data.path`), the server persists `data.provenance` so snapshots keep a refreshable file source.',
108
108
  ],
109
109
  },
110
+ {
111
+ type: 'board',
112
+ kind: 'node',
113
+ description: 'Text-only link to another board by durable id.',
114
+ endpoint: '/api/canvas/node',
115
+ mcpTool: 'canvas_node (action:"add")',
116
+ fields: [
117
+ { name: 'title', type: 'string', required: false, description: 'Pinned fallback title.' },
118
+ { name: 'data', type: 'object', required: true, description: 'Contains boardId, the durable target board id.' },
119
+ ],
120
+ example: { type: 'board', title: 'Research', data: { boardId: 'board-abc123' } },
121
+ },
110
122
  {
111
123
  type: 'status',
112
124
  kind: 'node',