pmx-canvas 0.7.2 → 0.8.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 (251) hide show
  1. package/.github/extensions/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
  2. package/.github/extensions/pmx-canvas/extension.mjs +25 -172
  3. package/.github/extensions/pmx-canvas/mcp-bootstrap.ts +198 -0
  4. package/.github/extensions/pmx-canvas/runtime.d.mts +41 -0
  5. package/.github/extensions/pmx-canvas/runtime.json +3 -0
  6. package/.github/extensions/pmx-canvas/runtime.mjs +194 -0
  7. package/.github/extensions/pmx-canvas/start-mcp.mjs +51 -0
  8. package/.github/extensions/pmx-canvas/tools.json +2560 -0
  9. package/CHANGELOG.md +60 -0
  10. package/Readme.md +44 -16
  11. package/dist/canvas/fonts/OFL.txt +93 -0
  12. package/dist/canvas/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  13. package/dist/canvas/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  14. package/dist/canvas/fonts/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
  15. package/dist/canvas/fonts/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
  16. package/dist/canvas/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  17. package/dist/canvas/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  18. package/dist/canvas/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  19. package/dist/canvas/fonts/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
  20. package/dist/canvas/fonts/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
  21. package/dist/canvas/fonts/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
  22. package/dist/canvas/global.css +844 -710
  23. package/dist/canvas/index.js +92 -90
  24. package/dist/canvas/mcp-app.html +7878 -0
  25. package/dist/canvas/mermaid-entry.js +1 -1
  26. package/dist/canvas/surface-theme.css +151 -46
  27. package/dist/json-render/index.css +1 -1
  28. package/dist/json-render/index.js +55 -55
  29. package/dist/types/cli/commands/record.d.ts +1 -1
  30. package/dist/types/client/canvas/ChoiceMenu.d.ts +13 -0
  31. package/dist/types/client/canvas/NodeContextMark.d.ts +24 -0
  32. package/dist/types/client/canvas/SessionReceipt.d.ts +3 -1
  33. package/dist/types/client/icons.d.ts +1 -2
  34. package/dist/types/client/mcp-app/index.d.ts +1 -0
  35. package/dist/types/client/nodes/McpAppNode.d.ts +2 -1
  36. package/dist/types/client/nodes/ax-node-actions.d.ts +2 -2
  37. package/dist/types/client/nodes/markdown-sanitize.d.ts +2 -0
  38. package/dist/types/client/nodes/use-workbench-asset.d.ts +2 -0
  39. package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -0
  40. package/dist/types/client/state/attention-store.d.ts +0 -5
  41. package/dist/types/client/state/canvas-store.d.ts +2 -0
  42. package/dist/types/client/state/context-status-store.d.ts +53 -0
  43. package/dist/types/client/state/intent-bridge.d.ts +4 -1
  44. package/dist/types/client/state/near-pin-store.d.ts +16 -0
  45. package/dist/types/client/state/presence-store.d.ts +5 -0
  46. package/dist/types/client/state/session-store.d.ts +33 -3
  47. package/dist/types/client/state/workbench-transport.d.ts +16 -0
  48. package/dist/types/client/theme/tokens.d.ts +1 -3
  49. package/dist/types/client/types.d.ts +9 -0
  50. package/dist/types/json-render/renderer/index.d.ts +2 -1
  51. package/dist/types/json-render/server.d.ts +3 -1
  52. package/dist/types/mcp/app-server.d.ts +3 -0
  53. package/dist/types/mcp/canvas-access.d.ts +1 -1
  54. package/dist/types/mcp/canvas-app.d.ts +4 -0
  55. package/dist/types/mcp/workbench-app.d.ts +4 -0
  56. package/dist/types/server/agent-presence.d.ts +1 -1
  57. package/dist/types/server/ax-state.d.ts +1 -1
  58. package/dist/types/server/canvas-db.d.ts +8 -0
  59. package/dist/types/server/canvas-geometry.d.ts +25 -0
  60. package/dist/types/server/canvas-operations.d.ts +1 -11
  61. package/dist/types/server/canvas-state.d.ts +25 -3
  62. package/dist/types/server/context-brief.d.ts +8 -1
  63. package/dist/types/server/context-reads.d.ts +30 -7
  64. package/dist/types/server/index.d.ts +6 -1
  65. package/dist/types/server/mcp-app-runtime.d.ts +1 -0
  66. package/dist/types/server/operations/http.d.ts +2 -0
  67. package/dist/types/server/operations/invoker.d.ts +4 -2
  68. package/dist/types/server/operations/ops/nodes.d.ts +0 -6
  69. package/dist/types/server/server.d.ts +1 -0
  70. package/dist/types/server/spatial-analysis.d.ts +2 -0
  71. package/dist/types/shared/agent-context.d.ts +22 -0
  72. package/dist/types/shared/agent-presence.d.ts +3 -10
  73. package/dist/types/shared/ax-surface-protocol.d.ts +2 -0
  74. package/dist/types/shared/canvas-app.d.ts +54 -0
  75. package/dist/types/shared/themes.d.ts +1 -1
  76. package/docs/RELEASE.md +7 -3
  77. package/docs/agent-plugin.md +123 -0
  78. package/docs/ax-host-adapter-contract.md +13 -2
  79. package/docs/ax-state-contract.md +40 -0
  80. package/docs/cli.md +7 -2
  81. package/docs/design/AgentContext.dc.html +186 -0
  82. package/docs/design/Before.dc.html +208 -0
  83. package/docs/design/BoardPins.dc.html +151 -0
  84. package/docs/design/Chrome.dc.html +208 -0
  85. package/docs/design/Context.dc.html +239 -0
  86. package/docs/design/Flows.dc.html +181 -0
  87. package/docs/design/Home.dc.html +209 -0
  88. package/docs/design/HomeGraph.dc.html +211 -0
  89. package/docs/design/HomeMap.dc.html +117 -0
  90. package/docs/design/HomeTeam.dc.html +176 -0
  91. package/docs/design/Light.dc.html +28 -0
  92. package/docs/design/Main.dc.html +280 -0
  93. package/docs/design/NearPin.dc.html +260 -0
  94. package/docs/design/Pane600.dc.html +120 -0
  95. package/docs/design/Relations.dc.html +297 -0
  96. package/docs/design/Themes.dc.html +121 -0
  97. package/docs/design/TokenSystem.dc.html +136 -0
  98. package/docs/design/Tokens.dc.html +163 -0
  99. package/docs/design/Types1.dc.html +177 -0
  100. package/docs/design/Types2.dc.html +226 -0
  101. package/docs/design/Types3.dc.html +195 -0
  102. package/docs/design/Uses.dc.html +152 -0
  103. package/docs/design/Vision.dc.html +116 -0
  104. package/docs/design/Zoom.dc.html +82 -0
  105. package/docs/design/canvas.json +274 -0
  106. package/docs/design.md +214 -0
  107. package/docs/environment.md +1 -1
  108. package/docs/http-api.md +80 -15
  109. package/docs/mcp.md +198 -3
  110. package/docs/node-types.md +34 -10
  111. package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
  112. package/docs/plans/plan-015-design-foundation-wave-0.md +177 -0
  113. package/docs/plans/plan-016-context-made-visible-wave-1.md +148 -0
  114. package/docs/product-vision-2026-09.md +47 -7
  115. package/docs/screenshot.png +0 -0
  116. package/docs/sdk.md +1 -0
  117. package/package.json +10 -4
  118. package/plugins/pmx-canvas/LICENSE +21 -0
  119. package/plugins/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
  120. package/plugins/pmx-canvas/assets/preview.png +0 -0
  121. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +930 -0
  122. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +3 -0
  123. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.mjs +194 -0
  124. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/steering-delivery.mjs +79 -0
  125. package/plugins/pmx-canvas/mcp.json +12 -0
  126. package/plugins/pmx-canvas/plugin.json +24 -0
  127. package/plugins/pmx-canvas/runtime.json +3 -0
  128. package/plugins/pmx-canvas/runtime.mjs +194 -0
  129. package/plugins/pmx-canvas/start-mcp.mjs +51 -0
  130. package/plugins/pmx-canvas/tools.json +2560 -0
  131. package/skills/pmx-canvas/SKILL.md +31 -16
  132. package/skills/pmx-canvas/evals/evals.json +69 -0
  133. package/skills/pmx-canvas/references/full-reference.md +14 -10
  134. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +20 -8
  135. package/skills/pmx-canvas/references/installing-pmx-canvas.md +76 -4
  136. package/skills/pmx-canvas-testing/SKILL.md +54 -4
  137. package/src/cli/commands/copilot.ts +5 -0
  138. package/src/cli/commands/view.ts +1 -1
  139. package/src/cli/index.ts +8 -3
  140. package/src/client/App.tsx +6 -1
  141. package/src/client/canvas/AgentPresenceLayer.tsx +3 -3
  142. package/src/client/canvas/AnnotationLayer.tsx +1 -1
  143. package/src/client/canvas/CanvasNode.tsx +208 -220
  144. package/src/client/canvas/CanvasViewport.tsx +6 -2
  145. package/src/client/canvas/ChoiceMenu.tsx +122 -0
  146. package/src/client/canvas/CommandBar.tsx +25 -23
  147. package/src/client/canvas/CommandPalette.tsx +3 -2
  148. package/src/client/canvas/ContextMenu.tsx +11 -0
  149. package/src/client/canvas/EdgeLayer.tsx +1 -1
  150. package/src/client/canvas/ExportDialog.tsx +33 -6
  151. package/src/client/canvas/FeedbackDialog.tsx +10 -8
  152. package/src/client/canvas/IntentLayer.tsx +2 -2
  153. package/src/client/canvas/Minimap.tsx +12 -7
  154. package/src/client/canvas/NodeContextMark.tsx +156 -0
  155. package/src/client/canvas/SessionPanel.tsx +4 -7
  156. package/src/client/canvas/SessionReceipt.tsx +114 -17
  157. package/src/client/canvas/ShortcutOverlay.tsx +6 -1
  158. package/src/client/canvas/SnapshotPanel.tsx +11 -2
  159. package/src/client/canvas/ToolRail.tsx +58 -108
  160. package/src/client/canvas/TopBar.tsx +16 -11
  161. package/src/client/icons.tsx +2 -12
  162. package/src/client/mcp-app/index.tsx +137 -0
  163. package/src/client/mcp-app/style.css +10 -0
  164. package/src/client/nodes/AttachmentNode.tsx +8 -1
  165. package/src/client/nodes/AxStepControls.tsx +1 -1
  166. package/src/client/nodes/ContextNode.tsx +9 -9
  167. package/src/client/nodes/ExtAppFrame.tsx +13 -8
  168. package/src/client/nodes/FileNode.tsx +8 -5
  169. package/src/client/nodes/ImageNode.tsx +4 -2
  170. package/src/client/nodes/InlineMarkdownEditor.tsx +2 -1
  171. package/src/client/nodes/LedgerNode.tsx +2 -2
  172. package/src/client/nodes/MarkdownNode.tsx +44 -9
  173. package/src/client/nodes/McpAppNode.tsx +22 -4
  174. package/src/client/nodes/PromptNode.tsx +5 -17
  175. package/src/client/nodes/ResponseNode.tsx +3 -15
  176. package/src/client/nodes/StatusNode.tsx +2 -2
  177. package/src/client/nodes/StatusSummary.tsx +3 -1
  178. package/src/client/nodes/TraceNode.tsx +6 -4
  179. package/src/client/nodes/WebpageNode.tsx +1 -1
  180. package/src/client/nodes/ax-node-actions.ts +2 -2
  181. package/src/client/nodes/iframe-document-url.ts +3 -2
  182. package/src/client/nodes/markdown-sanitize.ts +10 -0
  183. package/src/client/nodes/surface-url.ts +10 -0
  184. package/src/client/nodes/use-ax-surface-bridge.ts +4 -2
  185. package/src/client/nodes/use-surface-frame.ts +3 -1
  186. package/src/client/nodes/use-workbench-asset.ts +26 -0
  187. package/src/client/nodes/viewer-runtime-state.ts +78 -0
  188. package/src/client/state/attention-bridge.ts +0 -8
  189. package/src/client/state/attention-store.ts +0 -17
  190. package/src/client/state/canvas-store.ts +7 -2
  191. package/src/client/state/context-status-store.ts +66 -0
  192. package/src/client/state/human-store.ts +2 -0
  193. package/src/client/state/iframe-mode.ts +5 -0
  194. package/src/client/state/intent-bridge.ts +24 -6
  195. package/src/client/state/near-pin-store.ts +53 -0
  196. package/src/client/state/presence-store.ts +5 -10
  197. package/src/client/state/session-store.ts +106 -9
  198. package/src/client/state/sse-bridge.ts +26 -7
  199. package/src/client/state/theme-override.ts +1 -1
  200. package/src/client/state/workbench-transport.ts +36 -0
  201. package/src/client/theme/global.css +844 -710
  202. package/src/client/theme/surface-theme.css +151 -46
  203. package/src/client/theme/tokens.ts +7 -11
  204. package/src/client/types.ts +10 -0
  205. package/src/json-render/renderer/index.tsx +58 -38
  206. package/src/json-render/server.ts +79 -15
  207. package/src/mcp/app-server.ts +64 -0
  208. package/src/mcp/canvas-access.ts +14 -11
  209. package/src/mcp/canvas-app.ts +318 -0
  210. package/src/mcp/server.ts +1 -1
  211. package/src/mcp/workbench-app.ts +211 -0
  212. package/src/server/agent-presence.ts +45 -6
  213. package/src/server/ax-context.ts +1 -1
  214. package/src/server/ax-state.ts +1 -1
  215. package/src/server/board-export.ts +5 -2
  216. package/src/server/canvas-db.ts +46 -12
  217. package/src/server/canvas-geometry.ts +64 -0
  218. package/src/server/canvas-operations.ts +6 -24
  219. package/src/server/canvas-state.ts +105 -15
  220. package/src/server/canvas-validation.ts +19 -4
  221. package/src/server/context-brief.ts +39 -2
  222. package/src/server/context-reads.ts +86 -15
  223. package/src/server/demo-state.json +31 -31
  224. package/src/server/html-surface.ts +4 -5
  225. package/src/server/index.ts +19 -3
  226. package/src/server/mcp-app-runtime.ts +11 -0
  227. package/src/server/operations/composites.ts +4 -3
  228. package/src/server/operations/http.ts +4 -4
  229. package/src/server/operations/invoker.ts +13 -4
  230. package/src/server/operations/ops/ax-read.ts +1 -1
  231. package/src/server/operations/ops/ax-timeline.ts +37 -0
  232. package/src/server/operations/ops/boards.ts +1 -1
  233. package/src/server/operations/ops/canvas-wire.ts +3 -6
  234. package/src/server/operations/ops/intent.ts +31 -2
  235. package/src/server/operations/ops/json-render.ts +1 -1
  236. package/src/server/operations/ops/nodes.ts +8 -76
  237. package/src/server/operations/ops/query.ts +8 -2
  238. package/src/server/operations/ops/snapshots.ts +4 -1
  239. package/src/server/operations/ops/viewport.ts +29 -5
  240. package/src/server/operations/registry.ts +38 -4
  241. package/src/server/scope-fence.ts +55 -10
  242. package/src/server/server.ts +86 -6
  243. package/src/server/spatial-analysis.ts +2 -2
  244. package/src/server/workboard.ts +6 -1
  245. package/src/{server → shared}/agent-context.ts +2 -2
  246. package/src/shared/agent-presence.ts +3 -15
  247. package/src/shared/ax-surface-protocol.ts +3 -0
  248. package/src/shared/canvas-app.ts +62 -0
  249. package/src/shared/themes.ts +5 -5
  250. package/src/client/canvas/FocusFieldLayer.tsx +0 -116
  251. package/src/client/canvas/kind-colors.ts +0 -23
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Agent presence — the contract behind every agent surface in the rail chrome
3
- * (rail-chrome-v2 phase 2). Shared by server and client so the two cannot
4
- * drift. See design/rail-chrome-v2/PLAN.md, "Phase 2 contract".
2
+ * Agent presence — the contract behind every agent surface in the rail chrome.
3
+ * Shared by server and client so the two cannot drift. See
4
+ * docs/ax-state-contract.md, "Agent presence (contract)".
5
5
  *
