pmx-canvas 0.9.3 → 0.11.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 (237) hide show
  1. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  2. package/.github/extensions/pmx-canvas/tools.json +9418 -2016
  3. package/CHANGELOG.md +54 -1
  4. package/dist/canvas/chunks/abnfDiagram-N423BO3Z-p8dtp6va.js +1 -0
  5. package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +1 -0
  6. package/dist/canvas/chunks/architectureDiagram-T3A2C74G-5jhmxrzf.js +36 -0
  7. package/dist/canvas/chunks/blockDiagram-VBNYF7ZC-pw1fa9yb.js +132 -0
  8. package/dist/canvas/chunks/c4Diagram-5PPSVZJV-x07nq0tt.js +10 -0
  9. package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +1 -0
  10. package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +1 -0
  11. package/dist/canvas/chunks/cose-bilkent-JH36ORCC-nm39npq0.js +1 -0
  12. package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +1 -0
  13. package/dist/canvas/chunks/cynefinDiagram-MW4NZA55-vdxrwahz.js +62 -0
  14. package/dist/canvas/chunks/dagre-VZM6K2ZE-bqdm7wpd.js +4 -0
  15. package/dist/canvas/chunks/diagram-7IWD3JNH-gs5gpr93.js +30 -0
  16. package/dist/canvas/chunks/diagram-B4RE2ZJO-cgwjg47s.js +3 -0
  17. package/dist/canvas/chunks/diagram-LBJQPF4R-w3vahpav.js +24 -0
  18. package/dist/canvas/chunks/diagram-Q27KOJAE-42837z6h.js +24 -0
  19. package/dist/canvas/chunks/diagram-UB23O5K3-4fz83q59.js +41 -0
  20. package/dist/canvas/chunks/ebnfDiagram-BXEA7PRR-zphtgegj.js +1 -0
  21. package/dist/canvas/chunks/erDiagram-JOGREHBK-zvbbdxp4.js +85 -0
  22. package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +1 -0
  23. package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +1 -0
  24. package/dist/canvas/chunks/ganttDiagram-PKOTCBZU-282jzycm.js +292 -0
  25. package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +1 -0
  26. package/dist/canvas/chunks/gitGraphDiagram-DS77QQ5N-vqqy6p1b.js +106 -0
  27. package/dist/canvas/chunks/index-0rk4gp1g.js +2 -0
  28. package/dist/canvas/chunks/index-194m3hc3.js +2 -0
  29. package/dist/canvas/chunks/index-1r72hdzb.js +301 -0
  30. package/dist/canvas/chunks/index-353m7b0p.js +2 -0
  31. package/dist/canvas/chunks/index-3cy1bzwh.js +2 -0
  32. package/dist/canvas/chunks/index-5tv8v1hk.js +1 -0
  33. package/dist/canvas/chunks/index-6ceya556.js +332 -0
  34. package/dist/canvas/chunks/index-6qmsnxwy.js +4 -0
  35. package/dist/canvas/chunks/index-d3b3gqe9.js +2 -0
  36. package/dist/canvas/chunks/index-fjtdggjn.js +2 -0
  37. package/dist/canvas/chunks/index-g3pwnqeb.js +2 -0
  38. package/dist/canvas/chunks/index-gf4e0et4.js +2 -0
  39. package/dist/canvas/chunks/index-hj99nz1f.js +1 -0
  40. package/dist/canvas/chunks/index-nprm96xp.js +16 -0
  41. package/dist/canvas/chunks/index-qj8fxwsf.js +2 -0
  42. package/dist/canvas/chunks/index-r2fyc9w1.js +89 -0
  43. package/dist/canvas/chunks/index-rft2hcz5.js +2 -0
  44. package/dist/canvas/chunks/index-rh7nv68n.js +2 -0
  45. package/dist/canvas/chunks/index-s4j678gj.js +2 -0
  46. package/dist/canvas/chunks/index-tb1wf525.js +179 -0
  47. package/dist/canvas/chunks/index-vv56h019.js +2 -0
  48. package/dist/canvas/chunks/index-w2mra17t.js +207 -0
  49. package/dist/canvas/chunks/index-wgv7en21.js +232 -0
  50. package/dist/canvas/chunks/index-yw8h9vha.js +157 -0
  51. package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +1 -0
  52. package/dist/canvas/chunks/infoDiagram-6WML65LV-v1yvggqj.js +2 -0
  53. package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +70 -0
  54. package/dist/canvas/chunks/journeyDiagram-NVQOT4AX-zjcffveh.js +139 -0
  55. package/dist/canvas/chunks/kanban-definition-27J2QSJJ-dakqtfp1.js +89 -0
  56. package/dist/canvas/chunks/katex-dmg8k4zm.js +257 -0
  57. package/dist/canvas/chunks/mermaid.core-j2geswrd.js +12 -0
  58. package/dist/canvas/chunks/mindmap-definition-FAOFIHXS-142xhr3k.js +96 -0
  59. package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +1 -0
  60. package/dist/canvas/chunks/pegDiagram-VL7TDLO6-5wvk7vsd.js +1 -0
  61. package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +1 -0
  62. package/dist/canvas/chunks/pieDiagram-7S7Q4E2Y-ztdhwycc.js +39 -0
  63. package/dist/canvas/chunks/quadrantDiagram-CIZ2JOQS-c5p9gmg6.js +7 -0
  64. package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +1 -0
  65. package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +1 -0
  66. package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +1 -0
  67. package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +1 -0
  68. package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +1 -0
  69. package/dist/canvas/chunks/railroadDiagram-AXF67PYL-xkmqkjwf.js +1 -0
  70. package/dist/canvas/chunks/requirementDiagram-LRYGKXZP-9wm2dyaw.js +84 -0
  71. package/dist/canvas/chunks/sankeyDiagram-W5VNT64P-2w19etgb.js +40 -0
  72. package/dist/canvas/chunks/sequenceDiagram-SI44F4Z6-0jn0vwmz.js +162 -0
  73. package/dist/canvas/chunks/sizeCapture-X5ZJPWSS-x75epwdq.js +1 -0
  74. package/dist/canvas/chunks/stateDiagram-OKZ733FA-schjpy6d.js +1 -0
  75. package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +1 -0
  76. package/dist/canvas/chunks/swimlanes-SLNWSIFB-gkw4xce3.js +1 -0
  77. package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +8 -0
  78. package/dist/canvas/chunks/timeline-definition-Z64GVDOM-x7nkcy01.js +120 -0
  79. package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +1 -0
  80. package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +1 -0
  81. package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +34 -0
  82. package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +1 -0
  83. package/dist/canvas/chunks/wardleyDiagram-T6FBY63Y-t1j52t9r.js +78 -0
  84. package/dist/canvas/chunks/xychartDiagram-ELKLHX3M-38t16pme.js +7 -0
  85. package/dist/canvas/global.css +389 -229
  86. package/dist/canvas/index.js +141 -211
  87. package/dist/canvas/mcp-app.html +4205 -554
  88. package/dist/canvas/mermaid-entry.js +338 -338
  89. package/dist/canvas/surface-theme.css +6 -6
  90. package/dist/json-render/index.css +1 -1
  91. package/dist/json-render/index.js +49 -49
  92. package/dist/types/client/canvas/BarHint.d.ts +5 -3
  93. package/dist/types/client/canvas/CanvasNode.d.ts +0 -11
  94. package/dist/types/client/canvas/EdgeLayer.d.ts +1 -2
  95. package/dist/types/client/canvas/auto-fit.d.ts +4 -5
  96. package/dist/types/client/mermaid-entry.d.ts +1 -2
  97. package/dist/types/client/mermaid-render.d.ts +1 -0
  98. package/dist/types/client/nodes/BoardNode.d.ts +1 -1
  99. package/dist/types/client/nodes/ExtAppFrame.d.ts +7 -90
  100. package/dist/types/client/nodes/McpAppNode.d.ts +1 -25
  101. package/dist/types/client/nodes/MermaidNode.d.ts +1 -8
  102. package/dist/types/client/nodes/ViewerFrame.d.ts +29 -0
  103. package/dist/types/client/nodes/ViewerHost.d.ts +6 -0
  104. package/dist/types/client/nodes/iframe-document-url.d.ts +3 -9
  105. package/dist/types/client/nodes/primitive-copy.d.ts +5 -0
  106. package/dist/types/client/nodes/use-iframe-content-height.d.ts +1 -1
  107. package/dist/types/client/nodes/use-surface-frame.d.ts +4 -6
  108. package/dist/types/client/nodes/viewer-runtime-state.d.ts +2 -1
  109. package/dist/types/client/state/canvas-store.d.ts +3 -1
  110. package/dist/types/client/state/intent-bridge.d.ts +1 -0
  111. package/dist/types/client/state/presentation.d.ts +17 -6
  112. package/dist/types/json-render/server.d.ts +2 -1
  113. package/dist/types/mcp/canvas-access.d.ts +1 -12
  114. package/dist/types/mcp/resource-subscriptions.d.ts +7 -0
  115. package/dist/types/mcp/server.d.ts +2 -0
  116. package/dist/types/server/ax-wait.d.ts +1 -0
  117. package/dist/types/server/canvas-operations.d.ts +1 -0
  118. package/dist/types/server/canvas-state.d.ts +2 -0
  119. package/dist/types/server/index.d.ts +11 -6
  120. package/dist/types/server/operations/http.d.ts +10 -0
  121. package/dist/types/server/operations/index.d.ts +2 -2
  122. package/dist/types/server/operations/invoker.d.ts +4 -3
  123. package/dist/types/server/operations/mcp-contracts.d.ts +20 -0
  124. package/dist/types/server/operations/mcp.d.ts +2 -0
  125. package/dist/types/server/operations/ops/batch.d.ts +1 -0
  126. package/dist/types/server/operations/ops/boards.d.ts +0 -1
  127. package/dist/types/server/operations/registry.d.ts +2 -2
  128. package/dist/types/server/operations/types.d.ts +12 -2
  129. package/dist/types/server/server.d.ts +2 -0
  130. package/dist/types/server/tour-control.d.ts +13 -3
  131. package/dist/types/server/webpage-node.d.ts +1 -0
  132. package/dist/types/shared/ax-surface-protocol.d.ts +0 -9
  133. package/dist/types/shared/tour.d.ts +8 -0
  134. package/docs/api-reference.md +2 -2
  135. package/docs/cli.md +1 -1
  136. package/docs/design/Chrome.dc.html +32 -6
  137. package/docs/design/Types1.dc.html +3 -2
  138. package/docs/design/Types2.dc.html +1 -1
  139. package/docs/design/Types3.dc.html +1 -1
  140. package/docs/design/Zoom.dc.html +37 -60
  141. package/docs/design/canvas.json +1 -1
  142. package/docs/design.md +169 -4
  143. package/docs/http-api.md +82 -1
  144. package/docs/mcp.md +101 -21
  145. package/docs/node-types.md +38 -3
  146. package/docs/plans/plan-009-tech-debt-backlog.md +2 -2
  147. package/docs/plans/plan-019-post-0.9-roadmap.md +402 -13
  148. package/docs/product-vision-2026-09.md +9 -0
  149. package/docs/sdk.md +5 -2
  150. package/package.json +2 -2
  151. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  152. package/plugins/pmx-canvas/plugin.json +1 -1
  153. package/plugins/pmx-canvas/runtime.json +1 -1
  154. package/plugins/pmx-canvas/tools.json +9418 -2016
  155. package/skills/pmx-canvas/SKILL.md +19 -21
  156. package/skills/pmx-canvas/references/full-reference.md +44 -6
  157. package/skills/pmx-canvas/references/installing-pmx-canvas.md +7 -7
  158. package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +3 -5
  159. package/src/client/canvas/BarHint.tsx +6 -3
  160. package/src/client/canvas/CanvasNode.tsx +34 -47
  161. package/src/client/canvas/CanvasViewport.tsx +6 -7
  162. package/src/client/canvas/ContextMenu.tsx +63 -3
  163. package/src/client/canvas/EdgeLayer.tsx +365 -169
  164. package/src/client/canvas/ExpandedNodeOverlay.tsx +1 -2
  165. package/src/client/canvas/Presentation.tsx +53 -3
  166. package/src/client/canvas/ToolRail.tsx +2 -0
  167. package/src/client/canvas/TopBar.tsx +24 -9
  168. package/src/client/canvas/auto-fit.ts +9 -11
  169. package/src/client/canvas/canvas-area.ts +1 -1
  170. package/src/client/canvas/use-focus-trap.ts +6 -1
  171. package/src/client/mermaid-entry.ts +6 -20
  172. package/src/client/mermaid-render.ts +39 -0
  173. package/src/client/nodes/AxStepControls.tsx +45 -69
  174. package/src/client/nodes/BoardNode.tsx +65 -9
  175. package/src/client/nodes/ContextNode.tsx +88 -396
  176. package/src/client/nodes/ExtAppFrame.tsx +283 -1462
  177. package/src/client/nodes/FileNode.tsx +92 -81
  178. package/src/client/nodes/HtmlNode.tsx +8 -36
  179. package/src/client/nodes/LedgerNode.tsx +31 -17
  180. package/src/client/nodes/MarkdownNode.tsx +8 -17
  181. package/src/client/nodes/McpAppNode.tsx +48 -154
  182. package/src/client/nodes/MermaidNode.tsx +80 -69
  183. package/src/client/nodes/PromptNode.tsx +12 -14
  184. package/src/client/nodes/ResponseNode.tsx +8 -6
  185. package/src/client/nodes/StatusNode.tsx +22 -7
  186. package/src/client/nodes/StatusSummary.tsx +5 -3
  187. package/src/client/nodes/TraceNode.tsx +8 -8
  188. package/src/client/nodes/ViewerFrame.tsx +262 -0
  189. package/src/client/nodes/ViewerHost.tsx +41 -0
  190. package/src/client/nodes/WebpageNode.tsx +22 -24
  191. package/src/client/nodes/iframe-document-url.ts +20 -27
  192. package/src/client/nodes/primitive-copy.ts +97 -0
  193. package/src/client/nodes/use-iframe-content-height.ts +34 -10
  194. package/src/client/nodes/use-surface-frame.ts +22 -11
  195. package/src/client/nodes/viewer-runtime-state.ts +41 -35
  196. package/src/client/state/canvas-store.ts +18 -12
  197. package/src/client/state/intent-bridge.ts +1 -0
  198. package/src/client/state/presentation.ts +135 -15
  199. package/src/client/state/sse-bridge.ts +18 -19
  200. package/src/client/theme/global.css +389 -229
  201. package/src/client/theme/surface-theme.css +6 -6
  202. package/src/json-render/charts/tufte-components.tsx +44 -2
  203. package/src/json-render/renderer/index.css +62 -2
  204. package/src/json-render/renderer/index.tsx +31 -7
  205. package/src/json-render/server.ts +3 -2
  206. package/src/mcp/canvas-access.ts +6 -47
  207. package/src/mcp/resource-subscriptions.ts +181 -0
  208. package/src/mcp/server.ts +185 -219
  209. package/src/server/ax-wait.ts +21 -6
  210. package/src/server/board-export.ts +35 -24
  211. package/src/server/canvas-geometry.ts +1 -0
  212. package/src/server/canvas-operations.ts +6 -2
  213. package/src/server/canvas-state.ts +5 -0
  214. package/src/server/demo-state.json +106 -100
  215. package/src/server/html-primitives.ts +3 -9
  216. package/src/server/html-surface.ts +8 -0
  217. package/src/server/index.ts +30 -12
  218. package/src/server/operations/http.ts +69 -21
  219. package/src/server/operations/index.ts +8 -1
  220. package/src/server/operations/invoker.ts +53 -5
  221. package/src/server/operations/mcp-contracts.ts +354 -0
  222. package/src/server/operations/mcp.ts +153 -18
  223. package/src/server/operations/ops/ax-await.ts +18 -10
  224. package/src/server/operations/ops/ax-read.ts +2 -0
  225. package/src/server/operations/ops/ax-timeline.ts +2 -2
  226. package/src/server/operations/ops/batch.ts +27 -8
  227. package/src/server/operations/ops/boards.ts +1 -3
  228. package/src/server/operations/ops/nodes.ts +18 -3
  229. package/src/server/operations/ops/viewport.ts +11 -9
  230. package/src/server/operations/registry.ts +24 -4
  231. package/src/server/operations/types.ts +16 -2
  232. package/src/server/server.ts +17 -59
  233. package/src/server/tour-control.ts +66 -6
  234. package/src/server/webpage-node.ts +9 -4
  235. package/src/shared/ax-surface-protocol.ts +0 -11
  236. package/src/shared/content-height-reporter.ts +4 -3
  237. package/src/shared/tour.ts +8 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * MCP's output is the FORMATTER projection, never the larger HTTP payload.
