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
|
@@ -60,6 +60,26 @@
|
|
|
60
60
|
--hud-bar-height: 44px;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
.board-node, .md-card-preview { display: flex; flex-direction: column; height: 100%; min-height: 0; }
|
|
64
|
+
.board-node-content { flex: 1; min-height: 0; overflow: auto; padding: 16px; overflow-wrap: anywhere; }
|
|
65
|
+
.board-node-label { color: var(--c-dim); font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .1em; margin-bottom: 8px; }
|
|
66
|
+
.board-node-title { display: block; font-size: 17px; line-height: 1.35; margin-bottom: 6px; }
|
|
67
|
+
.board-node-meta { color: var(--c-dim); font-size: 11px; line-height: 1.5; }
|
|
68
|
+
.board-node-summary { color: var(--c-text-soft); font-size: 13px; line-height: 1.6; margin: 14px 0; }
|
|
69
|
+
.board-node-open { display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; width: 100%; padding: 10px 16px; border: 0; border-top: 1px solid var(--c-line); background: var(--c-panel); color: var(--c-accent); font: 500 12px var(--font); text-align: left; cursor: pointer; }
|
|
70
|
+
.board-node-open:hover:not(:disabled) { background: var(--c-accent-8); }
|
|
71
|
+
.board-node-open:disabled { color: var(--c-dim); cursor: default; }
|
|
72
|
+
.md-card-content { flex: 1; min-height: 0; overflow: auto; }
|
|
73
|
+
.md-card-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; border-top: 1px solid var(--c-line); padding: 6px 36px 6px 8px; background: var(--c-panel); }
|
|
74
|
+
.md-card-actions button { border: 1px solid transparent; border-radius: var(--radius-sm); background: transparent; color: var(--c-dim); padding: 4px 8px; font: 500 11px var(--font); cursor: pointer; }
|
|
75
|
+
.md-card-actions button:hover { color: var(--c-text); background: var(--c-accent-8); }
|
|
76
|
+
.md-card-actions .md-readme-btn[aria-pressed="true"] { color: var(--c-accent); background: var(--c-accent-8); border-color: var(--c-accent-25); }
|
|
77
|
+
.md-card-edit { margin-left: auto; }
|
|
78
|
+
.board-node-open:focus-visible, .md-card-actions button:focus-visible { outline: 2px solid var(--c-accent); outline-offset: -2px; }
|
|
79
|
+
.canvas-node[data-node-type="markdown"] .node-body,
|
|
80
|
+
.canvas-node[data-node-type="board"] .node-body { padding: 0; }
|
|
81
|
+
.md-card-content { padding: 10px 14px; }
|
|
82
|
+
|
|
63
83
|
:root[data-theme="light"] {
|
|
64
84
|
/* ── Core palette (light · paper + ink) ──────────────────── */
|
|
65
85
|
--c-bg: #F4EFE6;
|
|
@@ -1251,7 +1271,7 @@ body,
|
|
|
1251
1271
|
}
|
|
1252
1272
|
|
|
1253
1273
|
.agent-chip-end:hover {
|
|
1254
|
-
color: var(--c-danger
|
|
1274
|
+
color: var(--c-danger);
|
|
1255
1275
|
background: color-mix(in srgb, currentColor 12%, transparent);
|
|
1256
1276
|
}
|
|
1257
1277
|
|
|
@@ -3317,29 +3337,6 @@ body,
|
|
|
3317
3337
|
}
|
|
3318
3338
|
|
|
3319
3339
|
/* Raw markdown source editor */
|
|
3320
|
-
.md-edit-btn {
|
|
3321
|
-
position: absolute;
|
|
3322
|
-
top: 4px;
|
|
3323
|
-
right: 4px;
|
|
3324
|
-
padding: 3px 8px;
|
|
3325
|
-
font-size: 10px;
|
|
3326
|
-
background: var(--c-panel);
|
|
3327
|
-
border: 1px solid var(--c-line);
|
|
3328
|
-
border-radius: 4px;
|
|
3329
|
-
color: var(--c-text-soft);
|
|
3330
|
-
cursor: pointer;
|
|
3331
|
-
opacity: 0;
|
|
3332
|
-
transition: opacity 0.12s ease;
|
|
3333
|
-
}
|
|
3334
|
-
.canvas-node:hover .md-edit-btn,
|
|
3335
|
-
.md-edit-btn:focus-visible {
|
|
3336
|
-
opacity: 1;
|
|
3337
|
-
}
|
|
3338
|
-
.md-edit-btn:hover {
|
|
3339
|
-
color: var(--c-text);
|
|
3340
|
-
border-color: var(--c-accent-25);
|
|
3341
|
-
}
|
|
3342
|
-
|
|
3343
3340
|
.md-editor-split {
|
|
3344
3341
|
display: flex;
|
|
3345
3342
|
height: 100%;
|
|
@@ -6862,3 +6859,422 @@ html.is-node-dragging .intent-line-layer {
|
|
|
6862
6859
|
background: var(--c-panel);
|
|
6863
6860
|
color: var(--c-text);
|
|
6864
6861
|
}
|
|
6862
|
+
|
|
6863
|
+
/* ── Many boards (plan 012): top-bar switcher + Home ── */
|
|
6864
|
+
.board-switcher {
|
|
6865
|
+
display: inline-flex;
|
|
6866
|
+
/* The board name is the bar's identity: it keeps room for a short name
|
|
6867
|
+
("Home") when chips crowd a 600px pane; long names still ellipsize. */
|
|
6868
|
+
min-width: 88px;
|
|
6869
|
+
flex-shrink: 1;
|
|
6870
|
+
}
|
|
6871
|
+
.board-switcher-button {
|
|
6872
|
+
display: inline-flex;
|
|
6873
|
+
align-items: center;
|
|
6874
|
+
gap: 6px;
|
|
6875
|
+
max-width: 280px;
|
|
6876
|
+
padding: 4px 8px;
|
|
6877
|
+
margin-left: -8px;
|
|
6878
|
+
background: none;
|
|
6879
|
+
border: 1px solid transparent;
|
|
6880
|
+
border-radius: 8px;
|
|
6881
|
+
cursor: pointer;
|
|
6882
|
+
}
|
|
6883
|
+
.board-switcher-button:hover,
|
|
6884
|
+
.board-switcher-button[aria-expanded='true'] {
|
|
6885
|
+
border-color: var(--c-line);
|
|
6886
|
+
background: var(--c-panel);
|
|
6887
|
+
}
|
|
6888
|
+
.board-switcher-name {
|
|
6889
|
+
overflow: hidden;
|
|
6890
|
+
text-overflow: ellipsis;
|
|
6891
|
+
white-space: nowrap;
|
|
6892
|
+
}
|
|
6893
|
+
.board-switcher-caret {
|
|
6894
|
+
font-size: 10px;
|
|
6895
|
+
color: var(--c-text-soft);
|
|
6896
|
+
}
|
|
6897
|
+
.board-switcher-menu {
|
|
6898
|
+
width: min(340px, calc(100vw - 24px));
|
|
6899
|
+
min-width: 0;
|
|
6900
|
+
padding: 8px;
|
|
6901
|
+
background: var(--c-panel);
|
|
6902
|
+
backdrop-filter: none;
|
|
6903
|
+
overflow: hidden;
|
|
6904
|
+
}
|
|
6905
|
+
.board-switcher-heading {
|
|
6906
|
+
padding: 8px 10px 10px;
|
|
6907
|
+
font-size: 11px;
|
|
6908
|
+
font-weight: 600;
|
|
6909
|
+
color: var(--c-dim);
|
|
6910
|
+
text-transform: uppercase;
|
|
6911
|
+
letter-spacing: 0.08em;
|
|
6912
|
+
flex-shrink: 0;
|
|
6913
|
+
}
|
|
6914
|
+
.board-switcher-recents { min-height: 0; overflow-y: auto; }
|
|
6915
|
+
.board-switcher-menu .toolbar-menu-item { padding: 10px; gap: 12px; }
|
|
6916
|
+
.board-switcher-menu .toolbar-menu-item:hover,
|
|
6917
|
+
.board-switcher-menu .toolbar-menu-item:focus-visible {
|
|
6918
|
+
background: var(--c-accent-8);
|
|
6919
|
+
border-color: var(--c-accent-25);
|
|
6920
|
+
outline: none;
|
|
6921
|
+
}
|
|
6922
|
+
.board-switcher-icon { display: flex; align-items: center; justify-content: center; width: 20px; flex-shrink: 0; color: var(--c-accent); font-size: 22px; }
|
|
6923
|
+
.board-switcher-item-copy { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
6924
|
+
.board-switcher-item-name {
|
|
6925
|
+
color: var(--c-text);
|
|
6926
|
+
font-weight: 500;
|
|
6927
|
+
line-height: 1.35;
|
|
6928
|
+
overflow-wrap: anywhere;
|
|
6929
|
+
text-align: left;
|
|
6930
|
+
}
|
|
6931
|
+
.board-switcher-item-folder { font-size: 11px; line-height: 1.4; color: var(--c-dim); overflow-wrap: anywhere; }
|
|
6932
|
+
.board-switcher-item-meta {
|
|
6933
|
+
font-size: 10px;
|
|
6934
|
+
color: var(--c-dim);
|
|
6935
|
+
white-space: nowrap;
|
|
6936
|
+
align-self: flex-start;
|
|
6937
|
+
padding-top: 2px;
|
|
6938
|
+
}
|
|
6939
|
+
.board-switcher-actions { border-top: 1px solid var(--c-line); margin-top: 6px; padding-top: 6px; flex-shrink: 0; }
|
|
6940
|
+
|
|
6941
|
+
.home-view {
|
|
6942
|
+
position: absolute;
|
|
6943
|
+
inset: 0;
|
|
6944
|
+
/* Above the canvas's floating chrome (the updates tab sits at 60), below menus and prompts. */
|
|
6945
|
+
z-index: 70;
|
|
6946
|
+
overflow-y: auto;
|
|
6947
|
+
background: var(--c-bg);
|
|
6948
|
+
padding: 32px 16px;
|
|
6949
|
+
}
|
|
6950
|
+
.home-view-inner {
|
|
6951
|
+
max-width: 720px;
|
|
6952
|
+
margin: 0 auto;
|
|
6953
|
+
display: flex;
|
|
6954
|
+
flex-direction: column;
|
|
6955
|
+
gap: 18px;
|
|
6956
|
+
}
|
|
6957
|
+
.home-view-head {
|
|
6958
|
+
display: flex;
|
|
6959
|
+
align-items: center;
|
|
6960
|
+
justify-content: space-between;
|
|
6961
|
+
gap: 12px;
|
|
6962
|
+
}
|
|
6963
|
+
.home-view-title {
|
|
6964
|
+
margin: 0;
|
|
6965
|
+
font-size: 20px;
|
|
6966
|
+
font-weight: 650;
|
|
6967
|
+
color: var(--c-text);
|
|
6968
|
+
}
|
|
6969
|
+
.home-view-new {
|
|
6970
|
+
padding: 7px 14px;
|
|
6971
|
+
border-radius: 8px;
|
|
6972
|
+
border: 1px solid var(--c-accent);
|
|
6973
|
+
background: var(--c-accent);
|
|
6974
|
+
color: var(--c-bg);
|
|
6975
|
+
font-weight: 600;
|
|
6976
|
+
cursor: pointer;
|
|
6977
|
+
}
|
|
6978
|
+
.home-view-empty {
|
|
6979
|
+
color: var(--c-text-soft);
|
|
6980
|
+
font-size: 13px;
|
|
6981
|
+
}
|
|
6982
|
+
.home-view-list {
|
|
6983
|
+
list-style: none;
|
|
6984
|
+
margin: 0;
|
|
6985
|
+
padding: 0;
|
|
6986
|
+
display: flex;
|
|
6987
|
+
flex-direction: column;
|
|
6988
|
+
gap: 7px;
|
|
6989
|
+
}
|
|
6990
|
+
.home-board {
|
|
6991
|
+
display: flex;
|
|
6992
|
+
align-items: center;
|
|
6993
|
+
gap: 8px;
|
|
6994
|
+
padding: 7px 8px 7px 5px;
|
|
6995
|
+
border: 1px solid var(--c-line);
|
|
6996
|
+
border-radius: 10px;
|
|
6997
|
+
background: var(--c-panel);
|
|
6998
|
+
}
|
|
6999
|
+
.home-board.is-confirming {
|
|
7000
|
+
border-color: var(--c-danger);
|
|
7001
|
+
padding: 10px 12px;
|
|
7002
|
+
flex-wrap: wrap;
|
|
7003
|
+
}
|
|
7004
|
+
.home-board-open {
|
|
7005
|
+
flex: 1;
|
|
7006
|
+
min-width: 0;
|
|
7007
|
+
display: flex;
|
|
7008
|
+
flex-direction: column;
|
|
7009
|
+
align-items: flex-start;
|
|
7010
|
+
gap: 2px;
|
|
7011
|
+
padding: 6px 8px;
|
|
7012
|
+
background: none;
|
|
7013
|
+
border: none;
|
|
7014
|
+
border-radius: 8px;
|
|
7015
|
+
color: var(--c-text);
|
|
7016
|
+
cursor: pointer;
|
|
7017
|
+
text-align: left;
|
|
7018
|
+
}
|
|
7019
|
+
.home-board-open:hover {
|
|
7020
|
+
background: color-mix(in srgb, var(--c-accent) 10%, transparent);
|
|
7021
|
+
}
|
|
7022
|
+
.home-board-name {
|
|
7023
|
+
max-width: 100%;
|
|
7024
|
+
font-size: 14px;
|
|
7025
|
+
font-weight: 600;
|
|
7026
|
+
overflow: hidden;
|
|
7027
|
+
text-overflow: ellipsis;
|
|
7028
|
+
white-space: nowrap;
|
|
7029
|
+
}
|
|
7030
|
+
.home-board-meta {
|
|
7031
|
+
font: 11px var(--mono);
|
|
7032
|
+
color: var(--c-text-soft);
|
|
7033
|
+
}
|
|
7034
|
+
.home-board-confirm-text {
|
|
7035
|
+
flex: 1 1 240px;
|
|
7036
|
+
font-size: 13px;
|
|
7037
|
+
color: var(--c-text);
|
|
7038
|
+
}
|
|
7039
|
+
.home-board-actions {
|
|
7040
|
+
display: flex;
|
|
7041
|
+
gap: 2px;
|
|
7042
|
+
flex-shrink: 0;
|
|
7043
|
+
}
|
|
7044
|
+
.home-board-action {
|
|
7045
|
+
padding: 6px 7px;
|
|
7046
|
+
border-radius: 6px;
|
|
7047
|
+
border: 1px solid transparent;
|
|
7048
|
+
background: none;
|
|
7049
|
+
color: var(--c-dim);
|
|
7050
|
+
font-size: 11px;
|
|
7051
|
+
cursor: pointer;
|
|
7052
|
+
}
|
|
7053
|
+
.home-board-action:hover {
|
|
7054
|
+
border-color: var(--c-line);
|
|
7055
|
+
background: color-mix(in srgb, var(--c-text) 5%, transparent);
|
|
7056
|
+
color: var(--c-text-soft);
|
|
7057
|
+
}
|
|
7058
|
+
.home-board-action.is-danger {
|
|
7059
|
+
border-color: var(--c-danger);
|
|
7060
|
+
background: var(--c-danger);
|
|
7061
|
+
color: var(--c-bg);
|
|
7062
|
+
font-weight: 600;
|
|
7063
|
+
}
|
|
7064
|
+
.home-board-open:focus-visible,
|
|
7065
|
+
.home-board-action:focus-visible,
|
|
7066
|
+
.home-view-new:focus-visible,
|
|
7067
|
+
.board-switcher-button:focus-visible {
|
|
7068
|
+
outline: 2px solid var(--c-accent);
|
|
7069
|
+
outline-offset: 2px;
|
|
7070
|
+
}
|
|
7071
|
+
|
|
7072
|
+
/* ── Board export dialog (plan 013) ── */
|
|
7073
|
+
.export-dialog {
|
|
7074
|
+
width: min(520px, calc(100% - 32px));
|
|
7075
|
+
}
|
|
7076
|
+
.export-dialog-note {
|
|
7077
|
+
font-size: 12px;
|
|
7078
|
+
color: var(--c-text-soft);
|
|
7079
|
+
}
|
|
7080
|
+
.export-dialog-list {
|
|
7081
|
+
margin: 0;
|
|
7082
|
+
padding-left: 18px;
|
|
7083
|
+
font-size: 13px;
|
|
7084
|
+
color: var(--c-text);
|
|
7085
|
+
display: flex;
|
|
7086
|
+
flex-direction: column;
|
|
7087
|
+
gap: 4px;
|
|
7088
|
+
}
|
|
7089
|
+
.export-dialog-files {
|
|
7090
|
+
display: flex;
|
|
7091
|
+
gap: 8px;
|
|
7092
|
+
align-items: flex-start;
|
|
7093
|
+
font-size: 12px;
|
|
7094
|
+
color: var(--c-text);
|
|
7095
|
+
word-break: break-all;
|
|
7096
|
+
}
|
|
7097
|
+
.export-dialog-result {
|
|
7098
|
+
font-size: 12px;
|
|
7099
|
+
color: var(--c-text);
|
|
7100
|
+
display: flex;
|
|
7101
|
+
flex-direction: column;
|
|
7102
|
+
gap: 6px;
|
|
7103
|
+
word-break: break-all;
|
|
7104
|
+
}
|
|
7105
|
+
.export-dialog-links {
|
|
7106
|
+
display: flex;
|
|
7107
|
+
gap: 12px;
|
|
7108
|
+
}
|
|
7109
|
+
.export-dialog-links a {
|
|
7110
|
+
color: var(--c-accent);
|
|
7111
|
+
}
|
|
7112
|
+
|
|
7113
|
+
/* ── Home categories ── */
|
|
7114
|
+
.home-section {
|
|
7115
|
+
display: flex;
|
|
7116
|
+
flex-direction: column;
|
|
7117
|
+
gap: 8px;
|
|
7118
|
+
}
|
|
7119
|
+
.home-section-head {
|
|
7120
|
+
display: flex;
|
|
7121
|
+
align-items: center;
|
|
7122
|
+
gap: 9px;
|
|
7123
|
+
min-height: 42px;
|
|
7124
|
+
width: 100%;
|
|
7125
|
+
padding: 9px 10px;
|
|
7126
|
+
background: none;
|
|
7127
|
+
border: none;
|
|
7128
|
+
border-radius: 8px;
|
|
7129
|
+
color: var(--c-text-soft);
|
|
7130
|
+
font-size: 12px;
|
|
7131
|
+
font-weight: 600;
|
|
7132
|
+
text-transform: uppercase;
|
|
7133
|
+
letter-spacing: 0.06em;
|
|
7134
|
+
cursor: pointer;
|
|
7135
|
+
text-align: left;
|
|
7136
|
+
}
|
|
7137
|
+
.home-section-head:hover {
|
|
7138
|
+
color: var(--c-text);
|
|
7139
|
+
background: color-mix(in srgb, var(--c-text) 5%, transparent);
|
|
7140
|
+
}
|
|
7141
|
+
.home-section-chevron,
|
|
7142
|
+
.home-section-folder {
|
|
7143
|
+
flex: 0 0 18px;
|
|
7144
|
+
stroke: currentColor;
|
|
7145
|
+
stroke-width: 1.75;
|
|
7146
|
+
stroke-linecap: round;
|
|
7147
|
+
stroke-linejoin: round;
|
|
7148
|
+
}
|
|
7149
|
+
.home-section-chevron {
|
|
7150
|
+
transition: transform 120ms ease;
|
|
7151
|
+
}
|
|
7152
|
+
.home-section-head[aria-expanded='true'] .home-section-chevron {
|
|
7153
|
+
transform: rotate(90deg);
|
|
7154
|
+
}
|
|
7155
|
+
.home-section-folder { color: var(--c-dim); }
|
|
7156
|
+
.home-section-count {
|
|
7157
|
+
font: 11px var(--mono);
|
|
7158
|
+
text-transform: none;
|
|
7159
|
+
color: var(--c-dim);
|
|
7160
|
+
margin-left: auto;
|
|
7161
|
+
}
|
|
7162
|
+
.home-section-head:focus-visible {
|
|
7163
|
+
outline: 2px solid var(--c-accent);
|
|
7164
|
+
outline-offset: 2px;
|
|
7165
|
+
}
|
|
7166
|
+
.home-section-head {
|
|
7167
|
+
text-transform: none;
|
|
7168
|
+
letter-spacing: normal;
|
|
7169
|
+
max-width: 100%;
|
|
7170
|
+
}
|
|
7171
|
+
.home-section-name {
|
|
7172
|
+
font-size: 13px;
|
|
7173
|
+
font-weight: 650;
|
|
7174
|
+
overflow-wrap: anywhere;
|
|
7175
|
+
}
|
|
7176
|
+
.home-folder-children {
|
|
7177
|
+
display: flex;
|
|
7178
|
+
flex-direction: column;
|
|
7179
|
+
gap: 10px;
|
|
7180
|
+
padding-left: 12px;
|
|
7181
|
+
border-left: 1px solid var(--c-line);
|
|
7182
|
+
}
|
|
7183
|
+
.home-search { width: 100%; box-sizing: border-box; }
|
|
7184
|
+
.board-copy-dialog,
|
|
7185
|
+
.board-link-dialog {
|
|
7186
|
+
width: min(540px, calc(100vw - 32px));
|
|
7187
|
+
max-height: calc(100dvh - 40px);
|
|
7188
|
+
overflow-y: auto;
|
|
7189
|
+
box-sizing: border-box;
|
|
7190
|
+
}
|
|
7191
|
+
.board-copy-dialog form,
|
|
7192
|
+
.board-copy-field,
|
|
7193
|
+
.board-copy-options {
|
|
7194
|
+
display: flex;
|
|
7195
|
+
flex-direction: column;
|
|
7196
|
+
gap: 8px;
|
|
7197
|
+
}
|
|
7198
|
+
.board-copy-dialog h2,
|
|
7199
|
+
.board-link-dialog h2 { margin: 0 0 12px; font-size: 17px; }
|
|
7200
|
+
.board-copy-field { color: var(--c-text-soft); font-size: 12px; }
|
|
7201
|
+
.board-copy-cards {
|
|
7202
|
+
margin: 14px 0;
|
|
7203
|
+
padding: 8px;
|
|
7204
|
+
border: 1px solid var(--c-line);
|
|
7205
|
+
border-radius: 9px;
|
|
7206
|
+
max-height: 230px;
|
|
7207
|
+
overflow-y: auto;
|
|
7208
|
+
}
|
|
7209
|
+
.board-copy-cards legend { color: var(--c-text-soft); font-size: 12px; }
|
|
7210
|
+
.board-copy-card {
|
|
7211
|
+
display: flex;
|
|
7212
|
+
align-items: center;
|
|
7213
|
+
gap: 9px;
|
|
7214
|
+
padding: 8px;
|
|
7215
|
+
border-radius: 7px;
|
|
7216
|
+
cursor: pointer;
|
|
7217
|
+
}
|
|
7218
|
+
.board-copy-card:hover { background: color-mix(in srgb, var(--c-accent) 9%, transparent); }
|
|
7219
|
+
.board-copy-card span { display: flex; flex-direction: column; min-width: 0; }
|
|
7220
|
+
.board-copy-card strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
|
|
7221
|
+
.board-copy-card small,
|
|
7222
|
+
.board-link-results small { color: var(--c-text-soft); font: 11px var(--mono); }
|
|
7223
|
+
.board-copy-options { font-size: 13px; }
|
|
7224
|
+
.board-copy-options label { display: flex; gap: 8px; align-items: center; }
|
|
7225
|
+
.board-copy-note,
|
|
7226
|
+
.board-copy-status { color: var(--c-text-soft); font-size: 12px; line-height: 1.5; }
|
|
7227
|
+
.board-copy-error { color: var(--c-danger); font-size: 12px; margin-top: 8px; }
|
|
7228
|
+
.board-link-results { display: flex; flex-direction: column; gap: 5px; margin: 12px 0; padding: 0; max-height: 320px; overflow-y: auto; list-style: none; }
|
|
7229
|
+
.board-link-results button { width: 100%; display: flex; justify-content: space-between; gap: 12px; padding: 10px; border: 1px solid var(--c-line); border-radius: 8px; background: var(--c-panel); color: var(--c-text); text-align: left; cursor: pointer; }
|
|
7230
|
+
.board-link-results button:hover,
|
|
7231
|
+
.board-link-results button:focus-visible { border-color: var(--c-accent); outline: none; }
|
|
7232
|
+
.home-folder-dialog {
|
|
7233
|
+
width: min(460px, calc(100vw - 32px));
|
|
7234
|
+
max-height: calc(100dvh - 48px);
|
|
7235
|
+
overflow-y: auto;
|
|
7236
|
+
box-sizing: border-box;
|
|
7237
|
+
}
|
|
7238
|
+
.home-folder-hint {
|
|
7239
|
+
color: var(--c-text-soft);
|
|
7240
|
+
font-size: 12px;
|
|
7241
|
+
line-height: 1.5;
|
|
7242
|
+
overflow-wrap: anywhere;
|
|
7243
|
+
}
|
|
7244
|
+
.home-folder-options {
|
|
7245
|
+
margin: 12px 0;
|
|
7246
|
+
padding: 8px;
|
|
7247
|
+
border: 1px solid var(--c-line);
|
|
7248
|
+
border-radius: 8px;
|
|
7249
|
+
max-height: 240px;
|
|
7250
|
+
overflow-y: auto;
|
|
7251
|
+
}
|
|
7252
|
+
.home-folder-options legend { color: var(--c-text-soft); font-size: 12px; }
|
|
7253
|
+
.home-folder-option {
|
|
7254
|
+
display: flex;
|
|
7255
|
+
align-items: center;
|
|
7256
|
+
gap: 8px;
|
|
7257
|
+
padding: 8px;
|
|
7258
|
+
border-radius: 6px;
|
|
7259
|
+
font-size: 13px;
|
|
7260
|
+
overflow-wrap: anywhere;
|
|
7261
|
+
cursor: pointer;
|
|
7262
|
+
}
|
|
7263
|
+
.home-folder-option:has(input:checked) { background: color-mix(in srgb, var(--c-accent) 12%, transparent); }
|
|
7264
|
+
.home-folder-option input { accent-color: var(--c-accent); flex-shrink: 0; }
|
|
7265
|
+
.home-folder-new { display: flex; flex-direction: column; gap: 6px; font-size: 12px; overflow-wrap: anywhere; }
|
|
7266
|
+
.attachment-node { padding: 16px; display: flex; flex-direction: column; align-items: flex-start; gap: 10px; font-size: 13px; overflow: auto; height: 100%; box-sizing: border-box; }
|
|
7267
|
+
.attachment-node p { margin: 0; color: var(--c-text-soft); line-height: 1.5; }
|
|
7268
|
+
.attachment-node a, .md-import-source a { color: var(--c-accent); }
|
|
7269
|
+
.attachment-status { font-weight: 600; }
|
|
7270
|
+
.attachment-node .home-board-action { border-color: var(--c-line); color: var(--c-text); min-height: 34px; }
|
|
7271
|
+
.attachment-node .home-board-action:hover { background: var(--c-panel); border-color: var(--c-accent); }
|
|
7272
|
+
.attachment-review { width: 100%; border-top: 1px solid var(--c-line); padding-top: 12px; }
|
|
7273
|
+
.attachment-review pre { white-space: pre-wrap; overflow-wrap: anywhere; font: 12px/1.6 var(--mono); padding: 12px; background: var(--c-bg); border-radius: 8px; }
|
|
7274
|
+
.attachment-review h4 { margin-bottom: 6px; }
|
|
7275
|
+
.md-import-source { padding: 6px 12px; font-size: 11px; overflow-wrap: anywhere; color: var(--c-text-soft); border-top: 1px solid var(--c-line); }
|
|
7276
|
+
@media (max-width: 640px) {
|
|
7277
|
+
.home-board { flex-wrap: wrap; }
|
|
7278
|
+
.home-board-open { flex-basis: 100%; }
|
|
7279
|
+
.home-board-actions { padding: 0 8px 4px; }
|
|
7280
|
+
}
|
package/src/client/types.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface CanvasNodeState {
|
|
|
14
14
|
| 'webpage'
|
|
15
15
|
| 'json-render'
|
|
16
16
|
| 'graph'
|
|
17
|
+
| 'board'
|
|
17
18
|
| 'prompt'
|
|
18
19
|
| 'response'
|
|
19
20
|
| 'status'
|
|
@@ -74,6 +75,7 @@ export const TYPE_LABELS: Record<CanvasNodeState['type'], string> = {
|
|
|
74
75
|
webpage: 'WEB',
|
|
75
76
|
'json-render': 'UI',
|
|
76
77
|
graph: 'GRAPH',
|
|
78
|
+
board: 'BOARD',
|
|
77
79
|
prompt: 'ASK',
|
|
78
80
|
response: 'ANS',
|
|
79
81
|
status: 'STATUS',
|
|
@@ -149,7 +149,7 @@ function bundleDir(): string {
|
|
|
149
149
|
return candidates[candidates.length - 1];
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function escapeInlineScriptSource(source: string): string {
|
|
152
|
+
export function escapeInlineScriptSource(source: string): string {
|
|
153
153
|
return source.replace(/<\/script/gi, '<\\/script');
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -956,6 +956,20 @@ export function createJsonRenderNodeData(
|
|
|
956
956
|
};
|
|
957
957
|
}
|
|
958
958
|
|
|
959
|
+
/** The viewer bundle's script and stylesheet, as the viewer document inlines them. */
|
|
960
|
+
export async function readJsonRenderBundle(): Promise<{ js: string; css: string }> {
|
|
961
|
+
await ensureJsonRenderBundle();
|
|
962
|
+
const dir = bundleDir();
|
|
963
|
+
const jsPath = join(dir, 'index.js');
|
|
964
|
+
const cssPath = join(dir, 'index.css');
|
|
965
|
+
return {
|
|
966
|
+
js: existsSync(jsPath)
|
|
967
|
+
? readFileSync(jsPath, 'utf-8')
|
|
968
|
+
: 'document.body.innerHTML = "<pre>json-render bundle missing</pre>";',
|
|
969
|
+
css: existsSync(cssPath) ? readFileSync(cssPath, 'utf-8') : '',
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
|
|
959
973
|
export async function buildJsonRenderViewerHtml(options: {
|
|
960
974
|
title: string;
|
|
961
975
|
spec: JsonRenderSpec;
|
|
@@ -975,14 +989,7 @@ export async function buildJsonRenderViewerHtml(options: {
|
|
|
975
989
|
const sanitizeAxValue = (v?: string): string =>
|
|
976
990
|
typeof v === 'string' ? v.replace(/[^A-Za-z0-9_-]/g, '').slice(0, 80) : '';
|
|
977
991
|
try {
|
|
978
|
-
await
|
|
979
|
-
const dir = bundleDir();
|
|
980
|
-
const jsPath = join(dir, 'index.js');
|
|
981
|
-
const cssPath = join(dir, 'index.css');
|
|
982
|
-
const jsBundle = existsSync(jsPath)
|
|
983
|
-
? readFileSync(jsPath, 'utf-8')
|
|
984
|
-
: 'document.body.innerHTML = "<pre>json-render bundle missing</pre>";';
|
|
985
|
-
const cssBundle = existsSync(cssPath) ? readFileSync(cssPath, 'utf-8') : '';
|
|
992
|
+
const { js: jsBundle, css: cssBundle } = await readJsonRenderBundle();
|
|
986
993
|
const boot = [
|
|
987
994
|
`window.__PMX_CANVAS_JSON_RENDER_SPEC__ = ${JSON.stringify(options.spec)};`,
|
|
988
995
|
...(options.theme ? [`window.__PMX_CANVAS_JSON_RENDER_THEME__ = ${JSON.stringify(options.theme)};`] : []),
|
package/src/mcp/canvas-access.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { realpathSync } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { basename, dirname, resolve } from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
createCanvas,
|
|
6
|
-
canvasState,
|
|
7
|
-
type CanvasEdge,
|
|
8
|
-
type CanvasLayout,
|
|
9
|
-
type CanvasNodeState,
|
|
10
|
-
type PmxCanvas,
|
|
11
|
-
} from '../server/index.js';
|
|
4
|
+
import { createCanvas, canvasState, type CanvasLayout, type CanvasNodeState, type PmxCanvas } from '../server/index.js';
|
|
12
5
|
import type { PmxAxSource } from '../server/ax-state.js';
|
|
6
|
+
import type { ContextReadInput } from '../server/context-reads.js';
|
|
13
7
|
import { HttpOperationInvoker, LocalOperationInvoker, type OperationInvoker } from '../server/operations/index.js';
|
|
14
8
|
|
|
15
9
|
// openMcpApp / addDiagram / buildWebArtifact / refreshWebpageNode / addHtmlNode /
|
|
@@ -54,8 +48,8 @@ export interface CanvasAccess {
|
|
|
54
48
|
readonly remoteBaseUrl: string | null;
|
|
55
49
|
/** Operation-registry invoker (plan-005): local in-process or HTTP, matching the access mode. */
|
|
56
50
|
invoker(): OperationInvoker;
|
|
57
|
-
getLayout(): Promise<CanvasLayout>;
|
|
58
|
-
getNode(id: string): Promise<CanvasNodeState | undefined>;
|
|
51
|
+
getLayout(board?: string): Promise<CanvasLayout>;
|
|
52
|
+
getNode(id: string, board?: string): Promise<CanvasNodeState | undefined>;
|
|
59
53
|
getAxState(): Promise<AxStateResult>;
|
|
60
54
|
getAxContext(options?: { consumer?: string }): Promise<AxContextResult>;
|
|
61
55
|
getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult>;
|
|
@@ -72,7 +66,10 @@ export interface CanvasAccess {
|
|
|
72
66
|
ingestActivity(input: IngestActivityInput, options?: { source?: PmxAxSource }): Promise<IngestActivityResult>;
|
|
73
67
|
getPolicy(): Promise<GetPolicyResult>;
|
|
74
68
|
getHistory(): Promise<HistoryResult>;
|
|
75
|
-
getPinnedNodeIds(): Promise<string[]>;
|
|
69
|
+
getPinnedNodeIds(board?: string): Promise<string[]>;
|
|
70
|
+
prepareContextRead(board?: string): Promise<{ boardId: string | null; pinnedNodeIds: string[] }>;
|
|
71
|
+
/** Record a context read the agent made through this MCP server (plan-011). */
|
|
72
|
+
recordContextRead(read: ContextReadInput): Promise<void>;
|
|
76
73
|
runBatch(operations: RunBatchInput): Promise<RunBatchResult>;
|
|
77
74
|
getCodeGraph(): Promise<CodeGraphResult>;
|
|
78
75
|
// canvas_screenshot (still hand-written — binary payload) is the only webview
|
|
@@ -99,11 +96,15 @@ class LocalCanvasAccess implements CanvasAccess {
|
|
|
99
96
|
return this.operationInvoker;
|
|
100
97
|
}
|
|
101
98
|
|
|
102
|
-
async getLayout(): Promise<CanvasLayout> {
|
|
103
|
-
return
|
|
99
|
+
async getLayout(board?: string): Promise<CanvasLayout> {
|
|
100
|
+
return board
|
|
101
|
+
? ((await this.operationInvoker.invoke('layout.get', { board, includeBlobs: true })) as CanvasLayout)
|
|
102
|
+
: this.canvas.getLayout();
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
async getNode(id: string): Promise<CanvasNodeState | undefined> {
|
|
105
|
+
async getNode(id: string, board?: string): Promise<CanvasNodeState | undefined> {
|
|
106
|
+
if (board)
|
|
107
|
+
return (await this.operationInvoker.invoke('node.get', { id, board, includeBlobs: true })) as CanvasNodeState;
|
|
107
108
|
return this.canvas.getNode(id);
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -162,10 +163,23 @@ class LocalCanvasAccess implements CanvasAccess {
|
|
|
162
163
|
return this.canvas.getHistory();
|
|
163
164
|
}
|
|
164
165
|
|
|
165
|
-
async getPinnedNodeIds(): Promise<string[]> {
|
|
166
|
+
async getPinnedNodeIds(board?: string): Promise<string[]> {
|
|
167
|
+
if (board) {
|
|
168
|
+
const result = (await this.operationInvoker.invoke('pinned-context.get', { board })) as { nodeIds?: string[] };
|
|
169
|
+
return result.nodeIds ?? [];
|
|
170
|
+
}
|
|
166
171
|
return Array.from(canvasState.contextPinnedNodeIds);
|
|
167
172
|
}
|
|
168
173
|
|
|
174
|
+
async prepareContextRead(board?: string): Promise<{ boardId: string | null; pinnedNodeIds: string[] }> {
|
|
175
|
+
const boardId = board ?? canvasState.activeBoardId;
|
|
176
|
+
return { boardId, pinnedNodeIds: boardId ? await this.getPinnedNodeIds(boardId) : [] };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async recordContextRead(read: ContextReadInput): Promise<void> {
|
|
180
|
+
canvasState.recordContextRead(read, read.boardId === undefined ? canvasState.activeBoardId : read.boardId);
|
|
181
|
+
}
|
|
182
|
+
|
|
169
183
|
async runBatch(operations: RunBatchInput): Promise<RunBatchResult> {
|
|
170
184
|
return await this.canvas.runBatch(operations);
|
|
171
185
|
}
|
|
@@ -202,7 +216,12 @@ class RemoteCanvasAccess implements CanvasAccess {
|
|
|
202
216
|
private async requestJson<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
203
217
|
const response = await fetch(`${this.remoteBaseUrl}${path}`, {
|
|
204
218
|
method,
|
|
205
|
-
|
|
219
|
+
// These fetches serve the agent's MCP reads, which this server records
|
|
220
|
+
// itself — the daemon must not record them a second time (plan-011).
|
|
221
|
+
headers: {
|
|
222
|
+
'x-pmx-proxied-read': '1',
|
|
223
|
+
...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
|
|
224
|
+
},
|
|
206
225
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
207
226
|
});
|
|
208
227
|
const text = await response.text();
|
|
@@ -227,12 +246,16 @@ class RemoteCanvasAccess implements CanvasAccess {
|
|
|
227
246
|
return parsed as T;
|
|
228
247
|
}
|
|
229
248
|
|
|
230
|
-
async getLayout(): Promise<CanvasLayout> {
|
|
231
|
-
|
|
249
|
+
async getLayout(board?: string): Promise<CanvasLayout> {
|
|
250
|
+
const params = new URLSearchParams({ includeBlobs: 'true' });
|
|
251
|
+
if (board) params.set('board', board);
|
|
252
|
+
return await this.requestJson<CanvasLayout>('GET', `/api/canvas/state?${params}`);
|
|
232
253
|
}
|
|
233
254
|
|
|
234
|
-
async getNode(id: string): Promise<CanvasNodeState | undefined> {
|
|
235
|
-
const
|
|
255
|
+
async getNode(id: string, board?: string): Promise<CanvasNodeState | undefined> {
|
|
256
|
+
const params = new URLSearchParams({ includeBlobs: 'true' });
|
|
257
|
+
if (board) params.set('board', board);
|
|
258
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/node/${encodeURIComponent(id)}?${params}`);
|
|
236
259
|
if (response.status === 404) return undefined;
|
|
237
260
|
const text = await response.text();
|
|
238
261
|
let parsed: unknown;
|
|
@@ -346,11 +369,25 @@ class RemoteCanvasAccess implements CanvasAccess {
|
|
|
346
369
|
return response.reviewAnnotations ?? [];
|
|
347
370
|
}
|
|
348
371
|
|
|
349
|
-
async getPinnedNodeIds(): Promise<string[]> {
|
|
350
|
-
const
|
|
372
|
+
async getPinnedNodeIds(board?: string): Promise<string[]> {
|
|
373
|
+
const qs = board ? `?board=${encodeURIComponent(board)}` : '';
|
|
374
|
+
const response = await this.requestJson<{ nodeIds?: string[] }>('GET', `/api/canvas/pinned-context${qs}`);
|
|
351
375
|
return Array.isArray(response.nodeIds) ? response.nodeIds : [];
|
|
352
376
|
}
|
|
353
377
|
|
|
378
|
+
async prepareContextRead(board?: string): Promise<{ boardId: string | null; pinnedNodeIds: string[] }> {
|
|
379
|
+
let boardId = board;
|
|
380
|
+
if (!boardId) {
|
|
381
|
+
const boards = await this.requestJson<{ activeBoardId?: string | null }>('GET', '/api/canvas/boards');
|
|
382
|
+
boardId = boards.activeBoardId ?? undefined;
|
|
383
|
+
}
|
|
384
|
+
return { boardId: boardId ?? null, pinnedNodeIds: boardId ? await this.getPinnedNodeIds(boardId) : [] };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async recordContextRead(read: ContextReadInput): Promise<void> {
|
|
388
|
+
await this.requestJson('POST', '/api/canvas/ax/context-reads', read);
|
|
389
|
+
}
|
|
390
|
+
|
|
354
391
|
async runBatch(operations: RunBatchInput): Promise<RunBatchResult> {
|
|
355
392
|
return await this.requestJson<RunBatchResult>('POST', '/api/canvas/batch', { operations });
|
|
356
393
|
}
|