pmx-canvas 0.1.22 → 0.1.24
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/.github/extensions/pmx-canvas/extension.mjs +591 -0
- package/CHANGELOG.md +140 -0
- package/Readme.md +40 -8
- package/dist/canvas/global.css +36 -3
- package/dist/canvas/index.js +54 -54
- package/dist/types/client/nodes/ExtAppFrame.d.ts +1 -0
- package/dist/types/client/nodes/iframe-document-url.d.ts +8 -0
- package/dist/types/client/state/intent-bridge.d.ts +4 -0
- package/dist/types/client/types.d.ts +1 -0
- package/dist/types/json-render/catalog.d.ts +1 -1
- package/dist/types/mcp/canvas-access.d.ts +9 -0
- package/dist/types/server/ax-context.d.ts +3 -0
- package/dist/types/server/ax-state.d.ts +43 -0
- package/dist/types/server/canvas-db.d.ts +38 -0
- package/dist/types/server/canvas-state.d.ts +36 -16
- package/dist/types/server/index.d.ts +6 -0
- package/dist/types/server/mutation-history.d.ts +1 -1
- package/docs/cli.md +13 -0
- package/docs/http-api.md +24 -0
- package/docs/mcp.md +20 -2
- package/docs/plans/plan-004-pmx-ax-primitives.md +463 -0
- package/docs/screenshot.png +0 -0
- package/docs/sdk.md +5 -0
- package/package.json +3 -2
- package/skills/pmx-canvas/SKILL.md +22 -4
- package/skills/pmx-canvas/references/codex-app-adapter.md +107 -0
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +111 -0
- package/src/cli/agent.ts +34 -0
- package/src/cli/index.ts +2 -1
- package/src/client/App.tsx +2 -0
- package/src/client/canvas/CanvasNode.tsx +7 -0
- package/src/client/canvas/CommandPalette.tsx +2 -1
- package/src/client/canvas/use-node-drag.ts +29 -7
- package/src/client/canvas/use-node-resize.ts +27 -7
- package/src/client/nodes/ExtAppFrame.tsx +51 -10
- package/src/client/nodes/HtmlNode.tsx +5 -2
- package/src/client/nodes/iframe-document-url.ts +58 -0
- package/src/client/state/intent-bridge.ts +8 -0
- package/src/client/state/sse-bridge.ts +2 -2
- package/src/client/theme/global.css +36 -3
- package/src/client/types.ts +1 -0
- package/src/mcp/canvas-access.ts +38 -0
- package/src/mcp/server.ts +113 -4
- package/src/server/ax-context.ts +38 -0
- package/src/server/ax-state.ts +130 -0
- package/src/server/canvas-db.ts +745 -0
- package/src/server/canvas-operations.ts +80 -1
- package/src/server/canvas-schema.ts +3 -3
- package/src/server/canvas-state.ts +390 -50
- package/src/server/canvas-validation.ts +6 -0
- package/src/server/index.ts +18 -0
- package/src/server/mutation-history.ts +1 -0
- package/src/server/server.ts +197 -11
|
@@ -24,6 +24,7 @@ export declare function resolveExtAppContainerDimensions(target: ExtAppHostDimen
|
|
|
24
24
|
height: number;
|
|
25
25
|
};
|
|
26
26
|
export declare function shouldApplyExtAppSizeChange(height: unknown, isExpanded: boolean): height is number;
|
|
27
|
+
export declare function resolveExtAppInlineFrameHeight(appHeight: number, hostHeight: number): number;
|
|
27
28
|
export declare function ExtAppFrame({ node, expanded }: {
|
|
28
29
|
node: CanvasNodeState;
|
|
29
30
|
expanded?: boolean;
|
|
@@ -22,6 +22,10 @@ export declare function openWorkbenchFile(path: string): Promise<{
|
|
|
22
22
|
}>;
|
|
23
23
|
/** Fetch canvas state from server. */
|
|
24
24
|
export declare function fetchCanvasState(): Promise<Record<string, unknown>>;
|
|
25
|
+
export declare function saveCanvasTheme(theme: string): Promise<{
|
|
26
|
+
ok: boolean;
|
|
27
|
+
theme?: string;
|
|
28
|
+
}>;
|
|
25
29
|
/** Fetch available slash commands for prompt completion. */
|
|
26
30
|
export declare function fetchSlashCommands(): Promise<Array<{
|
|
27
31
|
name: string;
|
|
@@ -58,6 +58,7 @@ export declare const EXCALIDRAW_CREATE_VIEW_TOOL = "create_view";
|
|
|
58
58
|
export declare function isExcalidrawNode(node: CanvasNodeState): boolean;
|
|
59
59
|
export interface CanvasLayout {
|
|
60
60
|
viewport: ViewportState;
|
|
61
|
+
theme?: 'dark' | 'light' | 'high-contrast';
|
|
61
62
|
nodes: CanvasNodeState[];
|
|
62
63
|
edges: CanvasEdge[];
|
|
63
64
|
annotations?: CanvasAnnotation[];
|
|
@@ -287,8 +287,8 @@ export declare const allComponentDefinitions: {
|
|
|
287
287
|
props: z.ZodObject<{
|
|
288
288
|
text: z.ZodString;
|
|
289
289
|
variant: z.ZodNullable<z.ZodEnum<{
|
|
290
|
-
default: "default";
|
|
291
290
|
error: "error";
|
|
291
|
+
default: "default";
|
|
292
292
|
success: "success";
|
|
293
293
|
secondary: "secondary";
|
|
294
294
|
destructive: "destructive";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type CanvasLayout, type CanvasNodeState, type CanvasSnapshot, type PmxCanvas } from '../server/index.js';
|
|
2
|
+
import type { PmxAxSource } from '../server/ax-state.js';
|
|
2
3
|
type AddNodeInput = Parameters<PmxCanvas['addNode']>[0];
|
|
3
4
|
type AddWebpageNodeInput = Parameters<PmxCanvas['addWebpageNode']>[0];
|
|
4
5
|
type RefreshWebpageNodeResult = Awaited<ReturnType<PmxCanvas['refreshWebpageNode']>>;
|
|
@@ -20,6 +21,9 @@ type ArrangeLayout = Parameters<PmxCanvas['arrange']>[0];
|
|
|
20
21
|
type FocusNodeResult = ReturnType<PmxCanvas['focusNode']>;
|
|
21
22
|
type FitViewOptions = Parameters<PmxCanvas['fitView']>[0];
|
|
22
23
|
type FitViewResult = ReturnType<PmxCanvas['fitView']>;
|
|
24
|
+
type AxStateResult = ReturnType<PmxCanvas['getAxState']>;
|
|
25
|
+
type AxContextResult = ReturnType<PmxCanvas['getAxContext']>;
|
|
26
|
+
type SetAxFocusResult = ReturnType<PmxCanvas['setAxFocus']>;
|
|
23
27
|
type SearchResult = ReturnType<PmxCanvas['search']>;
|
|
24
28
|
type UndoRedoResult = Awaited<ReturnType<PmxCanvas['undo']>>;
|
|
25
29
|
type HistoryResult = ReturnType<PmxCanvas['getHistory']>;
|
|
@@ -68,6 +72,11 @@ export interface CanvasAccess {
|
|
|
68
72
|
noPan?: boolean;
|
|
69
73
|
}): Promise<FocusNodeResult>;
|
|
70
74
|
fitView(options?: FitViewOptions): Promise<FitViewResult>;
|
|
75
|
+
getAxState(): Promise<AxStateResult>;
|
|
76
|
+
getAxContext(): Promise<AxContextResult>;
|
|
77
|
+
setAxFocus(nodeIds: string[], options?: {
|
|
78
|
+
source?: PmxAxSource;
|
|
79
|
+
}): Promise<SetAxFocusResult>;
|
|
71
80
|
clear(): Promise<void>;
|
|
72
81
|
search(query: string): Promise<SearchResult>;
|
|
73
82
|
undo(): Promise<UndoRedoResult>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { CanvasLayout, CanvasNodeState } from './canvas-state.js';
|
|
2
|
+
import type { AgentContextNode } from './agent-context.js';
|
|
3
|
+
export type PmxAxSource = 'agent' | 'api' | 'browser' | 'cli' | 'codex' | 'copilot' | 'mcp' | 'sdk' | 'system';
|
|
4
|
+
export interface PmxAxFocusState {
|
|
5
|
+
nodeIds: string[];
|
|
6
|
+
primaryNodeId: string | null;
|
|
7
|
+
updatedAt: string | null;
|
|
8
|
+
source: PmxAxSource | null;
|
|
9
|
+
}
|
|
10
|
+
export interface PmxAxState {
|
|
11
|
+
version: 1;
|
|
12
|
+
focus: PmxAxFocusState;
|
|
13
|
+
}
|
|
14
|
+
export interface PmxAxPinnedContext {
|
|
15
|
+
preamble: string;
|
|
16
|
+
nodeIds: string[];
|
|
17
|
+
count: number;
|
|
18
|
+
nodes: AgentContextNode[];
|
|
19
|
+
}
|
|
20
|
+
export interface PmxAxFocusContext extends PmxAxFocusState {
|
|
21
|
+
nodes: AgentContextNode[];
|
|
22
|
+
}
|
|
23
|
+
export interface PmxAxContext {
|
|
24
|
+
version: 1;
|
|
25
|
+
generatedAt: string;
|
|
26
|
+
surface: {
|
|
27
|
+
nodeCount: number;
|
|
28
|
+
edgeCount: number;
|
|
29
|
+
};
|
|
30
|
+
pinned: PmxAxPinnedContext;
|
|
31
|
+
focus: PmxAxFocusContext;
|
|
32
|
+
}
|
|
33
|
+
export declare function createEmptyAxFocusState(): PmxAxFocusState;
|
|
34
|
+
export declare function createEmptyAxState(): PmxAxState;
|
|
35
|
+
export declare function normalizeAxFocusState(input: unknown, validNodeIds?: Set<string>): PmxAxFocusState;
|
|
36
|
+
export declare function normalizeAxState(input: unknown, validNodeIds?: Set<string>): PmxAxState;
|
|
37
|
+
export declare function buildAxContext(input: {
|
|
38
|
+
layout: CanvasLayout;
|
|
39
|
+
pinned: PmxAxPinnedContext;
|
|
40
|
+
focus: PmxAxFocusState;
|
|
41
|
+
focusNodes: AgentContextNode[];
|
|
42
|
+
}): PmxAxContext;
|
|
43
|
+
export declare function nodeSetFromLayout(nodes: CanvasNodeState[]): Set<string>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite persistence layer for canvas state.
|
|
3
|
+
*
|
|
4
|
+
* Uses Bun's built-in `bun:sqlite` for zero-dependency, synchronous,
|
|
5
|
+
* WAL-mode persistence. Replaces the previous JSON file-based approach.
|
|
6
|
+
*/
|
|
7
|
+
import { Database } from 'bun:sqlite';
|
|
8
|
+
import type { CanvasAnnotation, CanvasEdge, CanvasNodeState, CanvasSnapshot, CanvasSnapshotListOptions, ViewportState } from './canvas-state.js';
|
|
9
|
+
import { type PmxAxState } from './ax-state.js';
|
|
10
|
+
export type CanvasTheme = 'dark' | 'light' | 'high-contrast';
|
|
11
|
+
export declare function normalizeCanvasTheme(value: unknown, fallback?: CanvasTheme): CanvasTheme;
|
|
12
|
+
export interface PersistedCanvasState {
|
|
13
|
+
version: number;
|
|
14
|
+
theme?: CanvasTheme;
|
|
15
|
+
viewport: ViewportState;
|
|
16
|
+
nodes: CanvasNodeState[];
|
|
17
|
+
edges: CanvasEdge[];
|
|
18
|
+
annotations?: CanvasAnnotation[];
|
|
19
|
+
contextPins: string[];
|
|
20
|
+
ax?: PmxAxState;
|
|
21
|
+
}
|
|
22
|
+
export declare function openCanvasDb(dbPath: string): Database;
|
|
23
|
+
export declare function checkpointCanvasDb(db: Database): void;
|
|
24
|
+
export declare function finalizeCanvasDbForClose(db: Database): void;
|
|
25
|
+
export declare function saveStateToDB(db: Database, state: PersistedCanvasState): void;
|
|
26
|
+
/** Check if the DB has been populated with canvas state at least once. */
|
|
27
|
+
export declare function isDbPopulated(db: Database): boolean;
|
|
28
|
+
export declare function loadStateFromDB(db: Database): PersistedCanvasState | null;
|
|
29
|
+
export declare function saveSnapshotToDB(db: Database, snapshot: CanvasSnapshot, state: PersistedCanvasState): void;
|
|
30
|
+
export declare function loadSnapshotFromDB(db: Database, idOrName: string): {
|
|
31
|
+
snapshot: CanvasSnapshot;
|
|
32
|
+
state: PersistedCanvasState;
|
|
33
|
+
} | null;
|
|
34
|
+
export declare function listSnapshotsFromDB(db: Database, options?: CanvasSnapshotListOptions): CanvasSnapshot[];
|
|
35
|
+
export declare function deleteSnapshotFromDB(db: Database, id: string): boolean;
|
|
36
|
+
export declare function writeBlobToDB(db: Database, sha256: string, jsonValue: string): number;
|
|
37
|
+
export declare function readBlobFromDB(db: Database, sha256: string): string | null;
|
|
38
|
+
export declare function hasBlobInDB(db: Database, sha256: string): boolean;
|
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* - Agent tools (Phase 3) can read/mutate canvas state
|
|
6
6
|
* - Client syncs bidirectionally (SSE for server→client, POST for client→server)
|
|
7
7
|
*
|
|
8
|
-
* Persistence: canvas state auto-saves to `.pmx-canvas/
|
|
9
|
-
* workspace root on every mutation (debounced). Auto-loads on `loadFromDisk()`.
|
|
8
|
+
* Persistence: canvas state auto-saves to `.pmx-canvas/canvas.db` (SQLite WAL mode)
|
|
9
|
+
* in the workspace root on every mutation (debounced). Auto-loads on `loadFromDisk()`.
|
|
10
|
+
* Legacy `.pmx-canvas/state.json` is auto-migrated on first boot.
|
|
10
11
|
*/
|
|
12
|
+
import { type PersistedCanvasState, type CanvasTheme } from './canvas-db.js';
|
|
13
|
+
import { type PmxAxFocusState, type PmxAxSource, type PmxAxState } from './ax-state.js';
|
|
11
14
|
export declare const PMX_CANVAS_DIR = ".pmx-canvas";
|
|
12
15
|
export interface PersistedBlobRef {
|
|
13
16
|
__pmxCanvasBlob: 'v1';
|
|
@@ -17,14 +20,7 @@ export interface PersistedBlobRef {
|
|
|
17
20
|
bytes: number;
|
|
18
21
|
jsonBytes: number;
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
version: number;
|
|
22
|
-
viewport: ViewportState;
|
|
23
|
-
nodes: CanvasNodeState[];
|
|
24
|
-
edges: CanvasEdge[];
|
|
25
|
-
annotations?: CanvasAnnotation[];
|
|
26
|
-
contextPins: string[];
|
|
27
|
-
}
|
|
23
|
+
export type { PersistedCanvasState } from './canvas-db.js';
|
|
28
24
|
interface LoadFromDiskOptions {
|
|
29
25
|
clearExisting?: boolean;
|
|
30
26
|
}
|
|
@@ -106,6 +102,7 @@ export interface CanvasAnnotation {
|
|
|
106
102
|
}
|
|
107
103
|
export interface CanvasLayout {
|
|
108
104
|
viewport: ViewportState;
|
|
105
|
+
theme: CanvasTheme;
|
|
109
106
|
nodes: CanvasNodeState[];
|
|
110
107
|
edges: CanvasEdge[];
|
|
111
108
|
annotations: CanvasAnnotation[];
|
|
@@ -123,9 +120,9 @@ export interface CanvasNodeUpdate {
|
|
|
123
120
|
collapsed?: boolean;
|
|
124
121
|
dockPosition?: 'left' | 'right' | null;
|
|
125
122
|
}
|
|
126
|
-
export type CanvasChangeType = 'pins' | 'nodes';
|
|
123
|
+
export type CanvasChangeType = 'pins' | 'nodes' | 'ax';
|
|
127
124
|
export interface MutationRecordInfo {
|
|
128
|
-
operationType: 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'restoreSnapshot' | 'setPins' | 'arrange' | 'batch' | 'groupNodes' | 'ungroupNodes' | 'viewport';
|
|
125
|
+
operationType: 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'arrange' | 'batch' | 'groupNodes' | 'ungroupNodes' | 'viewport';
|
|
129
126
|
description: string;
|
|
130
127
|
forward: () => void;
|
|
131
128
|
inverse: () => void;
|
|
@@ -143,7 +140,9 @@ declare class CanvasStateManager {
|
|
|
143
140
|
private edges;
|
|
144
141
|
private annotations;
|
|
145
142
|
private _viewport;
|
|
143
|
+
private _theme;
|
|
146
144
|
private _contextPinnedNodeIds;
|
|
145
|
+
private _axState;
|
|
147
146
|
private _workspaceRoot;
|
|
148
147
|
private _changeListeners;
|
|
149
148
|
/** Register a listener for state changes. Used by MCP server to emit resource notifications. */
|
|
@@ -158,6 +157,9 @@ declare class CanvasStateManager {
|
|
|
158
157
|
/** Create a closure that runs with recording suppressed. */
|
|
159
158
|
private suppressed;
|
|
160
159
|
private recordMutation;
|
|
160
|
+
private currentNodeIdSet;
|
|
161
|
+
private normalizeAxForCurrentNodes;
|
|
162
|
+
private applyAxState;
|
|
161
163
|
private applyResolvedGroupBounds;
|
|
162
164
|
private getGroupSnapshot;
|
|
163
165
|
private normalizeNode;
|
|
@@ -166,6 +168,7 @@ declare class CanvasStateManager {
|
|
|
166
168
|
private recomputeParentGroupBounds;
|
|
167
169
|
private compactGroupChildren;
|
|
168
170
|
private _stateFilePath;
|
|
171
|
+
private _db;
|
|
169
172
|
private _saveTimer;
|
|
170
173
|
/** Set the workspace root to enable auto-persistence. */
|
|
171
174
|
setWorkspaceRoot(workspaceRoot: string): void;
|
|
@@ -185,15 +188,22 @@ declare class CanvasStateManager {
|
|
|
185
188
|
* No-op when the new layout already exists.
|
|
186
189
|
*/
|
|
187
190
|
private migrateLegacyLayout;
|
|
191
|
+
/**
|
|
192
|
+
* One-time migration: import state.json + snapshot JSON files + blob files
|
|
193
|
+
* into the SQLite database. Renames originals to `.bak`.
|
|
194
|
+
*/
|
|
195
|
+
private migrateJsonToSqlite;
|
|
188
196
|
getWorkspaceRoot(): string;
|
|
189
197
|
private emptyPersistedState;
|
|
190
|
-
/** Load canvas state from
|
|
198
|
+
/** Load canvas state from SQLite (or legacy JSON fallback). Call once on server startup. */
|
|
191
199
|
loadFromDisk(options?: LoadFromDiskOptions): boolean;
|
|
192
|
-
/** Debounced save — coalesces rapid mutations into a single
|
|
200
|
+
/** Debounced save — coalesces rapid mutations into a single write. */
|
|
193
201
|
private scheduleSave;
|
|
194
202
|
flushToDisk(): void;
|
|
195
|
-
/** Write current state to
|
|
203
|
+
/** Write current state to SQLite immediately. */
|
|
196
204
|
private saveToDisk;
|
|
205
|
+
/** Close the SQLite database cleanly. Call on server shutdown. */
|
|
206
|
+
close(): void;
|
|
197
207
|
private get snapshotsDir();
|
|
198
208
|
private applyPersistedState;
|
|
199
209
|
private readResolvedSnapshot;
|
|
@@ -217,6 +227,8 @@ declare class CanvasStateManager {
|
|
|
217
227
|
} | null;
|
|
218
228
|
/** Delete a snapshot. */
|
|
219
229
|
deleteSnapshot(id: string): boolean;
|
|
230
|
+
/** Remove all snapshots from the DB. Used by test teardown. */
|
|
231
|
+
clearAllSnapshots(): void;
|
|
220
232
|
get viewport(): ViewportState;
|
|
221
233
|
addNode(node: CanvasNodeState): void;
|
|
222
234
|
addJsonRenderNode(node: CanvasNodeState): void;
|
|
@@ -240,7 +252,16 @@ declare class CanvasStateManager {
|
|
|
240
252
|
skipped: number;
|
|
241
253
|
};
|
|
242
254
|
setViewport(v: Partial<ViewportState>): void;
|
|
255
|
+
get theme(): CanvasTheme;
|
|
256
|
+
setTheme(theme: CanvasTheme): CanvasTheme;
|
|
243
257
|
get contextPinnedNodeIds(): Set<string>;
|
|
258
|
+
getAxState(): PmxAxState;
|
|
259
|
+
getAxFocus(): PmxAxFocusState;
|
|
260
|
+
setAxFocus(nodeIds: string[], options?: {
|
|
261
|
+
source?: PmxAxSource;
|
|
262
|
+
recordHistory?: boolean;
|
|
263
|
+
}): PmxAxFocusState;
|
|
264
|
+
clearAxFocus(): PmxAxFocusState;
|
|
244
265
|
setContextPins(nodeIds: string[]): void;
|
|
245
266
|
clearContextPins(): void;
|
|
246
267
|
/** Move child nodes into a group. Sets data.parentGroup on children and data.children on the group. */
|
|
@@ -250,4 +271,3 @@ declare class CanvasStateManager {
|
|
|
250
271
|
clear(): void;
|
|
251
272
|
}
|
|
252
273
|
export declare const canvasState: CanvasStateManager;
|
|
253
|
-
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.js';
|
|
3
|
+
import type { PmxAxContext, PmxAxFocusState, PmxAxSource, PmxAxState } from './ax-state.js';
|
|
3
4
|
import { searchNodes } from './spatial-analysis.js';
|
|
4
5
|
import { diffLayouts } from './mutation-history.js';
|
|
5
6
|
import { fitCanvasView, gcCanvasSnapshots, listCanvasSnapshots } from './canvas-operations.js';
|
|
@@ -104,6 +105,11 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
104
105
|
focused: string;
|
|
105
106
|
panned: boolean;
|
|
106
107
|
} | null;
|
|
108
|
+
getAxState(): PmxAxState;
|
|
109
|
+
getAxContext(): PmxAxContext;
|
|
110
|
+
setAxFocus(nodeIds: string[], options?: {
|
|
111
|
+
source?: PmxAxSource;
|
|
112
|
+
}): PmxAxFocusState;
|
|
107
113
|
fitView(options?: {
|
|
108
114
|
width?: number;
|
|
109
115
|
height?: number;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - _replaying flag prevents undo/redo from recording new entries
|
|
13
13
|
*/
|
|
14
14
|
import type { CanvasNodeState, CanvasEdge } from './canvas-state.js';
|
|
15
|
-
export type MutationOp = 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'arrange' | 'restoreSnapshot' | 'setPins' | 'batch' | 'viewport' | 'groupNodes' | 'ungroupNodes';
|
|
15
|
+
export type MutationOp = 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'arrange' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'batch' | 'viewport' | 'groupNodes' | 'ungroupNodes';
|
|
16
16
|
export interface MutationEntry {
|
|
17
17
|
id: string;
|
|
18
18
|
timestamp: string;
|
package/docs/cli.md
CHANGED
|
@@ -112,6 +112,19 @@ pmx-canvas focus <node-id> # Pan viewport to a node
|
|
|
112
112
|
pmx-canvas focus <node-id> --no-pan # Select/raise without panning
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
## AX context
|
|
116
|
+
|
|
117
|
+
AX commands expose the host-agnostic context contract used by adapters. Pins
|
|
118
|
+
remain the explicit human-curated context set; AX focus is the current
|
|
119
|
+
attention target and persists with canvas state and snapshots.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pmx-canvas ax status # Persisted AX state
|
|
123
|
+
pmx-canvas ax context # Pinned + focused agent context
|
|
124
|
+
pmx-canvas ax focus node-1 node-2 # Set AX focus
|
|
125
|
+
pmx-canvas ax focus --clear # Clear AX focus
|
|
126
|
+
```
|
|
127
|
+
|
|
115
128
|
## WebView automation
|
|
116
129
|
|
|
117
130
|
Drive a headless Bun.WebView (Chromium or WebKit) pointed at the workbench:
|
package/docs/http-api.md
CHANGED
|
@@ -93,6 +93,30 @@ curl -X POST http://localhost:4313/api/canvas/context-pins \
|
|
|
93
93
|
curl http://localhost:4313/api/canvas/pinned-context
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
+
## AX context and focus
|
|
97
|
+
|
|
98
|
+
AX context is the host-agnostic agent-experience layer. It combines existing
|
|
99
|
+
context pins with a persisted focus node set that adapters can inject into
|
|
100
|
+
their native prompt/context hooks.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Get persisted AX state
|
|
104
|
+
curl http://localhost:4313/api/canvas/ax
|
|
105
|
+
|
|
106
|
+
# Get agent-readable pinned + focused context
|
|
107
|
+
curl http://localhost:4313/api/canvas/ax/context
|
|
108
|
+
|
|
109
|
+
# Set AX focus
|
|
110
|
+
curl -X POST http://localhost:4313/api/canvas/ax/focus \
|
|
111
|
+
-H "Content-Type: application/json" \
|
|
112
|
+
-d '{"nodeIds":["node-1"],"source":"api"}'
|
|
113
|
+
|
|
114
|
+
# Patch AX focus through the state endpoint
|
|
115
|
+
curl -X PATCH http://localhost:4313/api/canvas/ax \
|
|
116
|
+
-H "Content-Type: application/json" \
|
|
117
|
+
-d '{"focus":{"nodeIds":["node-1"],"source":"api"}}'
|
|
118
|
+
```
|
|
119
|
+
|
|
96
120
|
## Diagrams (Excalidraw preset)
|
|
97
121
|
|
|
98
122
|
```bash
|
package/docs/mcp.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MCP reference
|
|
2
2
|
|
|
3
|
-
PMX Canvas ships an MCP stdio server with **
|
|
3
|
+
PMX Canvas ships an MCP stdio server with **45 tools** + **9 core resources**,
|
|
4
4
|
plus per-skill resources at `canvas://skills/<name>`. The server emits
|
|
5
5
|
`notifications/resources/updated` when canvas state changes — humans pin
|
|
6
6
|
nodes in the browser, agents are notified immediately.
|
|
@@ -51,6 +51,8 @@ searchable and readable in pinned/spatial context.
|
|
|
51
51
|
| `canvas_arrange` | Auto-arrange (grid/column/flow) |
|
|
52
52
|
| `canvas_validate` | Validate collisions, containment, and missing edge endpoints |
|
|
53
53
|
| `canvas_focus_node` | Pan viewport to a node; use CLI `focus --no-pan` when you only need to select/raise |
|
|
54
|
+
| `canvas_get_ax` | Read the PMX AX state plus pinned/focused context |
|
|
55
|
+
| `canvas_set_ax_focus` | Set the host-agnostic AX focus node set; adapters can pass a source such as `codex` |
|
|
54
56
|
| `canvas_pin_nodes` | Pin nodes to include in agent context |
|
|
55
57
|
| `canvas_clear` | Clear all nodes and edges |
|
|
56
58
|
| `canvas_snapshot` | Save current canvas as a named snapshot |
|
|
@@ -80,6 +82,8 @@ Individual bundled skills are also readable at `canvas://skills/<name>`.
|
|
|
80
82
|
| Resource | Description |
|
|
81
83
|
|----------|-------------|
|
|
82
84
|
| `canvas://pinned-context` | Content of pinned nodes + nearby unpinned neighbors |
|
|
85
|
+
| `canvas://ax` | PMX AX state, currently including persisted focus |
|
|
86
|
+
| `canvas://ax-context` | Agent-readable pinned and focused AX context |
|
|
83
87
|
| `canvas://schema` | Running-server create schemas and json-render catalog metadata |
|
|
84
88
|
| `canvas://layout` | Full canvas state (all nodes, edges, viewport) |
|
|
85
89
|
| `canvas://summary` | Compact overview: counts, pinned titles, viewport |
|
|
@@ -93,13 +97,27 @@ Individual bundled skills are also readable at `canvas://skills/<name>`.
|
|
|
93
97
|
The MCP server emits `notifications/resources/updated` whenever canvas state
|
|
94
98
|
changes:
|
|
95
99
|
|
|
96
|
-
- Pin changes notify `canvas://pinned-context`
|
|
100
|
+
- Pin changes notify `canvas://pinned-context`, `canvas://ax`, and `canvas://ax-context`
|
|
101
|
+
- AX focus changes notify `canvas://ax` and `canvas://ax-context`
|
|
97
102
|
- All mutations notify `canvas://layout`, `canvas://summary`,
|
|
98
103
|
`canvas://spatial-context`, `canvas://history`, and `canvas://code-graph`
|
|
99
104
|
|
|
100
105
|
This closes the human-to-agent loop: spatial curation in the browser becomes
|
|
101
106
|
an immediate signal in the agent's context.
|
|
102
107
|
|
|
108
|
+
## Codex App Adapter
|
|
109
|
+
|
|
110
|
+
In the Codex app, PMX Canvas runs natively through the existing Codex surfaces:
|
|
111
|
+
MCP for tools/resources and the in-app Browser for the live `/workbench` view.
|
|
112
|
+
No separate PMX renderer is needed. Prefer MCP over the CLI for Codex-native
|
|
113
|
+
operation; keep the CLI for fallback scripts and manual debugging.
|
|
114
|
+
|
|
115
|
+
Use `canvas://ax-context` or `canvas_get_ax` to read pinned/focused context.
|
|
116
|
+
When Codex-hosted steering sets the current attention target, call
|
|
117
|
+
`canvas_set_ax_focus` with `source: "codex"` so the AX state records where the
|
|
118
|
+
focus came from. The full workflow lives in
|
|
119
|
+
`skills/pmx-canvas/references/codex-app-adapter.md`.
|
|
120
|
+
|
|
103
121
|
## Annotation Visibility
|
|
104
122
|
|
|
105
123
|
Human-drawn canvas annotations are rendered as browser SVG ink. MCP resources
|