pmx-canvas 0.6.4 → 0.6.5

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 (78) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/Readme.md +7 -1
  3. package/dist/canvas/global.css +58 -4
  4. package/dist/canvas/index.js +92 -92
  5. package/dist/canvas/mermaid-entry.js +258 -258
  6. package/dist/types/cli/agent.d.ts +1 -0
  7. package/dist/types/cli/commands/record.d.ts +12 -0
  8. package/dist/types/client/canvas/Presentation.d.ts +13 -0
  9. package/dist/types/client/canvas/use-node-drag.d.ts +2 -1
  10. package/dist/types/client/nodes/McpAppNode.d.ts +24 -1
  11. package/dist/types/client/state/presentation.d.ts +3 -0
  12. package/dist/types/client/types.d.ts +1 -1
  13. package/dist/types/json-render/catalog.d.ts +2 -2
  14. package/dist/types/json-render/server.d.ts +2 -0
  15. package/dist/types/server/canvas-db.d.ts +2 -0
  16. package/dist/types/server/canvas-operations.d.ts +22 -0
  17. package/dist/types/server/canvas-schema.d.ts +1 -0
  18. package/dist/types/server/canvas-state.d.ts +6 -0
  19. package/dist/types/server/canvas-validation.d.ts +3 -0
  20. package/dist/types/server/html-surface.d.ts +1 -1
  21. package/dist/types/server/index.d.ts +4 -0
  22. package/dist/types/server/operations/ops/app.d.ts +2 -0
  23. package/dist/types/server/operations/ops/json-render.d.ts +2 -0
  24. package/dist/types/server/operations/ops/nodes.d.ts +3 -1
  25. package/dist/types/server/web-artifacts.d.ts +3 -0
  26. package/dist/types/shared/tour.d.ts +72 -0
  27. package/docs/cli.md +89 -0
  28. package/docs/http-api.md +79 -4
  29. package/docs/mcp.md +25 -1
  30. package/docs/node-types.md +80 -0
  31. package/package.json +1 -1
  32. package/skills/pmx-canvas/SKILL.md +10 -2
  33. package/skills/pmx-canvas/references/full-reference.md +1 -1
  34. package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
  35. package/src/cli/agent.ts +5 -0
  36. package/src/cli/commands/nodes.ts +10 -0
  37. package/src/cli/commands/record.ts +218 -0
  38. package/src/cli/index.ts +2 -0
  39. package/src/client/App.tsx +7 -1
  40. package/src/client/canvas/AgentPresenceLayer.tsx +1 -1
  41. package/src/client/canvas/CanvasNode.tsx +8 -0
  42. package/src/client/canvas/IntentLayer.tsx +9 -2
  43. package/src/client/canvas/Presentation.tsx +108 -0
  44. package/src/client/canvas/TopBar.tsx +28 -2
  45. package/src/client/canvas/use-node-drag.ts +8 -3
  46. package/src/client/mermaid-entry.ts +39 -0
  47. package/src/client/nodes/HtmlNode.tsx +12 -13
  48. package/src/client/nodes/McpAppNode.tsx +105 -13
  49. package/src/client/nodes/MermaidNode.tsx +3 -2
  50. package/src/client/state/intent-store.ts +5 -1
  51. package/src/client/state/presentation.ts +7 -0
  52. package/src/client/state/session-store.ts +3 -0
  53. package/src/client/state/sse-bridge.ts +3 -0
  54. package/src/client/theme/global.css +58 -4
  55. package/src/json-render/server.ts +29 -0
  56. package/src/server/agent-presence.ts +15 -9
  57. package/src/server/canvas-db.ts +15 -0
  58. package/src/server/canvas-operations.ts +115 -22
  59. package/src/server/canvas-schema.ts +10 -0
  60. package/src/server/canvas-state.ts +39 -4
  61. package/src/server/canvas-validation.ts +9 -1
  62. package/src/server/demo-state.json +11 -7
  63. package/src/server/html-primitives.ts +199 -91
  64. package/src/server/html-surface.ts +8 -3
  65. package/src/server/index.ts +12 -0
  66. package/src/server/operations/composites.ts +3 -1
  67. package/src/server/operations/ops/app.ts +10 -0
  68. package/src/server/operations/ops/batch.ts +12 -3
  69. package/src/server/operations/ops/groups.ts +14 -7
  70. package/src/server/operations/ops/json-render.ts +37 -5
  71. package/src/server/operations/ops/nodes.ts +42 -16
  72. package/src/server/operations/ops/viewport.ts +32 -0
  73. package/src/server/operations/registry.ts +6 -0
  74. package/src/server/scope-fence.ts +1 -1
  75. package/src/server/server.ts +9 -6
  76. package/src/server/web-artifacts.ts +9 -2
  77. package/src/server/workboard.ts +3 -3
  78. package/src/shared/tour.ts +87 -0
