pmx-canvas 0.1.13 → 0.1.15
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.
- package/CHANGELOG.md +163 -0
- package/Readme.md +108 -1058
- package/dist/canvas/global.css +141 -0
- package/dist/canvas/index.js +137 -87
- package/dist/json-render/index.css +1 -1
- package/dist/types/client/nodes/ExtAppFrame.d.ts +2 -3
- package/dist/types/client/nodes/HtmlNode.d.ts +5 -0
- package/dist/types/client/nodes/McpAppNode.d.ts +2 -1
- package/dist/types/client/state/canvas-store.d.ts +5 -1
- package/dist/types/client/state/intent-bridge.d.ts +3 -1
- package/dist/types/client/types.d.ts +2 -2
- package/dist/types/json-render/catalog.d.ts +1 -1
- package/dist/types/mcp/canvas-access.d.ts +7 -1
- package/dist/types/server/agent-context.d.ts +1 -0
- package/dist/types/server/canvas-operations.d.ts +12 -2
- package/dist/types/server/canvas-provenance.d.ts +1 -1
- package/dist/types/server/canvas-serialization.d.ts +3 -0
- package/dist/types/server/canvas-state.d.ts +51 -4
- package/dist/types/server/demo.d.ts +5 -0
- package/dist/types/server/diagram-presets.d.ts +4 -0
- package/dist/types/server/index.d.ts +21 -3
- package/dist/types/server/mcp-app-runtime.d.ts +1 -0
- package/dist/types/server/web-artifacts.d.ts +18 -0
- package/dist/types/shared/canvas-node-kind.d.ts +5 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +43 -0
- package/skills/pmx-canvas-testing/SKILL.md +17 -0
- package/src/cli/agent.ts +66 -5
- package/src/cli/index.ts +2 -23
- package/src/client/canvas/AttentionHistory.tsx +14 -1
- package/src/client/canvas/CanvasNode.tsx +1 -1
- package/src/client/canvas/CanvasViewport.tsx +3 -0
- package/src/client/canvas/DockedNode.tsx +110 -12
- package/src/client/canvas/ExpandedNodeOverlay.tsx +8 -3
- package/src/client/canvas/Minimap.tsx +1 -0
- package/src/client/icons.tsx +1 -0
- package/src/client/nodes/ExtAppFrame.tsx +10 -35
- package/src/client/nodes/HtmlNode.tsx +151 -0
- package/src/client/nodes/McpAppNode.tsx +2 -2
- package/src/client/state/canvas-store.ts +24 -2
- package/src/client/state/intent-bridge.ts +4 -3
- package/src/client/state/sse-bridge.ts +2 -0
- package/src/client/theme/global.css +141 -0
- package/src/client/types.ts +3 -0
- package/src/mcp/canvas-access.ts +34 -7
- package/src/mcp/server.ts +199 -26
- package/src/server/agent-context.ts +50 -3
- package/src/server/canvas-operations.ts +55 -3
- package/src/server/canvas-provenance.ts +2 -1
- package/src/server/canvas-serialization.ts +38 -13
- package/src/server/canvas-state.ts +305 -34
- package/src/server/demo.ts +792 -0
- package/src/server/diagram-presets.ts +45 -25
- package/src/server/index.ts +64 -7
- package/src/server/mcp-app-runtime.ts +15 -5
- package/src/server/server.ts +169 -63
- package/src/server/web-artifacts.ts +116 -3
- package/src/shared/canvas-node-kind.ts +14 -0
|
@@ -7,6 +7,7 @@ type OpenMcpAppResult = Awaited<ReturnType<PmxCanvas['openMcpApp']>>;
|
|
|
7
7
|
type AddDiagramInput = Parameters<PmxCanvas['addDiagram']>[0];
|
|
8
8
|
type AddJsonRenderNodeInput = Parameters<PmxCanvas['addJsonRenderNode']>[0];
|
|
9
9
|
type AddJsonRenderNodeResult = ReturnType<PmxCanvas['addJsonRenderNode']>;
|
|
10
|
+
type AddHtmlNodeInput = Parameters<PmxCanvas['addHtmlNode']>[0];
|
|
10
11
|
type AddGraphNodeInput = Parameters<PmxCanvas['addGraphNode']>[0];
|
|
11
12
|
type AddGraphNodeResult = ReturnType<PmxCanvas['addGraphNode']>;
|
|
12
13
|
type UpdateNodePatch = Parameters<PmxCanvas['updateNode']>[1];
|
|
@@ -23,8 +24,11 @@ type HistoryResult = ReturnType<PmxCanvas['getHistory']>;
|
|
|
23
24
|
type SetContextPinsResult = ReturnType<PmxCanvas['setContextPins']>;
|
|
24
25
|
type RunBatchInput = Parameters<PmxCanvas['runBatch']>[0];
|
|
25
26
|
type RunBatchResult = Awaited<ReturnType<PmxCanvas['runBatch']>>;
|
|
27
|
+
type SnapshotListOptions = Parameters<PmxCanvas['listSnapshots']>[0];
|
|
26
28
|
type SnapshotList = ReturnType<PmxCanvas['listSnapshots']>;
|
|
27
29
|
type DeleteSnapshotResult = ReturnType<PmxCanvas['deleteSnapshot']>;
|
|
30
|
+
type GcSnapshotsOptions = Parameters<PmxCanvas['gcSnapshots']>[0];
|
|
31
|
+
type GcSnapshotsResult = ReturnType<PmxCanvas['gcSnapshots']>;
|
|
28
32
|
type DiffSnapshotResult = ReturnType<PmxCanvas['diffSnapshot']>;
|
|
29
33
|
type CodeGraphResult = ReturnType<PmxCanvas['getCodeGraph']>;
|
|
30
34
|
type ValidationResult = ReturnType<PmxCanvas['validate']>;
|
|
@@ -45,6 +49,7 @@ export interface CanvasAccess {
|
|
|
45
49
|
openMcpApp(input: OpenMcpAppInput): Promise<OpenMcpAppResult>;
|
|
46
50
|
addDiagram(input: AddDiagramInput): Promise<OpenMcpAppResult>;
|
|
47
51
|
addJsonRenderNode(input: AddJsonRenderNodeInput): Promise<AddJsonRenderNodeResult>;
|
|
52
|
+
addHtmlNode(input: AddHtmlNodeInput): Promise<string>;
|
|
48
53
|
addGraphNode(input: AddGraphNodeInput): Promise<AddGraphNodeResult>;
|
|
49
54
|
buildWebArtifact(input: WebArtifactInput): Promise<WebArtifactResult>;
|
|
50
55
|
updateNode(id: string, patch: UpdateNodePatch): Promise<void>;
|
|
@@ -67,12 +72,13 @@ export interface CanvasAccess {
|
|
|
67
72
|
setContextPins(nodeIds: string[], mode?: 'set' | 'add' | 'remove'): Promise<SetContextPinsResult>;
|
|
68
73
|
getPinnedNodeIds(): Promise<string[]>;
|
|
69
74
|
runBatch(operations: RunBatchInput): Promise<RunBatchResult>;
|
|
70
|
-
listSnapshots(): Promise<SnapshotList>;
|
|
75
|
+
listSnapshots(options?: SnapshotListOptions): Promise<SnapshotList>;
|
|
71
76
|
saveSnapshot(name: string): Promise<CanvasSnapshot | null>;
|
|
72
77
|
restoreSnapshot(id: string): Promise<{
|
|
73
78
|
ok: boolean;
|
|
74
79
|
}>;
|
|
75
80
|
deleteSnapshot(id: string): Promise<DeleteSnapshotResult>;
|
|
81
|
+
gcSnapshots(options?: GcSnapshotsOptions): Promise<GcSnapshotsResult>;
|
|
76
82
|
diffSnapshot(idOrName: string): Promise<DiffSnapshotResult>;
|
|
77
83
|
getCodeGraph(): Promise<CodeGraphResult>;
|
|
78
84
|
validate(): Promise<ValidationResult>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { type CanvasEdge, type CanvasNodeState, type CanvasNodeUpdate, type CanvasSnapshot } from './canvas-state.js';
|
|
1
|
+
import { canvasState, type CanvasEdge, type CanvasNodeState, type CanvasNodeUpdate, type CanvasSnapshot } from './canvas-state.js';
|
|
2
2
|
import { type GraphNodeInput, type JsonRenderNodeInput, type JsonRenderSpec } from '../json-render/server.js';
|
|
3
3
|
export type CanvasArrangeMode = 'grid' | 'column' | 'flow';
|
|
4
4
|
export type CanvasPinMode = 'set' | 'add' | 'remove';
|
|
5
|
+
export declare function setCanvasLayoutUpdateEmitter(emitter: (() => void) | null): void;
|
|
5
6
|
export interface CanvasFitViewOptions {
|
|
6
7
|
width?: number;
|
|
7
8
|
height?: number;
|
|
@@ -40,6 +41,12 @@ interface CanvasAddNodeInput {
|
|
|
40
41
|
title?: string;
|
|
41
42
|
content?: string;
|
|
42
43
|
data?: Record<string, unknown>;
|
|
44
|
+
toolName?: string;
|
|
45
|
+
category?: string;
|
|
46
|
+
status?: string;
|
|
47
|
+
duration?: string;
|
|
48
|
+
resultSummary?: string;
|
|
49
|
+
error?: string;
|
|
43
50
|
x?: number;
|
|
44
51
|
y?: number;
|
|
45
52
|
width?: number;
|
|
@@ -106,6 +113,8 @@ export declare function validateCanvasNodePatch(patch: {
|
|
|
106
113
|
height: number;
|
|
107
114
|
};
|
|
108
115
|
}): string | null;
|
|
116
|
+
export declare function mergeTraceNodeDataFields(base: Record<string, unknown>, input: Record<string, unknown>): Record<string, unknown>;
|
|
117
|
+
export declare function hasTraceNodeDataFields(input: Record<string, unknown>): boolean;
|
|
109
118
|
export declare function scheduleCodeGraphRecompute(onComplete?: () => void): void;
|
|
110
119
|
export declare function addCanvasNode(input: CanvasAddNodeInput): {
|
|
111
120
|
id: string;
|
|
@@ -142,7 +151,7 @@ export declare function setCanvasContextPins(nodeIds: string[], mode?: CanvasPin
|
|
|
142
151
|
count: number;
|
|
143
152
|
nodeIds: string[];
|
|
144
153
|
};
|
|
145
|
-
export declare function listCanvasSnapshots(): CanvasSnapshot[];
|
|
154
|
+
export declare function listCanvasSnapshots(options?: Parameters<typeof canvasState.listSnapshots>[0]): CanvasSnapshot[];
|
|
146
155
|
export declare function saveCanvasSnapshot(name: string): CanvasSnapshot | null;
|
|
147
156
|
export declare function restoreCanvasSnapshot(idOrName: string): Promise<{
|
|
148
157
|
ok: boolean;
|
|
@@ -150,6 +159,7 @@ export declare function restoreCanvasSnapshot(idOrName: string): Promise<{
|
|
|
150
159
|
export declare function deleteCanvasSnapshot(id: string): {
|
|
151
160
|
ok: boolean;
|
|
152
161
|
};
|
|
162
|
+
export declare function gcCanvasSnapshots(options?: Parameters<typeof canvasState.gcSnapshots>[0]): ReturnType<typeof canvasState.gcSnapshots>;
|
|
153
163
|
export declare function addCanvasEdge(input: {
|
|
154
164
|
from?: string;
|
|
155
165
|
to?: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type CanvasNodeType = 'markdown' | 'mcp-app' | 'webpage' | 'json-render' | 'graph' | 'prompt' | 'response' | 'status' | 'context' | 'ledger' | 'trace' | 'file' | 'image' | 'group';
|
|
1
|
+
export type CanvasNodeType = 'markdown' | 'mcp-app' | 'webpage' | 'json-render' | 'graph' | 'prompt' | 'response' | 'status' | 'context' | 'ledger' | 'trace' | 'file' | 'image' | 'html' | 'group';
|
|
2
2
|
export type CanvasNodeProvenanceSourceKind = 'workspace-file' | 'webpage-url' | 'mcp-tool' | 'artifact-file' | 'image-url';
|
|
3
3
|
export type CanvasNodeRefreshStrategy = 'file-watch' | 'file-read-write' | 'image-reload' | 'webpage-refresh' | 'mcp-app-rehydrate' | 'artifact-reopen';
|
|
4
4
|
export interface CanvasNodeProvenance {
|
|
@@ -11,10 +11,13 @@ export interface SerializedCanvasNode extends CanvasNodeState {
|
|
|
11
11
|
export interface SerializedCanvasLayout extends Omit<CanvasLayout, 'nodes'> {
|
|
12
12
|
nodes: SerializedCanvasNode[];
|
|
13
13
|
}
|
|
14
|
+
export declare function getCanvasNodeKind(node: CanvasNodeState, data: Record<string, unknown>): string;
|
|
14
15
|
export declare function getCanvasNodeTitle(node: CanvasNodeState): string | null;
|
|
15
16
|
export declare function getCanvasNodeContent(node: CanvasNodeState): string | null;
|
|
16
17
|
export declare function serializeCanvasNode(node: CanvasNodeState): SerializedCanvasNode;
|
|
18
|
+
export declare function serializeCanvasNodeWithBlobSummaries(node: CanvasNodeState): SerializedCanvasNode;
|
|
17
19
|
export declare function serializeCanvasLayout(layout: CanvasLayout): SerializedCanvasLayout;
|
|
20
|
+
export declare function serializeCanvasLayoutWithBlobSummaries(layout: CanvasLayout): SerializedCanvasLayout;
|
|
18
21
|
export interface CanvasSummary {
|
|
19
22
|
totalNodes: number;
|
|
20
23
|
totalEdges: number;
|
|
@@ -9,6 +9,21 @@
|
|
|
9
9
|
* workspace root on every mutation (debounced). Auto-loads on `loadFromDisk()`.
|
|
10
10
|
*/
|
|
11
11
|
export declare const PMX_CANVAS_DIR = ".pmx-canvas";
|
|
12
|
+
export interface PersistedBlobRef {
|
|
13
|
+
__pmxCanvasBlob: 'v1';
|
|
14
|
+
path: string;
|
|
15
|
+
sha256: string;
|
|
16
|
+
encoding: 'json+gzip';
|
|
17
|
+
bytes: number;
|
|
18
|
+
jsonBytes: number;
|
|
19
|
+
}
|
|
20
|
+
interface PersistedCanvasState {
|
|
21
|
+
version: number;
|
|
22
|
+
viewport: ViewportState;
|
|
23
|
+
nodes: CanvasNodeState[];
|
|
24
|
+
edges: CanvasEdge[];
|
|
25
|
+
contextPins: string[];
|
|
26
|
+
}
|
|
12
27
|
interface LoadFromDiskOptions {
|
|
13
28
|
clearExisting?: boolean;
|
|
14
29
|
}
|
|
@@ -20,9 +35,24 @@ export interface CanvasSnapshot {
|
|
|
20
35
|
nodeCount: number;
|
|
21
36
|
edgeCount: number;
|
|
22
37
|
}
|
|
38
|
+
export interface CanvasSnapshotListOptions {
|
|
39
|
+
limit?: number;
|
|
40
|
+
query?: string;
|
|
41
|
+
all?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface CanvasSnapshotGcOptions {
|
|
44
|
+
keep?: number;
|
|
45
|
+
dryRun?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface CanvasSnapshotGcResult {
|
|
48
|
+
ok: boolean;
|
|
49
|
+
kept: number;
|
|
50
|
+
deleted: CanvasSnapshot[];
|
|
51
|
+
dryRun: boolean;
|
|
52
|
+
}
|
|
23
53
|
export interface CanvasNodeState {
|
|
24
54
|
id: string;
|
|
25
|
-
type: 'markdown' | 'mcp-app' | 'webpage' | 'json-render' | 'graph' | 'prompt' | 'response' | 'status' | 'context' | 'ledger' | 'trace' | 'file' | 'image' | 'group';
|
|
55
|
+
type: 'markdown' | 'mcp-app' | 'webpage' | 'json-render' | 'graph' | 'prompt' | 'response' | 'status' | 'context' | 'ledger' | 'trace' | 'file' | 'image' | 'html' | 'group';
|
|
26
56
|
position: {
|
|
27
57
|
x: number;
|
|
28
58
|
y: number;
|
|
@@ -92,7 +122,7 @@ declare class CanvasStateManager {
|
|
|
92
122
|
onChange(cb: (type: CanvasChangeType) => void): void;
|
|
93
123
|
private notifyChange;
|
|
94
124
|
private _mutationRecorder;
|
|
95
|
-
private
|
|
125
|
+
private _suppressRecordingDepth;
|
|
96
126
|
/** Register a mutation recorder. Used by mutation-history to capture undo/redo closures. */
|
|
97
127
|
onMutation(cb: (info: MutationRecordInfo) => void): void;
|
|
98
128
|
/** Run a function with mutation recording suppressed (for undo/redo replay and computed edges). */
|
|
@@ -111,6 +141,16 @@ declare class CanvasStateManager {
|
|
|
111
141
|
private _saveTimer;
|
|
112
142
|
/** Set the workspace root to enable auto-persistence. */
|
|
113
143
|
setWorkspaceRoot(workspaceRoot: string): void;
|
|
144
|
+
private get blobsDir();
|
|
145
|
+
private relativeBlobPath;
|
|
146
|
+
private resolveBlobPath;
|
|
147
|
+
private writeBlobValue;
|
|
148
|
+
private readBlobValue;
|
|
149
|
+
private externalizeNodeDataBlobs;
|
|
150
|
+
private resolveNodeDataBlobs;
|
|
151
|
+
isBlobReference(value: unknown): value is PersistedBlobRef;
|
|
152
|
+
resolveBlobReference(value: unknown): unknown;
|
|
153
|
+
private externalizePersistedStateBlobs;
|
|
114
154
|
/**
|
|
115
155
|
* One-time migration: rename files from the pre-consolidation layout
|
|
116
156
|
* (`.pmx-canvas.json` + `.pmx-canvas-snapshots/`) into `.pmx-canvas/`.
|
|
@@ -129,10 +169,15 @@ declare class CanvasStateManager {
|
|
|
129
169
|
private get snapshotsDir();
|
|
130
170
|
private applyPersistedState;
|
|
131
171
|
private readResolvedSnapshot;
|
|
172
|
+
getSnapshotDataForPersistence(idOrName: string): {
|
|
173
|
+
snapshot: CanvasSnapshot;
|
|
174
|
+
state: PersistedCanvasState;
|
|
175
|
+
} | null;
|
|
132
176
|
/** Save current canvas state as a named snapshot. */
|
|
133
177
|
saveSnapshot(name: string): CanvasSnapshot | null;
|
|
134
|
-
/** List
|
|
135
|
-
listSnapshots(): CanvasSnapshot[];
|
|
178
|
+
/** List saved snapshots, newest first. */
|
|
179
|
+
listSnapshots(options?: CanvasSnapshotListOptions): CanvasSnapshot[];
|
|
180
|
+
gcSnapshots(options?: CanvasSnapshotGcOptions): CanvasSnapshotGcResult;
|
|
136
181
|
/** Restore canvas state from a snapshot. */
|
|
137
182
|
restoreSnapshot(idOrName: string): boolean;
|
|
138
183
|
/** Read a snapshot's data without restoring it (for diff). Resolves by ID or name. */
|
|
@@ -150,12 +195,14 @@ declare class CanvasStateManager {
|
|
|
150
195
|
updateNode(id: string, patch: Partial<CanvasNodeState>): void;
|
|
151
196
|
removeNode(id: string): void;
|
|
152
197
|
getNode(id: string): CanvasNodeState | undefined;
|
|
198
|
+
getNodeForPersistence(id: string): CanvasNodeState | undefined;
|
|
153
199
|
addEdge(edge: CanvasEdge): boolean;
|
|
154
200
|
removeEdge(id: string): boolean;
|
|
155
201
|
getEdges(): CanvasEdge[];
|
|
156
202
|
getEdgesForNode(nodeId: string): CanvasEdge[];
|
|
157
203
|
private removeEdgesForNode;
|
|
158
204
|
getLayout(): CanvasLayout;
|
|
205
|
+
getLayoutForPersistence(): CanvasLayout;
|
|
159
206
|
applyUpdates(updates: CanvasNodeUpdate[]): {
|
|
160
207
|
applied: number;
|
|
161
208
|
skipped: number;
|
|
@@ -9,11 +9,13 @@ export declare const DEFAULT_EXCALIDRAW_ELEMENTS: ReadonlyArray<Record<string, u
|
|
|
9
9
|
export declare const EXCALIDRAW_MCP_TRANSPORT: ExternalMcpTransportConfig;
|
|
10
10
|
export interface DiagramPresetOpenInput {
|
|
11
11
|
elements: unknown;
|
|
12
|
+
nodeId?: string;
|
|
12
13
|
title?: string;
|
|
13
14
|
x?: number;
|
|
14
15
|
y?: number;
|
|
15
16
|
width?: number;
|
|
16
17
|
height?: number;
|
|
18
|
+
timeoutMs?: number;
|
|
17
19
|
}
|
|
18
20
|
export interface ExcalidrawOpenMcpAppInput {
|
|
19
21
|
transport: ExternalMcpTransportConfig;
|
|
@@ -22,11 +24,13 @@ export interface ExcalidrawOpenMcpAppInput {
|
|
|
22
24
|
toolArguments: {
|
|
23
25
|
elements: string;
|
|
24
26
|
};
|
|
27
|
+
nodeId?: string;
|
|
25
28
|
title?: string;
|
|
26
29
|
x?: number;
|
|
27
30
|
y?: number;
|
|
28
31
|
width?: number;
|
|
29
32
|
height?: number;
|
|
33
|
+
timeoutMs?: number;
|
|
30
34
|
}
|
|
31
35
|
export declare function inferExcalidrawCameraUpdate(elements: Array<Record<string, unknown>>): Record<string, unknown> | null;
|
|
32
36
|
export declare function normalizeExcalidrawElements(elements: unknown): string;
|
|
@@ -2,7 +2,7 @@ import { EventEmitter } from 'node:events';
|
|
|
2
2
|
import type { CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.js';
|
|
3
3
|
import { searchNodes } from './spatial-analysis.js';
|
|
4
4
|
import { diffLayouts } from './mutation-history.js';
|
|
5
|
-
import { fitCanvasView } from './canvas-operations.js';
|
|
5
|
+
import { fitCanvasView, gcCanvasSnapshots, listCanvasSnapshots } from './canvas-operations.js';
|
|
6
6
|
import { type WebArtifactBuildInput, type WebArtifactCanvasBuildResult } from './web-artifacts.js';
|
|
7
7
|
import { type ExternalMcpTransportConfig } from './mcp-app-runtime.js';
|
|
8
8
|
import { type DiagramPresetOpenInput } from './diagram-presets.js';
|
|
@@ -23,6 +23,12 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
23
23
|
type: CanvasNodeState['type'];
|
|
24
24
|
title?: string;
|
|
25
25
|
content?: string;
|
|
26
|
+
toolName?: string;
|
|
27
|
+
category?: string;
|
|
28
|
+
status?: string;
|
|
29
|
+
duration?: string;
|
|
30
|
+
resultSummary?: string;
|
|
31
|
+
error?: string;
|
|
26
32
|
x?: number;
|
|
27
33
|
y?: number;
|
|
28
34
|
width?: number;
|
|
@@ -137,7 +143,7 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
137
143
|
count: number;
|
|
138
144
|
nodeIds: string[];
|
|
139
145
|
};
|
|
140
|
-
listSnapshots(): import("./canvas-state.js").CanvasSnapshot[];
|
|
146
|
+
listSnapshots(options?: Parameters<typeof listCanvasSnapshots>[0]): import("./canvas-state.js").CanvasSnapshot[];
|
|
141
147
|
saveSnapshot(name: string): import("./canvas-state.js").CanvasSnapshot | null;
|
|
142
148
|
restoreSnapshot(id: string): Promise<{
|
|
143
149
|
ok: boolean;
|
|
@@ -145,6 +151,7 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
145
151
|
deleteSnapshot(id: string): {
|
|
146
152
|
ok: boolean;
|
|
147
153
|
};
|
|
154
|
+
gcSnapshots(options?: Parameters<typeof gcCanvasSnapshots>[0]): ReturnType<typeof gcCanvasSnapshots>;
|
|
148
155
|
diffSnapshot(idOrName: string): {
|
|
149
156
|
ok: boolean;
|
|
150
157
|
text?: string;
|
|
@@ -198,12 +205,14 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
198
205
|
transport: ExternalMcpTransportConfig;
|
|
199
206
|
toolName: string;
|
|
200
207
|
toolArguments?: Record<string, unknown>;
|
|
208
|
+
nodeId?: string;
|
|
201
209
|
serverName?: string;
|
|
202
210
|
title?: string;
|
|
203
211
|
x?: number;
|
|
204
212
|
y?: number;
|
|
205
213
|
width?: number;
|
|
206
214
|
height?: number;
|
|
215
|
+
timeoutMs?: number;
|
|
207
216
|
}): Promise<{
|
|
208
217
|
ok: true;
|
|
209
218
|
id?: string;
|
|
@@ -225,6 +234,15 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
225
234
|
url: string;
|
|
226
235
|
spec: JsonRenderSpec;
|
|
227
236
|
};
|
|
237
|
+
addHtmlNode(input: {
|
|
238
|
+
html: string;
|
|
239
|
+
title?: string;
|
|
240
|
+
x?: number;
|
|
241
|
+
y?: number;
|
|
242
|
+
width?: number;
|
|
243
|
+
height?: number;
|
|
244
|
+
strictSize?: boolean;
|
|
245
|
+
}): string;
|
|
228
246
|
addGraphNode(input: GraphNodeInput): {
|
|
229
247
|
id: string;
|
|
230
248
|
url: string;
|
|
@@ -245,7 +263,7 @@ export type { CanvasNodeState, CanvasEdge, CanvasLayout, ViewportState } from '.
|
|
|
245
263
|
export type { CanvasAutomationWebViewOptions, CanvasAutomationWebViewStatus, PrimaryWorkbenchCanvasPromptRequest, PrimaryWorkbenchIntent, } from './server.js';
|
|
246
264
|
export { emitPrimaryWorkbenchEvent, consumePrimaryWorkbenchIntents, setPrimaryWorkbenchAutoOpenEnabled, setPrimaryWorkbenchCanvasPromptHandler, startCanvasServer, stopCanvasServer, getCanvasServerPort, openUrlInExternalBrowser, getCanvasAutomationWebViewStatus, startCanvasAutomationWebView, stopCanvasAutomationWebView, evaluateCanvasAutomationWebView, resizeCanvasAutomationWebView, screenshotCanvasAutomationWebView, } from './server.js';
|
|
247
265
|
export { canvasState } from './canvas-state.js';
|
|
248
|
-
export type { CanvasSnapshot } from './canvas-state.js';
|
|
266
|
+
export type { CanvasSnapshot, CanvasSnapshotGcResult, CanvasSnapshotListOptions } from './canvas-state.js';
|
|
249
267
|
export { findOpenCanvasPosition } from './placement.js';
|
|
250
268
|
export { searchNodes, buildSpatialContext, detectClusters, findNeighborhoods } from './spatial-analysis.js';
|
|
251
269
|
export type { SpatialCluster, SpatialContext, SpatialNeighbor, NodeSpatialInfo } from './spatial-analysis.js';
|
|
@@ -17,6 +17,7 @@ export interface WebArtifactBuildOutput {
|
|
|
17
17
|
fileSize: number;
|
|
18
18
|
projectPath: string;
|
|
19
19
|
metadata: Record<string, unknown>;
|
|
20
|
+
sourceContext: WebArtifactSourceContext;
|
|
20
21
|
logs?: {
|
|
21
22
|
stdout?: WebArtifactLogSummary;
|
|
22
23
|
stderr?: WebArtifactLogSummary;
|
|
@@ -30,6 +31,13 @@ export interface WebArtifactLogSummary {
|
|
|
30
31
|
truncated: boolean;
|
|
31
32
|
suppressedNoiseCount: number;
|
|
32
33
|
}
|
|
34
|
+
export interface WebArtifactSourceContext {
|
|
35
|
+
content: string;
|
|
36
|
+
sourceFiles: string[];
|
|
37
|
+
sourceFileCount: number;
|
|
38
|
+
sourcePreview: string;
|
|
39
|
+
deps?: string[];
|
|
40
|
+
}
|
|
33
41
|
export interface WebArtifactCanvasOpenResult {
|
|
34
42
|
nodeId: string;
|
|
35
43
|
url: string;
|
|
@@ -38,7 +46,10 @@ export interface WebArtifactCanvasBuildResult extends WebArtifactBuildOutput {
|
|
|
38
46
|
openedInCanvas: boolean;
|
|
39
47
|
nodeId?: string;
|
|
40
48
|
url?: string;
|
|
49
|
+
startedAt: string;
|
|
41
50
|
completedAt: string;
|
|
51
|
+
durationMs: number;
|
|
52
|
+
timeoutMs: number;
|
|
42
53
|
}
|
|
43
54
|
export declare function resolveWorkspacePath(pathLike: string, cwd?: string): string;
|
|
44
55
|
export declare function resolveWebArtifactScriptPath(kind: 'init' | 'bundle'): string;
|
|
@@ -46,6 +57,13 @@ export declare function executeWebArtifactBuild(input: WebArtifactBuildInput): P
|
|
|
46
57
|
export declare function openWebArtifactInCanvas(input: {
|
|
47
58
|
title: string;
|
|
48
59
|
filePath: string;
|
|
60
|
+
fileSize?: number;
|
|
61
|
+
projectPath?: string;
|
|
62
|
+
content?: string;
|
|
63
|
+
sourceFiles?: string[];
|
|
64
|
+
sourceFileCount?: number;
|
|
65
|
+
sourcePreview?: string;
|
|
66
|
+
deps?: string[];
|
|
49
67
|
}): WebArtifactCanvasOpenResult;
|
|
50
68
|
export declare function buildWebArtifactOnCanvas(input: WebArtifactBuildInput & {
|
|
51
69
|
openInCanvas?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmx-canvas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Spatial canvas workbench for coding agents — infinite 2D canvas with agent-native CLI, MCP integration, nodes, edges, file watching, and snapshots",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/server/index.ts",
|
|
@@ -225,6 +225,7 @@ The CLI targets `http://localhost:4313` by default. Override with `PMX_CANVAS_UR
|
|
|
225
225
|
| `mcp-app` | Hosted app/embed frame | Tool-backed MCP apps or external app content; not generic CLI-created notes |
|
|
226
226
|
| `json-render` | Native structured UI panel | Dashboards, forms, tables, interactive layouts from json-render specs |
|
|
227
227
|
| `graph` | Native chart panel | Line, bar, pie, area, scatter, radar, stacked-bar, and composed charts rendered inside the canvas |
|
|
228
|
+
| `html` | Sandboxed HTML+JS document | Self-contained HTML with optional inline `<script>` and CDN imports rendered in a sandbox-restricted iframe; canvas theme tokens are auto-injected |
|
|
228
229
|
| `group` | Spatial container/frame | Visually group related nodes together |
|
|
229
230
|
| `prompt` | Prompt thread root | Canvas-native prompt entry points for agent conversations. **Internal type — surfaces in `canvas://layout` for thread rendering but is not created via the public `canvas_add_node` API. Don't try to add one directly.** |
|
|
230
231
|
| `response` | Prompt reply / streamed answer | Agent responses linked to prompt threads. **Same internal-only restriction as `prompt`.** |
|
|
@@ -269,6 +270,7 @@ MCP node-type routing:
|
|
|
269
270
|
| Basic nodes (`markdown`, `status`, `context`, `ledger`, `trace`, `file`, `image`, `webpage`) | `canvas_add_node` |
|
|
270
271
|
| `json-render` | `canvas_add_json_render_node` |
|
|
271
272
|
| `graph` | `canvas_add_graph_node` |
|
|
273
|
+
| `html` | `canvas_add_html_node` |
|
|
272
274
|
| `web-artifact` | `canvas_build_web_artifact` |
|
|
273
275
|
| `external-app` / tool-backed `mcp-app` | `canvas_open_mcp_app` |
|
|
274
276
|
| `group` | `canvas_create_group` |
|
|
@@ -431,6 +433,27 @@ ID extraction for mixed tool responses:
|
|
|
431
433
|
**`canvas_ungroup`** — Release children from a group
|
|
432
434
|
- `groupId` (required): group to dissolve
|
|
433
435
|
|
|
436
|
+
### Group Layout Guidance
|
|
437
|
+
|
|
438
|
+
Use groups as spacious semantic regions, not as tight containers.
|
|
439
|
+
|
|
440
|
+
- Size the child nodes first, especially `graph`, `json-render`, `mcp-app`, image, and webpage
|
|
441
|
+
nodes whose rendered content may need more height than their visible title suggests.
|
|
442
|
+
- Give every group generous interior padding. Reserve extra top padding for the group header, then
|
|
443
|
+
keep children clear of the frame edges so headers, glow, resize handles, and node chrome do not
|
|
444
|
+
visually collide.
|
|
445
|
+
- If creating a group manually, compute its frame from the final child bounds plus padding. If the
|
|
446
|
+
group exists first, expand it before adding large children rather than shrinking children to fit.
|
|
447
|
+
- Use groups to label major regions of a board. Avoid wrapping every small relationship; too many
|
|
448
|
+
tight groups make the canvas harder to read than no groups.
|
|
449
|
+
- Keep edges local to a group where possible. Long cross-board edges can look like they come from
|
|
450
|
+
nowhere; use a nearby bridge/context node or split the relationship into shorter labeled edges.
|
|
451
|
+
- After grouping, verify the result in `canvas_get_layout` or the browser: child nodes should be
|
|
452
|
+
fully inside the group with padding, visible nodes should not overlap, and group headers should
|
|
453
|
+
not cover content.
|
|
454
|
+
- If a group makes important content less visible, enlarge the group, split it into clearer
|
|
455
|
+
regions, or remove the group. Visibility is more important than preserving a frame.
|
|
456
|
+
|
|
434
457
|
### Grouped Comparison Boards
|
|
435
458
|
|
|
436
459
|
Use groups as named comparison areas, not just visual boxes.
|
|
@@ -613,6 +636,26 @@ server's `ui://` resource as an iframe node on the canvas
|
|
|
613
636
|
- Prefer the dedicated `web-artifacts-builder` skill when you need the full React + shadcn workflow
|
|
614
637
|
- Use the `playwright-cli` skill when you need to validate the built artifact in a live browser
|
|
615
638
|
|
|
639
|
+
### HTML Nodes (Sandboxed iframe)
|
|
640
|
+
|
|
641
|
+
**`canvas_add_html_node`** — Add a self-contained HTML document rendered in a sandboxed iframe
|
|
642
|
+
- Required: `html` (full document or fragment; inline `<script>` and CDN `<script src="...">` are allowed)
|
|
643
|
+
- Optional: `title`, `x`, `y`, `width` (default 720), `height` (default 640), `strictSize`
|
|
644
|
+
- Iframe sandbox is `allow-scripts` only — no same-origin access, no top-navigation, no forms
|
|
645
|
+
- Canvas theme tokens are auto-injected as CSS custom properties (both `--c-*` and common `--color-*` aliases such as `--color-text-primary`, `--color-bg`, `--color-accent`) so authored HTML inherits the active theme
|
|
646
|
+
- Use for moderate-complexity visualizations and interactive widgets that need real JS but do not warrant a full React build (Chart.js demos, D3 sketches, custom HTML report views)
|
|
647
|
+
|
|
648
|
+
### Choosing the Right Visual Tier
|
|
649
|
+
|
|
650
|
+
When the output is more than markdown, pick the lightest tier that fits:
|
|
651
|
+
|
|
652
|
+
| Tier | Tool | Build cost | When to pick it |
|
|
653
|
+
|------|------|------------|-----------------|
|
|
654
|
+
| Declarative UI | `canvas_add_json_render_node` / `canvas_add_graph_node` | None | Schema-driven dashboards, forms, charts; agent-friendly to read back via `canvas_get_node` |
|
|
655
|
+
| Sandboxed HTML+JS | `canvas_add_html_node` | None | Self-contained HTML with inline JS or CDN scripts; one-off visualizations or report views |
|
|
656
|
+
| Hosted MCP app | `canvas_open_mcp_app` / `canvas_add_diagram` | None | Interactive editors backed by an external MCP server (e.g. Excalidraw) |
|
|
657
|
+
| Bundled React app | `canvas_build_web_artifact` | Heavy (npm install + bundle) | Multi-component UIs needing React state, routing, shadcn/ui, or Tailwind class composition |
|
|
658
|
+
|
|
616
659
|
### Native Structured UI
|
|
617
660
|
|
|
618
661
|
Use native `json-render` and `graph` nodes when the output should stay fully inside PMX Canvas:
|
|
@@ -72,6 +72,23 @@ Prefer extending the existing suites before inventing a one-off script.
|
|
|
72
72
|
- If a change spans server and client, add at least one server-side assertion and one browser or
|
|
73
73
|
API-level proof
|
|
74
74
|
|
|
75
|
+
## Layout And Embedded Content Checks
|
|
76
|
+
|
|
77
|
+
- For seeded or generated boards, add API-level geometry assertions: expected node/edge counts,
|
|
78
|
+
group counts, valid edge endpoints, no visible node overlaps, and group children contained with
|
|
79
|
+
header/padding space.
|
|
80
|
+
- For grouped layouts, test non-group node overlap separately from group containment. Group frames
|
|
81
|
+
are allowed to contain children; children should not overlap each other or collide with headers.
|
|
82
|
+
- For edge-heavy layouts, assert endpoints exist and long cross-board edges are intentional. If a
|
|
83
|
+
user says an edge “comes from nowhere,” add a regression check for missing endpoints or excessive
|
|
84
|
+
edge distance in that board.
|
|
85
|
+
- For `graph`, `json-render`, `mcp-app`, webpage, and image nodes, API geometry is not enough.
|
|
86
|
+
Verify the rendered browser frame when changing sizing: iframe/body `scrollHeight` and
|
|
87
|
+
`scrollWidth` should fit the available frame unless scrolling is the intended behavior.
|
|
88
|
+
- When checking embedded frame fit manually, start from a clean seeded state, rebuild stale bundles,
|
|
89
|
+
and inspect the actual iframe document in a browser. Server dimensions can look correct while the
|
|
90
|
+
embedded content is still clipped.
|
|
91
|
+
|
|
75
92
|
## Failure Handling
|
|
76
93
|
|
|
77
94
|
- Never wave away a failure without checking whether your change caused it
|