6
6
  * Presence is DERIVED from feeds that already exist (AX activity ingest,
7
7
  * agent-originated mutations) plus an explicit set for adapters with richer
@@ -148,15 +148,3 @@ export function agentPhaseLabel(presence: Pick<AgentPresence, 'phase' | 'detail'
148
148
  return 'Idle';
149
149
  }
150
150
  }
151
-
152
- /**
153
- * Stable per-agent identity hue (0-359) from the writer key. Phase colors
154
- * kept telling the human WHAT an agent is doing while erasing WHO — two
155
- * thinking agents rendered identically. Identity rides the glyph/border;
156
- * phase stays on dots and labels.
157
- */
158
- export function agentIdentityHue(key: string): number {
159
- let hash = 5381;
160
- for (let i = 0; i < key.length; i++) hash = ((hash << 5) + hash + key.charCodeAt(i)) | 0;
161
- return ((hash % 360) + 360) % 360;
162
- }
@@ -18,6 +18,9 @@ export const AX_SURFACE_ACK_SOURCE = 'pmx-canvas-ax-ack';
18
18
  /** Parent→surface pushes (theme updates, live AX state) into html/viewer iframes. */
19
19
  export const HTML_SURFACE_PUSH_SOURCE = 'pmx-canvas-html-node';
