@calca/canvas-base
v0.9.0
Published
Zero-dependency, runtime-agnostic document core for the Calca canvas. Web Standards only (ESM + `EventTarget`) — runs on Node ≥20.19, Bun, Deno, and workers. Contains no React or `@xyflow/*`; the React Flow projection lives in `@calca/canvas-flow`.
Readme
@calca/canvas-base
Zero-dependency, runtime-agnostic document core for the Calca canvas. Web Standards only (ESM + EventTarget) — runs on Node ≥20.19, Bun, Deno, and workers. Contains no React or @xyflow/*; the React Flow projection lives in @calca/canvas-flow.
Model
- Records —
ShapeRecord(id, type,parentId, fractionalindex,props,meta) andBindingRecord(fromId/toId), held in flat maps onCanvasStore. - Ordering —
keys.tsfractional indices (generateKeyBetween,generateNKeysBetween) for stable, O(1) reordering under user/agent/remote writes. - Commands —
create-shape|create-binding|update|delete|reparent. All mutations go throughstore.apply(commands, { origin, history }), which computes inverse commands, updates history, and emits achangeevent (CustomEvent<CanvasChange>) with{ origin, commands, inverse, added, updated, removed }. - History —
transact(fn)groupsapplycalls into one undo entry;history: falsefor remote/derived changes.undo()/redo(). - Persistence —
snapshot()/load()on a versionedCanvasSnapshot; orderedMigrations run on load and future versions are rejected. - Types —
ShapeDef<P>carries an optional Standard Schemaschema(type-only dev dependency; bring your own zod/valibot/arktype) plusdefaultProps. Validation runs insideapply.
import { createCanvasStore, generateKeyBetween } from "@calca/canvas-base";
const store = createCanvasStore();
store.addEventListener("change", (e) => console.log(e.detail));
store.apply(
[
{
op: "create-shape",
record: {
id: "frame-1",
type: "frame",
parentId: null,
index: generateKeyBetween(null, null),
props: { x: 0, y: 0, w: 640, h: 480 },
},
},
],
{ origin: "agent" },
);