@@ -21,6 +21,7 @@ import './commands/history.js';
21
21
  import './commands/ax.js';
22
22
  import './commands/pump.js';
23
23
  import './commands/webview.js';
24
+ import './commands/record.js';
24
25
  import './commands/apps.js';
25
26
  import './commands/copilot.js';
26
27
  import './commands/skills.js';
@@ -0,0 +1,12 @@
1
+ export declare function parseRecordOptions(flags: Record<string, string | boolean>): {
2
+ theme: "dark" | "light" | "high-contrast" | "midnight" | "sepia" | "arctic" | "ember" | "forest" | "volt" | undefined;
3
+ present: boolean;
4
+ tourFile: string | undefined;
5
+ chromePath: string | undefined;
6
+ width: number;
7
+ height: number;
8
+ fps: number;
9
+ mode: "realtime" | "deterministic";
10
+ output: string;
11
+ duration?: number | undefined;
12
+ };
@@ -0,0 +1,13 @@
1
+ import { type Camera } from '../../shared/tour.js';
2
+ import { canvasArea } from './canvas-area';
3
+ declare global {
4
+ interface Window {
5
+ pmxCapture?: {
6
+ ready: () => boolean;
7
+ area: typeof canvasArea;
8
+ camera: () => Camera;
9
+ frame: (camera: Camera) => Promise<Camera>;
10
+ };
11
+ }
12
+ }
13
+ export declare function Presentation(): import("preact/jsx-runtime").JSX.Element | null;
@@ -5,11 +5,12 @@ interface NodeDragOptions {
5
5
  viewport: Signal<ViewportState>;
6
6
  onMove: (id: string, x: number, y: number) => void;
7
7
  onDragEnd: () => void;
8
+ onClick?: () => void;
8
9
  }
9
10
  /**
10
11
  * Hook for dragging canvas nodes by their title bar.
11
12
  * Converts screen-space pointer delta to canvas-space position delta
12
13
  * (accounting for current viewport scale).
13
14
  */
14
- export declare function useNodeDrag({ nodeId, viewport, onMove, onDragEnd }: NodeDragOptions): (e: PointerEvent, currentX: number, currentY: number) => void;
15
+ export declare function useNodeDrag({ nodeId, viewport, onMove, onDragEnd, onClick }: NodeDragOptions): (e: PointerEvent, currentX: number, currentY: number) => void;
15
16
  export {};
@@ -1,6 +1,29 @@
1
+ import type { RefObject } from 'preact';
1
2
  import type { CanvasNodeState } from '../types';
2
3
  export declare function isSameOriginFrameDocumentUrl(url: string, origin?: string): boolean;
4
+ type ViewerFrameSource = {
5
+ src?: string;
6
+ srcdoc?: string;
7
+ };
8
+ /**
9
+ * Keep the last painted viewer in front while its replacement navigates. Viewer
10
+ * specs are immutable documents, so updates normally change `?v=` and reload the
11
+ * iframe; putting the new document in a hidden sibling avoids exposing Chromium's
12
+ * white navigation paint. Two animation frames after load gives the new document
13
+ * a composited paint before the old one is removed.
14
+ */
15
+ export declare function RefreshingViewerFrame({ source, iframeRef, onLoad, title, className, sandbox, allow, tabIndex, }: {
16
+ source: ViewerFrameSource;
17
+ iframeRef: RefObject<HTMLIFrameElement>;
18
+ onLoad: () => void;
19
+ title: string;
20
+ className?: string;
21
+ sandbox?: string;
22
+ allow?: string;
23
+ tabIndex?: number;
24
+ }): import("preact").JSX.Element;
3
25
  export declare function McpAppNode({ node, expanded }: {
4
26
  node: CanvasNodeState;
5
27
  expanded?: boolean;
6
- }): import("preact/jsx-runtime").JSX.Element;
28
+ }): import("preact").JSX.Element;
29
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare const presenting: import("@preact/signals-core").Signal<boolean>;
2
+ export declare const recordingCamera: import("@preact/signals-core").Signal<boolean>;
3
+ export declare function ownsCamera(): boolean;
@@ -54,7 +54,7 @@ export type AnnotationTool = 'pen' | 'eraser' | 'text' | null;
54
54
  export type ConnectionStatus = 'connecting' | 'connected' | 'disconnected';