20
20
 
21
+ /** Tab-local JSON viewer state handoff, independent of AX capabilities. */
22
+ export const JSON_VIEWER_STATE_SOURCE = 'pmx-canvas-json-state';
23
+
21
24
  /** Ext-app boot beacon: the iframe's scripts executed (WebKit watchdog liveness). */
22
25
  export const EXT_APP_BOOT_BEACON_SOURCE = 'pmx-canvas-ext-app-alive';
23
26
 
@@ -0,0 +1,62 @@
1
+ import { z } from 'zod';
2
+
3
+ export const CANVAS_APP_URI = 'ui://pmx/canvas';
4
+ // Independent of package version: older builds can share the same version.
5
+ export const CANVAS_APP_WRITE_CONTRACT = 'pmx-embedded-workbench-v4';
6
+
7
+ const point = z.object({ x: z.number(), y: z.number() });
8
+ export const canvasAppSnapshotSchema = z.object({
9
+ boardId: z.string().nullable(),
10
+ boardName: z.string(),
11
+ boards: z.array(z.object({ id: z.string(), name: z.string() })),
12
+ // A digest of this projection, not the board's content-delta cursor.
13
+ revision: z.string(),
14
+ nodes: z.array(
15
+ z.object({
16
+ id: z.string(),
17
+ type: z.string(),
18
+ title: z.string(),
19
+ text: z.string(),
20
+ contentRevision: z.number().int().nonnegative(),
21
+ // Only standalone Markdown within the editing limit; never a truncated draft.
22
+ editableContent: z.string().optional(),
23
+ position: point,
24
+ size: z.object({ width: z.number(), height: z.number() }),
25
+ image: z.string().optional(),
26
+ }),
27
+ ),
28
+ edges: z.array(z.object({ id: z.string(), from: z.string(), to: z.string(), label: z.string() })),
29
+ pinnedNodeIds: z.array(z.string()),
30
+ });
31
+
32
+ export type CanvasAppSnapshot = z.infer<typeof canvasAppSnapshotSchema>;
33
+
34
+ /** Share only curated text, never image bytes or the whole board's contents. */
35
+ export function canvasAppContext(
36
+ snapshot: Pick<CanvasAppSnapshot, 'boardId' | 'boardName' | 'pinnedNodeIds'> & {
37
+ nodes: Pick<CanvasAppSnapshot['nodes'][number], 'id' | 'type' | 'title' | 'text' | 'contentRevision'>[];
38
+ },
39
+ selectedIds: string[],
40
+ focusedIds: string[] = [],
41
+ ) {
42
+ const existing = new Set(snapshot.nodes.map((node) => node.id));
43
+ const selected = new Set(selectedIds.filter((id) => existing.has(id)).slice(0, 20));
44
+ const pinned = new Set(snapshot.pinnedNodeIds.filter((id) => existing.has(id)).slice(0, 20));
45
+ const focused = new Set(focusedIds.filter((id) => existing.has(id)).slice(0, 20));
46
+ return {
47
+ boardId: snapshot.boardId,
48
+ boardName: snapshot.boardName,
49
+ selectedNodeIds: [...selected],
50
+ focusedNodeIds: [...focused],
51
+ pinnedNodeIds: [...pinned],
52
+ nodes: snapshot.nodes
53
+ .filter((node) => selected.has(node.id) || pinned.has(node.id) || focused.has(node.id))
54
+ .map((node) => ({
55
+ id: node.id,
56
+ type: node.type,
57
+ title: node.title,
58
+ contentRevision: node.contentRevision,
59
+ text: node.text.slice(0, 700),
60
+ })),
61
+ };
62
+ }
@@ -11,8 +11,8 @@
11
11
  */
