pmx-canvas 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +67 -3
  2. package/CHANGELOG.md +73 -0
  3. package/Readme.md +35 -5
  4. package/dist/canvas/global.css +498 -28
  5. package/dist/canvas/index.js +110 -95
  6. package/dist/canvas/mermaid-entry.js +258 -258
  7. package/dist/types/cli/agent.d.ts +4 -0
  8. package/dist/types/cli/commands/backup.d.ts +1 -0
  9. package/dist/types/cli/commands/boards.d.ts +1 -0
  10. package/dist/types/cli/commands/export.d.ts +1 -0
  11. package/dist/types/cli/commands/record.d.ts +12 -0
  12. package/dist/types/client/canvas/BoardCopyDialog.d.ts +5 -0
  13. package/dist/types/client/canvas/BoardFolderDialog.d.ts +7 -0
  14. package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -0
  15. package/dist/types/client/canvas/BoardSwitcher.d.ts +8 -0
  16. package/dist/types/client/canvas/ExportDialog.d.ts +7 -0
  17. package/dist/types/client/canvas/HomeView.d.ts +9 -0
  18. package/dist/types/client/canvas/Presentation.d.ts +13 -0
  19. package/dist/types/client/canvas/use-node-drag.d.ts +2 -1
  20. package/dist/types/client/nodes/AttachmentNode.d.ts +6 -0
  21. package/dist/types/client/nodes/BoardNode.d.ts +5 -0
  22. package/dist/types/client/nodes/McpAppNode.d.ts +24 -1
  23. package/dist/types/client/state/boards-store.d.ts +79 -0
  24. package/dist/types/client/state/canvas-store.d.ts +2 -0
  25. package/dist/types/client/state/intent-bridge.d.ts +1 -0
  26. package/dist/types/client/state/presentation.d.ts +3 -0
  27. package/dist/types/client/state/session-store.d.ts +7 -0
  28. package/dist/types/client/types.d.ts +2 -2
  29. package/dist/types/json-render/catalog.d.ts +3 -3
  30. package/dist/types/json-render/charts/definitions.d.ts +1 -1
  31. package/dist/types/json-render/server.d.ts +8 -0
  32. package/dist/types/mcp/canvas-access.d.ts +10 -3
  33. package/dist/types/mcp/context-uri-template.d.ts +6 -0
  34. package/dist/types/server/agent-presence.d.ts +1 -1
  35. package/dist/types/server/attribution.d.ts +11 -0
  36. package/dist/types/server/ax-context.d.ts +3 -1
  37. package/dist/types/server/ax-interaction.d.ts +2 -2
  38. package/dist/types/server/ax-state.d.ts +13 -3
  39. package/dist/types/server/backup.d.ts +36 -0
  40. package/dist/types/server/board-export.d.ts +45 -0
  41. package/dist/types/server/canvas-db.d.ts +65 -9
  42. package/dist/types/server/canvas-operations.d.ts +45 -0
  43. package/dist/types/server/canvas-provenance.d.ts +1 -1
  44. package/dist/types/server/canvas-schema.d.ts +1 -0
  45. package/dist/types/server/canvas-state.d.ts +146 -3
  46. package/dist/types/server/canvas-validation.d.ts +3 -0
  47. package/dist/types/server/context-brief.d.ts +111 -0
  48. package/dist/types/server/context-reads.d.ts +55 -0
  49. package/dist/types/server/document-import.d.ts +66 -0
  50. package/dist/types/server/html-surface.d.ts +1 -1
  51. package/dist/types/server/index.d.ts +107 -4
  52. package/dist/types/server/mutation-history.d.ts +1 -0
  53. package/dist/types/server/operations/invoker.d.ts +2 -0
  54. package/dist/types/server/operations/mcp.d.ts +7 -1
  55. package/dist/types/server/operations/ops/app.d.ts +2 -0
  56. package/dist/types/server/operations/ops/ax-shared.d.ts +2 -2
  57. package/dist/types/server/operations/ops/backup.d.ts +2 -0
  58. package/dist/types/server/operations/ops/boards.d.ts +10 -0
  59. package/dist/types/server/operations/ops/export.d.ts +2 -0
  60. package/dist/types/server/operations/ops/imports.d.ts +2 -0
  61. package/dist/types/server/operations/ops/json-render.d.ts +2 -0
  62. package/dist/types/server/operations/ops/nodes.d.ts +4 -2
  63. package/dist/types/server/operations/ops/query.d.ts +2 -0
  64. package/dist/types/server/operations/registry.d.ts +9 -1
  65. package/dist/types/server/operations/types.d.ts +1 -1
  66. package/dist/types/server/server.d.ts +1 -0
  67. package/dist/types/server/trace-manager.d.ts +7 -0
  68. package/dist/types/server/web-artifacts.d.ts +3 -0
  69. package/dist/types/server/webpage-node.d.ts +22 -1
  70. package/dist/types/server/workbench-auth.d.ts +3 -0
  71. package/dist/types/shared/boards.d.ts +6 -0
  72. package/dist/types/shared/tour.d.ts +72 -0
  73. package/docs/RELEASE.md +5 -0
  74. package/docs/cli.md +157 -0
  75. package/docs/http-api.md +277 -6
  76. package/docs/mcp.md +96 -7
  77. package/docs/node-types.md +106 -0
  78. package/docs/plans/plan-010-attributed-ask-answers.md +39 -0
  79. package/docs/plans/plan-011-context-read-instrumentation.md +54 -0
  80. package/docs/plans/plan-012-many-boards-stage-1.md +98 -0
  81. package/docs/plans/plan-013-static-board-export.md +47 -0
  82. package/docs/plans/plan-014-connected-board-memory.md +140 -0
  83. package/docs/product-vision-2026-09.md +118 -18
  84. package/docs/sdk.md +50 -0
  85. package/package.json +1 -1
  86. package/skills/pmx-canvas/SKILL.md +62 -5
  87. package/skills/pmx-canvas/references/full-reference.md +44 -8
  88. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +3 -0
  89. package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
  90. package/skills/pmx-canvas-orchestration/SKILL.md +5 -0
  91. package/skills/pmx-canvas-testing/SKILL.md +4 -0
  92. package/src/cli/agent.ts +9 -0
  93. package/src/cli/commands/ax.ts +13 -0
  94. package/src/cli/commands/backup.ts +66 -0
  95. package/src/cli/commands/boards.ts +71 -0
  96. package/src/cli/commands/export.ts +30 -0
  97. package/src/cli/commands/nodes.ts +14 -1
  98. package/src/cli/commands/pins.ts +6 -2
  99. package/src/cli/commands/pump.ts +3 -0
  100. package/src/cli/commands/query.ts +33 -7
  101. package/src/cli/commands/record.ts +218 -0
  102. package/src/cli/commands/view.ts +7 -5
  103. package/src/cli/index.ts +21 -9
  104. package/src/client/App.tsx +28 -4
  105. package/src/client/canvas/AgentPresenceLayer.tsx +1 -1
  106. package/src/client/canvas/BoardCopyDialog.tsx +153 -0
  107. package/src/client/canvas/BoardFolderDialog.tsx +120 -0
  108. package/src/client/canvas/BoardLinkDialog.tsx +93 -0
  109. package/src/client/canvas/BoardSwitcher.tsx +149 -0
  110. package/src/client/canvas/CanvasNode.tsx +8 -0
  111. package/src/client/canvas/CanvasViewport.tsx +3 -0
  112. package/src/client/canvas/CommandPalette.tsx +107 -4
  113. package/src/client/canvas/ContextMenu.tsx +4 -0
  114. package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -0
  115. package/src/client/canvas/ExportDialog.tsx +197 -0
  116. package/src/client/canvas/HomeView.tsx +277 -0
  117. package/src/client/canvas/IntentLayer.tsx +9 -2
  118. package/src/client/canvas/Presentation.tsx +108 -0
  119. package/src/client/canvas/SessionPanel.tsx +28 -1
  120. package/src/client/canvas/ToolRail.tsx +38 -3
  121. package/src/client/canvas/TopBar.tsx +49 -7
  122. package/src/client/canvas/import-files.ts +19 -1
  123. package/src/client/canvas/kind-colors.ts +1 -0
  124. package/src/client/canvas/use-node-drag.ts +8 -3
  125. package/src/client/icons.tsx +2 -0
  126. package/src/client/mermaid-entry.ts +39 -0
  127. package/src/client/nodes/AttachmentNode.tsx +148 -0
  128. package/src/client/nodes/BoardNode.tsx +35 -0
  129. package/src/client/nodes/FileNode.tsx +6 -0
  130. package/src/client/nodes/HtmlNode.tsx +12 -13
  131. package/src/client/nodes/MarkdownNode.tsx +47 -19
  132. package/src/client/nodes/McpAppNode.tsx +105 -13
  133. package/src/client/nodes/MermaidNode.tsx +3 -2
  134. package/src/client/nodes/ax-node-actions.ts +2 -5
  135. package/src/client/nodes/use-ax-surface-bridge.ts +1 -1
  136. package/src/client/state/boards-store.ts +167 -0
  137. package/src/client/state/canvas-store.ts +22 -0
  138. package/src/client/state/intent-bridge.ts +30 -2
  139. package/src/client/state/intent-store.ts +5 -1
  140. package/src/client/state/node-factory.ts +1 -0
  141. package/src/client/state/presentation.ts +7 -0
  142. package/src/client/state/session-store.ts +8 -0
  143. package/src/client/state/sse-bridge.ts +52 -5
  144. package/src/client/theme/global.css +498 -28
  145. package/src/client/types.ts +2 -0
  146. package/src/json-render/server.ts +45 -9
  147. package/src/mcp/canvas-access.ts +59 -22
  148. package/src/mcp/context-uri-template.ts +28 -0
  149. package/src/mcp/server.ts +122 -15
  150. package/src/server/agent-context.ts +116 -29
  151. package/src/server/agent-presence.ts +15 -9
  152. package/src/server/attribution.ts +29 -0
  153. package/src/server/ax-context.ts +4 -2
  154. package/src/server/ax-interaction.ts +1 -0
  155. package/src/server/ax-state-manager.ts +72 -3
  156. package/src/server/ax-state.ts +41 -0
  157. package/src/server/backup.ts +178 -0
  158. package/src/server/board-export.ts +686 -0
  159. package/src/server/canvas-db.ts +612 -105
  160. package/src/server/canvas-operations.ts +183 -31
  161. package/src/server/canvas-provenance.ts +1 -0
  162. package/src/server/canvas-schema.ts +22 -0
  163. package/src/server/canvas-state.ts +812 -48
  164. package/src/server/canvas-validation.ts +9 -1
  165. package/src/server/context-brief.ts +372 -0
  166. package/src/server/context-reads.ts +238 -0
  167. package/src/server/demo-state.json +670 -9
  168. package/src/server/document-import.ts +223 -0
  169. package/src/server/html-primitives.ts +199 -91
  170. package/src/server/html-surface.ts +8 -3
  171. package/src/server/index.ts +243 -5
  172. package/src/server/mutation-history.ts +10 -13
  173. package/src/server/operations/composites.ts +29 -5
  174. package/src/server/operations/http.ts +9 -3
  175. package/src/server/operations/index.ts +8 -0
  176. package/src/server/operations/invoker.ts +14 -3
  177. package/src/server/operations/mcp.ts +50 -11
  178. package/src/server/operations/ops/app.ts +15 -0
  179. package/src/server/operations/ops/ax-read.ts +10 -6
  180. package/src/server/operations/ops/ax-timeline.ts +75 -0
  181. package/src/server/operations/ops/backup.ts +134 -0
  182. package/src/server/operations/ops/batch.ts +20 -4
  183. package/src/server/operations/ops/boards.ts +351 -0
  184. package/src/server/operations/ops/export.ts +74 -0
  185. package/src/server/operations/ops/ext-app.ts +4 -0
  186. package/src/server/operations/ops/groups.ts +14 -7
  187. package/src/server/operations/ops/imports.ts +220 -0
  188. package/src/server/operations/ops/json-render.ts +37 -5
  189. package/src/server/operations/ops/nodes.ts +99 -21
  190. package/src/server/operations/ops/query.ts +206 -9
  191. package/src/server/operations/ops/viewport.ts +32 -0
  192. package/src/server/operations/registry.ts +129 -31
  193. package/src/server/operations/types.ts +1 -1
  194. package/src/server/scope-fence.ts +1 -1
  195. package/src/server/server.ts +155 -8
  196. package/src/server/spatial-analysis.ts +9 -17
  197. package/src/server/trace-manager.ts +19 -0
  198. package/src/server/web-artifacts.ts +11 -3
  199. package/src/server/webpage-node.ts +173 -13
  200. package/src/server/workbench-auth.ts +11 -0
  201. package/src/server/workboard.ts +3 -3
  202. package/src/shared/boards.ts +18 -0
  203. package/src/shared/node-sizes.ts +1 -0
  204. package/src/shared/tour.ts +87 -0
