pmx-canvas 0.7.3 → 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 (235) 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 +47 -0
  10. package/Readme.md +21 -7
  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 +723 -672
  23. package/dist/canvas/index.js +90 -88
  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/types/cli/commands/record.d.ts +1 -1
  29. package/dist/types/client/canvas/NodeContextMark.d.ts +24 -0
  30. package/dist/types/client/canvas/SessionReceipt.d.ts +3 -1
  31. package/dist/types/client/icons.d.ts +1 -2
  32. package/dist/types/client/mcp-app/index.d.ts +1 -0
  33. package/dist/types/client/nodes/ax-node-actions.d.ts +2 -2
  34. package/dist/types/client/nodes/markdown-sanitize.d.ts +2 -0
  35. package/dist/types/client/nodes/use-workbench-asset.d.ts +2 -0
  36. package/dist/types/client/state/attention-store.d.ts +0 -5
  37. package/dist/types/client/state/context-status-store.d.ts +53 -0
  38. package/dist/types/client/state/intent-bridge.d.ts +3 -0
  39. package/dist/types/client/state/near-pin-store.d.ts +16 -0
  40. package/dist/types/client/state/presence-store.d.ts +5 -0
  41. package/dist/types/client/state/session-store.d.ts +33 -3
  42. package/dist/types/client/state/workbench-transport.d.ts +16 -0
  43. package/dist/types/client/theme/tokens.d.ts +1 -3
  44. package/dist/types/client/types.d.ts +9 -0
  45. package/dist/types/json-render/server.d.ts +1 -1
  46. package/dist/types/mcp/app-server.d.ts +3 -0
  47. package/dist/types/mcp/canvas-access.d.ts +1 -1
  48. package/dist/types/mcp/canvas-app.d.ts +4 -0
  49. package/dist/types/mcp/workbench-app.d.ts +4 -0
  50. package/dist/types/server/agent-presence.d.ts +1 -1
  51. package/dist/types/server/ax-state.d.ts +1 -1
  52. package/dist/types/server/canvas-db.d.ts +8 -0
  53. package/dist/types/server/canvas-geometry.d.ts +25 -0
  54. package/dist/types/server/canvas-operations.d.ts +1 -11
  55. package/dist/types/server/canvas-state.d.ts +25 -3
  56. package/dist/types/server/context-brief.d.ts +8 -1
  57. package/dist/types/server/context-reads.d.ts +30 -7
  58. package/dist/types/server/index.d.ts +6 -1
  59. package/dist/types/server/mcp-app-runtime.d.ts +1 -0
  60. package/dist/types/server/operations/http.d.ts +2 -0
  61. package/dist/types/server/operations/invoker.d.ts +4 -2
  62. package/dist/types/server/operations/ops/nodes.d.ts +0 -6
  63. package/dist/types/server/server.d.ts +1 -0
  64. package/dist/types/server/spatial-analysis.d.ts +2 -0
  65. package/dist/types/shared/agent-context.d.ts +22 -0
  66. package/dist/types/shared/agent-presence.d.ts +3 -10
  67. package/dist/types/shared/canvas-app.d.ts +54 -0
  68. package/dist/types/shared/themes.d.ts +1 -1
  69. package/docs/RELEASE.md +7 -3
  70. package/docs/agent-plugin.md +123 -0
  71. package/docs/ax-state-contract.md +40 -0
  72. package/docs/cli.md +7 -2
  73. package/docs/design/AgentContext.dc.html +186 -0
  74. package/docs/design/Before.dc.html +208 -0
  75. package/docs/design/BoardPins.dc.html +151 -0
  76. package/docs/design/Chrome.dc.html +208 -0
  77. package/docs/design/Context.dc.html +239 -0
  78. package/docs/design/Flows.dc.html +181 -0
  79. package/docs/design/Home.dc.html +209 -0
  80. package/docs/design/HomeGraph.dc.html +211 -0
  81. package/docs/design/HomeMap.dc.html +117 -0
  82. package/docs/design/HomeTeam.dc.html +176 -0
  83. package/docs/design/Light.dc.html +28 -0
  84. package/docs/design/Main.dc.html +280 -0
  85. package/docs/design/NearPin.dc.html +260 -0
  86. package/docs/design/Pane600.dc.html +120 -0
  87. package/docs/design/Relations.dc.html +297 -0
  88. package/docs/design/Themes.dc.html +121 -0
  89. package/docs/design/TokenSystem.dc.html +136 -0
  90. package/docs/design/Tokens.dc.html +163 -0
  91. package/docs/design/Types1.dc.html +177 -0
  92. package/docs/design/Types2.dc.html +226 -0
  93. package/docs/design/Types3.dc.html +195 -0
  94. package/docs/design/Uses.dc.html +152 -0
  95. package/docs/design/Vision.dc.html +116 -0
  96. package/docs/design/Zoom.dc.html +82 -0
  97. package/docs/design/canvas.json +274 -0
  98. package/docs/design.md +214 -0
  99. package/docs/environment.md +1 -1
  100. package/docs/http-api.md +80 -15
  101. package/docs/mcp.md +198 -3
  102. package/docs/node-types.md +34 -10
  103. package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
  104. package/docs/plans/plan-015-design-foundation-wave-0.md +177 -0
  105. package/docs/plans/plan-016-context-made-visible-wave-1.md +148 -0
  106. package/docs/product-vision-2026-09.md +47 -7
  107. package/docs/screenshot.png +0 -0
  108. package/docs/sdk.md +1 -0
  109. package/package.json +10 -4
  110. package/plugins/pmx-canvas/LICENSE +21 -0
  111. package/plugins/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
  112. package/plugins/pmx-canvas/assets/preview.png +0 -0
  113. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +930 -0
  114. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +3 -0
  115. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.mjs +194 -0
  116. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/steering-delivery.mjs +79 -0
  117. package/plugins/pmx-canvas/mcp.json +12 -0
  118. package/plugins/pmx-canvas/plugin.json +24 -0
  119. package/plugins/pmx-canvas/runtime.json +3 -0
  120. package/plugins/pmx-canvas/runtime.mjs +194 -0
  121. package/plugins/pmx-canvas/start-mcp.mjs +51 -0
  122. package/plugins/pmx-canvas/tools.json +2560 -0
  123. package/skills/pmx-canvas/SKILL.md +10 -7
  124. package/skills/pmx-canvas/references/full-reference.md +11 -7
  125. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +20 -8
  126. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  127. package/skills/pmx-canvas-testing/SKILL.md +5 -4
  128. package/src/cli/commands/copilot.ts +5 -0
  129. package/src/cli/commands/view.ts +1 -1
  130. package/src/cli/index.ts +8 -3
  131. package/src/client/App.tsx +6 -1
  132. package/src/client/canvas/AgentPresenceLayer.tsx +3 -3
  133. package/src/client/canvas/AnnotationLayer.tsx +1 -1
  134. package/src/client/canvas/CanvasNode.tsx +32 -49
  135. package/src/client/canvas/CanvasViewport.tsx +0 -2
  136. package/src/client/canvas/CommandPalette.tsx +3 -2
  137. package/src/client/canvas/ContextMenu.tsx +11 -0
  138. package/src/client/canvas/EdgeLayer.tsx +1 -1
  139. package/src/client/canvas/ExportDialog.tsx +33 -6
  140. package/src/client/canvas/IntentLayer.tsx +2 -2
  141. package/src/client/canvas/Minimap.tsx +12 -7
  142. package/src/client/canvas/NodeContextMark.tsx +156 -0
  143. package/src/client/canvas/SessionPanel.tsx +4 -7
  144. package/src/client/canvas/SessionReceipt.tsx +114 -17
  145. package/src/client/canvas/ShortcutOverlay.tsx +6 -1
  146. package/src/client/canvas/SnapshotPanel.tsx +11 -2
  147. package/src/client/canvas/ToolRail.tsx +12 -9
  148. package/src/client/canvas/TopBar.tsx +16 -11
  149. package/src/client/icons.tsx +2 -12
  150. package/src/client/mcp-app/index.tsx +137 -0
  151. package/src/client/mcp-app/style.css +10 -0
  152. package/src/client/nodes/AttachmentNode.tsx +8 -1
  153. package/src/client/nodes/AxStepControls.tsx +1 -1
  154. package/src/client/nodes/ContextNode.tsx +9 -9
  155. package/src/client/nodes/ExtAppFrame.tsx +13 -8
  156. package/src/client/nodes/FileNode.tsx +8 -5
  157. package/src/client/nodes/ImageNode.tsx +4 -2
  158. package/src/client/nodes/InlineMarkdownEditor.tsx +2 -1
  159. package/src/client/nodes/LedgerNode.tsx +2 -2
  160. package/src/client/nodes/MarkdownNode.tsx +44 -9
  161. package/src/client/nodes/McpAppNode.tsx +6 -4
  162. package/src/client/nodes/PromptNode.tsx +5 -17
  163. package/src/client/nodes/ResponseNode.tsx +3 -15
  164. package/src/client/nodes/StatusNode.tsx +2 -2
  165. package/src/client/nodes/StatusSummary.tsx +3 -1
  166. package/src/client/nodes/TraceNode.tsx +6 -4
  167. package/src/client/nodes/WebpageNode.tsx +1 -1
  168. package/src/client/nodes/ax-node-actions.ts +2 -2
  169. package/src/client/nodes/iframe-document-url.ts +3 -2
  170. package/src/client/nodes/markdown-sanitize.ts +10 -0
  171. package/src/client/nodes/surface-url.ts +10 -0
  172. package/src/client/nodes/use-surface-frame.ts +3 -1
  173. package/src/client/nodes/use-workbench-asset.ts +26 -0
  174. package/src/client/state/attention-bridge.ts +0 -8
  175. package/src/client/state/attention-store.ts +0 -17
  176. package/src/client/state/canvas-store.ts +4 -2
  177. package/src/client/state/context-status-store.ts +66 -0
  178. package/src/client/state/human-store.ts +2 -0
  179. package/src/client/state/iframe-mode.ts +5 -0
  180. package/src/client/state/intent-bridge.ts +10 -4
  181. package/src/client/state/near-pin-store.ts +53 -0
  182. package/src/client/state/presence-store.ts +5 -10
  183. package/src/client/state/session-store.ts +106 -9
  184. package/src/client/state/sse-bridge.ts +26 -7
  185. package/src/client/state/theme-override.ts +1 -1
  186. package/src/client/state/workbench-transport.ts +36 -0
  187. package/src/client/theme/global.css +723 -672
  188. package/src/client/theme/surface-theme.css +151 -46
  189. package/src/client/theme/tokens.ts +7 -11
  190. package/src/client/types.ts +10 -0
  191. package/src/json-render/server.ts +71 -14
  192. package/src/mcp/app-server.ts +64 -0
  193. package/src/mcp/canvas-access.ts +14 -11
  194. package/src/mcp/canvas-app.ts +318 -0
  195. package/src/mcp/server.ts +1 -1
  196. package/src/mcp/workbench-app.ts +211 -0
  197. package/src/server/agent-presence.ts +45 -6
  198. package/src/server/ax-context.ts +1 -1
  199. package/src/server/ax-state.ts +1 -1
  200. package/src/server/board-export.ts +5 -2
  201. package/src/server/canvas-db.ts +46 -12
  202. package/src/server/canvas-geometry.ts +64 -0
  203. package/src/server/canvas-operations.ts +6 -24
  204. package/src/server/canvas-state.ts +105 -15
  205. package/src/server/canvas-validation.ts +19 -4
  206. package/src/server/context-brief.ts +39 -2
  207. package/src/server/context-reads.ts +86 -15
  208. package/src/server/demo-state.json +31 -31
  209. package/src/server/html-surface.ts +4 -5
  210. package/src/server/index.ts +19 -3
  211. package/src/server/mcp-app-runtime.ts +11 -0
  212. package/src/server/operations/composites.ts +4 -3
  213. package/src/server/operations/http.ts +4 -4
  214. package/src/server/operations/invoker.ts +13 -4
  215. package/src/server/operations/ops/ax-read.ts +1 -1
  216. package/src/server/operations/ops/ax-timeline.ts +37 -0
  217. package/src/server/operations/ops/boards.ts +1 -1
  218. package/src/server/operations/ops/canvas-wire.ts +3 -6
  219. package/src/server/operations/ops/intent.ts +31 -2
  220. package/src/server/operations/ops/json-render.ts +1 -1
  221. package/src/server/operations/ops/nodes.ts +8 -76
  222. package/src/server/operations/ops/query.ts +8 -2
  223. package/src/server/operations/ops/snapshots.ts +4 -1
  224. package/src/server/operations/ops/viewport.ts +29 -5
  225. package/src/server/operations/registry.ts +38 -4
  226. package/src/server/scope-fence.ts +55 -10
  227. package/src/server/server.ts +83 -5
  228. package/src/server/spatial-analysis.ts +2 -2
  229. package/src/server/workboard.ts +6 -1
  230. package/src/{server → shared}/agent-context.ts +2 -2
  231. package/src/shared/agent-presence.ts +3 -15
  232. package/src/shared/canvas-app.ts +62 -0
  233. package/src/shared/themes.ts +5 -5
  234. package/src/client/canvas/FocusFieldLayer.tsx +0 -116
  235. package/src/client/canvas/kind-colors.ts +0 -23
