pmx-canvas 0.1.22 → 0.1.23
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 +70 -0
- package/Readme.md +4 -3
- package/dist/types/server/canvas-db.d.ts +33 -0
- package/dist/types/server/canvas-state.d.ts +18 -14
- package/docs/screenshot.png +0 -0
- package/package.json +2 -2
- package/skills/pmx-canvas/SKILL.md +8 -4
- package/src/server/canvas-db.ts +710 -0
- package/src/server/canvas-operations.ts +1 -1
- package/src/server/canvas-schema.ts +3 -3
- package/src/server/canvas-state.ts +277 -48
- package/src/server/canvas-validation.ts +6 -0
- package/src/server/server.ts +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,75 @@
|
|
|
3
3
|
All notable changes to `pmx-canvas` are documented here. This project follows
|
|
4
4
|
[Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.1.23] - 2026-05-12
|
|
7
|
+
|
|
8
|
+
Persistence overhaul. Canvas state, snapshots, context pins, and the
|
|
9
|
+
large-payload blob store all move from filesystem JSON files into a
|
|
10
|
+
single SQLite database at `.pmx-canvas/canvas.db` (WAL mode). The
|
|
11
|
+
old `state.json`, `snapshots/`, and per-blob files are auto-imported
|
|
12
|
+
on first boot and renamed to `.bak`. Adds dock-aware validation and
|
|
13
|
+
arrange behavior, accepts the documented `?type=` query string on
|
|
14
|
+
node creation, and grows the schema-metadata kebab-case aliases to
|
|
15
|
+
include both singular and plural variants.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **SQLite persistence (`src/server/canvas-db.ts`, 710 lines).**
|
|
20
|
+
Canvas state, snapshots, context pins, and the blob sidecar
|
|
21
|
+
store now live in `.pmx-canvas/canvas.db` (Bun SQLite in WAL
|
|
22
|
+
mode). Shape preserved across the migration: nodes, edges,
|
|
23
|
+
annotations, viewport, context pins, history, snapshots, and
|
|
24
|
+
blob payloads with checksum validation.
|
|
25
|
+
- Override DB path: `PMX_CANVAS_DB_PATH` env var.
|
|
26
|
+
- Backward-compatible legacy path: `PMX_CANVAS_STATE_FILE` (if
|
|
27
|
+
you set a `.db` path there, it's used as the DB; if not, it's
|
|
28
|
+
treated as a legacy JSON path for migration).
|
|
29
|
+
- `stopCanvasServer()` now calls `canvasState.close()` which
|
|
30
|
+
checkpoints WAL data into the DB file — stop the server (or
|
|
31
|
+
flush/close the SDK) before committing `canvas.db`.
|
|
32
|
+
- SQLite WAL/SHM files (`*.db-wal`, `*.db-shm`) are gitignored;
|
|
33
|
+
`canvas.db` itself is git-committable.
|
|
34
|
+
- **Legacy migration on first boot.** Existing
|
|
35
|
+
`.pmx-canvas/state.json`, root `.pmx-canvas.json`,
|
|
36
|
+
`.pmx-canvas/snapshots/`, `.pmx-canvas-snapshots/`, and blob
|
|
37
|
+
files are imported into the SQLite database and renamed to
|
|
38
|
+
`.bak` on first start. The migration is idempotent and only
|
|
39
|
+
runs when the DB is empty.
|
|
40
|
+
- **HTTP node create accepts `?type=` query string.** `POST
|
|
41
|
+
/api/canvas/node?type=html-primitive` with the body's `data`
|
|
42
|
+
fields is accepted as an alternative to passing `type` in the
|
|
43
|
+
body — handy for `curl` and shell-based agents. The body form
|
|
44
|
+
still wins when both are present.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- **Docked nodes are excluded from layout collision validation.**
|
|
49
|
+
`validateCanvasLayout` no longer flags `dockPosition !== null`
|
|
50
|
+
nodes as overlap or containment violations. Docked HUD-style
|
|
51
|
+
nodes intentionally sit on top of canvas content; the validator
|
|
52
|
+
now models that.
|
|
53
|
+
- **Docked nodes are treated as arrange-locked.** `arrange()`
|
|
54
|
+
now skips translating nodes with `dockPosition !== null` along
|
|
55
|
+
with pinned and explicitly arrange-locked nodes. Dock geometry
|
|
56
|
+
is anchored to the HUD layer, not the world grid.
|
|
57
|
+
- **Schema kebab-case aliases include plural forms.** `--embedded-
|
|
58
|
+
node-ids`, `--embedded-urls`, and `--slide-titles` are now
|
|
59
|
+
documented aliases for the array-shaped HTML sidecar fields,
|
|
60
|
+
alongside the existing singular `--embedded-node-id`,
|
|
61
|
+
`--embedded-url`, and `--slide-title`.
|
|
62
|
+
- **Bun engine bumped to `>=1.3.14`.** `package.json#engines.bun`
|
|
63
|
+
raised from `>=1.3.12` to `>=1.3.14` to pick up the
|
|
64
|
+
`bun:sqlite` improvements the new persistence layer depends on.
|
|
65
|
+
|
|
66
|
+
### Internal
|
|
67
|
+
|
|
68
|
+
- Regression coverage for: `validateCanvasLayout` ignoring docked
|
|
69
|
+
nodes as collision candidates, html primitive node creation
|
|
70
|
+
accepting the documented query-string `?type=` form, and the
|
|
71
|
+
existing canvas-state and operations suites continuing to pass
|
|
72
|
+
against the SQLite-backed persistence (including snapshot
|
|
73
|
+
save/restore, blob round-trip, and undo/redo history).
|
|
74
|
+
|
|
6
75
|
## [0.1.22] - 2026-05-12
|
|
7
76
|
|
|
8
77
|
CLI ergonomics and response-size polish on top of 0.1.21. Adds a
|
|
@@ -1045,6 +1114,7 @@ otherwise have to discover by trial and error.
|
|
|
1045
1114
|
- Regression coverage for snapshot flat-`id` aliases on both MCP and
|
|
1046
1115
|
HTTP surfaces, plus async / top-level-`await` WebView script bodies.
|
|
1047
1116
|
|
|
1117
|
+
[0.1.23]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.23
|
|
1048
1118
|
[0.1.22]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.22
|
|
1049
1119
|
[0.1.21]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.21
|
|
1050
1120
|
[0.1.20]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.20
|
package/Readme.md
CHANGED
|
@@ -57,11 +57,12 @@ picks up immediately.
|
|
|
57
57
|
|
|
58
58
|
### 05 / Save
|
|
59
59
|
|
|
60
|
-
Spatial state auto-saves to `.pmx-canvas/
|
|
60
|
+
Spatial state auto-saves to `.pmx-canvas/canvas.db` (debounced ~500 ms) —
|
|
61
61
|
git-committable, shareable across machines, and survives both browser
|
|
62
62
|
refresh and server restart. Named [snapshots](docs/mcp.md#tools), full
|
|
63
63
|
undo/redo, and an auto-detected code graph (JS/TS, Python, Go, Rust) make
|
|
64
|
-
the canvas durable rather than throwaway.
|
|
64
|
+
the canvas durable rather than throwaway. Stop the server before committing
|
|
65
|
+
the DB so SQLite WAL data is checkpointed into the file.
|
|
65
66
|
|
|
66
67
|
### 06 / Any agent
|
|
67
68
|
|
|
@@ -168,7 +169,7 @@ the agent can read `canvas://skills` and pull in companion skills
|
|
|
168
169
|
one machine. No built-in multi-user auth or presence — collaboration means
|
|
169
170
|
human ↔ agent on the same machine, plus any other browser tab/agent
|
|
170
171
|
pointed at the same `localhost:4313`. To share across machines, commit
|
|
171
|
-
`.pmx-canvas/
|
|
172
|
+
`.pmx-canvas/canvas.db`.
|
|
172
173
|
- **What leaves your machine.** The core canvas runs entirely on
|
|
173
174
|
`localhost`. Network egress only happens for explicit, opt-in flows:
|
|
174
175
|
`webpage` nodes fetch the URL you give them; `mcp-app` /
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
export interface PersistedCanvasState {
|
|
10
|
+
version: number;
|
|
11
|
+
viewport: ViewportState;
|
|
12
|
+
nodes: CanvasNodeState[];
|
|
13
|
+
edges: CanvasEdge[];
|
|
14
|
+
annotations?: CanvasAnnotation[];
|
|
15
|
+
contextPins: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare function openCanvasDb(dbPath: string): Database;
|
|
18
|
+
export declare function checkpointCanvasDb(db: Database): void;
|
|
19
|
+
export declare function finalizeCanvasDbForClose(db: Database): void;
|
|
20
|
+
export declare function saveStateToDB(db: Database, state: PersistedCanvasState): void;
|
|
21
|
+
/** Check if the DB has been populated with canvas state at least once. */
|
|
22
|
+
export declare function isDbPopulated(db: Database): boolean;
|
|
23
|
+
export declare function loadStateFromDB(db: Database): PersistedCanvasState | null;
|
|
24
|
+
export declare function saveSnapshotToDB(db: Database, snapshot: CanvasSnapshot, state: PersistedCanvasState): void;
|
|
25
|
+
export declare function loadSnapshotFromDB(db: Database, idOrName: string): {
|
|
26
|
+
snapshot: CanvasSnapshot;
|
|
27
|
+
state: PersistedCanvasState;
|
|
28
|
+
} | null;
|
|
29
|
+
export declare function listSnapshotsFromDB(db: Database, options?: CanvasSnapshotListOptions): CanvasSnapshot[];
|
|
30
|
+
export declare function deleteSnapshotFromDB(db: Database, id: string): boolean;
|
|
31
|
+
export declare function writeBlobToDB(db: Database, sha256: string, jsonValue: string): number;
|
|
32
|
+
export declare function readBlobFromDB(db: Database, sha256: string): string | null;
|
|
33
|
+
export declare function hasBlobInDB(db: Database, sha256: string): boolean;
|
|
@@ -5,9 +5,11 @@
|
|
|
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 } from './canvas-db.js';
|
|
11
13
|
export declare const PMX_CANVAS_DIR = ".pmx-canvas";
|
|
12
14
|
export interface PersistedBlobRef {
|
|
13
15
|
__pmxCanvasBlob: 'v1';
|
|
@@ -17,14 +19,7 @@ export interface PersistedBlobRef {
|
|
|
17
19
|
bytes: number;
|
|
18
20
|
jsonBytes: number;
|
|
19
21
|
}
|
|
20
|
-
|
|
21
|
-
version: number;
|
|
22
|
-
viewport: ViewportState;
|
|
23
|
-
nodes: CanvasNodeState[];
|
|
24
|
-
edges: CanvasEdge[];
|
|
25
|
-
annotations?: CanvasAnnotation[];
|
|
26
|
-
contextPins: string[];
|
|
27
|
-
}
|
|
22
|
+
export type { PersistedCanvasState } from './canvas-db.js';
|
|
28
23
|
interface LoadFromDiskOptions {
|
|
29
24
|
clearExisting?: boolean;
|
|
30
25
|
}
|
|
@@ -166,6 +161,7 @@ declare class CanvasStateManager {
|
|
|
166
161
|
private recomputeParentGroupBounds;
|
|
167
162
|
private compactGroupChildren;
|
|
168
163
|
private _stateFilePath;
|
|
164
|
+
private _db;
|
|
169
165
|
private _saveTimer;
|
|
170
166
|
/** Set the workspace root to enable auto-persistence. */
|
|
171
167
|
setWorkspaceRoot(workspaceRoot: string): void;
|
|
@@ -185,15 +181,22 @@ declare class CanvasStateManager {
|
|
|
185
181
|
* No-op when the new layout already exists.
|
|
186
182
|
*/
|
|
187
183
|
private migrateLegacyLayout;
|
|
184
|
+
/**
|
|
185
|
+
* One-time migration: import state.json + snapshot JSON files + blob files
|
|
186
|
+
* into the SQLite database. Renames originals to `.bak`.
|
|
187
|
+
*/
|
|
188
|
+
private migrateJsonToSqlite;
|
|
188
189
|
getWorkspaceRoot(): string;
|
|
189
190
|
private emptyPersistedState;
|
|
190
|
-
/** Load canvas state from
|
|
191
|
+
/** Load canvas state from SQLite (or legacy JSON fallback). Call once on server startup. */
|
|
191
192
|
loadFromDisk(options?: LoadFromDiskOptions): boolean;
|
|
192
|
-
/** Debounced save — coalesces rapid mutations into a single
|
|
193
|
+
/** Debounced save — coalesces rapid mutations into a single write. */
|
|
193
194
|
private scheduleSave;
|
|
194
195
|
flushToDisk(): void;
|
|
195
|
-
/** Write current state to
|
|
196
|
+
/** Write current state to SQLite immediately. */
|
|
196
197
|
private saveToDisk;
|
|
198
|
+
/** Close the SQLite database cleanly. Call on server shutdown. */
|
|
199
|
+
close(): void;
|
|
197
200
|
private get snapshotsDir();
|
|
198
201
|
private applyPersistedState;
|
|
199
202
|
private readResolvedSnapshot;
|
|
@@ -217,6 +220,8 @@ declare class CanvasStateManager {
|
|
|
217
220
|
} | null;
|
|
218
221
|
/** Delete a snapshot. */
|
|
219
222
|
deleteSnapshot(id: string): boolean;
|
|
223
|
+
/** Remove all snapshots from the DB. Used by test teardown. */
|
|
224
|
+
clearAllSnapshots(): void;
|
|
220
225
|
get viewport(): ViewportState;
|
|
221
226
|
addNode(node: CanvasNodeState): void;
|
|
222
227
|
addJsonRenderNode(node: CanvasNodeState): void;
|
|
@@ -250,4 +255,3 @@ declare class CanvasStateManager {
|
|
|
250
255
|
clear(): void;
|
|
251
256
|
}
|
|
252
257
|
export declare const canvasState: CanvasStateManager;
|
|
253
|
-
export {};
|
package/docs/screenshot.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmx-canvas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
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",
|
|
@@ -106,6 +106,6 @@
|
|
|
106
106
|
},
|
|
107
107
|
"homepage": "https://github.com/pskoett/pmx-canvas#readme",
|
|
108
108
|
"engines": {
|
|
109
|
-
"bun": ">=1.3.
|
|
109
|
+
"bun": ">=1.3.14"
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -960,12 +960,16 @@ When the human wants to explore a different approach without losing current work
|
|
|
960
960
|
|
|
961
961
|
## Persistence
|
|
962
962
|
|
|
963
|
-
Canvas state auto-saves to `.pmx-canvas/
|
|
964
|
-
loads automatically on server start. The
|
|
963
|
+
Canvas state auto-saves to `.pmx-canvas/canvas.db` on every mutation (debounced 500ms). State
|
|
964
|
+
loads automatically on server start. The SQLite DB is git-committable — spatial knowledge
|
|
965
965
|
persists across sessions.
|
|
966
966
|
|
|
967
|
-
Snapshots
|
|
968
|
-
|
|
967
|
+
Snapshots, context pins, and large node blobs are stored in the same DB. Web artifacts land in
|
|
968
|
+
`.pmx-canvas/artifacts/`. Legacy JSON state, snapshot, and blob files are auto-imported into
|
|
969
|
+
SQLite and renamed to `.bak` on first boot.
|
|
970
|
+
|
|
971
|
+
Stop the server or flush/close the SDK before committing `canvas.db`; shutdown checkpoints SQLite
|
|
972
|
+
WAL data into the DB file.
|
|
969
973
|
|
|
970
974
|
## Real-Time Collaboration
|
|
971
975
|
|