@@ -1,4 +1,5 @@
1
1
  import { findOpenCanvasPosition } from '../utils/placement.js';
2
+ import { ownsCamera } from './presentation';
2
3
  import { normalizeExtAppToolResult } from '../utils/ext-app-tool-result.js';
3
4
  import type { CanvasAnnotation, CanvasEdge, CanvasNodeState } from '../types';
4
5
  import {
@@ -26,10 +27,13 @@ import {
26
27
  workbenchConnectionEpoch,
27
28
  reconnectAttempt,
28
29
  reconnectDelay,
30
+ resetCanvasInteractionState,
29
31
  } from './canvas-store';
32
+ import { applyBoards, loadBoards, setBoardSwitchHandler } from './boards-store';
30
33
  import {
31
34
  fetchAgentPresence,
32
35
  fetchAxSurfaceState,
36
+ fetchCanvasState,
33
37
  reportClientViewportSize,
34
38
  requestJson,
35
39
  tabAgentKey,
@@ -303,6 +307,7 @@ function isCanvasNodeType(value: unknown): value is CanvasNodeState['type'] {
303
307
  value === 'webpage' ||
304
308
  value === 'json-render' ||
305
309
  value === 'graph' ||
310
+ value === 'board' ||
306
311
  value === 'prompt' ||
307
312
  value === 'response' ||
308
313
  value === 'status' ||
@@ -455,14 +460,44 @@ function handleAgentSessionEnded(data: Record<string, unknown>): void {
455
460
  }
456
461
 
457
462
  let stopHumanPresence: (() => void) | null = null;
463
+ let boardResyncGeneration = 0;
464
+ let layoutRevision = 0;
465
+ let pinsRevision = 0;
458
466
 
459
467
  function handleHumanPresence(data: Record<string, unknown>): void {
460
468
  applyHumanSnapshot(data as Partial<HumanPresenceSnapshot>);
461
469
  }
462
470
 
471
+ /**
472
+ * The open board changed (this tab, another tab, or a restart): the canvas is
473
+ * a different board now, so take its layout like a fresh connection would —
474
+ * camera included — and drop what belonged to the old board.
475
+ */
476
+ function resyncForBoardSwitch(): void {
477
+ const generation = ++boardResyncGeneration;
478
+ savedLayout = null;
479
+ hasInitialServerLayout.value = false;
480
+ resetIntents();
481
+ resetCanvasInteractionState();
482
+ axSurfaceState.value = null;
483
+ void refreshAxSurface();
484
+ const layoutAtRequest = layoutRevision;
485
+ const pinsAtRequest = pinsRevision;
486
+ void fetchCanvasState().then((layout) => {
487
+ if (generation === boardResyncGeneration && layoutAtRequest === layoutRevision)
488
+ handleCanvasLayoutUpdate({ layout });
489
+ });
490
+ void requestJson<{ nodeIds?: unknown }>('fetchPinnedContext', '/api/canvas/pinned-context', {}).then((data) => {
491
+ if (generation === boardResyncGeneration && pinsAtRequest === pinsRevision)
492
+ handleContextPinsChanged({ nodeIds: data.nodeIds });
493
+ });
494
+ }
495
+ setBoardSwitchHandler(resyncForBoardSwitch);
496
+
463
497
  function handleConnected(data: Record<string, unknown>): void {
464
498
  sessionId.value = (data.sessionId as string) || '';
465
499
  connectionStatus.value = 'connected';
500
+ void loadBoards();
466
501
  // Reconnect marker for holders of server-minted URLs (Finding S).
467
502
  workbenchConnectionEpoch.value += 1;
468
503
  reloadIfServerUpgraded(data.version, data.bundleStamp);
@@ -485,8 +520,7 @@ function handleConnected(data: Record<string, unknown>): void {
485
520
  // The AX surface snapshot (work items, gates) likewise — it used to arrive
486
521
  // only on the first ax-state-changed, so a fresh load showed an empty
487
522
  // session panel while persisted work items existed.
488
- void fetchAxSurfaceState().then((state) => {
489
- axSurfaceState.value = state;
523
+ void refreshAxSurface().then(() => {
490
524
  if (sessionActive.value) void refreshTimeline();
491
525
  });
492
526
  // A ?theme= session override (host-default theming) wins over the
@@ -902,6 +936,7 @@ function handleCanvasLayoutUpdate(data: Record<string, unknown>): void {
902
936
  }
903
937
  | undefined;
904
938
  if (!layout?.nodes) return;
939
+ layoutRevision += 1;
905
940
  const shouldApplyViewport = !hasInitialServerLayout.value;
906
941
  hasInitialServerLayout.value = true;
907
942
 
@@ -945,6 +980,7 @@ function reconnectDelayMs(attempt: number): number {
945
980
  }
946
981
 
947
982
  function handleCanvasFocusNode(data: Record<string, unknown>): void {
983
+ if (ownsCamera()) return;
948
984
  const nodeId = data.nodeId as string;
949
985
  if (nodeId && nodes.value.has(nodeId)) {
950
986
  if (data.noPan === true) {
@@ -956,6 +992,7 @@ function handleCanvasFocusNode(data: Record<string, unknown>): void {
956
992
  }
957
993
 
958
994
  function handleCanvasViewportUpdate(data: Record<string, unknown>): void {
995
+ if (ownsCamera()) return;
959
996
  const viewport = data.viewport as Record<string, unknown> | undefined;
960
997
  if (!viewport) return;
961
998
  const x = typeof viewport.x === 'number' ? viewport.x : 0;
@@ -990,6 +1027,7 @@ function handleThemeChanged(data: Record<string, unknown>): void {
990
1027
  }
991
1028
 
992
1029
  function handleContextPinsChanged(data: Record<string, unknown>): void {
1030
+ pinsRevision += 1;
993
1031
  const nodeIds = Array.isArray(data.nodeIds) ? data.nodeIds.filter((id): id is string => typeof id === 'string') : [];
994
1032
  replaceContextPinsFromServer(nodeIds);
995
1033
  syncAttentionFromSse({ event: 'context-pins-changed', data });
@@ -1000,13 +1038,21 @@ function handleContextPinsChanged(data: Record<string, unknown>): void {
1000
1038
  // (debounced). The snapshot feeds AX-enabled surfaces (HtmlNode/McpAppNode push it
1001
1039
  // into their iframes), so authored boards reflect the live work queue / focus.
1002
1040
  let axRefreshTimer: ReturnType<typeof setTimeout> | null = null;
1041
+ let axRefreshSequence = 0;
1042
+ async function refreshAxSurface(): Promise<void> {
1043
+ const sequence = ++axRefreshSequence;
1044
+ const generation = boardResyncGeneration;
1045
+ const state = await fetchAxSurfaceState();
1046
+ if (sequence === axRefreshSequence && generation === boardResyncGeneration) axSurfaceState.value = state;
1047
+ }
1048
+
1003
1049
  function handleAxStateChanged(): void {
1004
1050
  if (axRefreshTimer) clearTimeout(axRefreshTimer);
1051
+ // Invalidate any in-flight read before the debounced replacement starts.
1052
+ axRefreshSequence += 1;
1005
1053
  axRefreshTimer = setTimeout(() => {
1006
1054
  axRefreshTimer = null;
1007
- void fetchAxSurfaceState().then((state) => {
1008
- axSurfaceState.value = state;
1009
- });
1055
+ void refreshAxSurface();
1010
1056
  // The session panel's timeline — only worth fetching while it is mounted.
1011
1057
  if (sessionActive.value) void refreshTimeline();
1012
1058
  }, 150);
@@ -1061,6 +1107,7 @@ export const EVENT_HANDLERS: Record<string, (data: Record<string, unknown>) => v
1061
1107
  'ext-app-update': handleExtAppUpdate,
1062
1108
  'ext-app-result': handleExtAppResult,
1063
1109
  'context-pins-changed': handleContextPinsChanged,
1110
+ 'boards-changed': applyBoards,
1064
1111
  'canvas-layout-update': handleCanvasLayoutUpdate,
1065
1112
  'canvas-focus-node': handleCanvasFocusNode,
1066
1113
  'canvas-viewport-update': handleCanvasViewportUpdate,
@@ -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;
@@ -1210,13 +1230,13 @@ body,
1210
1230
  font-size: 9px;
1211
1231
  padding: 1px 6px;
1212
1232
  }
1213
- /* Idle cursors leave the board (user feedback, 0.5.1 cycle): with many agents
1233
+ /* Parked idle cursors leave the board (user feedback, 0.5.1 cycle): with many agents
1214
1234
  attached, a wall of parked "Idle" pills covers real node content. The
1215
1235
  element stays MOUNTED — counts, twin-merge, and cursor-follow logic are
1216
1236
  untouched, and visibility (not display) keeps its layout box so position
1217
- polls still resolve — but it paints nothing; any non-idle phase fades it
1218
- back in. Placed after .is-worker so opacity: 0 wins that collision. */
1219
- .agent-cursor.phase-idle {
1237
+ polls still resolve — but it paints nothing. An explicitly reported cursor
1238
+ remains visible even while idle so cursor-only movement cannot disappear. */
1239
+ .agent-cursor.phase-idle:not(.has-explicit-cursor) {
1220
1240
  opacity: 0;
1221
1241
  visibility: hidden;
1222
1242
  transition:
@@ -1251,7 +1271,7 @@ body,
1251
1271
  }
1252
1272
 
1253
1273
  .agent-chip-end:hover {
1254
- color: var(--c-danger, #ef4444);
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%;
@@ -4211,6 +4208,33 @@ html.is-node-resizing .ext-app-preview-catcher {
4211
4208
  }
4212
4209
 
4213
4210
  /* MCP app iframe */
4211
+ .mcp-app-frame-stack {
4212
+ position: relative;
4213
+ flex: 1;
4214
+ min-width: 0;
4215
+ min-height: 0;
4216
+ background: var(--c-panel-soft);
4217
+ overflow: hidden;
4218
+ border-radius: 0 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px);
4219
+ }
4220
+
4221
+ .mcp-app-frame-stack:has(.html-node-frame) {
4222
+ width: 100%;
4223
+ height: 100%;
4224
+ min-height: 300px;
4225
+ background: var(--c-bg);
4226
+ border-radius: 6px;
4227
+ }
4228
+
4229
+ .mcp-app-frame-stack:has(.html-node-frame-presentation) {
4230
+ min-height: 0;
4231
+ border-radius: 0;
4232
+ }
4233
+
4234
+ .mcp-app-frame-stack:has(.html-node-frame-expanded) {
4235
+ min-height: 70vh;
4236
+ }
4237
+
4214
4238
  .mcp-app-frame {
4215
4239
  width: 100%;
4216
4240
  height: 100%;
@@ -6808,3 +6832,449 @@ html.is-node-dragging .intent-ghost,
6808
6832
  html.is-node-dragging .intent-line-layer {
6809
6833
  opacity: 0.5;
6810
6834
  }
6835
+
6836
+ .present-button {
6837
+ color: var(--c-text);
6838
+ background: var(--c-bg);
6839
+ border: 1px solid var(--c-line);
6840
+ border-radius: 6px;
6841
+ padding: 4px 12px;
6842
+ cursor: pointer;
6843
+ }
6844
+ .is-presenting > :not(.app-main):not(.presentation-error),
6845
+ .is-presenting .app-main > :not(.canvas-region),
6846
+ .is-presenting .canvas-region > :not(.canvas-viewport) {
6847
+ display: none !important;
6848
+ }
6849
+ .is-presenting .canvas-viewport { pointer-events: none; }
6850
+ .presentation-error {
6851
+ position: fixed;
6852
+ bottom: 16px;
6853
+ left: 16px;
6854
+ z-index: 100;
6855
+ max-width: calc(100% - 32px);
6856
+ padding: 10px 16px;
6857
+ border: 1px solid var(--c-line);
6858
+ border-radius: 6px;
6859
+ background: var(--c-panel);
6860
+ color: var(--c-text);
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
+ }