3
+ * `result` wraps prose/arrays without pretending they are JSON objects. Each
4
+ * operation validates its stable fields; loose payloads preserve full:true and
5
+ * extensible node/app data. No schema advertises an unconstrained root record.
6
+ */
7
+ import { z } from 'zod';
8
+ import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
9
+ export declare function operationOutputSchema(operations: string[]): z.ZodObject<{
10
+ result: z.ZodUnion<readonly [...z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>[], z.ZodObject<{
11
+ operation: z.ZodNull;
12
+ outcome: z.ZodLiteral<"error">;
13
+ data: z.ZodObject<{
14
+ message: z.ZodString;
15
+ }, z.core.$strict>;
16
+ }, z.core.$strict>]>;
17
+ }, z.core.$strict>;
18
+ export declare function structuredOperationResult(operation: string, formatted: CallToolResult): CallToolResult;
19
+ export declare function operationToolError(error: unknown): CallToolResult;
20
+ export declare function operationToolAnnotations(toolName: string): ToolAnnotations;
@@ -12,6 +12,8 @@ export interface OperationToolHost extends OperationMcpToolHost {
12
12
  recordContextRead(read: ContextReadInput): Promise<void>;
13
13
  }
14
14
  export declare function registerOperationTools(server: McpServer, getHost: () => Promise<OperationToolHost>): void;
