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.
- package/.github/extensions/pmx-canvas/extension.mjs +67 -3
- package/CHANGELOG.md +54 -0
- package/Readme.md +29 -5
- package/dist/canvas/global.css +440 -24
- package/dist/canvas/index.js +90 -75
- package/dist/types/cli/agent.d.ts +3 -0
- package/dist/types/cli/commands/backup.d.ts +1 -0
- package/dist/types/cli/commands/boards.d.ts +1 -0
- package/dist/types/cli/commands/export.d.ts +1 -0
- package/dist/types/client/canvas/BoardCopyDialog.d.ts +5 -0
- package/dist/types/client/canvas/BoardFolderDialog.d.ts +7 -0
- package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -0
- package/dist/types/client/canvas/BoardSwitcher.d.ts +8 -0
- package/dist/types/client/canvas/ExportDialog.d.ts +7 -0
- package/dist/types/client/canvas/HomeView.d.ts +9 -0
- package/dist/types/client/nodes/AttachmentNode.d.ts +6 -0
- package/dist/types/client/nodes/BoardNode.d.ts +5 -0
- package/dist/types/client/state/boards-store.d.ts +79 -0
- package/dist/types/client/state/canvas-store.d.ts +2 -0
- package/dist/types/client/state/intent-bridge.d.ts +1 -0
- package/dist/types/client/state/session-store.d.ts +7 -0
- package/dist/types/client/types.d.ts +2 -2
- package/dist/types/json-render/catalog.d.ts +1 -1
- package/dist/types/json-render/charts/definitions.d.ts +1 -1
- package/dist/types/json-render/server.d.ts +6 -0
- package/dist/types/mcp/canvas-access.d.ts +10 -3
- package/dist/types/mcp/context-uri-template.d.ts +6 -0
- package/dist/types/server/agent-presence.d.ts +1 -1
- package/dist/types/server/attribution.d.ts +11 -0
- package/dist/types/server/ax-context.d.ts +3 -1
- package/dist/types/server/ax-interaction.d.ts +2 -2
- package/dist/types/server/ax-state.d.ts +13 -3
- package/dist/types/server/backup.d.ts +36 -0
- package/dist/types/server/board-export.d.ts +45 -0
- package/dist/types/server/canvas-db.d.ts +63 -9
- package/dist/types/server/canvas-operations.d.ts +23 -0
- package/dist/types/server/canvas-provenance.d.ts +1 -1
- package/dist/types/server/canvas-state.d.ts +140 -3
- package/dist/types/server/context-brief.d.ts +111 -0
- package/dist/types/server/context-reads.d.ts +55 -0
- package/dist/types/server/document-import.d.ts +66 -0
- package/dist/types/server/index.d.ts +103 -4
- package/dist/types/server/mutation-history.d.ts +1 -0
- package/dist/types/server/operations/invoker.d.ts +2 -0
- package/dist/types/server/operations/mcp.d.ts +7 -1
- package/dist/types/server/operations/ops/ax-shared.d.ts +2 -2
- package/dist/types/server/operations/ops/backup.d.ts +2 -0
- package/dist/types/server/operations/ops/boards.d.ts +10 -0
- package/dist/types/server/operations/ops/export.d.ts +2 -0
- package/dist/types/server/operations/ops/imports.d.ts +2 -0
- package/dist/types/server/operations/ops/nodes.d.ts +1 -1
- package/dist/types/server/operations/ops/query.d.ts +2 -0
- package/dist/types/server/operations/registry.d.ts +9 -1
- package/dist/types/server/operations/types.d.ts +1 -1
- package/dist/types/server/server.d.ts +1 -0
- package/dist/types/server/trace-manager.d.ts +7 -0
- package/dist/types/server/webpage-node.d.ts +22 -1
- package/dist/types/server/workbench-auth.d.ts +3 -0
- package/dist/types/shared/boards.d.ts +6 -0
- package/docs/RELEASE.md +5 -0
- package/docs/cli.md +68 -0
- package/docs/http-api.md +198 -2
- package/docs/mcp.md +71 -6
- package/docs/node-types.md +26 -0
- package/docs/plans/plan-010-attributed-ask-answers.md +39 -0
- package/docs/plans/plan-011-context-read-instrumentation.md +54 -0
- package/docs/plans/plan-012-many-boards-stage-1.md +98 -0
- package/docs/plans/plan-013-static-board-export.md +47 -0
- package/docs/plans/plan-014-connected-board-memory.md +140 -0
- package/docs/product-vision-2026-09.md +118 -18
- package/docs/sdk.md +50 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +53 -4
- package/skills/pmx-canvas/references/full-reference.md +43 -7
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +3 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
- package/skills/pmx-canvas-orchestration/SKILL.md +5 -0
- package/skills/pmx-canvas-testing/SKILL.md +4 -0
- package/src/cli/agent.ts +4 -0
- package/src/cli/commands/ax.ts +13 -0
- package/src/cli/commands/backup.ts +66 -0
- package/src/cli/commands/boards.ts +71 -0
- package/src/cli/commands/export.ts +30 -0
- package/src/cli/commands/nodes.ts +4 -1
- package/src/cli/commands/pins.ts +6 -2
- package/src/cli/commands/pump.ts +3 -0
- package/src/cli/commands/query.ts +33 -7
- package/src/cli/commands/view.ts +7 -5
- package/src/cli/index.ts +19 -9
- package/src/client/App.tsx +21 -3
- package/src/client/canvas/BoardCopyDialog.tsx +153 -0
- package/src/client/canvas/BoardFolderDialog.tsx +120 -0
- package/src/client/canvas/BoardLinkDialog.tsx +93 -0
- package/src/client/canvas/BoardSwitcher.tsx +149 -0
- package/src/client/canvas/CanvasViewport.tsx +3 -0
- package/src/client/canvas/CommandPalette.tsx +107 -4
- package/src/client/canvas/ContextMenu.tsx +4 -0
- package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -0
- package/src/client/canvas/ExportDialog.tsx +197 -0
- package/src/client/canvas/HomeView.tsx +277 -0
- package/src/client/canvas/SessionPanel.tsx +28 -1
- package/src/client/canvas/ToolRail.tsx +38 -3
- package/src/client/canvas/TopBar.tsx +34 -18
- package/src/client/canvas/import-files.ts +19 -1
- package/src/client/canvas/kind-colors.ts +1 -0
- package/src/client/icons.tsx +2 -0
- package/src/client/nodes/AttachmentNode.tsx +148 -0
- package/src/client/nodes/BoardNode.tsx +35 -0
- package/src/client/nodes/FileNode.tsx +6 -0
- package/src/client/nodes/MarkdownNode.tsx +47 -19
- package/src/client/nodes/ax-node-actions.ts +2 -5
- package/src/client/nodes/use-ax-surface-bridge.ts +1 -1
- package/src/client/state/boards-store.ts +167 -0
- package/src/client/state/canvas-store.ts +22 -0
- package/src/client/state/intent-bridge.ts +30 -2
- package/src/client/state/node-factory.ts +1 -0
- package/src/client/state/session-store.ts +5 -0
- package/src/client/state/sse-bridge.ts +49 -5
- package/src/client/theme/global.css +440 -24
- package/src/client/types.ts +2 -0
- package/src/json-render/server.ts +16 -9
- package/src/mcp/canvas-access.ts +59 -22
- package/src/mcp/context-uri-template.ts +28 -0
- package/src/mcp/server.ts +122 -15
- package/src/server/agent-context.ts +116 -29
- package/src/server/attribution.ts +29 -0
- package/src/server/ax-context.ts +4 -2
- package/src/server/ax-interaction.ts +1 -0
- package/src/server/ax-state-manager.ts +72 -3
- package/src/server/ax-state.ts +41 -0
- package/src/server/backup.ts +178 -0
- package/src/server/board-export.ts +686 -0
- package/src/server/canvas-db.ts +599 -107
- package/src/server/canvas-operations.ts +68 -9
- package/src/server/canvas-provenance.ts +1 -0
- package/src/server/canvas-schema.ts +12 -0
- package/src/server/canvas-state.ts +774 -45
- package/src/server/context-brief.ts +372 -0
- package/src/server/context-reads.ts +238 -0
- package/src/server/demo-state.json +659 -2
- package/src/server/document-import.ts +223 -0
- package/src/server/index.ts +231 -5
- package/src/server/mutation-history.ts +10 -13
- package/src/server/operations/composites.ts +26 -4
- package/src/server/operations/http.ts +9 -3
- package/src/server/operations/index.ts +8 -0
- package/src/server/operations/invoker.ts +14 -3
- package/src/server/operations/mcp.ts +50 -11
- package/src/server/operations/ops/app.ts +5 -0
- package/src/server/operations/ops/ax-read.ts +10 -6
- package/src/server/operations/ops/ax-timeline.ts +75 -0
- package/src/server/operations/ops/backup.ts +134 -0
- package/src/server/operations/ops/batch.ts +8 -1
- package/src/server/operations/ops/boards.ts +351 -0
- package/src/server/operations/ops/export.ts +74 -0
- package/src/server/operations/ops/ext-app.ts +4 -0
- package/src/server/operations/ops/imports.ts +220 -0
- package/src/server/operations/ops/nodes.ts +57 -5
- package/src/server/operations/ops/query.ts +206 -9
- package/src/server/operations/registry.ts +123 -31
- package/src/server/operations/types.ts +1 -1
- package/src/server/server.ts +146 -2
- package/src/server/spatial-analysis.ts +9 -17
- package/src/server/trace-manager.ts +19 -0
- package/src/server/web-artifacts.ts +2 -1
- package/src/server/webpage-node.ts +173 -13
- package/src/server/workbench-auth.ts +11 -0
- package/src/shared/boards.ts +18 -0
- 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
|
}
|
|
@@ -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',
|