pmx-canvas 0.8.0 → 0.9.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/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/tools.json +601 -22
- package/CHANGELOG.md +66 -0
- package/Readme.md +131 -532
- package/dist/canvas/global.css +2759 -510
- package/dist/canvas/index.js +168 -107
- package/dist/canvas/mcp-app.html +2936 -632
- package/dist/json-render/index.css +81 -1
- package/dist/json-render/index.js +20 -20
- package/dist/types/client/canvas/BarHint.d.ts +12 -2
- package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -1
- package/dist/types/client/canvas/BoardPinButton.d.ts +10 -0
- package/dist/types/client/canvas/BoardSwitcher.d.ts +1 -1
- package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
- package/dist/types/client/canvas/CommandBar.d.ts +3 -4
- package/dist/types/client/canvas/ConnectionBanner.d.ts +2 -1
- package/dist/types/client/canvas/ContextChip.d.ts +3 -0
- package/dist/types/client/canvas/CrossBoardReferences.d.ts +5 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +14 -3
- package/dist/types/client/canvas/EmptyState.d.ts +4 -4
- package/dist/types/client/canvas/HomeGraphView.d.ts +2 -0
- package/dist/types/client/canvas/HomeMapView.d.ts +1 -0
- package/dist/types/client/canvas/HomeReviewView.d.ts +6 -0
- package/dist/types/client/canvas/HomeView.d.ts +9 -7
- package/dist/types/client/canvas/KnowledgeDialog.d.ts +4 -0
- package/dist/types/client/canvas/LifecycleChrome.d.ts +5 -0
- package/dist/types/client/canvas/LifecycleFields.d.ts +7 -0
- package/dist/types/client/canvas/LifecyclePanel.d.ts +1 -0
- package/dist/types/client/canvas/MemoryInspector.d.ts +7 -0
- package/dist/types/client/canvas/NearPinLayer.d.ts +14 -0
- package/dist/types/client/canvas/NodeContextMark.d.ts +40 -7
- package/dist/types/client/canvas/RelationPresetsDialog.d.ts +4 -0
- package/dist/types/client/canvas/RelationsPanel.d.ts +4 -0
- package/dist/types/client/canvas/SessionPanel.d.ts +2 -0
- package/dist/types/client/canvas/canvas-area.d.ts +1 -1
- package/dist/types/client/canvas/graph-geometry.d.ts +27 -0
- package/dist/types/client/canvas/graph-model.d.ts +37 -0
- package/dist/types/client/canvas/memory-layout.d.ts +23 -0
- package/dist/types/client/canvas/use-chrome-height.d.ts +2 -0
- package/dist/types/client/canvas/use-focus-trap.d.ts +1 -0
- package/dist/types/client/canvas/use-memory-graph.d.ts +8 -0
- package/dist/types/client/canvas/use-pan-zoom.d.ts +4 -1
- package/dist/types/client/icons.d.ts +0 -10
- package/dist/types/client/nodes/AxStepControls.d.ts +1 -1
- package/dist/types/client/nodes/HtmlNode.d.ts +1 -0
- package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +4 -4
- package/dist/types/client/nodes/MarkdownNode.d.ts +5 -1
- package/dist/types/client/nodes/McpAppNode.d.ts +4 -4
- package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -1
- package/dist/types/client/state/boards-store.d.ts +81 -9
- package/dist/types/client/state/canvas-store.d.ts +14 -1
- package/dist/types/client/state/context-chip-store.d.ts +22 -0
- package/dist/types/client/state/context-status-store.d.ts +35 -0
- package/dist/types/client/state/intent-bridge.d.ts +11 -8
- package/dist/types/client/state/knowledge-store.d.ts +22 -0
- package/dist/types/client/state/near-pin-store.d.ts +44 -1
- package/dist/types/client/state/presence-store.d.ts +4 -4
- package/dist/types/client/state/presentation.d.ts +28 -0
- package/dist/types/client/state/relation-store.d.ts +10 -0
- package/dist/types/client/state/session-store.d.ts +41 -2
- package/dist/types/client/state/workspace-settings.d.ts +7 -0
- package/dist/types/client/types.d.ts +13 -1
- package/dist/types/client/utils/keyboard.d.ts +2 -0
- package/dist/types/client/utils/time-ago.d.ts +4 -0
- package/dist/types/mcp/context-uri-template.d.ts +3 -1
- package/dist/types/server/agent-presence.d.ts +10 -3
- package/dist/types/server/ax-context.d.ts +1 -1
- package/dist/types/server/canvas-db.d.ts +30 -7
- package/dist/types/server/canvas-operations.d.ts +24 -2
- package/dist/types/server/canvas-provenance.d.ts +22 -2
- package/dist/types/server/canvas-serialization.d.ts +2 -0
- package/dist/types/server/canvas-state.d.ts +93 -15
- package/dist/types/server/canvas-validation.d.ts +1 -1
- package/dist/types/server/context-brief.d.ts +94 -6
- package/dist/types/server/context-reads.d.ts +33 -7
- package/dist/types/server/edit-summary.d.ts +20 -0
- package/dist/types/server/index.d.ts +71 -10
- package/dist/types/server/knowledge-lifecycle.d.ts +221 -0
- package/dist/types/server/lifecycle-status.d.ts +10 -0
- package/dist/types/server/memory-graph.d.ts +9 -0
- package/dist/types/server/mutation-history.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-shared.d.ts +1 -1
- package/dist/types/server/operations/ops/boards.d.ts +2 -1
- package/dist/types/server/operations/ops/lifecycle.d.ts +2 -0
- package/dist/types/server/operations/ops/memory-graph.d.ts +2 -0
- package/dist/types/server/operations/ops/nodes.d.ts +5 -0
- package/dist/types/server/operations/ops/query.d.ts +23 -0
- package/dist/types/server/operations/registry.d.ts +1 -0
- package/dist/types/server/placement.d.ts +7 -0
- package/dist/types/server/scope-fence.d.ts +2 -1
- package/dist/types/server/spatial-analysis.d.ts +6 -3
- package/dist/types/server/tour-control.d.ts +32 -0
- package/dist/types/server/wiki-links.d.ts +14 -0
- package/dist/types/server/workbench-auth.d.ts +2 -0
- package/dist/types/shared/agent-context.d.ts +2 -0
- package/dist/types/shared/agent-presence.d.ts +15 -12
- package/dist/types/shared/auto-arrange.d.ts +1 -0
- package/dist/types/shared/boards.d.ts +4 -4
- package/dist/types/shared/knowledge-lifecycle.d.ts +41 -0
- package/dist/types/shared/knowledge-presets.d.ts +6 -0
- package/dist/types/shared/lifecycle-status.d.ts +24 -0
- package/dist/types/shared/memory-graph.d.ts +53 -0
- package/dist/types/shared/relations.d.ts +68 -0
- package/dist/types/shared/semantic-attention.d.ts +2 -0
- package/dist/types/shared/tour.d.ts +38 -0
- package/dist/types/shared/workspace-settings.d.ts +11 -0
- package/docs/RELEASE.md +21 -3
- package/docs/adr-001-bun-only-runtime.md +7 -1
- package/docs/adr-002-shared-product-separate-runtimes.md +93 -0
- package/docs/api-reference.md +109 -0
- package/docs/ax-host-adapter-contract.md +1 -1
- package/docs/ax-state-contract.md +5 -4
- package/docs/cli.md +153 -15
- package/docs/demos/pmx-canvas-demo.mp4 +0 -0
- package/docs/design/AgentContext.dc.html +3 -3
- package/docs/design/BoardPins.dc.html +37 -22
- package/docs/design/Categories.dc.html +208 -0
- package/docs/design/CategoryEditor.dc.html +33 -0
- package/docs/design/Chrome.dc.html +55 -9
- package/docs/design/ContextChip.dc.html +224 -0
- package/docs/design/ContextChip600.dc.html +136 -0
- package/docs/design/EmptyState.dc.html +45 -0
- package/docs/design/Flows.dc.html +23 -1
- package/docs/design/HeaderMarks.dc.html +183 -0
- package/docs/design/Home.dc.html +19 -1
- package/docs/design/HomeGraph.dc.html +86 -202
- package/docs/design/HomeMap.dc.html +23 -1
- package/docs/design/HomeTeam.dc.html +32 -14
- package/docs/design/Lifecycle.dc.html +22 -0
- package/docs/design/Lifecycle600.dc.html +18 -0
- package/docs/design/LifecycleVariants.dc.html +157 -0
- package/docs/design/LinkAuthorship.dc.html +139 -0
- package/docs/design/LinksChanged.dc.html +203 -0
- package/docs/design/LinksOptions.dc.html +137 -0
- package/docs/design/LinksOptions600.dc.html +134 -0
- package/docs/design/NearPin.dc.html +9 -10
- package/docs/design/Pane600.dc.html +4 -3
- package/docs/design/Relations.dc.html +3 -2
- package/docs/design/Restore.dc.html +152 -0
- package/docs/design/SeeChange.dc.html +267 -0
- package/docs/design/Uses.dc.html +12 -6
- package/docs/design/canvas.json +146 -19
- package/docs/design.md +454 -18
- package/docs/environment.md +0 -1
- package/docs/evals/curation-effect.md +128 -0
- package/docs/http-api.md +465 -38
- package/docs/mcp.md +150 -19
- package/docs/node-types.md +58 -3
- package/docs/plans/design-gaps-2026-10-08.md +53 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +379 -0
- package/docs/plans/plan-016-context-made-visible-wave-1.md +150 -5
- package/docs/plans/plan-017-knowledge-categories-and-lifecycle.md +175 -0
- package/docs/plans/plan-018-connected-memory-wave-2.md +594 -0
- package/docs/plans/plan-019-post-0.9-roadmap.md +198 -0
- package/docs/product-context-vision-2026-09.md +17 -1
- package/docs/product-vision-2026-09.md +1082 -43
- package/docs/product-vision-review-2026-09.md +13 -1
- package/docs/screenshots/workbench-live-dark.png +0 -0
- package/docs/screenshots/workbench-live-light.png +0 -0
- package/docs/sdk.md +62 -2
- package/package.json +1 -1
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/plugin.json +1 -1
- package/plugins/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/tools.json +601 -22
- package/skills/pmx-canvas/SKILL.md +234 -43
- package/skills/pmx-canvas/evals/evals.json +62 -0
- package/skills/pmx-canvas/references/ax-html-control-surface.md +6 -0
- package/skills/pmx-canvas/references/full-reference.md +120 -21
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
- package/skills/pmx-canvas/references/importing-context.md +132 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
- package/skills/pmx-canvas/references/tours-and-recording.md +16 -1
- package/skills/pmx-canvas-testing/SKILL.md +20 -0
- package/src/cli/agent.ts +2 -0
- package/src/cli/commands/ax.ts +12 -0
- package/src/cli/commands/boards.ts +87 -11
- package/src/cli/commands/edges.ts +28 -0
- package/src/cli/commands/groups.ts +7 -0
- package/src/cli/commands/nodes.ts +4 -2
- package/src/cli/commands/query.ts +34 -2
- package/src/cli/commands/record.ts +199 -48
- package/src/cli/index.ts +7 -1
- package/src/client/App.tsx +177 -70
- package/src/client/canvas/AttentionHistory.tsx +0 -2
- package/src/client/canvas/BarHint.tsx +40 -2
- package/src/client/canvas/BoardCopyDialog.tsx +1 -1
- package/src/client/canvas/BoardFolderDialog.tsx +7 -7
- package/src/client/canvas/BoardLinkDialog.tsx +138 -33
- package/src/client/canvas/BoardPinButton.tsx +33 -0
- package/src/client/canvas/BoardSwitcher.tsx +26 -40
- package/src/client/canvas/CanvasNode.tsx +360 -237
- package/src/client/canvas/CanvasViewport.tsx +45 -7
- package/src/client/canvas/CommandBar.tsx +29 -23
- package/src/client/canvas/CommandPalette.tsx +5 -12
- package/src/client/canvas/ConnectionBanner.tsx +3 -3
- package/src/client/canvas/ContextChip.tsx +296 -0
- package/src/client/canvas/ContextMenu.tsx +63 -5
- package/src/client/canvas/CrossBoardReferences.tsx +60 -0
- package/src/client/canvas/EdgeLayer.tsx +339 -76
- package/src/client/canvas/EmptyState.tsx +33 -18
- package/src/client/canvas/ExpandedNodeOverlay.tsx +72 -40
- package/src/client/canvas/ExternalWriters.tsx +57 -42
- package/src/client/canvas/HomeGraphView.tsx +942 -0
- package/src/client/canvas/HomeMapView.tsx +641 -0
- package/src/client/canvas/HomeReviewView.tsx +167 -0
- package/src/client/canvas/HomeView.tsx +687 -160
- package/src/client/canvas/IntentLayer.tsx +19 -12
- package/src/client/canvas/KnowledgeDialog.tsx +488 -0
- package/src/client/canvas/LifecycleChrome.tsx +133 -0
- package/src/client/canvas/LifecycleFields.tsx +91 -0
- package/src/client/canvas/LifecyclePanel.tsx +533 -0
- package/src/client/canvas/MemoryInspector.tsx +104 -0
- package/src/client/canvas/Minimap.tsx +3 -3
- package/src/client/canvas/NearPinLayer.tsx +187 -0
- package/src/client/canvas/NodeContextMark.tsx +175 -97
- package/src/client/canvas/Presentation.tsx +23 -57
- package/src/client/canvas/RelationPresetsDialog.tsx +413 -0
- package/src/client/canvas/RelationsPanel.tsx +82 -0
- package/src/client/canvas/SelectionBar.tsx +3 -6
- package/src/client/canvas/SessionPanel.tsx +90 -50
- package/src/client/canvas/SessionReceipt.tsx +311 -39
- package/src/client/canvas/ShortcutOverlay.tsx +1 -1
- package/src/client/canvas/SnapshotPanel.tsx +27 -36
- package/src/client/canvas/ToolRail.tsx +367 -192
- package/src/client/canvas/TopBar.tsx +354 -124
- package/src/client/canvas/canvas-area.ts +10 -3
- package/src/client/canvas/graph-geometry.ts +195 -0
- package/src/client/canvas/graph-model.ts +254 -0
- package/src/client/canvas/memory-layout.ts +106 -0
- package/src/client/canvas/use-chrome-height.ts +20 -0
- package/src/client/canvas/use-focus-trap.ts +31 -5
- package/src/client/canvas/use-memory-graph.ts +58 -0
- package/src/client/canvas/use-node-drag.ts +2 -1
- package/src/client/canvas/use-pan-zoom.ts +64 -7
- package/src/client/icons.tsx +0 -63
- package/src/client/mcp-app/style.css +0 -6
- package/src/client/nodes/AxStepControls.tsx +74 -50
- package/src/client/nodes/BoardNode.tsx +5 -1
- package/src/client/nodes/ContextNode.tsx +18 -11
- package/src/client/nodes/ExtAppFrame.tsx +0 -1
- package/src/client/nodes/FileNode.tsx +46 -42
- package/src/client/nodes/HtmlNode.tsx +44 -2
- package/src/client/nodes/ImageNode.tsx +1 -1
- package/src/client/nodes/InlineFormatBar.tsx +6 -9
- package/src/client/nodes/InlineMarkdownEditor.tsx +56 -17
- package/src/client/nodes/MarkdownNode.tsx +377 -109
- package/src/client/nodes/McpAppNode.tsx +50 -20
- package/src/client/nodes/MdFormatBar.tsx +21 -18
- package/src/client/nodes/PromptNode.tsx +1 -1
- package/src/client/nodes/ResponseNode.tsx +1 -1
- package/src/client/nodes/viewer-runtime-state.ts +54 -22
- package/src/client/state/attention-bridge.ts +64 -15
- package/src/client/state/boards-store.ts +193 -39
- package/src/client/state/canvas-store.ts +99 -12
- package/src/client/state/context-chip-store.ts +70 -0
- package/src/client/state/context-status-store.ts +53 -0
- package/src/client/state/intent-bridge.ts +51 -30
- package/src/client/state/knowledge-store.ts +43 -0
- package/src/client/state/near-pin-store.ts +115 -6
- package/src/client/state/presence-store.ts +7 -10
- package/src/client/state/presentation.ts +91 -0
- package/src/client/state/relation-store.ts +81 -0
- package/src/client/state/session-store.ts +144 -18
- package/src/client/state/sse-bridge.ts +206 -29
- package/src/client/state/workspace-settings.ts +11 -0
- package/src/client/theme/global.css +2759 -510
- package/src/client/types.ts +12 -0
- package/src/client/utils/keyboard.ts +10 -0
- package/src/client/utils/time-ago.ts +14 -0
- package/src/json-render/renderer/index.css +6 -1
- package/src/json-render/renderer/index.tsx +31 -1
- package/src/mcp/canvas-app.ts +12 -2
- package/src/mcp/context-uri-template.ts +7 -4
- package/src/mcp/server.ts +63 -41
- package/src/mcp/workbench-app.ts +39 -6
- package/src/server/agent-presence.ts +128 -28
- package/src/server/ax-context.ts +27 -13
- package/src/server/board-export.ts +12 -1
- package/src/server/canvas-db.ts +331 -42
- package/src/server/canvas-operations.ts +196 -84
- package/src/server/canvas-provenance.ts +53 -22
- package/src/server/canvas-serialization.ts +2 -0
- package/src/server/canvas-state.ts +950 -122
- package/src/server/canvas-validation.ts +9 -2
- package/src/server/context-brief.ts +345 -38
- package/src/server/context-reads.ts +179 -30
- package/src/server/demo-state.json +355 -24
- package/src/server/edit-summary.ts +74 -0
- package/src/server/html-surface.ts +8 -1
- package/src/server/index.ts +203 -13
- package/src/server/knowledge-lifecycle.ts +304 -0
- package/src/server/lifecycle-status.ts +232 -0
- package/src/server/memory-graph.ts +166 -0
- package/src/server/mutation-history.ts +18 -6
- package/src/server/operations/composites.ts +32 -11
- package/src/server/operations/http.ts +7 -2
- package/src/server/operations/index.ts +4 -0
- package/src/server/operations/mcp.ts +32 -1
- package/src/server/operations/ops/ax-presence.ts +1 -1
- package/src/server/operations/ops/ax-read.ts +57 -6
- package/src/server/operations/ops/ax-timeline.ts +28 -2
- package/src/server/operations/ops/backup.ts +5 -3
- package/src/server/operations/ops/batch.ts +35 -4
- package/src/server/operations/ops/boards.ts +188 -47
- package/src/server/operations/ops/canvas-wire.ts +31 -1
- package/src/server/operations/ops/edges.ts +36 -2
- package/src/server/operations/ops/groups.ts +6 -1
- package/src/server/operations/ops/json-render.ts +5 -1
- package/src/server/operations/ops/lifecycle.ts +262 -0
- package/src/server/operations/ops/memory-graph.ts +45 -0
- package/src/server/operations/ops/nodes.ts +107 -24
- package/src/server/operations/ops/query.ts +281 -68
- package/src/server/operations/ops/snapshots.ts +73 -1
- package/src/server/operations/ops/validate.ts +5 -1
- package/src/server/operations/ops/viewport.ts +97 -3
- package/src/server/operations/registry.ts +77 -26
- package/src/server/placement.ts +29 -0
- package/src/server/scope-fence.ts +78 -6
- package/src/server/server.ts +115 -16
- package/src/server/spatial-analysis.ts +48 -23
- package/src/server/tour-control.ts +82 -0
- package/src/server/wiki-links.ts +137 -0
- package/src/server/workbench-auth.ts +11 -0
- package/src/shared/agent-context.ts +3 -0
- package/src/shared/agent-presence.ts +15 -15
- package/src/shared/auto-arrange.ts +2 -0
- package/src/shared/boards.ts +4 -4
- package/src/shared/knowledge-lifecycle.ts +22 -0
- package/src/shared/knowledge-presets.ts +80 -0
- package/src/shared/lifecycle-status.ts +26 -0
- package/src/shared/memory-graph.ts +74 -0
- package/src/shared/relations.ts +98 -0
- package/src/shared/semantic-attention.ts +15 -1
- package/src/shared/tour.ts +23 -5
- package/src/shared/workspace-settings.ts +13 -0
- package/dist/types/client/canvas/ContextPinBar.d.ts +0 -1
- package/dist/types/client/canvas/FocusFieldLayer.d.ts +0 -1
- package/dist/types/client/canvas/kind-colors.d.ts +0 -3
- package/dist/types/server/agent-context.d.ts +0 -22
- package/src/client/canvas/ContextPinBar.tsx +0 -25
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { CanvasNodeState } from './canvas-state.js';
|
|
2
|
+
|
|
3
|
+
const ORDINALS = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth'];
|
|
4
|
+
/** "See change" shows this much of each side, centred on the first difference. */
|
|
5
|
+
export const CHANGE_EXCERPT = 1200;
|
|
6
|
+
|
|
7
|
+
export function cardText(node: CanvasNodeState): string {
|
|
8
|
+
const { content, text } = node.data;
|
|
9
|
+
return typeof content === 'string' ? content : typeof text === 'string' ? text : '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function cardTitle(node: CanvasNodeState): string {
|
|
13
|
+
return typeof node.data.title === 'string' ? node.data.title : '';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const paragraphs = (text: string) =>
|
|
17
|
+
text
|
|
18
|
+
.split(/\n\s*\n/)
|
|
19
|
+
.map((paragraph) => paragraph.trim().replace(/\s+/g, ' '))
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* What an agent's edit did to a card, in a few words, from the card before
|
|
24
|
+
* the session and now (AgentContext.dc.html: "rewrote the second paragraph").
|
|
25
|
+
* Paragraphs are aligned on their shared start and end, so a paragraph added
|
|
26
|
+
* at the top reads as added, not as everything rewritten.
|
|
27
|
+
*/
|
|
28
|
+
export function describeEdit(before: CanvasNodeState, after: CanvasNodeState): string {
|
|
29
|
+
const parts: string[] = [];
|
|
30
|
+
if (cardTitle(before) !== cardTitle(after)) parts.push(`renamed it “${cardTitle(after)}”`);
|
|
31
|
+
const was = cardText(before);
|
|
32
|
+
const now = cardText(after);
|
|
33
|
+
if (was !== now) {
|
|
34
|
+
const a = paragraphs(was);
|
|
35
|
+
const b = paragraphs(now);
|
|
36
|
+
let head = 0;
|
|
37
|
+
while (head < a.length && head < b.length && a[head] === b[head]) head += 1;
|
|
38
|
+
let tail = 0;
|
|
39
|
+
while (tail < a.length - head && tail < b.length - head && a[a.length - 1 - tail] === b[b.length - 1 - tail])
|
|
40
|
+
tail += 1;
|
|
41
|
+
const removed = a.length - head - tail;
|
|
42
|
+
const added = b.length - head - tail;
|
|
43
|
+
const count = (n: number) => `${n} paragraph${n === 1 ? '' : 's'}`;
|
|
44
|
+
if (removed === 0 && added === 0) parts.push('reformatted its text');
|
|
45
|
+
else if (a.length === 0) parts.push('wrote its text');
|
|
46
|
+
else if (b.length === 0) parts.push('cleared its text');
|
|
47
|
+
else if (removed === 0) parts.push(`added ${count(added)}`);
|
|
48
|
+
else if (added === 0) parts.push(`removed ${count(removed)}`);
|
|
49
|
+
else if (removed === 1 && added === 1) parts.push(`rewrote the ${ORDINALS[head] ?? `#${head + 1}`} paragraph`);
|
|
50
|
+
else if (Math.max(removed, added) > Math.max(a.length, b.length) / 2) parts.push('rewrote most of it');
|
|
51
|
+
else parts.push(`changed ${count(Math.max(removed, added))} of ${Math.max(a.length, b.length)}`);
|
|
52
|
+
}
|
|
53
|
+
return parts.join(' and ') || 'changed its settings';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Before and after for "See change": each side cut to CHANGE_EXCERPT around
|
|
58
|
+
* the first character that differs, with "…" where text was cut, so a change
|
|
59
|
+
* deep in a long card is what shows.
|
|
60
|
+
*/
|
|
61
|
+
export function changeExcerpts(was: string, now: string): { before: string; after: string } {
|
|
62
|
+
let at = 0;
|
|
63
|
+
while (at < was.length && at < now.length && was[at] === now[at]) at += 1;
|
|
64
|
+
const from = Math.max(0, at - Math.floor(CHANGE_EXCERPT / 4));
|
|
65
|
+
// Never cut between the two halves of a surrogate pair (an emoji).
|
|
66
|
+
const low = (text: string, index: number) => /[\uDC00-\uDFFF]/.test(text[index] ?? '');
|
|
67
|
+
const cut = (text: string) => {
|
|
68
|
+
const start = low(text, from) ? from - 1 : from;
|
|
69
|
+
let end = start + CHANGE_EXCERPT;
|
|
70
|
+
if (low(text, end)) end -= 1;
|
|
71
|
+
return `${start > 0 ? '…' : ''}${text.slice(start, end)}${end < text.length ? '…' : ''}`;
|
|
72
|
+
};
|
|
73
|
+
return { before: cut(was), after: cut(now) };
|
|
74
|
+
}
|
|
@@ -60,7 +60,14 @@ function buildThemeBridge(themeToken: string): string {
|
|
|
60
60
|
const PMX_CANVAS_THEME_TOKEN = ${token};
|
|
61
61
|
window.addEventListener('message', (event) => {
|
|
62
62
|
const message = event.data;
|
|
63
|
-
if (!message || message.source !== '${HTML_SURFACE_PUSH_SOURCE}' || message.
|
|
63
|
+
if (event.source !== window.parent || !message || message.source !== '${HTML_SURFACE_PUSH_SOURCE}' || message.token !== PMX_CANVAS_THEME_TOKEN) return;
|
|
64
|
+
if (message.type === 'check-ready') {
|
|
65
|
+
const reply = (type) => window.parent.postMessage({ source: '${HTML_SURFACE_PUSH_SOURCE}', type, token: PMX_CANVAS_THEME_TOKEN }, '*');
|
|
66
|
+
reply('mounted');
|
|
67
|
+
requestAnimationFrame(() => requestAnimationFrame(() => reply('ready')));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (message.type !== 'theme-update') return;
|
|
64
71
|
if (typeof message.theme !== 'string') return;
|
|
65
72
|
document.documentElement.setAttribute('data-pmx-canvas-theme', message.theme);
|
|
66
73
|
document.documentElement.setAttribute('data-theme', message.theme);
|
package/src/server/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { derivedTour, type Tour } from '../shared/tour.js';
|
|
2
|
+
import { derivedTour, type Camera, type Tour, type TourStop } from '../shared/tour.js';
|
|
3
|
+
import { exitTour, goToTourStop, moveCamera, type TourStep, type TourStepResult } from './tour-control.js';
|
|
3
4
|
export type { Tour, TourStop } from '../shared/tour.js';
|
|
5
|
+
export type { TourStep, TourStepResult } from './tour-control.js';
|
|
4
6
|
import { agentPresence } from './agent-presence.js';
|
|
5
7
|
import { checkFenceTarget, type FenceTarget } from './scope-fence.js';
|
|
6
8
|
import type { AgentPhase, AgentPresence, AgentPresenceSnapshot } from '../shared/agent-presence.js';
|
|
@@ -38,6 +40,7 @@ import type {
|
|
|
38
40
|
PmxAxWorkItemStatus,
|
|
39
41
|
} from './ax-state.js';
|
|
40
42
|
import type { AxTimelineQuery, CanvasBoard } from './canvas-db.js';
|
|
43
|
+
import type { MemoryGraph } from '../shared/memory-graph.js';
|
|
41
44
|
import type { Attachment, DocumentImport, ImportSection } from './document-import.js';
|
|
42
45
|
import { workbenchToken } from './workbench-auth.js';
|
|
43
46
|
import type { BackupFile, BackupStatus } from './backup.js';
|
|
@@ -45,7 +48,8 @@ import type { ExportManifest } from './board-export.js';
|
|
|
45
48
|
import { onFileNodeChanged } from './file-watcher.js';
|
|
46
49
|
import { findOpenCanvasPosition, computeGroupBounds } from './placement.js';
|
|
47
50
|
import { overlapsAny } from '../shared/placement.js';
|
|
48
|
-
import {
|
|
51
|
+
import { buildSpatialContext } from './spatial-analysis.js';
|
|
52
|
+
import { searchCanvasNodes } from './operations/ops/query.js';
|
|
49
53
|
import { mutationHistory, diffLayouts, formatDiff } from './mutation-history.js';
|
|
50
54
|
import { recomputeCodeGraph, buildCodeGraphSummary, formatCodeGraph } from './code-graph.js';
|
|
51
55
|
import {
|
|
@@ -75,14 +79,17 @@ import {
|
|
|
75
79
|
import {
|
|
76
80
|
buildNodePatch,
|
|
77
81
|
createBasicCanvasNode,
|
|
82
|
+
createDestinationNode,
|
|
78
83
|
removeNodeCore,
|
|
79
84
|
setGroupChildrenFromApi,
|
|
80
85
|
} from './operations/ops/nodes.js';
|
|
81
86
|
import { streamJsonRenderCore } from './operations/ops/json-render.js';
|
|
87
|
+
import { boardsPayload } from './operations/ops/boards.js';
|
|
82
88
|
import { executeOperation, runCanvasBatchOperation, type OpenMcpAppCoreResult } from './operations/index.js';
|
|
83
89
|
import { validateCanvasLayout } from './canvas-validation.js';
|
|
84
90
|
import { describeCanvasSchema, validateStructuredCanvasPayload } from './canvas-schema.js';
|
|
85
91
|
import { serializeCanvasNode, type SerializedCanvasNode } from './canvas-serialization.js';
|
|
92
|
+
import { contextReadFromPayload } from './context-reads.js';
|
|
86
93
|
import {
|
|
87
94
|
buildHtmlPrimitive,
|
|
88
95
|
getHtmlPrimitiveSemanticMetadata,
|
|
@@ -288,10 +295,14 @@ export class PmxCanvas extends EventEmitter {
|
|
|
288
295
|
* or keep the whole node — both work. (Previously returned a bare id string.)
|
|
289
296
|
*/
|
|
290
297
|
addNode(input: {
|
|
298
|
+
destinationBoardId?: string;
|
|
299
|
+
requestId?: string;
|
|
300
|
+
expectedBoardId?: string | null;
|
|
291
301
|
intentId?: string;
|
|
292
302
|
type: CanvasNodeState['type'];
|
|
293
303
|
title?: string;
|
|
294
304
|
content?: string;
|
|
305
|
+
data?: Record<string, unknown>;
|
|
295
306
|
children?: string[];
|
|
296
307
|
childIds?: string[];
|
|
297
308
|
childLayout?: 'grid' | 'column' | 'flow';
|
|
@@ -308,6 +319,19 @@ export class PmxCanvas extends EventEmitter {
|
|
|
308
319
|
height?: number;
|
|
309
320
|
strictSize?: boolean;
|
|
310
321
|
}): SdkCanvasNode {
|
|
322
|
+
if (input.expectedBoardId !== undefined && input.expectedBoardId !== canvasState.activeBoardId)
|
|
323
|
+
throw new Error('The active board changed. Refresh before retrying this action.');
|
|
324
|
+
if (input.destinationBoardId !== undefined || input.requestId !== undefined) {
|
|
325
|
+
const revision = canvasState.getContentRevision().revision;
|
|
326
|
+
const node = createDestinationNode(input);
|
|
327
|
+
if (
|
|
328
|
+
(input.destinationBoardId ?? canvasState.activeBoardId) === canvasState.activeBoardId ||
|
|
329
|
+
revision !== canvasState.getContentRevision().revision
|
|
330
|
+
)
|
|
331
|
+
emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
|
|
332
|
+
emitPrimaryWorkbenchEvent('boards-changed', boardsPayload());
|
|
333
|
+
return toSdkNode(node);
|
|
334
|
+
}
|
|
311
335
|
assertInsideFence(
|
|
312
336
|
'node.add',
|
|
313
337
|
createTarget(input, input.type === 'group' ? (input.childIds ?? input.children ?? []) : []),
|
|
@@ -441,17 +465,41 @@ export class PmxCanvas extends EventEmitter {
|
|
|
441
465
|
}
|
|
442
466
|
|
|
443
467
|
addEdge(input: {
|
|
468
|
+
destinationBoardId?: string;
|
|
469
|
+
requestId?: string;
|
|
470
|
+
expectedBoardId?: string | null;
|
|
444
471
|
intentId?: string;
|
|
445
472
|
from?: string;
|
|
446
473
|
to?: string;
|
|
474
|
+
toBoardId?: string;
|
|
447
475
|
fromSearch?: string;
|
|
448
476
|
toSearch?: string;
|
|
449
477
|
type: CanvasEdge['type'];
|
|
478
|
+
kind?: string;
|
|
479
|
+
reason?: string;
|
|
450
480
|
label?: string;
|
|
451
481
|
style?: CanvasEdge['style'];
|
|
452
482
|
animated?: boolean;
|
|
453
483
|
}): string {
|
|
454
|
-
|
|
484
|
+
if (input.expectedBoardId !== undefined && input.expectedBoardId !== canvasState.activeBoardId)
|
|
485
|
+
throw new Error('The active board changed. Refresh before retrying this action.');
|
|
486
|
+
if (input.destinationBoardId !== undefined || input.requestId !== undefined) {
|
|
487
|
+
const revision = canvasState.getContentRevision().revision;
|
|
488
|
+
const edge = addCanvasEdge(input);
|
|
489
|
+
if (
|
|
490
|
+
(input.destinationBoardId ?? canvasState.activeBoardId) === canvasState.activeBoardId ||
|
|
491
|
+
revision !== canvasState.getContentRevision().revision
|
|
492
|
+
)
|
|
493
|
+
emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
|
|
494
|
+
emitPrimaryWorkbenchEvent('boards-changed', boardsPayload());
|
|
495
|
+
return edge.id;
|
|
496
|
+
}
|
|
497
|
+
assertInsideFence('edge.add', {
|
|
498
|
+
nodeIds:
|
|
499
|
+
input.toBoardId && input.toBoardId !== canvasState.activeBoardId
|
|
500
|
+
? [input.from ?? '']
|
|
501
|
+
: [input.from ?? '', input.to ?? ''],
|
|
502
|
+
});
|
|
455
503
|
return this.runIntentCommit(
|
|
456
504
|
input.intentId,
|
|
457
505
|
['connect'],
|
|
@@ -464,6 +512,13 @@ export class PmxCanvas extends EventEmitter {
|
|
|
464
512
|
);
|
|
465
513
|
}
|
|
466
514
|
|
|
515
|
+
async updateEdge(
|
|
516
|
+
id: string,
|
|
517
|
+
patch: Partial<Pick<CanvasEdge, 'type' | 'kind' | 'reason' | 'label' | 'style' | 'animated'>>,
|
|
518
|
+
): Promise<CanvasEdge> {
|
|
519
|
+
return (await executeOperation('edge.update', { id, ...patch }, { source: 'sdk' })) as CanvasEdge;
|
|
520
|
+
}
|
|
521
|
+
|
|
467
522
|
addAnnotation(input: Omit<CanvasAnnotation, 'id' | 'createdAt'> & { id?: string; createdAt?: string }): string {
|
|
468
523
|
const id = input.id ?? `ann-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
469
524
|
canvasState.addAnnotation({
|
|
@@ -483,7 +538,8 @@ export class PmxCanvas extends EventEmitter {
|
|
|
483
538
|
|
|
484
539
|
removeEdge(id: string): void {
|
|
485
540
|
const edge = canvasState.getEdges().find((entry) => entry.id === id);
|
|
486
|
-
if (edge)
|
|
541
|
+
if (edge)
|
|
542
|
+
assertInsideFence('edge.remove', { nodeIds: edge.toBoardId !== undefined ? [edge.from] : [edge.from, edge.to] });
|
|
487
543
|
removeCanvasEdge(id);
|
|
488
544
|
emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
|
|
489
545
|
}
|
|
@@ -493,6 +549,9 @@ export class PmxCanvas extends EventEmitter {
|
|
|
493
549
|
* If childIds are provided, the group auto-sizes to contain them with padding.
|
|
494
550
|
*/
|
|
495
551
|
createGroup(input: {
|
|
552
|
+
destinationBoardId?: string;
|
|
553
|
+
requestId?: string;
|
|
554
|
+
expectedBoardId?: string | null;
|
|
496
555
|
intentId?: string;
|
|
497
556
|
title?: string;
|
|
498
557
|
childIds?: string[];
|
|
@@ -503,6 +562,19 @@ export class PmxCanvas extends EventEmitter {
|
|
|
503
562
|
color?: string;
|
|
504
563
|
childLayout?: 'grid' | 'column' | 'flow';
|
|
505
564
|
}): string {
|
|
565
|
+
if (input.expectedBoardId !== undefined && input.expectedBoardId !== canvasState.activeBoardId)
|
|
566
|
+
throw new Error('The active board changed. Refresh before retrying this action.');
|
|
567
|
+
if (input.destinationBoardId !== undefined || input.requestId !== undefined) {
|
|
568
|
+
const revision = canvasState.getContentRevision().revision;
|
|
569
|
+
const { id } = createCanvasGroup(input);
|
|
570
|
+
if (
|
|
571
|
+
(input.destinationBoardId ?? canvasState.activeBoardId) === canvasState.activeBoardId ||
|
|
572
|
+
revision !== canvasState.getContentRevision().revision
|
|
573
|
+
)
|
|
574
|
+
emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
|
|
575
|
+
emitPrimaryWorkbenchEvent('boards-changed', boardsPayload());
|
|
576
|
+
return id;
|
|
577
|
+
}
|
|
506
578
|
assertInsideFence('group.create', { nodeIds: input.childIds ?? [] });
|
|
507
579
|
return this.runIntentCommit(
|
|
508
580
|
input.intentId,
|
|
@@ -570,6 +642,26 @@ export class PmxCanvas extends EventEmitter {
|
|
|
570
642
|
emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
|
|
571
643
|
}
|
|
572
644
|
|
|
645
|
+
/** Present a tour stop (index, `next` or `previous`) in the workbench. Not recorded in undo history. */
|
|
646
|
+
goToTourStop(step: TourStep, options?: { present?: boolean }): TourStepResult {
|
|
647
|
+
const result = goToTourStop(step);
|
|
648
|
+
emitPrimaryWorkbenchEvent('canvas-tour-step', { ...result, present: options?.present !== false });
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/** Leave presentation in every viewer. */
|
|
653
|
+
exitTour(): void {
|
|
654
|
+
exitTour();
|
|
655
|
+
emitPrimaryWorkbenchEvent('canvas-tour-exit', {});
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/** Ease the camera to a node, world rect or viewport. Not recorded in undo history. */
|
|
659
|
+
moveCamera(stop: TourStop): { viewport: Camera } {
|
|
660
|
+
const result = moveCamera(stop);
|
|
661
|
+
emitPrimaryWorkbenchEvent('canvas-camera-move', result);
|
|
662
|
+
return { viewport: result.viewport };
|
|
663
|
+
}
|
|
664
|
+
|
|
573
665
|
clear(): void {
|
|
574
666
|
assertInsideFence('canvas.clear', { boardWide: true });
|
|
575
667
|
for (const node of canvasState.getLayout().nodes) {
|
|
@@ -804,10 +896,10 @@ export class PmxCanvas extends EventEmitter {
|
|
|
804
896
|
)) as { import: DocumentImport; nodeIds: string[] };
|
|
805
897
|
}
|
|
806
898
|
|
|
807
|
-
async createBoard(name: string,
|
|
899
|
+
async createBoard(name: string, folder?: string): Promise<CanvasBoard> {
|
|
808
900
|
const result = (await executeOperation(
|
|
809
901
|
'board.create',
|
|
810
|
-
{ name, ...(
|
|
902
|
+
{ name, ...(folder ? { folder } : {}) },
|
|
811
903
|
{ source: 'sdk' },
|
|
812
904
|
)) as {
|
|
813
905
|
board: CanvasBoard;
|
|
@@ -815,8 +907,27 @@ export class PmxCanvas extends EventEmitter {
|
|
|
815
907
|
return result.board;
|
|
816
908
|
}
|
|
817
909
|
|
|
818
|
-
/**
|
|
819
|
-
async
|
|
910
|
+
/** Workspace category and folder actions do not open a board. */
|
|
911
|
+
async knowledge<T extends import('./knowledge-lifecycle.js').KnowledgeInput>(
|
|
912
|
+
input: T,
|
|
913
|
+
): Promise<import('./knowledge-lifecycle.js').KnowledgeResults[T['action']]> {
|
|
914
|
+
const { action, ...payload } = input;
|
|
915
|
+
return (await executeOperation(`knowledge.${action}`, payload, {
|
|
916
|
+
source: 'sdk',
|
|
917
|
+
})) as import('./knowledge-lifecycle.js').KnowledgeResults[T['action']];
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
async lifecycle<T extends import('./knowledge-lifecycle.js').LifecycleInput>(
|
|
921
|
+
input: T,
|
|
922
|
+
): Promise<import('./knowledge-lifecycle.js').LifecycleResults[T['action']]> {
|
|
923
|
+
const { action, ...payload } = input;
|
|
924
|
+
return (await executeOperation(action === 'review' ? 'node.review' : `node.lifecycle-${action}`, payload, {
|
|
925
|
+
source: 'sdk',
|
|
926
|
+
})) as import('./knowledge-lifecycle.js').LifecycleResults[T['action']];
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/** Rename a board and/or file it under a folder (`folder: null` unfiles it). */
|
|
930
|
+
async updateBoard(id: string, patch: Parameters<typeof canvasState.updateBoard>[1]): Promise<void> {
|
|
820
931
|
await executeOperation('board.update', { id, ...patch }, { source: 'sdk' });
|
|
821
932
|
}
|
|
822
933
|
|
|
@@ -824,10 +935,19 @@ export class PmxCanvas extends EventEmitter {
|
|
|
824
935
|
await executeOperation('board.update', { id, readmeNodeId }, { source: 'sdk' });
|
|
825
936
|
}
|
|
826
937
|
|
|
938
|
+
/** Pin a board into the agent's working set (its README and pinned cards reach every brief). */
|
|
939
|
+
async pinBoard(id: string, reason?: string): Promise<void> {
|
|
940
|
+
await executeOperation('board.pin', { id, ...(reason ? { reason } : {}) }, { source: 'sdk' });
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
async unpinBoard(id: string): Promise<void> {
|
|
944
|
+
await executeOperation('board.unpin', { id }, { source: 'sdk' });
|
|
945
|
+
}
|
|
946
|
+
|
|
827
947
|
async createBoardFrom(input: {
|
|
828
948
|
sourceBoardId: string;
|
|
829
949
|
name: string;
|
|
830
|
-
|
|
950
|
+
folder?: string;
|
|
831
951
|
nodeIds?: string[];
|
|
832
952
|
includeReadme?: boolean;
|
|
833
953
|
includeStructure?: boolean;
|
|
@@ -843,6 +963,13 @@ export class PmxCanvas extends EventEmitter {
|
|
|
843
963
|
>;
|
|
844
964
|
}
|
|
845
965
|
|
|
966
|
+
/** Same projection as Home Graph. Discovery summaries never count as full-content reads. */
|
|
967
|
+
async getMemoryGraph(
|
|
968
|
+
options: { focus?: string; depth?: number; projection?: 'full' | 'boards' } = {},
|
|
969
|
+
): Promise<MemoryGraph> {
|
|
970
|
+
return (await executeOperation('memory-graph.get', options, { source: 'sdk' })) as MemoryGraph;
|
|
971
|
+
}
|
|
972
|
+
|
|
846
973
|
/** Budgeted cross-board brief. Budget is measured in UTF-16 characters. */
|
|
847
974
|
async getContextBrief(
|
|
848
975
|
options: { consumer?: string; since?: number; budget?: number } = {},
|
|
@@ -911,6 +1038,11 @@ export class PmxCanvas extends EventEmitter {
|
|
|
911
1038
|
return canvasState.getNodeReadStatus(board ?? canvasState.activeBoardId);
|
|
912
1039
|
}
|
|
913
1040
|
|
|
1041
|
+
/** A board's latest agent read of any kind (a pinned board's map counts), or null. */
|
|
1042
|
+
getBoardLastRead(board?: string): ReturnType<typeof canvasState.getBoardLastRead> {
|
|
1043
|
+
return canvasState.getBoardLastRead(board ?? canvasState.activeBoardId);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
914
1046
|
listWorkItems(): PmxAxWorkItem[] {
|
|
915
1047
|
return canvasState.getWorkItems();
|
|
916
1048
|
}
|
|
@@ -1253,14 +1385,36 @@ export class PmxCanvas extends EventEmitter {
|
|
|
1253
1385
|
}
|
|
1254
1386
|
|
|
1255
1387
|
getNode(id: string, options?: { board?: string }): SdkCanvasNode | undefined {
|
|
1388
|
+
const boardId = options?.board ?? canvasState.activeBoardId;
|
|
1389
|
+
const board = options?.board ? canvasState.readBoard(options.board) : null;
|
|
1390
|
+
if (options?.board && !board) throw new Error(`Board "${options.board}" not found.`);
|
|
1256
1391
|
const node = options?.board
|
|
1257
|
-
?
|
|
1392
|
+
? board?.layout.nodes.find((candidate) => candidate.id === id)
|
|
1258
1393
|
: canvasState.getNode(id);
|
|
1259
|
-
|
|
1394
|
+
if (!node) return undefined;
|
|
1395
|
+
const result = toSdkNode(node);
|
|
1396
|
+
canvasState.recordContextRead(
|
|
1397
|
+
contextReadFromPayload(
|
|
1398
|
+
{
|
|
1399
|
+
channel: 'operation',
|
|
1400
|
+
resource: 'node.get',
|
|
1401
|
+
source: 'sdk',
|
|
1402
|
+
consumer: null,
|
|
1403
|
+
agentId: null,
|
|
1404
|
+
pinnedNodeIds: options?.board ? (board?.state.contextPins ?? []) : [...canvasState.contextPinnedNodeIds],
|
|
1405
|
+
},
|
|
1406
|
+
result,
|
|
1407
|
+
),
|
|
1408
|
+
boardId,
|
|
1409
|
+
);
|
|
1410
|
+
return result;
|
|
1260
1411
|
}
|
|
1261
1412
|
|
|
1262
|
-
search(
|
|
1263
|
-
|
|
1413
|
+
search(
|
|
1414
|
+
query: string,
|
|
1415
|
+
options: { scope?: 'active' | 'library'; boardId?: string; folder?: string; limit?: number } = {},
|
|
1416
|
+
) {
|
|
1417
|
+
return searchCanvasNodes(query, options).results;
|
|
1264
1418
|
}
|
|
1265
1419
|
|
|
1266
1420
|
getSpatialContext() {
|
|
@@ -1273,6 +1427,10 @@ export class PmxCanvas extends EventEmitter {
|
|
|
1273
1427
|
if (!entry) return { ok: false, description: 'Nothing to undo' };
|
|
1274
1428
|
await syncCanvasRuntimeBackends();
|
|
1275
1429
|
emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
|
|
1430
|
+
emitPrimaryWorkbenchEvent('context-pins-changed', {
|
|
1431
|
+
count: canvasState.contextPinnedNodeIds.size,
|
|
1432
|
+
nodeIds: Array.from(canvasState.contextPinnedNodeIds),
|
|
1433
|
+
});
|
|
1276
1434
|
// Undo can reverse an AX mutation (work item, focus, …); nudge AX surfaces to
|
|
1277
1435
|
// re-fetch so a live board reflects the reversal (debounced client-side).
|
|
1278
1436
|
emitPrimaryWorkbenchEvent('ax-state-changed', {});
|
|
@@ -1284,6 +1442,10 @@ export class PmxCanvas extends EventEmitter {
|
|
|
1284
1442
|
if (!entry) return { ok: false, description: 'Nothing to redo' };
|
|
1285
1443
|
await syncCanvasRuntimeBackends();
|
|
1286
1444
|
emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
|
|
1445
|
+
emitPrimaryWorkbenchEvent('context-pins-changed', {
|
|
1446
|
+
count: canvasState.contextPinnedNodeIds.size,
|
|
1447
|
+
nodeIds: Array.from(canvasState.contextPinnedNodeIds),
|
|
1448
|
+
});
|
|
1287
1449
|
emitPrimaryWorkbenchEvent('ax-state-changed', {});
|
|
1288
1450
|
return { ok: true, description: `Redid: ${entry.description}` };
|
|
1289
1451
|
}
|
|
@@ -1339,6 +1501,24 @@ export class PmxCanvas extends EventEmitter {
|
|
|
1339
1501
|
return result;
|
|
1340
1502
|
}
|
|
1341
1503
|
|
|
1504
|
+
/** Put one card back to its content in a snapshot (the receipt's Undo on an edit). */
|
|
1505
|
+
async restoreSnapshotNode(
|
|
1506
|
+
snapshotId: string,
|
|
1507
|
+
nodeId: string,
|
|
1508
|
+
options: { preserveHumanEdits?: boolean } = {},
|
|
1509
|
+
): Promise<{ ok: boolean; id: string; restored: boolean; skipped?: 'human-edit' }> {
|
|
1510
|
+
return (await executeOperation(
|
|
1511
|
+
'snapshot.restore-node',
|
|
1512
|
+
{ id: snapshotId, nodeId, ...options },
|
|
1513
|
+
{ source: 'sdk' },
|
|
1514
|
+
)) as {
|
|
1515
|
+
ok: boolean;
|
|
1516
|
+
id: string;
|
|
1517
|
+
restored: boolean;
|
|
1518
|
+
skipped?: 'human-edit';
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1342
1522
|
deleteSnapshot(id: string): { ok: boolean } {
|
|
1343
1523
|
return deleteCanvasSnapshot(id);
|
|
1344
1524
|
}
|
|
@@ -1759,3 +1939,13 @@ export type {
|
|
|
1759
1939
|
PmxAxWorkItemStatus,
|
|
1760
1940
|
} from './ax-state.js';
|
|
1761
1941
|
export type { AxTimelineQuery } from './canvas-db.js';
|
|
1942
|
+
export type {
|
|
1943
|
+
Lifecycle,
|
|
1944
|
+
KnowledgeCategory,
|
|
1945
|
+
FolderProperties,
|
|
1946
|
+
ReviewRecord,
|
|
1947
|
+
KnowledgeInput,
|
|
1948
|
+
KnowledgeResults,
|
|
1949
|
+
LifecycleInput,
|
|
1950
|
+
LifecycleResults,
|
|
1951
|
+
} from './knowledge-lifecycle.js';
|