15
+ /** Registry metadata only: never boots a canvas or records a content read. */
16
+ export declare function registerOperationHelpTool(server: McpServer): void;
15
17
  /**
16
18
  * Register composite (action-discriminated) MCP tools (plan-006). Each action
17
19
  * dispatches to a registered operation, reusing that op's `mcp.buildInput` and
@@ -5,6 +5,7 @@ export interface BatchEnvelope {
5
5
  refs: Record<string, unknown>;
6
6
  failedIndex?: number;
7
7
  error?: string;
8
+ cancelled?: boolean;
8
9
  }
9
10
  export declare const batchOperations: Operation[];
10
11
  /**
@@ -1,6 +1,5 @@
1
1
  import { type CanvasLayout } from '../../canvas-state.js';
2
2
  import { type Operation } from '../types.js';
3
- export declare const boardCatalogEpoch: `${string}-${string}-${string}-${string}-${string}`;
4
3
  export declare function boardsPayload(reloaded?: boolean): Record<string, unknown>;
5
4
  /** Read a board without opening it. Omitted board preserves the active-board contract. */
6
5
  export declare function readTargetBoard(boardId?: unknown, includeBlobs?: boolean): {
@@ -1,4 +1,4 @@
1
- import { type Operation } from './types.js';
1
+ import { type Operation, type OperationExecutionOptions } from './types.js';
2
2
  export declare function registerOperation(op: Operation): void;
3
3
  export declare function getOperation(name: string): Operation;
4
4
  export declare function listOperations(): Operation[];
@@ -12,7 +12,7 @@ export declare function getOperationEventEmitter(): OperationEventEmitter | null
12
12
  export declare function isEmitSuppressed(): boolean;
13
13
  /** Suppress this call chain's per-entry frames; the batch emits its final frame afterward. */
14
14
  export declare function runWithSuppressedEmits<T>(fn: () => Promise<T>): Promise<T>;
15
- export interface ExecuteOperationMeta {
15
+ export interface ExecuteOperationMeta extends OperationExecutionOptions {
16
16
  /**
17
17
  * Skip the synthesized auto-ghost: set by the workbench's own HTTP calls
18
18
  * (a human dragging a node is not agent activity) and by canvas.batch for
@@ -20,7 +20,17 @@ export declare class OperationError extends Error {
20
20
  readonly details?: Record<string, unknown>;
21
21
  constructor(message: string, status?: OperationErrorStatus, details?: Record<string, unknown>);
22
22
  }
23
- export interface OperationContext {
23
+ export interface OperationProgress {
24
+ progress: number;
25
+ total?: number;
26
+ message?: string;
27
+ }
28
+ /** Cooperative cancellation: completed writes are never rolled back. */
29
+ export interface OperationExecutionOptions {
30
+ signal?: AbortSignal;
31
+ onProgress?: (progress: OperationProgress) => void | Promise<void>;
32
+ }
33
+ export interface OperationContext extends OperationExecutionOptions {
24
34
  /** Emit a workbench SSE event (e.g. extra `canvas-layout-update` frames, focus, viewport). */
25
35
  emit(event: string, payload?: Record<string, unknown>): void;
26
36
  }
@@ -53,7 +63,7 @@ export interface OperationMcpToolHost {
53
63
  * restore summary).
54
64
  */
55
65
  invoker(): {
56
- invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
66
+ invoke(name: string, input: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
57
67
  };
58
68
  }
59
69
  export interface OperationMcpTool {
@@ -80,6 +80,8 @@ export interface PrimaryWorkbenchCanvasPromptRequest {
80
80
  contextNodeIds: string[];
81
81
  }
82
82
  type PrimaryWorkbenchCanvasPromptHandler = (request: PrimaryWorkbenchCanvasPromptRequest) => Promise<void>;
83
+ /** In-process invalidation observers share the same events as daemon SSE clients. */
84
+ export declare function onWorkbenchEvent(listener: (event: string) => void): () => void;
83
85
  export declare function setPrimaryWorkbenchAutoOpenEnabled(enabled: boolean): void;
84
86
  export declare function isPrimaryWorkbenchAutoOpenEnabled(): boolean;
85
87
  export declare function hasWorkbenchSubscribers(): boolean;
@@ -8,13 +8,21 @@
8
8
  *
9
9
  * This module must never import server.ts or index.ts.
10
10
  */
11
- import { type Camera, type Tour, type TourStop } from '../shared/tour.js';
11
+ import { type Camera, type Tour, type TourPresenter, type TourStop } from '../shared/tour.js';
12
12
  export type TourStep = number | 'next' | 'previous';
13
- export interface TourStepResult {
13
+ export interface TourDelivery {
14
+ catalogEpoch: string;
15
+ boardGeneration: number;
16
+ sequence: number;
17
+ }
18
+ export interface TourStepResult extends TourDelivery {
14
19
  index: number;
15
20
  total: number;
16
21
  stop: TourStop;
17
22
  }
23
+ export interface TourSuperseded {
24
+ superseded: true;
25
+ }
18
26
  export interface TourState {
19
27
  tour: Tour;
20
28
  derived: boolean;
@@ -23,7 +31,9 @@ export interface TourState {
23
31
  export declare function currentTour(): TourState;
24
32
  /** Move the shared cursor. `next`/`previous` clamp at the ends; an explicit index must exist. */
25
33
  export declare function goToTourStop(step: TourStep): TourStepResult;
26
- export declare function exitTour(): void;
34
+ export declare function goToTourStop(step: TourStep, presenter: TourPresenter | undefined): TourStepResult | TourSuperseded;
35
+ export declare function exitTour(): TourDelivery;
36
+ export declare function exitTour(presenter: TourPresenter | undefined): TourDelivery | TourSuperseded;
27
37
  /**
28
38
  * Resolve an eased camera move and store its end camera as the board viewport
29
39
  * (unrecorded). The size is the last window a browser reported, so this is the
@@ -37,6 +37,7 @@ export declare function checkWebpageHop(originScope: AddressScope, hopScope: Add
37
37
  export declare function fetchWebpageSnapshot(inputUrl: string, options?: {
38
38
  resolveHost?: ResolveHost;
39
39
  timeoutMs?: number;
40
+ signal?: AbortSignal;
40
41
  }): Promise<WebpageSnapshot>;
41
42
  export declare function getWebpageFetchErrorDetails(error: unknown): {
42
43
  message: string;
@@ -16,12 +16,3 @@ export declare const AX_SURFACE_ACK_SOURCE = "pmx-canvas-ax-ack";
16
16
  export declare const HTML_SURFACE_PUSH_SOURCE = "pmx-canvas-html-node";
17
17
  /** Tab-local JSON viewer state handoff, independent of AX capabilities. */
18
18
  export declare const JSON_VIEWER_STATE_SOURCE = "pmx-canvas-json-state";
19
- /** Ext-app boot beacon: the iframe's scripts executed (WebKit watchdog liveness). */
20
- export declare const EXT_APP_BOOT_BEACON_SOURCE = "pmx-canvas-ext-app-alive";
21
- /**
22
- * Parent→ext-app paint probe (Finding N): the parent asks the app document to
23
- * answer with a double-rAF `paint-tick` beacon. A frame whose rendering
24
- * pipeline is live answers within a frame or two; silence past the timeout is
25
- * the paint-fail signal that drives the recovery ladder.
26
- */
27
- export declare const EXT_APP_PAINT_PROBE_SOURCE = "pmx-canvas-ext-app-paint-probe";
@@ -81,6 +81,14 @@ export declare const tourSchema: z.ZodObject<{
81
81
  pullback: z.ZodOptional<z.ZodNumber>;
82
82
  }, z.core.$strip>>;
83
83
  }, z.core.$strip>;
84
+ /** Latest intent from one document, scoped to the server's board activation. */
85
+ export declare const tourPresenterSchema: z.ZodObject<{
86
+ clientId: z.ZodString;
87
+ intent: z.ZodNumber;
88
+ catalogEpoch: z.ZodString;
89
+ boardGeneration: z.ZodNumber;
90
+ }, z.core.$strip>;
91
+ export type TourPresenter = z.infer<typeof tourPresenterSchema>;
84
92
  export type Tour = z.infer<typeof tourSchema>;
85
93
  export type TourStop = z.infer<typeof tourStopSchema>;
86
94
  export type Camera = z.infer<typeof cameraSchema>;
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## MCP Server
8
8
 
9
- 24 tools: 18 action-discriminated composites (`canvas_import`, `canvas_board`, `canvas_node`, `canvas_render`, `canvas_edge`, `canvas_group`, `canvas_history`, `canvas_view`, `canvas_query`, `canvas_webview`, `canvas_app`, `canvas_snapshot`, `canvas_ax_state`, `canvas_ax_work`, `canvas_ax_gate`, `canvas_ax_timeline`, `canvas_ax_delivery`, `canvas_intent`) plus 6 standalone tools (`canvas_batch`, `canvas_pin_nodes`, `canvas_invoke_command`, `canvas_ax_interaction`, `canvas_ingest_activity`, `canvas_screenshot`). `canvas_import` is the agent half of the human-consented attachment workflow: agents list/read requested imports, submit reviewable Markdown, or report unavailable; they cannot request, cancel, or commit. The 57 legacy single-purpose tools the composites replaced were removed in v0.3.0; v0.4.0 shipped the `canvas_snapshot` composite and removed the 6 deprecated snapshot standalones. Prefer composites for new MCP calls; see `docs/mcp.md` for the authoritative tool list and the legacy-to-composite migration reference.
9
+ 25 tools: 18 action-discriminated composites (`canvas_import`, `canvas_board`, `canvas_node`, `canvas_render`, `canvas_edge`, `canvas_group`, `canvas_history`, `canvas_view`, `canvas_query`, `canvas_webview`, `canvas_app`, `canvas_snapshot`, `canvas_ax_state`, `canvas_ax_work`, `canvas_ax_gate`, `canvas_ax_timeline`, `canvas_ax_delivery`, `canvas_intent`) plus 7 standalone tools (`canvas_help`, `canvas_batch`, `canvas_pin_nodes`, `canvas_invoke_command`, `canvas_ax_interaction`, `canvas_ingest_activity`, `canvas_screenshot`). `canvas_help { op }` returns one MCP registry operation's public arguments and guidance without accessing the canvas; omit `op` to list names. `canvas_import` is the agent half of the human-consented attachment workflow: agents list/read requested imports, submit reviewable Markdown, or report unavailable; they cannot request, cancel, or commit. The 57 legacy single-purpose tools the composites replaced were removed in v0.3.0; v0.4.0 shipped the `canvas_snapshot` composite and removed the 6 deprecated snapshot standalones. Prefer composites for new MCP calls; see `docs/mcp.md` for the authoritative tool list and the legacy-to-composite migration reference.
10
10
 
11
11
  ### Agent-assisted document imports
12
12
 
@@ -18,7 +18,7 @@ The `diagram` action of `canvas_app` is a thin preset in `src/server/diagram-pre
18
18
 
19
19
  15 static canvas resources: `canvas://boards`, `canvas://pinned-context`, `canvas://schema`, `canvas://layout`, `canvas://summary`, `canvas://spatial-context`, `canvas://history`, `canvas://code-graph`, `canvas://ax`, `canvas://ax-context`, `canvas://ax-timeline`, `canvas://ax-work`, `canvas://ax-pending-steering`, `canvas://ax-delivery`, `canvas://skills`; two canvas resource templates: `canvas://context{?budget,consumer,since}` and `canvas://graph{?focus,depth}`. Bundled skills additionally register per-skill `canvas://skills/<name>` resources and the `skill://{name}/{+path}` file template.
20
20
 
21
- Resource change notifications: the MCP server emits `notifications/resources/updated` when canvas state changes. Pin changes notify `canvas://pinned-context`; all mutations notify `canvas://layout`, `canvas://summary`, `canvas://spatial-context`, `canvas://history`, `canvas://code-graph`, and `canvas://graph`. This enables real-time human→agent collaboration — humans pin nodes in the browser, agents are notified immediately.
21
+ Resource change notifications: subscribe to each live URI to receive `notifications/resources/updated` when canvas state or knowledge policies change. Notifications invalidate cached data without reading it. Subscriptions belong to the connection; subscribe again after reconnecting. See [Resource subscriptions](mcp.md#resource-subscriptions-010).
22
22
 
23
23
  ### AX Primitives (host-agnostic)
24
24
 
package/docs/cli.md CHANGED
@@ -100,7 +100,7 @@ Tours persist with the board and snapshots, participate in undo, and clear with
100
100
  the board. A file containing `null` resets the tour: when none is saved, groups
101
101
  are sorted by y, then x, then ID. A saved empty `stops` array deliberately has no
102
102
  stops. SDK: `canvas.setTour(tourOrNull)` / `canvas.getTour()`, which returns
103
- the tour directly; read stops from `.stops`.
103
+ `{ tour, derived, position }` as of 0.10; read stops from `.tour.stops`.
104
104
 
105
105
  ### Driving a tour (agents)
106
106
 
@@ -13,7 +13,7 @@
13
13
  body{margin:0}
14
14
  </style>
15
15
  </helmet>
16
- <div style="width: 1440px; height: 1000px; box-sizing: border-box; padding: 40px 48px; background: #081524; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: grid; grid-template-columns: 500px repeat(2, minmax(0, 1fr)); gap: 40px">
16
+ <div style="width: 1440px; height: 1360px; box-sizing: border-box; padding: 40px 48px; background: #081524; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: grid; grid-template-columns: 500px repeat(2, minmax(0, 1fr)); gap: 40px">
17
17
 
18
18
  <section style="display: flex; flex-direction: column; gap: 14px">
19
19
  <div><h2 style="margin: 0; font-size: 15px; font-weight: 600">7 · Tool rail in a 690px-tall window</h2><p style="margin: 2px 0 0; font-size: 12px; color: #8ea3bd">20 buttons need 850px. Settings-type tools move into one menu.</p></div>
@@ -62,7 +62,7 @@ body{margin:0}
62
62
  </section>
63
63
 
64
64
  <section style="display: flex; flex-direction: column; gap: 14px">
65
- <div><h2 style="margin: 0; font-size: 15px; font-weight: 600">8 · Node header</h2><p style="margin: 2px 0 0; font-size: 12px; color: #8ea3bd">Expand and the context pin always show; ⋯ on hover opens the existing node menu (now with Open in new tab), and Set as README stays in the markdown footer.</p></div>
65
+ <div><h2 style="margin: 0; font-size: 15px; font-weight: 600">8 · Node header</h2><p style="margin: 2px 0 0; font-size: 12px; color: #8ea3bd">Expand and the context pin always show; ⋯ on hover or keyboard focus opens the node menu. Set as README lives here, becoming Remove as README on the current README. Edit stays visible below Markdown.</p></div>
66
66
 
67
67
  <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Today</span>
68
68
  <div style="border-radius: 12px 12px 8px 8px; border: 1px solid #1b2c44; background: #0f1d31; box-shadow: 0 0 0 1px rgba(244,197,66,0.3), 0 8px 24px rgba(244,197,66,0.1); overflow: hidden">
@@ -78,7 +78,8 @@ body{margin:0}
78
78
  <div style="border-radius: 10px; border: 1px solid #1b2c44; background: #0f1d31; box-shadow: 0 1px 2px rgba(0,0,0,0.4), 0 6px 20px rgba(0,0,0,0.24); overflow: hidden">
79
79
  <div style="display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 8px 0 12px; border-bottom: 1px solid #1b2c44">
80
80
  <svg width="16" height="16" viewBox="0 0 24 24" style="color: #8ea3bd; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round"><path d="M6 5h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2zM8 10h8M8 14h5"></path></svg>
81
- <span style="flex: 1; font-size: 12px; font-weight: 600">PMX Canvas — guided tour</span>
81
+ <span style="flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 12px; font-weight: 600">PMX Canvas — guided tour</span>
82
+ <span style="flex-shrink: 0; font-size: 11px; color: #8ea3bd">README</span>
82
83
  <button aria-label="Expand (Enter)" style="width: 22px; height: 22px; border: 0; border-radius: 6px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="13" height="13" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round"><path d="M14 4h6v6M20 4l-7 7M10 20H4v-6M4 20l7-7"></path></svg></button>
83
84
  <button aria-label="Add to context (P)" style="width: 18px; height: 18px; box-sizing: border-box; border: 1.5px solid #6b7d94; border-radius: 999px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="10" height="10" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round"><path d="M9 4h6l-.8 5.5 2.8 3H7l2.8-3zM12 12.5V20"></path></svg></button>
84
85
  <!-- Reserve the hover-action slots: revealing More/Close must not move the pin. -->
@@ -111,7 +112,8 @@ body{margin:0}
111
112
  <div style="height: 236px; display: flex; flex-direction: column; border: 1px solid #1b2c44; border-radius: 10px; overflow: hidden; background: #0f1d31">
112
113
  <div style="flex-shrink: 0; height: 36px; display: flex; align-items: center; padding: 0 12px; border-bottom: 1px solid #1b2c44; font-size: 12px; font-weight: 600">Research index</div>
113
114
  <div style="flex: 1; min-height: 0; display: flex; flex-direction: column">
114
- <div style="flex: 1; min-height: 0; padding: 10px 12px; overflow: auto; font-size: 13px; color: #b9c6d6">Read the source, then follow <a href="#reference-footer" style="color: #e6eef7">the evidence</a>.<div style="margin-top: 20px; font-size: 12px; color: #8ea3bd">Set as README <span style="float: right">Edit</span></div></div>
115
+ <div style="flex: 1; min-height: 0; padding: 10px 12px; overflow: auto; font-size: 13px; color: #b9c6d6">Read the source, then follow <a href="#reference-footer" style="color: #e6eef7">the evidence</a>.</div>
116
+ <div style="flex-shrink: 0; border-top: 1px solid #1b2c44; padding: 6px 36px 6px 8px; text-align: right"><button style="border: 1px solid transparent; border-radius: 6px; padding: 4px 8px; background: transparent; color: #8ea3bd; font: 500 11px 'IBM Plex Sans', sans-serif">Edit</button></div>
115
117
  <div id="reference-footer" role="region" aria-label="Cross-board references" tabindex="0" style="flex-shrink: 0; max-height: min(40%, 132px); overflow: auto; overscroll-behavior: contain">
116
118
  <div style="padding: 6px 12px; border-top: 1px solid #1b2c44; font-size: 12px; color: #b9c6d6">↗ references: Research / Interview notes</div>
117
119
  <div style="padding: 6px 12px; border-top: 1px solid #1b2c44; font-size: 12px; color: #b9c6d6">↗ references: Research / Findings</div>
@@ -174,11 +176,35 @@ body{margin:0}
174
176
  <span style="font-size: 11px; color: #8ea3bd">Fit and focus keep a lane clear for floating chrome.</span>
175
177
  </div>
176
178
  </div>
179
+
180
+ <div style="display: flex; flex-direction: column; gap: 8px">
181
+ <h2 style="margin: 0; font-size: 15px; font-weight: 600">Present · hover / keyboard focus</h2>
182
+ <p style="margin: 0; font-size: 11px; color: #8ea3bd">2026-10-25: the button describes this tooltip with aria-describedby. In the narrow ⋯ menu, the same text appears in flow below Present so the scrolling menu cannot clip it.</p>
183
+ <div role="tooltip" style="max-width: 240px; padding: 10px 12px; border: 1px solid #2a4566; border-radius: 10px; background: #0f1d31; color: #b9c6d6; font-size: 12px; line-height: 1.35">
184
+ <strong style="color: #e6eef7">Present board</strong>
185
+ <p>A presentation follows either a saved tour or the board’s groups.</p>
186
+ <p>Saved tour: ask your agent to define the stops and their order. Each stop can focus on a card, a group, a rectangular area, or a specific camera position.</p>
187
+ <p style="margin-bottom: 0">No saved tour: every group becomes a stop, ordered top-to-bottom, then left-to-right. It doesn’t automatically visit every card.</p>
188
+ </div>
189
+ </div>
190
+ <div style="display: flex; flex-direction: column; gap: 8px">
191
+ <h2 style="margin: 0; font-size: 15px; font-weight: 600">Presentation · navigation</h2>
192
+ <p style="margin: 0; font-size: 11px; color: #8ea3bd">Owner decision, 2026-10-25: Back and Next sit beside Exit at the top-right. Instructions appear only in hover/focus tooltips, never in a corner panel. End buttons stay focusable but unavailable; their descriptions remain accessible.</p>
193
+ <div style="position: relative; height: 180px; border: 1px solid #1b2c44; border-radius: 10px; background: #081524">
194
+ <div style="position: absolute; top: 16px; right: 16px; display: flex; gap: 8px">
195
+ <button style="padding: 8px 14px; min-height: 38px; border: 1px solid #8ea3bd; border-radius: 8px; background: #0f1d31; color: #e6eef7; font: inherit; font-weight: 600; opacity: .5" aria-disabled="true">Back</button>
196
+ <button style="padding: 8px 14px; min-height: 38px; border: 1px solid #8ea3bd; border-radius: 8px; background: #0f1d31; color: #e6eef7; font: inherit; font-weight: 600">Next</button>
197
+ <button style="padding: 8px 14px; min-height: 38px; border: 1px solid #8ea3bd; border-radius: 8px; background: #0f1d31; color: #e6eef7; font: inherit; font-weight: 600">Exit presentation</button>
198
+ </div>
199
+ <div style="position: absolute; top: 66px; right: 16px; width: 168px; padding: 10px 12px; border: 1px solid #2a4566; border-radius: 10px; background: #0f1d31; box-shadow: 0 14px 36px #0006; font-size: 12px"><strong>Next stop</strong><br><span style="color: #8ea3bd">→ / ↓ / Space</span></div>
200
+ </div>
201
+ <p style="margin: 0; font-size: 11px; color: #8ea3bd">Back: Previous stop, ← / ↑. Next: Next stop, → / ↓ / Space. Exit: Exit presentation, Esc. Fit clears the top controls; exact camera targets and recording remain exact.</p>
202
+ </div>
177
203
  </section>
178
204
 
179
205
  </div>
180
206
  </x-dc>
181
- <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":1000}}'>
207
+ <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":1360}}'>
182
208
  class Component extends DCLogic {
183
209
  renderVals() {
184
210
  var P = {
@@ -245,7 +271,7 @@ class Component extends DCLogic {
245
271
  optIn('Trace', 'Show execution traces. Turning off keeps existing traces and never starts an agent.', false, false),
246
272
  setting('Send feedback', '', 'Share feedback about PMX Canvas.')
247
273
  ];
248
- var nodeMenu = [m('Focus', ''), m('Rename…', ''), m('Collapse', ''), m('Open in new tab', '', true), msep, m('Delete', '⌫')];
274
+ var nodeMenu = [m('Focus', ''), m('Rename…', ''), m('Collapse', ''), m('Open in new tab', ''), msep, m('Set as README', '', true), msep, m('Delete', '⌫')];
249
275
  var oldIcons = [{ d: P.ctx }, { d: P.ask }, { d: P.open }, { d: P.expand }, { d: P.chevron }, { d: P.close }];
250
276
  return { railToday: railToday, railNew: railNew, settings: settings, nodeMenu: nodeMenu, oldIcons: oldIcons };
251
277
  }
@@ -22,7 +22,7 @@ body{margin:0}
22
22
  </div>
23
23
 
24
24
  <div style="{{s.row}}">
25
- <div><div style="{{s.type}}">markdown</div><p style="{{s.what}}">Body 11 → 12px. “Set as README / Edit” footer moves into ⋯; double-click still edits.</p></div>
25
+ <div><div style="{{s.type}}">markdown</div><p style="{{s.what}}">Body 11 → 12px. Set as README moves into ⋯, becoming Remove as README when assigned. A quiet README header word stays visible on the designated card, including heading display (Chrome §8). Edit stays visible; double-click still edits. Heading display has no footer.</p></div>
26
26
  <div style="{{o.node}}">
27
27
  <div style="{{o.head}}"><svg width="16" height="16" viewBox="0 0 24 24" style="{{o.icon}} color: #4BBCFF"><path d="{{i.note}}"></path></svg><span style="{{o.title}}">PMX Canvas — guided tour</span><span style="{{o.ctl}}">⤢⌄×</span></div>
28
28
  <div style="padding: 10px 14px; display: flex; flex-direction: column; gap: 6px">
@@ -39,6 +39,7 @@ body{margin:0}
39
39
  <div style="color: #b9c6d6">An infinite spatial canvas that agents drive over MCP, HTTP or the Bun SDK — and that humans steer by pinning nodes.</div>
40
40
  <div style="color: #b9c6d6">• Pin a node in the browser<br>• The agent reads it from <code style="{{n.code}}">canvas://pinned-context</code></div>
41
41
  </div>
42
+ <div style="border-top: 1px solid #1b2c44; padding: 6px 36px 6px 8px; text-align: right"><button style="border: 1px solid transparent; border-radius: 6px; padding: 4px 8px; background: transparent; color: #8ea3bd; font: 500 11px 'IBM Plex Sans', sans-serif">Edit</button></div>
42
43
  </div>
43
44
  </div>
44
45
 
@@ -120,7 +121,7 @@ body{margin:0}
120
121
  </div>
121
122
 
122
123
  <div style="{{s.row}}">
123
- <div><div style="{{s.type}}">file</div><p style="{{s.what}}">The extra toolbar row folds into the header (path · lines). Code 10 → 12px with dim line numbers.</p></div>
124
+ <div><div style="{{s.type}}">file</div><p style="{{s.what}}">The extra toolbar row folds into the header (path · lines). Only metadata clips as space tightens; Reload and AX evidence hints remain visible on hover/focus. Code 10 → 12px with dim line numbers.</p></div>
124
125
  <div style="{{o.node}}">
125
126
  <div style="{{o.head}}"><svg width="16" height="16" viewBox="0 0 24 24" style="{{o.icon}} color: #4BBCFF"><path d="{{i.file}}"></path></svg><span style="{{o.title}}">node-sizes.ts</span><span style="{{o.ctl}}">⤢⌄×</span></div>
126
127
  <div style="display: flex; align-items: center; gap: 6px; padding: 4px 12px; border-bottom: 1px solid #1b2c44; font-family: 'IBM Plex Mono', monospace; font-size: 9px; color: #8ea3bd"><span style="padding: 1px 4px; border-radius: 3px; background: #13243b; color: #4BBCFF">TS</span><span>46 lines</span><span style="color: #5c6b80">src/shared/node-sizes.ts</span></div>
@@ -90,7 +90,7 @@ body{margin:0}
90
90
  </div>
91
91
 
92
92
  <div style="{{s.row}}">
93
- <div><div style="{{s.type}}">html</div><p style="{{s.what}}">The white outline around the frame goes. The page gets the canvas tokens, so it sits on the node surface.</p></div>
93
+ <div><div style="{{s.type}}">html</div><p style="{{s.what}}">The white outline around the frame goes. The page gets the canvas tokens, so it sits on the node surface. Owner decision, 2026-10-25: generated primitives have no shared header copy buttons. Copy JSON lives in the card's ⋯ menu and includes live edits. Copy prompt is removed; primitive-specific copy controls remain.</p></div>
94
94
  <div style="{{o.node}}">
95
95
  <div style="{{o.head}}"><svg width="16" height="16" viewBox="0 0 24 24" style="{{o.icon}} color: #4BBCFF"><path d="{{i.html}}"></path></svg><span style="{{o.title}}">Release gates</span><span style="{{o.ctl}}">↗⤢⌄×</span></div>
96
96
  <div style="flex: 1; margin: 8px; border: 1px solid #f2f2f2; border-radius: 2px; padding: 10px 12px; display: flex; flex-direction: column; gap: 5px">
@@ -49,7 +49,7 @@ body{margin:0}
49
49
  </div>
50
50
 
51
51
  <div style="{{s.row}}">
52
- <div><div style="{{s.type}}">graph</div><p style="{{s.what}}">Chart sits on the node surface. The title isn't repeated inside; the chart type becomes header meta.</p></div>
52
+ <div><div style="{{s.type}}">graph</div><p style="{{s.what}}">Chart sits on the node surface. The title isn't repeated inside; the chart type becomes header meta. Close slopegraph labels separate by 14px when space permits; points stay at their values, with short same-ink leaders.</p><svg width="200" height="60" role="img" aria-label="Close slopegraph labels, values unchanged"><path d="M10 42L70 20M10 52L70 26L75 34" fill="none" stroke="#4BBCFF" stroke-width="1.5"></path><circle cx="70" cy="20" r="2.5" fill="#4BBCFF"></circle><circle cx="70" cy="26" r="2.5" fill="#4BBCFF"></circle><text x="78" y="20" dominant-baseline="central" fill="#e6eef7" font-size="11">59 cli</text><text x="78" y="34" dominant-baseline="central" fill="#e6eef7" font-size="11">58 client</text></svg></div>
53
53
  <div style="{{o.node}}">
54
54
  <div style="{{o.head}}"><svg width="16" height="16" viewBox="0 0 24 24" style="{{o.icon}} color: #f4c542"><path d="{{i.chart}}"></path></svg><span style="{{o.title}}">bar — merged PRs per week</span><span style="{{o.ctl}}">↗⤢⌄×</span></div>
55
55
  <div style="flex: 1; margin: 0 8px 8px; position: relative; background: #000000; border-radius: 8px; padding: 10px 12px; font-family: system-ui, sans-serif">
@@ -2,79 +2,56 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <title>Zoomed out — fit all</title>
5
+ <title>Native spatial zoom</title>
6
6
  <script src="./support.js"></script>
7
7
  </head>
8
8
  <body>
9
9
  <x-dc>
10
10
  <helmet>
11
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&amp;family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap">
12
- <style>
13
- body{margin:0}
14
- </style>
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap">
12
+ <style>body{margin:0}</style>
15
13
  </helmet>
16
- <div style="width: 1440px; height: 640px; box-sizing: border-box; padding: 40px 48px; background: #081524; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: flex; flex-direction: column; gap: 20px">
17
- <div><h2 style="margin: 0; font-size: 15px; font-weight: 600">11 · Fit all on the demo board lands at 8%</h2><p style="margin: 2px 0 0; font-size: 12px; color: #8ea3bd">41 of 70 nodes are embedded frames and render as empty cards. Below ~25%, draw each node as its kind and title at screen size.</p></div>
18
- <div style="flex: 1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px">
19
-
20
- <div style="display: flex; flex-direction: column; gap: 8px">
21
- <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Today</span>
22
- <div style="position: relative; flex: 1; border-radius: 10px; border: 1px solid #1b2c44; overflow: hidden; background: #081524">
23
- <sc-for list="{{sections}}" as="sct" hint-placeholder-count="2">
24
- <div style="position: absolute; left: 24px; top: {{sct.y}}px; width: 600px; height: 10px; border-radius: 3px; background: #0f1d31; border: 1px solid #1b2c44; box-shadow: 0 0 0 1px rgba(244,197,66,0.3)"></div>
14
+ <div style="width:1440px;height:640px;box-sizing:border-box;padding:40px 48px;background:#081524;color:#e6eef7;font-family:'IBM Plex Sans',system-ui,sans-serif;font-size:13px;line-height:1.5">
15
+ <h2 style="margin:0;font-size:20px;font-weight:600">11 · Keep the native board at every zoom</h2>
16
+ <p style="color:#8ea3bd">Maintainer decision, 2026-10-25. The same positions, sizes, groups, connections and contents scale together. No alternate grid or content enlargement.</p>
17
+ <div style="display:flex;gap:24px;margin-top:28px">
18
+ <sc-for list="{{views}}" as="view" hint-placeholder-count="3">
19
+ <section style="width:432px">
20
+ <h3 style="font-size:14px">{{view.label}}</h3>
21
+ <svg width="432" height="380" viewBox="0 0 432 380" style="border:1px solid #1b2c44;border-radius:10px;background:#081524">
22
+ <g transform="translate(16 44)">
23
+ <g transform="scale({{view.scale}})">
24
+ <rect x="0" y="0" width="396" height="308" rx="8" fill="none" stroke="#536c88"/>
25
+ <text x="12" y="22" fill="#e6eef7" font-size="16">Release evidence</text>
26
+ <path d="M182 128 L218 218" stroke="#6b7d94" stroke-width="2" fill="none"/>
27
+ <path d="M211 213 L218 218 L218 210" stroke="#6b7d94" stroke-width="2" fill="none"/>
28
+ <g transform="translate(12 36)">
29
+ <rect width="170" height="154" rx="8" fill="#0f1d31" stroke="#1b2c44"/>
30
+ <text x="12" y="22" fill="#e6eef7" font-size="{{view.titleSize}}">Build durations</text>
31
+ <path d="M14 126 L45 99 L77 110 L110 72 L153 84" fill="none" stroke="#62c4b8" stroke-width="3"/>
32
+ <path d="M14 44 V138 H156" fill="none" stroke="#536c88"/>
33
+ <text x="16" y="59" fill="#8ea3bd" font-size="11">37 → 19 minutes</text>
34
+ </g>
35
+ <g transform="translate(218 146)">
36
+ <rect width="166" height="146" rx="8" fill="#0f1d31" stroke="#1b2c44"/>
37
+ <text x="12" y="22" fill="#e6eef7" font-size="{{view.titleSize}}">Release gates</text>
38
+ <text x="12" y="56" fill="#e6eef7" font-size="13">✓ Tests passed</text>
39
+ <text x="12" y="82" fill="#e6eef7" font-size="13">✓ Review complete</text>
40
+ <text x="12" y="108" fill="#8ea3bd" font-size="12">Source: build report</text>
41
+ </g>
42
+ </g>
43
+ </g>
44
+ </svg>
45
+ </section>
25
46
  </sc-for>
26
- <sc-for list="{{tiles}}" as="t" hint-placeholder-count="20">
27
- <div style="position: absolute; left: {{t.x}}px; top: {{t.y}}px; width: 112px; height: 72px; box-sizing: border-box; border-radius: 4px; background: #0f1d31; border: 1px solid #1b2c44; overflow: hidden"><div style="height: 6px; background: #13243b"></div></div>
28
- </sc-for>
29
- <span style="position: absolute; right: 12px; bottom: 10px; font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: #5c6b80">8%</span>
30
- </div>
31
- </div>
32
-
33
- <div style="display: flex; flex-direction: column; gap: 8px">
34
- <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Proposed</span>
35
- <div style="position: relative; flex: 1; border-radius: 10px; border: 1px solid #1b2c44; overflow: hidden; background: #081524">
36
- <sc-for list="{{sections}}" as="sct" hint-placeholder-count="2">
37
- <div style="position: absolute; left: 24px; top: {{sct.yt}}px; font-size: 15px; font-weight: 600; line-height: 20px">{{sct.title}}</div>
38
- </sc-for>
39
- <sc-for list="{{tiles}}" as="t" hint-placeholder-count="20">
40
- <div style="position: absolute; left: {{t.x}}px; top: {{t.y}}px; width: 112px; height: 72px; box-sizing: border-box; padding: 8px 10px; border-radius: 6px; background: #0f1d31; border: 1px solid #1b2c44; display: flex; flex-direction: column; gap: 4px">
41
- <div style="display: flex; align-items: center; gap: 6px"><svg width="14" height="14" viewBox="0 0 24 24" style="flex-shrink: 0; color: #8ea3bd; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round"><path d="{{t.icon}}"></path></svg><span style="font-size: 11px; color: #8ea3bd">{{t.kind}}</span></div>
42
- <span style="font-size: 12px; font-weight: 600; line-height: 1.3">{{t.title}}</span>
43
- </div>
44
- </sc-for>
45
- <span style="position: absolute; right: 12px; bottom: 10px; font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: #6b7d94">8%</span>
46
- </div>
47
- </div>
48
-
49
47
  </div>
48
+ <p style="color:#8ea3bd">Automatic previews stay on the board. Zoom or expand to read details. Title text grows at most 1.2×; the header line and content space stay fixed.</p>
50
49
  </div>
51
50
  </x-dc>
52
51
  <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":640}}'>
53
52
  class Component extends DCLogic {
54
53
  renderVals() {
55
- var chart = 'M4 4v16h16M8 16v-4M12 16V8M16 16v-6';
56
- var html = 'M8 8l-4 4 4 4M16 8l4 4-4 4M13.5 5l-3 14';
57
- var charts = [
58
- ['line', 'p95 latency'], ['bar', 'Merged PRs'], ['area', 'Nodes created'], ['pie', 'Node types'], ['stacked', 'CI minutes'],
59
- ['composed', 'Throughput'], ['scatter', 'PR size'], ['radar', 'Coverage'], ['sparkline', 'Build time'], ['bullet', 'Release KPIs']
60
- ];
61
- var prims = [
62
- ['html', 'Choice grid'], ['html', 'Plan timeline'], ['html', 'Review sheet'], ['html', 'PR write-up'], ['html', 'System map'],
63
- ['html', 'Walkthrough'], ['html', 'Design sheet'], ['html', 'Component gallery'], ['html', 'Flowchart'], ['html', 'Triage board']
64
- ];
65
- var sections = [
66
- { y: 22, yt: 18, title: '④ Charts' },
67
- { y: 222, yt: 218, title: '⑦ HTML primitives' }
68
- ];
69
- var tiles = [];
70
- function place(list, top, icon) {
71
- list.forEach(function (it, i) {
72
- tiles.push({ x: 24 + (i % 5) * 122, y: top + Math.floor(i / 5) * 82, kind: it[0], title: it[1], icon: icon });
73
- });
74
- }
75
- place(charts, 50, chart);
76
- place(prims, 250, html);
77
- return { sections: sections, tiles: tiles };
54
+ return { views: [{ label: '100%', scale: 1, titleSize: 12 }, { label: '39%', scale: 0.39, titleSize: 14.4 }, { label: '20%', scale: 0.2, titleSize: 14.4 }] };
78
55
  }
79
56
  }
80
57
  </script>
@@ -38,7 +38,7 @@
38
38
  "y": 10260
39
39
  },
40
40
  "Chrome.dc.html": {
41
- "h": 1000,
41
+ "h": 1360,
42
42
  "title": "Rail, node header, section labels",
43
43
  "w": 1440,
44
44
  "x": 1520,