55
55
  export declare const TYPE_LABELS: Record<CanvasNodeState['type'], string>;
56
56
  /** Node types that support the full-viewport expand/focus overlay. */
57
- export declare const EXPANDABLE_TYPES: Set<"html" | "markdown" | "mcp-app" | "webpage" | "json-render" | "graph" | "prompt" | "response" | "status" | "context" | "ledger" | "trace" | "file" | "diff" | "mermaid" | "image" | "group">;
57
+ export declare const EXPANDABLE_TYPES: Set<"file" | "group" | "html" | "markdown" | "mcp-app" | "webpage" | "json-render" | "graph" | "prompt" | "response" | "status" | "context" | "ledger" | "trace" | "diff" | "mermaid" | "image">;
58
58
  export declare const EXCALIDRAW_SERVER_NAME = "Excalidraw";
59
59
  export declare const EXCALIDRAW_CREATE_VIEW_TOOL = "create_view";
60
60
  export declare function isExcalidrawNode(node: CanvasNodeState): boolean;
@@ -474,10 +474,10 @@ export declare const allComponentDefinitions: {
474
474
  text: z.ZodString;
475
475
  variant: z.ZodNullable<z.ZodEnum<{
476
476
  error: "error";
477
+ success: "success";
478
+ default: "default";
477
479
  info: "info";
478
480
  warning: "warning";
479
- default: "default";
480
- success: "success";
481
481
  secondary: "secondary";
482
482
  destructive: "destructive";
483
483
  outline: "outline";
@@ -65,6 +65,8 @@ export declare const GRAPH_NODE_SIZE: {
65
65
  export type GraphChartType = 'LineChart' | 'BarChart' | 'PieChart' | 'AreaChart' | 'ScatterChart' | 'RadarChart' | 'StackedBarChart' | 'ComposedChart' | 'Sparkline' | 'DotPlot' | 'BulletChart' | 'Slopegraph';
66
66
  export declare function inferJsonRenderNodeTitle(spec: JsonRenderSpec, fallback?: string): string;
67
67
  export declare function normalizeAndValidateJsonRenderSpec(spec: unknown): JsonRenderSpec;
68
+ /** shadcn 0.21 ignores these props unless the renderer supplies a two-way binding. */
69
+ export declare function jsonRenderFormWarnings(spec: JsonRenderSpec): string[];
68
70
  export declare function normalizeGraphType(value: string): GraphChartType;
69
71
  export declare function buildGraphSpec(input: GraphNodeInput): JsonRenderSpec;
70
72
  export declare function buildGraphConfig(input: GraphNodeInput): Record<string, unknown>;
@@ -5,12 +5,14 @@
5
5
  * WAL-mode persistence. Replaces the previous JSON file-based approach.
6
6
  */
7
7
  import { Database } from 'bun:sqlite';
8
+ import { type Tour } from '../shared/tour.js';
8
9
  import { type CanvasThemeName } from '../shared/themes.js';
9
10
  import type { CanvasAnnotation, CanvasEdge, CanvasNodeState, CanvasSnapshot, CanvasSnapshotListOptions, ViewportState } from './canvas-state.js';
10
11
  import { type PmxAxState, type PmxAxEvent, type PmxAxEvidence, type PmxAxSteeringMessage, type PmxAxHostCapability, type PmxAxTimelineSummary } from './ax-state.js';
11
12
  export type CanvasTheme = CanvasThemeName;
12
13
  export declare function normalizeCanvasTheme(value: unknown, fallback?: CanvasTheme): CanvasTheme;
13
14
  export interface PersistedCanvasState {
15
+ tour?: Tour;
14
16
  version: number;
15
17
  theme?: CanvasTheme;
16
18
  viewport: ViewportState;
@@ -73,6 +73,21 @@ export declare const LEDGER_NODE_DEFAULT_SIZE: {
73
73
  width: number;
74
74
  height: number;
75
75
  };
76
+ export interface CanvasSizeAdjustment {
77
+ requested: {
78
+ width: number | null;
79
+ height: number | null;
80
+ };
81
+ applied: {
82
+ width: number;
83
+ height: number;
84
+ };
85
+ reason: 'defaulted' | 'clamped-to-minimum' | 'defaulted-and-clamped-to-minimum' | 'fit-to-children';
86
+ }
87
+ export declare function describeCreateSize(requestedWidth: number | undefined, requestedHeight: number | undefined, resolvedWidth: number, resolvedHeight: number, applied: {
88
+ width: number;
89
+ height: number;
90
+ }): CanvasSizeAdjustment | undefined;
76
91
  interface CanvasCreateGroupInput {
77
92
  title?: string;
78
93
  childIds?: string[];
@@ -146,6 +161,7 @@ export declare function addCanvasNode(input: CanvasAddNodeInput): {
146
161
  id: string;
147
162
  node: CanvasNodeState;
148
163
  needsCodeGraphRecompute: boolean;
164
+ sizeAdjustment?: CanvasSizeAdjustment;
149
165
  };
150
166
  export declare function resolveCanvasNode(nodeRef: CanvasNodeLookupInput): {
151
167
  ok: true;
@@ -213,11 +229,13 @@ export declare function removeCanvasEdge(id: string): {
213
229
  export declare function createCanvasGroup(input: CanvasCreateGroupInput): {
214
230
  id: string;
215
231
  node: CanvasNodeState;
232
+ sizeAdjustment?: CanvasSizeAdjustment;
216
233
  };
217
234
  export declare function groupCanvasNodes(groupId: string, childIds: string[], options?: {
218
235
  childLayout?: CanvasArrangeMode;
219
236
  }): {
220
237
  ok: boolean;
238
+ sizeAdjustment?: CanvasSizeAdjustment;
221
239
  };
222
240
  export declare function ungroupCanvasNodes(groupId: string): {
223
241
  ok: boolean;
@@ -230,6 +248,8 @@ export declare function createCanvasJsonRenderNode(input: JsonRenderNodeInput):
230
248
  url: string;
231
249
  spec: JsonRenderSpec;
232
250
  node: CanvasNodeState;
251
+ warnings: string[];
252
+ sizeAdjustment?: CanvasSizeAdjustment;
233
253
  };
234
254
  /**
235
255
  * Create an empty streaming json-render node. Unlike createCanvasJsonRenderNode
@@ -248,6 +268,7 @@ export declare function createCanvasStreamingJsonRenderNode(input: {
248
268
  url: string;
249
269
  spec: JsonRenderSpec;
250
270
  node: CanvasNodeState;
271
+ sizeAdjustment?: CanvasSizeAdjustment;
251
272
  };
252
273
  /**
253
274
  * Apply a batch of SpecStream patches to an existing json-render node, bumping
@@ -269,6 +290,7 @@ export declare function createCanvasGraphNode(input: GraphNodeInput): {
269
290
  url: string;
270
291
  spec: JsonRenderSpec;
271
292
  node: CanvasNodeState;
293
+ sizeAdjustment?: CanvasSizeAdjustment;
272
294
  };
273
295
  export declare function setClientViewportSize(width: unknown, height: unknown): void;
274
296
  export declare function getClientViewportSize(): {
@@ -22,6 +22,7 @@ export interface StructuredValidationResult {
22
22
  ok: true;
23
23
  type: 'json-render' | 'graph' | 'html-primitive';
24
24
  normalizedSpec?: JsonRenderSpec;
25
+ warnings?: string[];
25
26
  normalizedPrimitive?: {
26
27
  kind: string;
27
28
  title: string;
@@ -11,6 +11,7 @@
11
11
  * `.pmx-canvas-snapshots/`, loose blob files) are no longer imported as of 0.4.0 —
12
12
  * the one-shot boot migration into SQLite was retired.
13
13
  */
14
+ import { type Tour } from '../shared/tour.js';
14
15
  import { type PersistedCanvasState, type CanvasTheme, type AxTimelineQuery } from './canvas-db.js';
15
16
  import { type PmxAxActivityKind, type PmxAxElicitation, type PmxAxModeRequest, type PmxAxMode, type PmxAxCommandDescriptor, type PmxAxPolicy, type PmxAxFocusState, type PmxAxSource, type PmxAxState, type PmxAxWorkItem, type PmxAxWorkItemStatus, type PmxAxApprovalGate, type PmxAxReviewAnnotation, type PmxAxReviewKind, type PmxAxReviewSeverity, type PmxAxReviewStatus, type PmxAxReviewAnchorType, type PmxAxReviewRegion, type PmxAxEvent, type PmxAxEventKind, type PmxAxEvidence, type PmxAxEvidenceKind, type PmxAxSteeringMessage, type PmxAxHostCapability, type PmxAxTimelineSummary } from './ax-state.js';
16
17
  export declare const PMX_CANVAS_DIR = ".pmx-canvas";
@@ -102,6 +103,7 @@ export interface CanvasAnnotation {
102
103
  createdAt: string;
103
104
  }
104
105
  export interface CanvasLayout {
106
+ tour?: Tour;
105
107
  viewport: ViewportState;
106
108
  theme: CanvasTheme;
107
109
  nodes: CanvasNodeState[];
@@ -140,6 +142,7 @@ declare class CanvasStateManager {
140
142
  private edges;
141
143
  private annotations;
142
144
  private _viewport;
145
+ private _tour;
143
146
  private _theme;
144
147
  private _contextPinnedNodeIds;
145
148
  private _workspaceRoot;
@@ -236,6 +239,7 @@ declare class CanvasStateManager {
236
239
  nodes: CanvasNodeState[];
237
240
  edges: CanvasEdge[];
238
241
  annotations: CanvasAnnotation[];
242
+ tour?: Tour;
239
243
  } | null;
240
244
  /** Delete a snapshot. */
241
245
  renameSnapshot(id: string, name: string): boolean;
@@ -261,6 +265,8 @@ declare class CanvasStateManager {
261
265
  private removeEdgesForNode;
262
266
  getLayout(): CanvasLayout;
263
267
  getLayoutForPersistence(): CanvasLayout;
268
+ getTour(): Tour | undefined;
269
+ setTour(tour: Tour | null): void;
264
270
  applyUpdates(updates: CanvasNodeUpdate[], options?: ApplyUpdatesOptions): {
265
271
  applied: number;
266
272
  skipped: number;
@@ -26,6 +26,8 @@ export interface CanvasValidationResult {
26
26
  collisions: CanvasValidationPair[];
27
27
  containments: CanvasContainmentIssue[];
28
28
  containmentViolations: CanvasContainmentIssue[];
29
+ /** Group frames overlapping nodes that are not members (advisory). */
30
+ groupFrameOverlaps: CanvasValidationPair[];
29
31
  missingEdgeEndpoints: Array<{
30
32
  edgeId: string;
31
33
  from: string;
@@ -39,6 +41,7 @@ export interface CanvasValidationResult {
39
41
  collisions: number;
40
42
  containments: number;
41
43
  containmentViolations: number;
44
+ groupFrameOverlaps: number;
42
45
  missingEdgeEndpoints: number;
43
46
  sizeWarnings: number;
44
47
  };
@@ -60,7 +60,7 @@ export declare function buildContentHeightReporter(frameToken: string): string;
60
60
  * of the main SPA bundle). Escaping is mandatory: the source is arbitrary node
61
61
  * data and must not be parsed as markup.
62
62
  */
63
- export declare function buildMermaidSurfaceHtml(source: string, inlineEntry?: string | null): string;
63
+ export declare function buildMermaidSurfaceHtml(source: string, inlineEntry?: string | null, fit?: 'contain' | 'none', frameToken?: string): string;
64
64
  export interface HtmlSurfaceOptions {
65
65
  theme: SurfaceTheme;
66
66
  /**
@@ -1,4 +1,6 @@
1
1
  import { EventEmitter } from 'node:events';
2
+ import { type Tour } from '../shared/tour.js';
3
+ export type { Tour, TourStop } from '../shared/tour.js';
2
4
  import type { AgentPhase, AgentPresence, AgentPresenceSnapshot } from '../shared/agent-presence.js';
3
5
  import { canvasState } from './canvas-state.js';
4
6
  import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.js';
@@ -140,6 +142,8 @@ export declare class PmxCanvas extends EventEmitter {
140
142
  ungroupNodes(groupId: string, options?: {
141
143
  intentId?: string;
142
144
  }): boolean;
145
+ getTour(): Tour;
146
+ setTour(tour: Tour | null): void;
143
147
  clear(): void;
144
148
  arrange(layout?: 'grid' | 'column' | 'flow'): void;
145
149
  focusNode(id: string, options?: {
@@ -1,3 +1,4 @@
1
+ import { type CanvasSizeAdjustment } from '../../canvas-operations.js';
1
2
  import { type ExternalMcpTransportConfig } from '../../mcp-app-runtime.js';
2
3
  import { type Operation, type OperationContext } from '../types.js';
3
4
  export interface OpenMcpAppCoreInput {
@@ -20,6 +21,7 @@ export interface OpenMcpAppCoreResult {
20
21
  toolCallId: string;
21
22
  sessionId: string;
22
23
  resourceUri: string;
24
+ sizeAdjustment?: CanvasSizeAdjustment;
23
25
  }
24
26
  /**
25
27
  * Open an external MCP app: connect + call + read resource (openExternalMcpApp),
@@ -1,3 +1,4 @@
1
+ import { type CanvasSizeAdjustment } from '../../canvas-operations.js';
1
2
  import { type Operation } from '../types.js';
2
3
  /** Legacy server.ts parseGraphPayloadData: a graph dataset must be an array of records. */
3
4
  export declare function parseGraphPayloadData(value: unknown): Array<Record<string, unknown>> | null;
@@ -21,6 +22,7 @@ export interface StreamJsonRenderResult {
21
22
  specVersion: number;
22
23
  elementCount: number;
23
24
  streamStatus: 'open' | 'closed';
25
+ sizeAdjustment?: CanvasSizeAdjustment;
24
26
  }
25
27
  /**
26
28
  * Create-or-append core for streaming json-render nodes (the SDK's
@@ -1,4 +1,5 @@
1
1
  import { type CanvasLayout, type CanvasNodeState } from '../../canvas-state.js';
2
+ import { type CanvasSizeAdjustment } from '../../canvas-operations.js';
2
3
  import { type Operation } from '../types.js';
3
4
  export declare const NODE_TYPES: readonly ["markdown", "status", "context", "ledger", "trace", "file", "diff", "mermaid", "image", "mcp-app", "webpage", "html", "group"];
4
5
  /** Per-type default node frame size (formerly copy-pasted ladders). */
@@ -30,7 +31,7 @@ export declare function agentSafeFullLayoutPayload(layout: CanvasLayout): Record
30
31
  * agents using either key (or a cached schema) work — matching the
31
32
  * external-app / web-artifact responses that already return both.
32
33
  */
33
- export declare function createdNodePayloadFromNode(node: CanvasNodeState, options?: Record<string, unknown>): Record<string, unknown>;
34
+ export declare function createdNodePayloadFromNode(node: CanvasNodeState, options?: Record<string, unknown>, extras?: Record<string, unknown>): Record<string, unknown>;
34
35
  /**
35
36
  * Create a basic (non-webpage / non-group / non-primitive) node. Union of the
36
37
  * legacy handleCanvasAddNode generic branch; the SDK passes fileMode 'path',
@@ -41,6 +42,7 @@ export declare function createBasicCanvasNode(body: Record<string, unknown>, opt
41
42
  }): {
42
43
  node: CanvasNodeState;
43
44
  needsCodeGraphRecompute: boolean;
45
+ sizeAdjustment?: CanvasSizeAdjustment;
44
46
  };
45
47
  /**
46
48
  * Build a node patch with the full HTTP superset semantics (webpage
@@ -1,3 +1,4 @@
1
+ import { type CanvasSizeAdjustment } from './canvas-operations.js';
1
2
  export interface WebArtifactBuildInput {
2
3
  title: string;
3
4
  appTsx: string;
@@ -41,11 +42,13 @@ export interface WebArtifactSourceContext {
41
42
  export interface WebArtifactCanvasOpenResult {
42
43
  nodeId: string;
43
44
  url: string;
45
+ sizeAdjustment: CanvasSizeAdjustment;
44
46
  }
45
47
  export interface WebArtifactCanvasBuildResult extends WebArtifactBuildOutput {
46
48
  openedInCanvas: boolean;
47
49
  nodeId?: string;
48
50
  url?: string;
51
+ sizeAdjustment?: CanvasSizeAdjustment;
49
52
  startedAt: string;
50
53
  completedAt: string;
51
54
  durationMs: number;
@@ -0,0 +1,72 @@
1
+ import { z } from 'zod';
2
+ export declare const cameraSchema: z.ZodObject<{
3
+ x: z.ZodNumber;
4
+ y: z.ZodNumber;
5
+ scale: z.ZodNumber;
6
+ }, z.core.$strip>;
7
+ export declare const tourStopSchema: z.ZodObject<{
8
+ target: z.ZodUnion<readonly [z.ZodObject<{
9
+ nodeId: z.ZodString;
10
+ }, z.core.$strict>, z.ZodObject<{
11
+ viewport: z.ZodObject<{
12
+ x: z.ZodNumber;
13
+ y: z.ZodNumber;
14
+ scale: z.ZodNumber;
15
+ }, z.core.$strip>;
16
+ }, z.core.$strict>]>;
17
+ duration: z.ZodOptional<z.ZodNumber>;
18
+ easing: z.ZodOptional<z.ZodEnum<{
19
+ linear: "linear";
20
+ "ease-in-out": "ease-in-out";
21
+ "ease-out": "ease-out";
22
+ }>>;
23
+ padding: z.ZodOptional<z.ZodNumber>;
24
+ pullback: z.ZodOptional<z.ZodNumber>;
25
+ }, z.core.$strip>;
26
+ export declare const tourSchema: z.ZodObject<{
27
+ stops: z.ZodArray<z.ZodObject<{
28
+ target: z.ZodUnion<readonly [z.ZodObject<{
29
+ nodeId: z.ZodString;
30
+ }, z.core.$strict>, z.ZodObject<{
31
+ viewport: z.ZodObject<{
32
+ x: z.ZodNumber;
33
+ y: z.ZodNumber;
34
+ scale: z.ZodNumber;
35
+ }, z.core.$strip>;
36
+ }, z.core.$strict>]>;
37
+ duration: z.ZodOptional<z.ZodNumber>;
38
+ easing: z.ZodOptional<z.ZodEnum<{
39
+ linear: "linear";
40
+ "ease-in-out": "ease-in-out";
41
+ "ease-out": "ease-out";
42
+ }>>;
43
+ padding: z.ZodOptional<z.ZodNumber>;
44
+ pullback: z.ZodOptional<z.ZodNumber>;
45
+ }, z.core.$strip>>;
46
+ }, z.core.$strip>;
47
+ export type Tour = z.infer<typeof tourSchema>;
48
+ export type TourStop = z.infer<typeof tourStopSchema>;
49
+ export type Camera = z.infer<typeof cameraSchema>;
50
+ export interface TourNode {
51
+ id: string;
52
+ type: string;
53
+ position: {
54
+ x: number;
55
+ y: number;
56
+ };
57
+ size: {
58
+ width: number;
59
+ height: number;
60
+ };
61
+ }
62
+ export declare function derivedTour(nodes: TourNode[]): Tour;
63
+ export declare function resolveStop(stop: TourStop, nodes: TourNode[], width: number, height: number): Camera;
64
+ /** Interpolate world-space centres and log zoom; translations remain screen-space. */
65
+ export declare function interpolateCamera(from: Camera, to: Camera, progress: number, width: number, height: number, easing?: TourStop['easing'], pullback?: number): Camera;
66
+ /** Each segment includes its endpoint; zero-duration stops take one frame. */
67
+ export declare function segmentFrameCount(duration: number, fps: number): number;
68
+ export declare function tourFrames(tour: Tour, initial: Camera, nodes: TourNode[], width: number, height: number, fps: number): Generator<{
69
+ x: number;
70
+ y: number;
71
+ scale: number;
72
+ }, void, unknown>;
package/docs/cli.md CHANGED
@@ -13,6 +13,91 @@ pmx-canvas node list --port 4750 # target a non-default dae
13
13
  pmx-canvas --server-url http://127.0.0.1:4750 status # same, by URL
14
14
  ```
15
15
 
16
+ ## Tour and presentation
17
+
18
+ This is the small tour slice of the [vision's “board is the presentation”](product-vision-2026-09.md#12-tours-the-board-is-the-presentation-m):
19
+ the existing cards remain the content, rather than being rebuilt as a separate deck.
20
+ The same stops and camera interpolation drive interactive presentation and optional capture.
21
+ A visual stop editor and carrying tours into static exports/share links are not implemented here.
22
+
23
+ Click **Present** in the top bar, or open `/workbench?present=1` for a chrome-free
24
+ embedded/capture view. Right/Down/Space advances, Left/Up goes back, Esc exits.
25
+ The camera is local to the viewer; board content continues updating over SSE.
26
+ Stops do not auto-advance. Missing targets show an error and can be skipped.
27
+
28
+ Save this as `tour.json` (replace the IDs with IDs from `pmx-canvas node list`):
29
+
30
+ ```json
31
+ {
32
+ "stops": [
33
+ { "target": { "nodeId": "overview-group" }, "duration": 1.5, "padding": 60 },
34
+ { "target": { "nodeId": "detail-note" }, "duration": 2, "easing": "ease-in-out", "pullback": 0.7 },
35
+ { "target": { "viewport": { "x": 80, "y": 100, "scale": 0.5 } }, "duration": 1 }
36
+ ]
37
+ }
38
+ ```
39
+
40
+ ```bash
41
+ pmx-canvas tour set --file tour.json
42
+ pmx-canvas tour get
43
+ ```
44
+
45
+ A stop targets either a node ID (including a group ID) or an explicit viewport.
46
+ Viewport translation is screen-space: `screen = world * scale + offset`.
47
+ `duration` is transition time in seconds (default 1, zero jumps); `padding` is
48
+ screen pixels around a target (default 40). Easing is `linear`, `ease-in-out`
49
+ (default), or `ease-out`. Zoom interpolates logarithmically about the moving
50
+ world-space centre. `pullback` (0–4, default 0) subtracts a sinusoidal log-zoom
51
+ offset mid-move without changing endpoints. Tours allow at most 1000 stops.
52
+
53
+ Tours persist with the board and snapshots, participate in undo, and clear with
54
+ the board. A file containing `null` resets the tour: when none is saved, groups
55
+ are sorted by y, then x, then ID. A saved empty `stops` array deliberately has no
56
+ stops. SDK: `canvas.setTour(tourOrNull)` / `canvas.getTour()`.
57
+
58
+ ### Optional capture of the same tour
59
+
60
+ `record` is an optional local utility, not a second presentation model. It reads
61
+ the saved/derived tour, or the identical tour JSON supplied with `--tour-file`.
62
+ Start the server first. Capture launches its own Bun.WebView Chrome session;
63
+ it does not replace a server automation session or move other viewers' cameras.
64
+
65
+ ```bash
66
+ # Capture the saved/derived board tour
67
+ pmx-canvas record --mode deterministic --present --fps 30 --resolution 1920x1080 --output tour.mp4
68
+
69
+ # Try the same tour model from a file without changing the saved tour
70
+ pmx-canvas record --mode deterministic --tour-file tour.json --present --output frames
71
+
72
+ # Alternatively, sample the live presentation while agents update the board
73
+ pmx-canvas record --duration 10 --present --theme light --output activity.mp4
74
+
75
+ # Keep recording until SIGINT (Ctrl+C) or SIGTERM, then encode
76
+ pmx-canvas record --stop-on-signal --output live.mp4
77
+ ```
78
+
79
+ Options: `--mode realtime|deterministic` (default realtime), `--resolution WIDTHxHEIGHT`
80
+ (default 1280x720), `--fps` (1–120, default 30), `--theme`, `--present`,
81
+ `--duration SECONDS` or `--stop-on-signal` for realtime, `--tour-file FILE`
82
+ for deterministic mode, `--chrome-path PATH`, and required `--output PATH`.
83
+ Global `--port` / `--server-url` work here too. Existing output paths are refused.
84
+
85
+ An `.mp4` output uses ffmpeg/H.264; any other path is a PNG sequence directory.
86
+ Frames and `recording.json` are retained at `<output>.frames` even after video
87
+ encoding. If ffmpeg is missing, the command clearly reports the PNG fallback
88
+ and returns that directory. Encoding failures retain frames and exit nonzero.
89
+
90
+ Deterministic mode resolves target geometry once and writes
91
+ `max(1, ceil(duration × fps))` frames per stop, including each endpoint. It sets
92
+ the camera, waits for two animation frames, checks the applied camera, then
93
+ captures. Duplicate a target to hold it. This makes **camera timing** deterministic,
94
+ not animated iframe content, fonts loaded later, network assets, or concurrent
95
+ board edits. Realtime mode samples the live board; slow captures repeat frames
96
+ to preserve wall-clock speed, with the repeated-frame count in `recording.json`.
97
+ Capture has no audio. Chrome and a Bun runtime with WebView support are required.
98
+ Recording is a local CLI/file operation, not a remote HTTP/MCP file-writing API;
99
+ HTTP/MCP manage tours and retain their existing screenshot/automation surfaces.
100
+
16
101
  ## pump — reactive steering for CLI agents
17
102
 
18
103
  `pmx-canvas pump` turns any CLI agent (Codex, Amp, a script) into a steerable
@@ -99,6 +184,10 @@ pmx-canvas node update <node-id> --width 840 --height 620
99
184
  pmx-canvas node update <node-id> --title "Renamed" --full
100
185
  ```
101
186
 
187
+ Mermaid `node add` and `node update` accept `--fit contain|none`, stored as
188
+ `data.fit`. Contain is the default and shrinks only overflowing diagrams;
189
+ none keeps 100% with scrolling. [Sizing and readability](node-types.md#mermaid-nodes).
190
+
102
191
  `--from-search` / `--to-search` must each resolve to exactly one node — broad
103
192
  queries fail rather than guess. Use the full visible title.
104
193