package/docs/design.md ADDED
@@ -0,0 +1,214 @@
1
+ # Design: the system and when it gets built
2
+
3
+ *Updated 2026-10-04. Companion to [the vision](product-vision-2026-09.md): the vision
4
+ says what the product does and in what order; this file says how it looks and which
5
+ design work ships with which part of that order. Design never ships ahead of the
6
+ function that produces its data, and function never ships with today's styling when
7
+ its design is already drawn.*
8
+
9
+ The drawings are the **look-and-feel proposal canvas**: edited as a private Design
10
+ artifact (<https://claude.ai/artifact/NbLiHkjK9d57Se55vskoc3>) and kept in the repo
11
+ as source under [`design/`](design/), so agents and contributors can read the exact
12
+ sizes, colours and copy. The drawings are the visual authority; this file is the
13
+ contract and the schedule.
14
+
15
+ **Before you build a surface** (any agent or contributor):
16
+
17
+ 1. Find its artboard in [The drawings](#the-drawings) and read the source for exact sizes,
18
+ colours, spacing and copy.
19
+ 2. Find its wave in the [Build plan](#build-plan) and confirm the function that produces its
20
+ data ships in the same change.
21
+ 3. Apply the [Principles](#principles), above all the fixed meaning colours.
22
+ 4. Meet the [Definition of done](#definition-of-done-for-any-design-work) before calling it done.
23
+
24
+ ## Token system
25
+
26
+ Drawn on [`TokenSystem.dc.html`](design/TokenSystem.dc.html); per-theme meaning colours on
27
+ [`Themes.dc.html`](design/Themes.dc.html).
28
+
29
+ ### Today
30
+
31
+ About 55 custom properties in `src/client/theme/global.css`, almost all colour:
32
+
33
+ - **Palette:** `--c-bg`, `--c-panel`, `--c-panel-soft`, `--c-line`, `--c-text`, `--c-text-soft`,
34
+ `--c-muted`, `--c-dim`.
35
+ - **Status colours that components borrow for meaning:** `--c-accent`, `--c-ok`, `--c-warn`,
36
+ `--c-warn-alt`, `--c-danger`, `--c-purple`, `--c-thinking`, `--c-subagent`.
37
+ - **22 fixed alpha copies:** `--c-warn-10…60` (nine), `--c-accent-8…40` (eight), `--c-ok-10…25`,
38
+ `--c-danger-12`.
39
+ - **Surfaces and effects:** `--c-panel-glass`, `--c-panel-overlay`, `--c-surface-*`,
40
+ `--c-input-bg`, `--c-shadow`, `--c-shadow-heavy`, `--c-glow-accent`.
41
+ - **The only non-colour tokens:** `--font`, `--mono`, `--radius`, `--radius-sm`,
42
+ `--hud-bar-height`.
43
+ - **A second scheme for embedded viewers:** `--color-*` in `surface-theme.css`, collapsed to dark
44
+ or light.
45
+
46
+ What that causes: meaning is borrowed (pins and agent attention both use `--c-warn`, which
47
+ `global.css` references 93 times, while the light theme hard-codes `#4BBCFF` for pins); there is no
48
+ type, spacing or shadow scale (20 font sizes, 12 radii and 195 raw hex colours sit in the CSS);
49
+ every new tint needs a new token; embedded viewers draw their own palette; `--c-subagent`
50
+ (`#00E5FF`) sits next to the pin blue; and nothing checks any of it.
51
+
52
+ ### Proposed: three layers
53
+
54
+ Components reference tokens only. Raw colour values live only in the theme files.
55
+
56
+ 1. **Palette** — defined by each theme, names kept: `--c-bg`, `--c-panel`, `--c-panel-soft`,
57
+ `--c-line`, `--c-text`, `--c-text-soft`, `--c-muted`, `--c-dim`, `--c-accent`,
58
+ `--c-on-accent`, `--c-ok`, `--c-danger`. The accent is the theme's flavour (primary buttons,
59
+ selection) and never carries a meaning below.
60
+ 2. **Meaning** — one hue in every theme, tuned only for lightness; used only for its meaning:
61
+
62
+ | Token | Means | Harbor (dark) | Daylight (light) |
63
+ |---|---|---|---|
64
+ | `--c-pin` | In the agent's context | `#4BBCFF` | `#1A7ABF` |
65
+ | `--c-agent` | What the agent did (replaces `--c-thinking`) | `#B388FF` | `#7C4DDB` |
66
+ | `--c-warn` | Needs a look: warning, stale, out of date | `#f4c542` | `#94600A` |
67
+ | `--c-rel-supports` | Evidence for | `#5FCFC0` | `#1E8C80` |
68
+ | `--c-rel-contradicts` | Evidence against | `#E59B6B` | `#B8572A` |
69
+ | `--c-rel-cites` | Where a claim or number comes from | `#7C9CFF` | tune in wave 0 |
70
+ | `--c-rel-derived` | Data lineage, flow | `#D5DEEA` | tune in wave 0 |
71
+ | `--c-rel-informs` | Answers, depends on | `#E58FB0` | tune in wave 0 |
72
+ | `--c-rel-related` | Loose link (dotted) | `#8ea3bd` | tune in wave 0 |
73
+
74
+ The other seven themes' values are drawn on the Themes board and fixed by the token test.
75
+ 3. **Scale** — the same in every theme:
76
+ - Fonts: `--font-ui` IBM Plex Sans, `--font-code` IBM Plex Mono, both bundled.
77
+ - Type: `--fs-meta` 11, `--fs-ui` 12, `--fs-body` 13, `--fs-title` 15, `--fs-heading` 20.
78
+ - Spacing: `--space-1` 4, `--space-2` 8, `--space-3` 12, `--space-4` 16, `--space-6` 24,
79
+ `--space-8` 32.
80
+ - Radius: `--r-control` 6, `--r-node` 10, `--r-overlay` 14, `--r-pill` 999.
81
+ - Elevation: `--e-1` nodes, `--e-2` floating chrome and menus. No glows. The one per-theme
82
+ exception (decided 2026-10-05): the drawn black shadows are for dark surfaces; Daylight and
83
+ Sepia use ink-tinted ones (`rgba(8, 21, 36, …)` at 10/8% and 18%).
84
+
85
+ ### Moving from today to proposed (wave 0)
86
+
87
+ - Tints are derived — `color-mix(in srgb, var(--c-pin) 12%, transparent)` — so the 22 alpha
88
+ copies, `--c-warn-alt` and `--c-glow-accent` go.
89
+ - `--c-thinking` becomes `--c-agent`.
90
+ - **Kinds carry no colour.** Type icons use `--c-muted`; the glyph tells the kind, plus the kind
91
+ word on zoomed-out tiles (Zoom board). Minimap rects and group-chip kind dots are neutral, and
92
+ only meaning colours mark them (pinned, agent at work). `KIND_COLOR`, `--kind-accent` and
93
+ `--c-purple` go. Nine kind hues cannot all stay clear of ten meaning colours in nine themes,
94
+ and every hue a kind borrows is one a meaning loses.
95
+ - **Writers are told apart by name and initial, not hue.** An agent writer's cursor, avatar and
96
+ activity line use `--c-agent`, a subagent's `--c-subagent`, a human's `--c-text-soft`;
97
+ `WRITER_PALETTE` goes. Two agents on one board share the violet and differ by label.
98
+ - The frame host hands embedded viewers the same tokens, replacing `--color-*`.
99
+ - **Decided 2026-10-05:** `--c-subagent` stays as its own meaning colour, a fuchsia beside the
100
+ agent violet (`#E879F9` on dark themes, `#A83BB0` on Daylight and Sepia, `#ff99ff` on High
101
+ contrast) instead of a second cyan next to the pin blue. It reads as agent work, is told
102
+ apart from the main agent at a glance, and is held apart from every other meaning colour by
103
+ the token test.
104
+ - **Checks:** a token test that keeps meaning colours apart from each other and from the palette,
105
+ with at least 3:1 contrast on `--c-panel`, in all nine themes; a check that rejects raw hex
106
+ outside the theme files; a check that every control computes `--font-ui`.
107
+
108
+ ## The drawings
109
+
110
+ Each `.dc.html` file is one artboard; `canvas.json` is the canvas layout (positions,
111
+ titles, notes). They render in the Design canvas (they need its runtime); in the repo
112
+ they are read as source. When an artboard changes in the canvas, re-export it here in
113
+ the same change.
114
+
115
+ | File | Shows | Wave |
116
+ |---|---|---|
117
+ | [`Before.dc.html`](design/Before.dc.html) | The demo board as it renders today | — |
118
+ | [`Main.dc.html`](design/Main.dc.html), [`Light.dc.html`](design/Light.dc.html) | The same board with the foundation applied, dark and light | 0 |
119
+ | [`Tokens.dc.html`](design/Tokens.dc.html) | Typeface, type scale, radii, elevation, one meaning per colour | 0 |
120
+ | [`TokenSystem.dc.html`](design/TokenSystem.dc.html) | The token system today vs proposed: palette, meaning, scale | 0 |
121
+ | [`Themes.dc.html`](design/Themes.dc.html) | Meaning colours across all nine themes, proposed names | 0 |
122
+ | [`Chrome.dc.html`](design/Chrome.dc.html) | Rail at 690 px, node header (expand and context pin always visible, the rest under ⋯), section headings, floating chrome | 0 |
123
+ | [`Context.dc.html`](design/Context.dc.html) | Nodes in context: every state, the count chip, the command bar | 0–1 |
124
+ | [`NearPin.dc.html`](design/NearPin.dc.html) | Near a pin: the dotted “near” chip on a pinned node's unpinned neighbours, tethers on hover, the moment of change, combinations, 600 px; ships with the brief carrying neighbours (title + summary, reason “near <pin>”) | 1 |
125
+ | [`AgentContext.dc.html`](design/AgentContext.dc.html) | What the agent did: read, created, edited, suggested, out of date; lens and receipt | 1 |
126
+ | [`BoardPins.dc.html`](design/BoardPins.dc.html) | Board pins: where you pin a board, the tiered brief, the context chip across boards | 1 |
127
+ | [`Home.dc.html`](design/Home.dc.html) | Home — Folders: library tree, pinned boards, README, board details | 1 |
128
+ | [`HomeMap.dc.html`](design/HomeMap.dc.html), [`HomeGraph.dc.html`](design/HomeGraph.dc.html) | Home — Map and Graph views of the library | 2 |
129
+ | [`HomeTeam.dc.html`](design/HomeTeam.dc.html) | Home on a team server: workspace and personal libraries, per-user pins, moving a board into the workspace, locked links (look ahead) | Later (Part 3, step 5) |
130
+ | [`Relations.dc.html`](design/Relations.dc.html) | Relations on a research board, today vs proposed; edge anatomy; relation inks | 3 |
131
+ | [`Flows.dc.html`](design/Flows.dc.html) | Data lineage with staleness, what a finding rests on, opening a group | 3, 5 |
132
+ | [`Uses.dc.html`](design/Uses.dc.html) | One relation layer for research, dependencies, decisions and agent flows | 3 |
133
+ | [`Types1.dc.html`](design/Types1.dc.html)–[`Types3.dc.html`](design/Types3.dc.html) | Every node type, today vs proposed | 0, 4 |
134
+ | [`Zoom.dc.html`](design/Zoom.dc.html) | Fit-all readability when frames are unmounted | 4 |
135
+ | [`Pane600.dc.html`](design/Pane600.dc.html) | Home and a board at the narrow end, 600 px | all |
136
+ | [`Vision.dc.html`](design/Vision.dc.html) | How each design area maps to the vision, and the open decisions | — |
137
+
138
+ ## Principles
139
+
140
+ - **One meaning per colour, the same in every theme.** Blue = in the agent's
141
+ context (pinned, by you or the agent). Violet = what the agent did (read, created,
142
+ edited, suggested). Amber = needs a look (warning, stale, out of date). Relation
143
+ inks (supports, contradicts, cites, derived from, informs…) never reuse those
144
+ three. Each theme keeps its own surfaces and accent; the meaning colours keep
145
+ their hue and are tuned only for lightness. A token test keeps them apart in all
146
+ nine themes.
147
+ - **Context pins at two levels, one look.** A card pin sends that card's content; a board
148
+ pin brings the whole board into the agent's working set, from anywhere in the library
149
+ ([vision move 0a](product-vision-2026-09.md#0a-board-pins-a-working-set-across-boards-sm)).
150
+ Both use the outline pin (not in context) and the filled blue badge (in context), and the
151
+ context chip counts both: "4 cards · 3 boards in context".
152
+ - **Every mark has a glyph and a word**, so it reads without colour, and explains
153
+ itself on hover.
154
+ - **One font and one scale.** IBM Plex Sans and Mono, bundled with the app (no CDN);
155
+ every control inherits the font. Type 11/12/13/15/20; radii 6/10/14/pill; two
156
+ shadow levels. No glow is used as elevation.
157
+ - **Quiet chrome, with expand and the context pin always in reach.** Every node header
158
+ shows its kind, title, expand (`⤢`, Enter — on the types that expand: markdown,
159
+ mcp-app, webpage, json-render, graph, context, ledger, file, diff, mermaid, image,
160
+ html) and context pin: an outline pin in a circle when the node is not in context
161
+ (click or `P` to add), the filled blue badge when it is (click to remove), always in
162
+ the same place. The other controls (ask agent, open in new tab, collapse, set as
163
+ README, close) appear on hover under ⋯ and ×. Section labels are a heading
164
+ display of the markdown node. Rarely used tools live in one settings menu on the rail.
165
+ - **Relations are the edge layer, not node types.** Design work adds no node type;
166
+ the vision's own `ask` node (move 3) is separate.
167
+ - **Any size, from a side pane to a full window.** The workbench lives in narrow agent-host
168
+ panes, the resizable side browser in the Claude desktop app, and full browser windows. Every
169
+ drawing shows the narrow end (600 px) and a full window before it is built, and the build
170
+ is checked at 600, 1024 and 1920 px (proposal board
171
+ "At 600 px" for the narrow end). Decided 2026-10-05, replacing the single 600 px reference.
172
+
173
+ ## Build plan
174
+
175
+ Each wave pairs a vision move with the design that shows it. A wave is done only
176
+ when both halves pass its check. Status reflects 0.7.0 (see the vision's
177
+ implementation table).
178
+
179
+ | Wave | Vision | Function (build) | Design (proposal boards) | Done when |
180
+ |---|---|---|---|---|
181
+ | **0. Foundation** — ships alone, first | Moves 5, 9, 10 | Bundle Plex; controls inherit the font; type/radius/shadow tokens; meaning tokens per theme; theme renames (`dark` → Harbor, `light` → Daylight, neutral; no aliases); pin style that survives selection and attention; violet header bar replaces amber halos and focus fields | Today / Proposed, Tokens, Themes, Rail and node header, Nodes in context | No control renders in a fallback font; token test passes for all nine themes; reference cases pass at 600, 1024 and 1920 px; demo board regenerated |
182
+ | **1. Context made visible** — next batch, with the curation evaluation | Part 1 bet, moves 0a, 2, 7 | Surface `context_reads` (already recorded since 0.7.0: who read, which nodes were delivered) per node and per board as read / not read / changed since read, using revisions; who pinned; activity lens and session receipt from presence activity and revisions; board pins (`board_pins`, tiered brief, `canvas_board pin/unpin`) and Home's pinned-boards section | What the agent did with context, Nodes in context, Board pins, Home (Folders) | From the board alone, a person can answer "did the agent read what I pinned, and is its copy current?" The evaluation in `docs/evals/` reads the same data |
183
+ | **2. Connected memory views** — next batch, remaining wiki/graph work | Moves 0, 14 | Board map projection (generated like the code graph), wiki links, direct cross-board card edges, previous-board chains | Home Map, Home Graph | Map and Graph are generated from the library; orphans visible; works at 600 px and in a full window |
184
+ | **3. Relations layer** | Moves 0, 1, 13 | Edge `reason` and an open `kind` with a per-board vocabulary; relations carried in the brief's text form; "derived from" with staleness from provenance and recipes; relation queries for the inspector | Relations, Lineage, One relation layer | Hovering a node explains each link; a changed source turns downstream nodes amber; the agent's brief includes relations |
185
+ | **4. Frame host and viewers** | Moves 5, 9 | One frame host passing the full theme tokens; json-render defaults to the canvas theme with an opt-out for design experiments; zoomed-out cards for unmounted frames | Node types 1–3, Zoomed out | No viewer draws its own background; 20 portals mount no more frames than none; fit-all is readable |
186
+ | **5. Agent output and groups as pages** | Moves 3, 0 | Work items, gates and asks as nodes; make board / inline board; opening a group keeps its outside links | One relation layer (agent flow), Lineage (open a group) | Agent output is styled by the same shell and tokens; a group opened as a board shows its outside stubs |
187
+ | **Later — share ladder** | Part 3 | Read-only link, comments, second writer; at step 5, workspace and personal libraries with per-user pins | HomeTeam (look ahead): Home with two roots (Workspace, Personal), your pins across both, an explicit move with its effects spelled out, locked cards for boards a viewer cannot read | Exports and shared views inherit the tokens; a viewer sees the same meaning colours; nothing personal crosses into the workspace without an explicit share or move |
188
+
189
+ **Folders and portals (decided 2026-10-06).** Home uses folders as the visible hierarchy (where a
190
+ board lives, one place); portals and links are how boards relate: a many-to-many graph the agent
191
+ navigates through links and backlinks on `canvas://boards`, and that Map and Graph draw. This
192
+ replaces the review's portal-derived levels as the navigation tree. Where a board's folder and its
193
+ links disagree, the Map shows it (folders as regions) rather than forcing them to match.
194
+
195
+ ## Definition of done for any design work
196
+
197
+ 1. It is drawn in the proposal canvas first, at 600 px and at full-window width.
198
+ 2. It is built to the drawing, with the function it shows (see the plan above).
199
+ 3. It passes in all nine themes and at the three Chromium widths, 600, 1024 and 1920 px.
200
+ 4. It touches no meaning colour except through the meaning tokens.
201
+ 5. The demo board is regenerated (`bun run scripts/generate-demo-board.ts`) and
202
+ `tests/unit/demo.test.ts` passes.
203
+ 6. User-facing docs (`docs/node-types.md`, `docs/http-api.md`, `docs/mcp.md`) and the
204
+ skills reflect it.
205
+
206
+ ## History
207
+
208
+ - **Rail-chrome v2 (August 2026).** The rail, top bar, presence layer, session panel,
209
+ command bar, external steering and canvas-quality waves shipped from the design
210
+ handoff that lived in `design/rail-chrome-v2/`. That folder was retired on
211
+ 2026-10-04: its presence contract now lives in
212
+ [`ax-state-contract.md`](ax-state-contract.md#agent-presence-contract), this file
213
+ holds the design direction, and the mockups and plan remain in git history
214
+ (`git log -- design/`).
@@ -28,7 +28,7 @@ optional; defaults are listed.
28
28
 
29
29
  | Variable | Read by | Effect |
30
30
  |---|---|---|
31
- | `PMX_CANVAS_THEME` | server | Startup theme: `dark` (default), `light`, `high-contrast`, `midnight`, `sepia`, `arctic`, `ember`, `forest`, `volt`. |
31
+ | `PMX_CANVAS_THEME` | server | Startup theme: `harbor` (default), `daylight`, `high-contrast`, `midnight`, `sepia`, `arctic`, `ember`, `forest`, `volt`. |
32
32
  | `AMP_ORB` | server | Set by Amp orb services. When present, the served canvas page tells the browser it is running in an orb, which skips the iframe embed probe, forces `srcdoc` surface rendering when embedded (the orb portal blocks `src`-URL child iframes and the probe is unreliable there), and defaults the event transport to polling instead of SSE (the portal proxy buffers streams; `?transport=sse` overrides for diagnosis). |
33
33
  | `PORT` | server | Portal-assigned service port, honored ONLY when `AMP_ORB` is also set (Amp orbs) — after `--port`, `PMX_WEB_CANVAS_PORT`, and `PMX_CANVAS_PORT`, before the 4313 default. A stray `PORT` in normal shells never changes the port. |
34
34
  | `PMX_CANVAS_DISABLE_BROWSER_OPEN` | server | `1` suppresses auto-opening the browser (used by tests/CI). |
package/docs/http-api.md CHANGED
@@ -3,6 +3,13 @@
3
3
  REST endpoints for all canvas operations + an SSE event stream. Works from
4
4
  any language. Default base URL: `http://localhost:4313`.
5
5
 
6
+ `GET /health` includes `contracts: ["pmx-embedded-workbench-v4"]` when the daemon
7
+ enforces `expectedBoardId` and `expectedContentRevision` preconditions, bulk-write
8
+ fences/locks using validated geometry and affected automatic parent frames only,
9
+ and the `allowExternalMcp: false` operation restriction (including
10
+ deferred backend rehydration). The experimental MCP App requires this contract
11
+ when attaching to a daemon; package version equality alone is insufficient.
12
+
6
13
  A non-empty request body that is not valid JSON returns
7
14
  `400 { "ok": false, "error": "Malformed JSON body." }` on every route; empty
8
15
  bodies are treated as an empty request. As of 0.4.0 every error response is a
@@ -215,7 +222,10 @@ curl http://localhost:4313/api/canvas/state
215
222
  # Search the library (omit scope for the active board)
216
223
  curl "http://localhost:4313/api/canvas/search?q=auth&limit=10&scope=library"
217
224
 
218
- # Validate the current layout
225
+ # Validate the current layout. Reports collisions, groupFrameOverlaps (a group
226
+ # frame covering a node that is not one of its members, nested members
227
+ # included — e.g. after a group auto-fits around enlarged children),
228
+ # containmentViolations, missingEdgeEndpoints, and advisory sizeWarnings.
219
229
  curl http://localhost:4313/api/canvas/validate
220
230
 
221
231
  # Inspect running-server schemas
@@ -276,6 +286,14 @@ switch command: if it no longer matches the active board, the request returns
276
286
  `409` without creating a node or opening a board from Home. The file-drop UI
277
287
  uses this to prevent delayed uploads from landing on a different board.
278
288
 
289
+ Operation-registry requests may also carry `expectedBoardId` (a board ID or
290
+ null for Home). A mismatch returns `409` before auto-opening a board or running
291
+ the operation. This is a precondition, not a board switch or permission grant.
292
+ `PATCH /api/canvas/node/:id` accepts optional `expectedContentRevision`, matched
293
+ against the node's top-level `contentRevision`; stale content returns `409`
294
+ without changing the node. Conditional edits cannot request webpage refresh.
295
+ The private MCP App uses these guards to reject stale board actions and note drafts.
296
+
279
297
  Mermaid nodes accept `data: { "fit": "contain" | "none" }` on both
280
298
  `POST /api/canvas/node` and `PATCH /api/canvas/node/:id`. The default, `contain`,
281
299
  shrinks overflowing diagrams without changing aspect ratio; `none` keeps 100%
@@ -364,6 +382,12 @@ curl -X POST http://localhost:4313/api/canvas/viewport \
364
382
  -H "Content-Type: application/json" \
365
383
  -d '{"x":64,"y":96,"scale":0.8}'
366
384
 
385
+ # Centre world point (400, 300) in the visible canvas area at zoom 0.8
386
+ # (omit scale to keep the current zoom). Responds with the resulting viewport.
387
+ curl -X POST http://localhost:4313/api/canvas/viewport \
388
+ -H "Content-Type: application/json" \
389
+ -d '{"center":{"x":400,"y":300},"scale":0.8}'
390
+
367
391
  # Fit selected nodes (omit nodeIds to fit the whole canvas)
368
392
  curl -X POST http://localhost:4313/api/canvas/fit \
369
393
  -H "Content-Type: application/json" \
@@ -377,17 +401,30 @@ curl -X POST http://localhost:4313/api/canvas/focus \
377
401
 
378
402
  Viewport `x`/`y`, fit `width`/`height`, and fit padding are interpreted against
379
403
  the **canvas area**, excluding the tool rail, top bar, and other surrounding
380
- page chrome—not the full browser window. Camera, fit, and focus-only requests
404
+ page chrome—not the full browser window.
405
+
406
+ `{x, y, scale}` is the transform of the canvas world inside the canvas area:
407
+ a world point `(wx, wy)` appears at canvas-area point `(wx * scale + x,
408
+ wy * scale + y)`. The canvas area starts right of the 52 px tool rail and below
409
+ the 44 px top bar in the default layout, so to put world `(wx, wy)` at browser
410
+ window point `(sx, sy)` send `x = sx - 52 - wx * scale` and
411
+ `y = sy - 44 - wy * scale`. `center` does this for the middle of the canvas
412
+ area, using the size the open workbench last reported (1440×900 when no tab has
413
+ reported one); it cannot be combined with `x`/`y`. Floating overlays inside the
414
+ canvas area (command bar, minimap) are not subtracted. Camera, fit, and focus-only requests
381
415
  do not create or refresh agent writer presence. Use the explicit presence API
382
416
  when an agent should publish a cursor or `focusNodeId`.
383
417
 
384
418
  ## Pins
385
419
 
386
420
  ```bash
387
- # Pin nodes for agent context
421
+ # Pin nodes for agent context (mode: set | add | remove; default set). Each newly
422
+ # pinned node records who pinned it and when; an optional reason (max 280 chars)
423
+ # is kept with it and shown to the human on hover. Pins that stay keep their
424
+ # original attribution.
388
425
  curl -X POST http://localhost:4313/api/canvas/context-pins \
389
426
  -H "Content-Type: application/json" \
390
- -d '{"nodeIds":["node-1","node-2"]}'
427
+ -d '{"nodeIds":["node-1","node-2"],"mode":"add","reason":"the chart the finding rests on"}'
391
428
 
392
429
  # Get pinned context
393
430
  curl http://localhost:4313/api/canvas/pinned-context
@@ -400,7 +437,10 @@ curl 'http://localhost:4313/api/canvas/context?consumer=codex&budget=16000'
400
437
  `consumer` is optional. Supply a stable unique value only for a durable,
401
438
  board-scoped cursor; without `consumer` or `since`, each request starts from the
402
439
  beginning. An explicit `since` never advances a cursor. The brief prioritizes
403
- pins, then relevant current-board changes and asks, then compact linked or
440
+ pins, then each pin's neighbours (reason `near`: up to five unpinned cards within
441
+ 600 px, nearest first, as title + a short summary with `near: { pinNodeId,
442
+ pinTitle }`; pin a card to send its full content), then relevant current-board
443
+ changes and asks, then compact linked or
404
444
  same-folder board context. Every item carries source board/card IDs. If card text
405
445
  is clipped, use those IDs with the board/node read route rather than guessing
406
446
  from the excerpt. Reading inactive boards does not open them.
@@ -467,11 +507,20 @@ curl "http://localhost:4313/api/canvas/ax/timeline?limit=50"
467
507
  # automatically; the workbench's own reads are not.
468
508
  curl "http://localhost:4313/api/canvas/ax/context-reads?limit=50"
469
509
 
510
+ # Read status — per node on a board (default: the open board), the latest agent
511
+ # read that delivered its content: when, who, and the content revision it had.
512
+ # A node whose current contentRevision is newer was changed since it was read.
513
+ # pins maps each pinned node with a known pinner to { pinnedBy, pinnedAt, reason }.
514
+ curl "http://localhost:4313/api/canvas/ax/context-status?board=<board-id>"
515
+
470
516
  # Context reads — a proxy (an MCP server attached to this daemon, a host adapter)
471
517
  # records the read its agent actually made. Its own fetches send
472
518
  # `x-pmx-proxied-read: 1` so they are not recorded twice.
473
519
  # Pass boardId captured at read time (null for Home); omitting it uses the
474
520
  # currently open board. MCP bytes measure the final formatted text response.
521
+ # readNodeIds lists every serialized node in what the agent received (pinned or
522
+ # not); the server keeps those on the board and stamps their revisions. Omitted,
523
+ # it defaults to deliveredNodeIds.
475
524
  curl -X POST http://localhost:4313/api/canvas/ax/context-reads \
476
525
  -H "Content-Type: application/json" \
477
526
  -d '{"channel":"adapter","resource":"copilot:prompt-context","consumer":"copilot","pinnedNodeIds":["node-1"],"deliveredNodeIds":["node-1"],"bytes":2048}'
@@ -530,7 +579,7 @@ without the workbench's own `x-pmx-workbench: 1` marker) registers its caller
530
579
  as a `tooling` writer, the activity feed drives attach/detach and phase
531
580
  (`session-start`, `session-end`, `tool-start`, `tool-result`), and adapters
532
581
  with richer hooks can set a phase, cursor, or focus explicitly. Writers fade
533
- 90 s after their last write; attached sessions expire after 30 min of quiet
582
+ 5 min after their last write; attached sessions expire after 30 min of quiet
534
583
  without a `session-end`.
535
584
 
536
585
  ```bash
@@ -611,20 +660,30 @@ the stream carries one `agent-session-ended` frame:
611
660
  ```json
612
661
  { "label": "Copilot", "parentAgentId": null, "endedAt": "…", "endedBy": "agent",
613
662
  "unchanged": false, "counts": { "items": 4, "done": 3, "cancelled": 1, "rejected": 0, "held": 0 },
614
- "snapshot": { "id": "…", "name": "Before session · Copilot · 14:00" } }
663
+ "snapshot": { "id": "…", "name": "Before session · Copilot · 14:00" },
664
+ "context": { "read": [{ "id": "…", "title": "Brief" }], "pinned": [{ "id": "…", "title": "Chart", "reason": "…" }],
665
+ "created": [], "edited": [], "changedSinceRead": [{ "id": "…", "title": "Brief" }] } }
615
666
  ```
616
667
 
668
+ `context` is what the session did with the board's context: nodes whose content an
669
+ agent read since the session attached, pins the agent made (with their reason), nodes
670
+ it created and edited (against the pre-session snapshot), and pinned nodes changed
671
+ after their latest read. A session that only read or pinned is not `unchanged`.
672
+
617
673
  `items`/`done` count the work items on the board; cancelled items, rejected
618
674
  gates, and held gates have separate counts. `snapshot` is null when the board was
619
675
  empty at attach or unchanged at end (the redundant snapshot is removed).
620
676
  The browser shows a receipt only for a changed top-level session: `unchanged: true`
621
- and worker endings (`parentAgentId` set) never open or replace a popup. An empty
677
+ and worker endings (`parentAgentId` set) never open, replace, or extend a popup —
678
+ the orchestrator's receipt covers the board since it attached. An empty
622
679
  board that stays empty is unchanged too. This only filters the popup: session-end
623
680
  events, timeline records, and changed-session snapshots remain available.
624
- There is at most one receipt, replaced by the next qualifying ending and kept until
625
- dismissed so restore/diff actions do not disappear on a timer. Its *View
626
- diff* is `GET /api/canvas/snapshots/<id>/diff` against that snapshot, and
627
- restoring the snapshot undoes the session. Adapters should end their session
681
+ There is at most one receipt. Qualifying endings that arrive while it is up merge
682
+ into it ("3 sessions ended", their labels, the union of their context); a merged
683
+ receipt has no *View diff* and points to History, which keeps each session's
684
+ snapshot. The receipt is kept until dismissed so restore/diff actions do not
685
+ disappear on a timer. A single session's *View diff* is `GET /api/canvas/snapshots/<id>/diff` against that
686
+ snapshot, and restoring the snapshot undoes the session. Adapters should end their session
628
687
  explicitly so the human gets the receipt promptly rather than after the idle
629
688
  expiry. The browser's *Start agent session* button is this same endpoint
630
689
  (`source: "browser"`, `attached: true`) — subsequent agent-less writes are
@@ -786,7 +845,10 @@ registry's kind validation. Pick the operation by method + path:
786
845
  # (default 8000, max 60000), so cleanup is optional.
787
846
  curl -X POST http://localhost:4313/api/canvas/ax/intent \
788
847
  -H "Content-Type: application/json" \
789
- -d '{"kind":"create","position":{"x":400,"y":300},"nodeType":"markdown","label":"Add evidence","reason":"collecting run logs"}'
848
+ -d '{"kind":"create","nodeType":"markdown","label":"Add evidence","reason":"collecting run logs"}'
849
+ # A create may omit "position": the ghost forms where the canvas will auto-place
850
+ # the node (returned in intent.position), and a create linked by intentId with no
851
+ # x/y lands there. Pass "position" only when you know where the node goes.
790
852
 
791
853
  # Update the ghost (position/label/reason/confidence/ttlMs; vetoed:true dissolves
792
854
  # it AND poisons the id so a later linked settle is rejected)
@@ -834,7 +896,10 @@ all consume this stream. Auto-reconnect with exponential backoff.
834
896
  `agent-presence` frames carry the full presence snapshot (the same body as
835
897
  `GET /api/canvas/ax/presence`) on every change, including TTL expiry — a
836
898
  client never needs its own expiry timer. `agent-session-ended` carries the
837
- session receipt (see [Agent presence](#agent-presence)).
899
+ session receipt (see [Agent presence](#agent-presence)). `context-status-changed`
900
+ (`{ boardId }`) says an agent read landed; the workbench refetches
901
+ `GET /api/canvas/ax/context-status`. It is never an MCP notification, so a read
902
+ cannot cause more reads.
838
903
 
839
904
  Every frame's payload is wrapped in an envelope that sets `sessionId` (the
840
905
  workbench session, used for reconnect/reload detection) and `timestamp` —
@@ -869,7 +934,7 @@ override the orb default).
869
934
 
870
935
  `/workbench?theme=<name>` gives that browser session its own theme without
871
936
  touching the server-global theme other clients see — embedding hosts use it to
872
- match their chrome (the bundled Copilot extension opens `?theme=light`).
937
+ match their chrome (the bundled Copilot extension opens `?theme=daylight`).
873
938
  `?theme=auto` follows the host's `prefers-color-scheme` live. Picking a theme
874
939
  from the toolbar ends the override and saves globally as usual.
875
940