12
12
 
13
13
  export const CANVAS_THEMES = [
14
- 'dark',
15
- 'light',
14
+ 'harbor',
15
+ 'daylight',
16
16
  'high-contrast',
17
17
  'midnight',
18
18
  'sepia',
@@ -28,7 +28,7 @@ export function isCanvasTheme(value: unknown): value is CanvasThemeName {
28
28
  return typeof value === 'string' && (CANVAS_THEMES as readonly string[]).includes(value);
29
29
  }
30
30
 
31
- export function normalizeCanvasThemeName(value: unknown, fallback: CanvasThemeName = 'dark'): CanvasThemeName {
31
+ export function normalizeCanvasThemeName(value: unknown, fallback: CanvasThemeName = 'harbor'): CanvasThemeName {
32
32
  return isCanvasTheme(value) ? value : fallback;
33
33
  }
34
34
 
@@ -43,8 +43,8 @@ export interface CanvasThemeMeta {
43
43
  }
44
44
 
45
45
  export const CANVAS_THEME_META: Record<CanvasThemeName, CanvasThemeMeta> = {
46
- dark: { label: 'Dark', scheme: 'dark', swatchBg: '#081524', swatchAccent: '#4BBCFF' },
47
- light: { label: 'Light', scheme: 'light', swatchBg: '#F4EFE6', swatchAccent: '#1A7ABF' },
46
+ harbor: { label: 'Harbor', scheme: 'dark', swatchBg: '#081524', swatchAccent: '#4BBCFF' },
47
+ daylight: { label: 'Daylight', scheme: 'light', swatchBg: '#F6F7F9', swatchAccent: '#1A7ABF' },
48
48
  'high-contrast': { label: 'High contrast', scheme: 'dark', swatchBg: '#000000', swatchAccent: '#00ffff' },
49
49
  midnight: { label: 'Midnight', scheme: 'dark', swatchBg: '#0A0D1C', swatchAccent: '#8B96FF' },
50
50
  sepia: { label: 'Sepia', scheme: 'light', swatchBg: '#F2E7D5', swatchAccent: '#B4632C' },
@@ -1,116 +0,0 @@
1
- import { attentionPrimaryNodeIds, attentionRegions, attentionSecondaryNodeIds } from '../state/attention-store';
2
- import { nodes } from '../state/canvas-store';
3
-
4
- interface Rect {
5
- left: number;
6
- top: number;
7
- width: number;
8
- height: number;
9
- }
10
-
11
- function getNodeRect(nodeId: string): Rect | null {
12
- const node = nodes.value.get(nodeId);
13
- if (!node) return null;
14
- return {
15
- left: node.position.x,
16
- top: node.position.y,
17
- width: node.size.width,
18
- height: node.size.height,
19
- };
20
- }
21
-
22
- function getRegionRect(nodeIds: string[]): Rect | null {
23
- const rects = nodeIds.map((nodeId) => getNodeRect(nodeId)).filter((rect): rect is Rect => rect !== null);
24
- if (rects.length === 0) return null;
25
-
26
- const minLeft = Math.min(...rects.map((rect) => rect.left));
27
- const minTop = Math.min(...rects.map((rect) => rect.top));
28
- const maxRight = Math.max(...rects.map((rect) => rect.left + rect.width));
29
- const maxBottom = Math.max(...rects.map((rect) => rect.top + rect.height));
30
- const padX = 54;
31
- const padY = 46;
32
-
33
- return {
34
- left: minLeft - padX,
35
- top: minTop - padY,
36
- width: maxRight - minLeft + padX * 2,
37
- height: maxBottom - minTop + padY * 2,
38
- };
39
- }
40
-
41
- function rectStyle(rect: Rect, radius: number): Record<string, string> {
42
- return {
43
- left: `${rect.left}px`,
44
- top: `${rect.top}px`,
45
- width: `${rect.width}px`,
46
- height: `${rect.height}px`,
47
- borderRadius: `${radius}px`,
48
- };
49
- }
50
-
51
- export function FocusFieldLayer() {
52
- const primaryNodeIds = Array.from(attentionPrimaryNodeIds.value);
53
- const secondaryNodeIds = Array.from(attentionSecondaryNodeIds.value);
54
- const regions = attentionRegions.value;
55
-
56
- if (primaryNodeIds.length === 0 && secondaryNodeIds.length === 0) return null;
57
-
58
- // A region that spans (nearly) the whole populated board distinguishes
59
- // nothing — pins far apart degrade the glow into a border around
60
- // everything. Skip those; the per-node halos still mark the pins.
61
- const all = [...nodes.value.values()];
62
- const boardArea =
63
- all.length > 0
64
- ? (Math.max(...all.map((n) => n.position.x + n.size.width)) - Math.min(...all.map((n) => n.position.x))) *
65
- (Math.max(...all.map((n) => n.position.y + n.size.height)) - Math.min(...all.map((n) => n.position.y)))
66
- : 0;
67
-
68
- return (
69
- <div class="attention-field-layer" aria-hidden="true">
70
- {regions.map((region) => {
71
- const rect = getRegionRect(region.nodeIds);
72
- if (!rect) return null;
73
- if (boardArea > 0 && rect.width * rect.height >= boardArea * 0.75) return null;
74
- return <div key={region.id} class="attention-field-region" style={rectStyle(rect, 42)} />;
75
- })}
76
- {secondaryNodeIds.map((nodeId) => {
77
- const rect = getNodeRect(nodeId);
78
- if (!rect) return null;
79
- return (
80
- <div
81
- key={`secondary-${nodeId}`}
82
- class="attention-field-node attention-field-secondary"
83
- style={rectStyle(
84
- {
85
- left: rect.left - 18,
86
- top: rect.top - 18,
87
- width: rect.width + 36,
88
- height: rect.height + 36,
89
- },
90
- 28,
91
- )}
92
- />
93
- );
94
- })}
95
- {primaryNodeIds.map((nodeId) => {
96
- const rect = getNodeRect(nodeId);
97
- if (!rect) return null;
98
- return (
99
- <div
100
- key={`primary-${nodeId}`}
101
- class="attention-field-node attention-field-primary"
102
- style={rectStyle(
103
- {
104
- left: rect.left - 24,
105
- top: rect.top - 24,
106
- width: rect.width + 48,
107
- height: rect.height + 48,
108
- },
109
- 30,
110
- )}
111
- />
112
- );
113
- })}
114
- </div>
115
- );
116
- }
@@ -1,23 +0,0 @@
1
- import type { CanvasNodeState } from '../types';
2
-
3
- /** Kind color per node type, as CSS tokens so every theme keeps its palette. */
4
- export const KIND_COLOR: Record<CanvasNodeState['type'], string> = {
5
- markdown: 'var(--c-accent)',
6
- 'mcp-app': 'var(--c-ok)',
7
- webpage: 'var(--c-warn)',
8
- 'json-render': 'var(--c-ok)',
9
- graph: 'var(--c-purple)',
10
- board: 'var(--c-accent)',
11
- prompt: 'var(--c-accent)',
12
- response: 'var(--c-ok)',
13
- status: 'var(--c-warn)',
14
- context: 'var(--c-muted)',
15
- ledger: 'var(--c-dim)',
16
- trace: 'var(--c-purple)',
17
- file: 'var(--c-accent)',
18
- diff: 'var(--c-ok)',
19
- mermaid: 'var(--c-purple)',
20
- image: 'var(--c-ok)',
21
- html: 'var(--c-warn)',
22
- group: 'var(--c-accent)',
23
- };