@pixerate/editor 0.233.0 → 0.235.0

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 (38) hide show
  1. package/dist/animation-editor/controller.d.cts +25 -0
  2. package/dist/animation-editor/controller.d.ts +25 -0
  3. package/dist/animation-editor/index.cjs +159 -50
  4. package/dist/animation-editor/index.d.cts +1 -0
  5. package/dist/animation-editor/index.d.ts +1 -0
  6. package/dist/animation-editor/index.js +115 -6
  7. package/dist/animation-editor/layers.d.cts +2 -0
  8. package/dist/animation-editor/layers.d.ts +2 -0
  9. package/dist/animation-editor/sequence.d.cts +33 -0
  10. package/dist/animation-editor/sequence.d.ts +33 -0
  11. package/dist/canvas/editorDocument.d.cts +49 -4
  12. package/dist/canvas/editorDocument.d.ts +49 -4
  13. package/dist/canvas/index.cjs +47 -20
  14. package/dist/canvas/index.js +40 -13
  15. package/dist/{chunk-I277KJLQ.js → chunk-G3RUKJ5C.js} +105 -1
  16. package/dist/{chunk-6RZVFYGJ.cjs → chunk-JWCKJNDR.cjs} +105 -1
  17. package/dist/effects/index.cjs +19 -11
  18. package/dist/effects/index.d.cts +1 -0
  19. package/dist/effects/index.d.ts +1 -0
  20. package/dist/effects/index.js +9 -1
  21. package/dist/effects/template-tools.d.cts +25 -0
  22. package/dist/effects/template-tools.d.ts +25 -0
  23. package/dist/effects/templates.d.cts +30 -1
  24. package/dist/effects/templates.d.ts +30 -1
  25. package/dist/video-editor/controller.d.cts +26 -0
  26. package/dist/video-editor/controller.d.ts +26 -0
  27. package/dist/video-editor/index.cjs +187 -42
  28. package/dist/video-editor/index.d.cts +1 -0
  29. package/dist/video-editor/index.d.ts +1 -0
  30. package/dist/video-editor/index.js +145 -0
  31. package/dist/video-editor/sequence.d.cts +38 -0
  32. package/dist/video-editor/sequence.d.ts +38 -0
  33. package/package.json +1 -1
  34. package/skills/pixerate-animation-editor/SKILL.md +10 -0
  35. package/skills/pixerate-animation-motion-graphics/SKILL.md +4 -0
  36. package/skills/pixerate-canvas/SKILL.md +2 -2
  37. package/skills/pixerate-video-editor/SKILL.md +11 -0
  38. package/skills/pixerate-video-montage/SKILL.md +2 -0
@@ -1,9 +1,3 @@
1
- import {
2
- effectLayerInput,
3
- effectLayerOffered,
4
- effectLayerRole,
5
- effectSubjectKind
6
- } from "../chunk-I277KJLQ.js";
7
1
  import {
8
2
  createVectorMorph
9
3
  } from "../chunk-YMREOA67.js";
@@ -15,6 +9,14 @@ import {
15
9
  namedWithSuggestion,
16
10
  suggestName
17
11
  } from "../chunk-EUKY4KFX.js";
12
+ import {
13
+ effectLayerInput,
14
+ effectLayerOffered,
15
+ effectLayerRole,
16
+ effectSubjectKind,
17
+ effectTemplateTools,
18
+ fillEffectTemplate
19
+ } from "../chunk-G3RUKJ5C.js";
18
20
  import {
19
21
  IDENTITY_MATRIX,
20
22
  applyMatrix,
@@ -1802,7 +1804,10 @@ var MotionLayersEngine = class {
1802
1804
  this.reset();
1803
1805
  this.listeners.clear();
1804
1806
  }
1807
+ /** Goes up each time pictures come in or are cleared, so a view that caches frames knows to draw again. */
1808
+ revision = 0;
1805
1809
  notify() {
1810
+ this.revision++;
1806
1811
  this.listeners.forEach((l) => l());
1807
1812
  }
1808
1813
  size(b) {
@@ -4484,6 +4489,57 @@ async function checkMotionShotLayers(scene, provider) {
4484
4489
  return issues;
4485
4490
  }
4486
4491
 
4492
+ // src/animation-editor/sequence.ts
4493
+ var fresh = (used, base) => {
4494
+ const clean = base.replace(/[^\w.-]+/g, "-").replace(/^[^A-Za-z_]+/, "") || "item";
4495
+ if (!used.has(clean)) return used.add(clean), clean;
4496
+ for (let i = 2; ; i++) if (!used.has(`${clean}-${i}`)) return used.add(`${clean}-${i}`), `${clean}-${i}`;
4497
+ };
4498
+ function motionShotsFromSequence(sequence, options = {}) {
4499
+ const sx = (options.width ?? sequence.stage.width) / sequence.stage.width;
4500
+ const sy = (options.height ?? sequence.stage.height) / sequence.stage.height;
4501
+ const scale = (r) => ({ x: r.x * sx, y: r.y * sy, width: r.width * sx, height: r.height * sy });
4502
+ const full = { x: 0, y: 0, width: sequence.stage.width * sx, height: sequence.stage.height * sy };
4503
+ const shotIds = new Set(options.shotIds ?? []);
4504
+ const layerIds = new Set(options.layerIds ?? []);
4505
+ const notes = [];
4506
+ const provider = options.provider ? { provider: options.provider } : {};
4507
+ const shots = sequence.shots.map((s, i) => {
4508
+ const id = fresh(shotIds, `shot${i + 1}`);
4509
+ const framing = s.camera?.framing ? scale(s.camera.framing) : void 0;
4510
+ const view = framing ?? full;
4511
+ const layers = [];
4512
+ for (const layer of s.layers) {
4513
+ if (layer.subject && "video" in layer.subject) {
4514
+ notes.push(`Shot ${i + 1}: a layer plays a video, which an animation cannot; it was left out (the video editor plays it).`);
4515
+ continue;
4516
+ }
4517
+ const { id: given, rect, subject, ...rest } = layer;
4518
+ layers.push({ ...rest, id: fresh(layerIds, given ?? "layer"), ...subject ? { subject } : {}, ...rect ? { rect: scale(rect) } : {}, ...layer.effect ? provider : {} });
4519
+ }
4520
+ if (s.title) layers.push({ id: fresh(layerIds, `${id}-title`), subject: { text: s.title }, role: "overlay", rect: { x: view.x + view.width * 0.1, y: view.y + view.height * 0.38, width: view.width * 0.8, height: view.height * 0.24 } });
4521
+ if (s.caption) layers.push({ id: fresh(layerIds, `${id}-caption`), subject: { text: s.caption }, role: "overlay", rect: { x: view.x + view.width * 0.1, y: view.y + view.height * 0.8, width: view.width * 0.8, height: view.height * 0.1 } });
4522
+ const camera = framing || s.camera?.in !== void 0 || s.camera?.during !== void 0 ? { ...framing ?? {}, ...s.camera?.in !== void 0 ? { in: s.camera.in } : {}, ...s.camera?.during !== void 0 ? { during: s.camera.during } : {} } : void 0;
4523
+ return {
4524
+ id,
4525
+ label: s.title ?? s.caption ?? `Shot ${i + 1}`,
4526
+ ...camera ? { camera } : {},
4527
+ run: [],
4528
+ hold: s.hold,
4529
+ ...layers.length ? { layers } : {},
4530
+ ...i > 0 && s.transition ? { transition: { ...s.transition, ...provider } } : {}
4531
+ };
4532
+ });
4533
+ return { shots, notes };
4534
+ }
4535
+ function motionSceneFromSequence(sequence, options = {}) {
4536
+ const { shots, notes } = motionShotsFromSequence(sequence, options);
4537
+ return {
4538
+ scene: { version: 1, name: sequence.name, ...sequence.title ? { description: sequence.title } : {}, artwork: { width: sequence.stage.width, height: sequence.stage.height, background: options.background ?? "#000000", nodes: [] }, shots },
4539
+ notes
4540
+ };
4541
+ }
4542
+
4487
4543
  // src/animation-editor/effects.ts
4488
4544
  var sourceOf = (c) => c.source;
4489
4545
  function motionSceneEffects(scene) {
@@ -4667,6 +4723,7 @@ var AnimationEditorController = class {
4667
4723
  redoLabel: h.redoName,
4668
4724
  revision: this.revision,
4669
4725
  effectsProvider: this.effects.provider,
4726
+ layerPictures: this.layers.revision,
4670
4727
  recording: this.recording ? {
4671
4728
  shot: this.recording.options.id,
4672
4729
  at: this.recording.options.at,
@@ -5175,6 +5232,42 @@ var AnimationEditorController = class {
5175
5232
  if (!r.ok) return r;
5176
5233
  return this.layerResult(shot, void 0);
5177
5234
  }
5235
+ /**
5236
+ * Fills a provider sequence template and loads it: `replace` (default) makes a new scene the
5237
+ * size of the template's stage from its shots; `append` adds its shots after the current ones,
5238
+ * scaled to the artwork. Values are checked first and every problem is refused at once. One
5239
+ * undo step. Returns the new shot and layer ids, slots left open and notes.
5240
+ */
5241
+ async fillTemplate(template, values = {}, options = {}) {
5242
+ const reason = this.readOnlyReason();
5243
+ if (reason) return this.refuse(reason);
5244
+ const mode = options.mode ?? "replace";
5245
+ let filled;
5246
+ try {
5247
+ filled = await fillEffectTemplate(this.effects.provider, template, values, options.name ? { name: options.name } : {});
5248
+ } catch (error) {
5249
+ if (isEffectError(error) || error instanceof Error) return this.refuse(error.message);
5250
+ throw error;
5251
+ }
5252
+ const provider = this.effects.provider.id;
5253
+ const label = `Template ${filled.template.name}`;
5254
+ let shots;
5255
+ let notes;
5256
+ let r;
5257
+ if (mode === "replace") {
5258
+ const made = motionSceneFromSequence(filled.sequence, { provider });
5259
+ shots = made.scene.shots;
5260
+ notes = made.notes;
5261
+ r = this.apply({ op: "replace_scene", scene: made.scene }, label);
5262
+ } else {
5263
+ const made = motionShotsFromSequence(filled.sequence, { provider, width: this.scene.artwork.width, height: this.scene.artwork.height, shotIds: this.scene.shots.map((s) => s.id), layerIds: this.scene.shots.flatMap((s) => (s.layers ?? []).map((l) => l.id)) });
5264
+ shots = made.shots;
5265
+ notes = made.notes;
5266
+ r = this.apply({ op: "insert_shots", shots: made.shots }, label);
5267
+ }
5268
+ if (!r.ok) return r;
5269
+ return { ok: true, template, mode, shots: shots.map((s) => s.id), layers: shots.flatMap((s) => (s.layers ?? []).map((l) => l.id)), openSlots: filled.openSlots, notes: [...filled.notes, ...notes] };
5270
+ }
5178
5271
  // ---------------------------------------------------------------------------------------
5179
5272
  // Recording
5180
5273
  // ---------------------------------------------------------------------------------------
@@ -7087,6 +7180,20 @@ function createAnimationEffectsToolProvider(controller, options = {}) {
7087
7180
  extraTools: (ctx) => [
7088
7181
  ...animationCameraMoveTools(controller, ctx),
7089
7182
  ...animationShotLayerTools(controller, ctx),
7183
+ ...effectTemplateTools(
7184
+ {
7185
+ provider: () => controller.effectsProvider,
7186
+ fillDetails: "mode replace (default) makes a new scene from the template's shots, the size of its stage; mode append adds its shots after the current ones, scaled to the artwork. Each shot keeps its framing and camera moves, its layers and its transition; its title and caption become overlay word layers. Returns { template, mode, shots (new shot ids), layers (new layer ids), openSlots, notes }. Check the result with render_animation_frame.",
7187
+ fillOptions: { mode: { type: "string", enum: ["replace", "append"], description: "replace (default): a new scene from the template; append: its shots after the current ones." } },
7188
+ fill: async (template, values, options2) => {
7189
+ const r = await controller.fillTemplate(template, values, { name: options2.name, mode: options2.mode });
7190
+ if (!r.ok) throw new Error(r.error);
7191
+ const { ok: _ok, ...rest } = r;
7192
+ return rest;
7193
+ }
7194
+ },
7195
+ ctx
7196
+ ),
7090
7197
  {
7091
7198
  name: "add_vector_effect",
7092
7199
  title: "Add a vector effect",
@@ -7296,6 +7403,7 @@ export {
7296
7403
  motionRevealPadding,
7297
7404
  motionRulerTicks,
7298
7405
  motionSceneEffects,
7406
+ motionSceneFromSequence,
7299
7407
  motionSceneHasLayers,
7300
7408
  motionSceneShapeProblems,
7301
7409
  motionSceneToJson,
@@ -7304,6 +7412,7 @@ export {
7304
7412
  motionShotCueTime,
7305
7413
  motionShotLayersInfo,
7306
7414
  motionShotTimeScale,
7415
+ motionShotsFromSequence,
7307
7416
  motionStageSvg,
7308
7417
  motionTakeProblems,
7309
7418
  motionTakeShot,
@@ -144,6 +144,8 @@ export declare class MotionLayersEngine implements MotionLayerPictures {
144
144
  /** Forgets every picture and failure (a new provider, or the host changed). */
145
145
  reset(): void;
146
146
  dispose(): void;
147
+ /** Goes up each time pictures come in or are cleared, so a view that caches frames knows to draw again. */
148
+ revision: number;
147
149
  private notify;
148
150
  private size;
149
151
  private snap;
@@ -144,6 +144,8 @@ export declare class MotionLayersEngine implements MotionLayerPictures {
144
144
  /** Forgets every picture and failure (a new provider, or the host changed). */
145
145
  reset(): void;
146
146
  dispose(): void;
147
+ /** Goes up each time pictures come in or are cleared, so a view that caches frames knows to draw again. */
148
+ revision: number;
147
149
  private notify;
148
150
  private size;
149
151
  private snap;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * An effects provider's `EffectSequence` (what a sequence template fills to) as animation shots:
3
+ * each shot keeps its framing and camera moves, its layers and its transition. Its title and
4
+ * caption become overlay word layers placed in the shot's framing. Rects and framings are scaled
5
+ * from the sequence's stage to the artwork.
6
+ */
7
+ import type { EffectSequence } from '../effects/types.cjs';
8
+ import type { MotionScene, MotionShot } from './types.cjs';
9
+ export interface MotionShotsFromSequence {
10
+ shots: MotionShot[];
11
+ /** What could not be carried over, in sentences. */
12
+ notes: string[];
13
+ }
14
+ export interface MotionSequenceOptions {
15
+ /** The artwork size the shots are for (default the sequence's stage). */
16
+ width?: number;
17
+ height?: number;
18
+ /** Shot ids already used (new ones avoid them). */
19
+ shotIds?: Iterable<string>;
20
+ /** Layer ids already used (new ones avoid them). */
21
+ layerIds?: Iterable<string>;
22
+ /** The provider the sequence's effects come from, saved on layers and transitions. */
23
+ provider?: string;
24
+ }
25
+ /** The shots of a sequence, sized for the artwork (see the module comment). */
26
+ export declare function motionShotsFromSequence(sequence: EffectSequence, options?: MotionSequenceOptions): MotionShotsFromSequence;
27
+ /** A new scene from a sequence: an empty artwork the size of its stage, and its shots. */
28
+ export declare function motionSceneFromSequence(sequence: EffectSequence, options?: Pick<MotionSequenceOptions, 'provider'> & {
29
+ background?: string;
30
+ }): {
31
+ scene: MotionScene;
32
+ notes: string[];
33
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * An effects provider's `EffectSequence` (what a sequence template fills to) as animation shots:
3
+ * each shot keeps its framing and camera moves, its layers and its transition. Its title and
4
+ * caption become overlay word layers placed in the shot's framing. Rects and framings are scaled
5
+ * from the sequence's stage to the artwork.
6
+ */
7
+ import type { EffectSequence } from '../effects/types.js';
8
+ import type { MotionScene, MotionShot } from './types.js';
9
+ export interface MotionShotsFromSequence {
10
+ shots: MotionShot[];
11
+ /** What could not be carried over, in sentences. */
12
+ notes: string[];
13
+ }
14
+ export interface MotionSequenceOptions {
15
+ /** The artwork size the shots are for (default the sequence's stage). */
16
+ width?: number;
17
+ height?: number;
18
+ /** Shot ids already used (new ones avoid them). */
19
+ shotIds?: Iterable<string>;
20
+ /** Layer ids already used (new ones avoid them). */
21
+ layerIds?: Iterable<string>;
22
+ /** The provider the sequence's effects come from, saved on layers and transitions. */
23
+ provider?: string;
24
+ }
25
+ /** The shots of a sequence, sized for the artwork (see the module comment). */
26
+ export declare function motionShotsFromSequence(sequence: EffectSequence, options?: MotionSequenceOptions): MotionShotsFromSequence;
27
+ /** A new scene from a sequence: an empty artwork the size of its stage, and its shots. */
28
+ export declare function motionSceneFromSequence(sequence: EffectSequence, options?: Pick<MotionSequenceOptions, 'provider'> & {
29
+ background?: string;
30
+ }): {
31
+ scene: MotionScene;
32
+ notes: string[];
33
+ };
@@ -1,16 +1,19 @@
1
1
  import type { HistoryManager } from '../history/HistoryManager.cjs';
2
+ import type { EditorAssetRegistry } from '../persistence/assets.cjs';
2
3
  import { type EditorDocument } from '../persistence/editorDocument.cjs';
3
- import type { CanvasScene, CanvasScenesStore } from './scenes.cjs';
4
+ import type { CanvasScene, CanvasScenesStore, CanvasViewport } from './scenes.cjs';
4
5
  import type { CanvasEdge, CanvasNode } from './types.cjs';
5
6
  /** What the document needs of an edge (React Flow's and Svelte Flow's edges fit). */
6
7
  export type CanvasEdgeLike = Pick<CanvasEdge, 'id' | 'source' | 'target'> & {
7
8
  selected?: boolean;
8
9
  };
9
- /** A canvas as it is saved: its nodes, edges and (with a scenes store) scenes. */
10
+ /** A canvas as it is saved: its nodes, edges, (with a scenes store) scenes and (with `getViewport`) the view. */
10
11
  export interface CanvasEditorDocument<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdgeLike = CanvasEdge> {
11
12
  nodes: TNode[];
12
13
  edges: TEdge[];
13
14
  scenes?: CanvasScene[];
15
+ /** Where the canvas was looking when it was saved. */
16
+ viewport?: CanvasViewport;
14
17
  }
15
18
  export interface CanvasEditorDocumentOptions<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdgeLike = CanvasEdge> {
16
19
  getNodes: () => TNode[];
@@ -28,12 +31,54 @@ export interface CanvasEditorDocumentOptions<TNode extends CanvasNode = CanvasNo
28
31
  subscribe?: (listener: () => void) => () => void;
29
32
  /** Why the canvas cannot be changed (a sentence), or `false`. */
30
33
  readOnly?: () => string | false;
34
+ /**
35
+ * The view saved with the document and restored on load. Panning and zooming are not
36
+ * document changes: they do not bump the revision, so they alone do not trigger a save.
37
+ */
38
+ getViewport?: () => CanvasViewport;
39
+ setViewport?: (viewport: CanvasViewport) => void;
40
+ /** Node fields left out of the document, besides `selected`, `dragging`, `measured` and `resizing`. */
41
+ omitNodeKeys?: readonly string[];
42
+ /** Edge fields left out of the document, besides `selected`. */
43
+ omitEdgeKeys?: readonly string[];
44
+ /**
45
+ * Checks a node of a document being loaded, after the built-in checks. Return what is wrong
46
+ * (a sentence) to refuse the whole load, or nothing to accept the node.
47
+ */
48
+ validateNode?: (node: TNode, index: number) => string | void;
49
+ /**
50
+ * Turns any document handed to `loadDocument` (a raw one or a migrated file) into the current
51
+ * shape before it is checked, e.g. an older layout the host stored without a file envelope.
52
+ */
53
+ normalize?: (document: unknown) => unknown;
54
+ /** The host's format version of the document (default 1); files with an older one are migrated. */
55
+ formatVersion?: number;
56
+ /** `migrations[v]` turns a version `v` file's document into version `v + 1`. Only files carry a version. */
57
+ migrations?: Record<number, (document: unknown) => unknown>;
58
+ /**
59
+ * Keeps inline binary data in node and edge data (image data URLs, long base64) out of the
60
+ * document as asset references; autosave and the document store save the bytes with it.
61
+ */
62
+ assets?: EditorAssetRegistry;
63
+ }
64
+ /** What to leave out of canvas nodes and edges: the runtime fields plus the host's own. */
65
+ export interface CanvasDocumentOmitOptions {
66
+ omitNodeKeys?: readonly string[];
67
+ omitEdgeKeys?: readonly string[];
31
68
  }
69
+ /**
70
+ * A string that changes only when the saved part of the nodes or edges changes (not selection,
71
+ * measuring or the omitted fields). The React and Svelte document bindings compare it to count
72
+ * changes the history does not see.
73
+ */
74
+ export declare function canvasDocumentContentKey(nodes: readonly CanvasNode[], edges: readonly CanvasEdgeLike[], options?: CanvasDocumentOmitOptions): string;
32
75
  /**
33
76
  * The save / load / undo contract for a canvas whose nodes and edges live in the host (React
34
77
  * Flow or Svelte Flow state), kind `canvas`. The document is the nodes and edges without
35
- * selection or measurement, plus the scenes when a store is given. Recorded loads are one
36
- * undo step in the shared `history`; opening (`history: "reset"`) clears it.
78
+ * selection, measurement or `omitNodeKeys` / `omitEdgeKeys`, plus the scenes when a store is
79
+ * given and the viewport with `getViewport`. Loads are normalized, then checked (built-in
80
+ * checks, then `validateNode`) before anything changes. Recorded loads are one undo step in the
81
+ * shared `history`; opening (`history: "reset"`) clears it.
37
82
  */
38
83
  export declare function createCanvasEditorDocument<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdgeLike = CanvasEdge>(options: CanvasEditorDocumentOptions<TNode, TEdge>): EditorDocument<CanvasEditorDocument<TNode, TEdge>>;
39
84
  export interface CanvasGestureRecorderOptions<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdgeLike = CanvasEdge> {
@@ -1,16 +1,19 @@
1
1
  import type { HistoryManager } from '../history/HistoryManager.js';
2
+ import type { EditorAssetRegistry } from '../persistence/assets.js';
2
3
  import { type EditorDocument } from '../persistence/editorDocument.js';
3
- import type { CanvasScene, CanvasScenesStore } from './scenes.js';
4
+ import type { CanvasScene, CanvasScenesStore, CanvasViewport } from './scenes.js';
4
5
  import type { CanvasEdge, CanvasNode } from './types.js';
5
6
  /** What the document needs of an edge (React Flow's and Svelte Flow's edges fit). */
6
7
  export type CanvasEdgeLike = Pick<CanvasEdge, 'id' | 'source' | 'target'> & {
7
8
  selected?: boolean;
8
9
  };
9
- /** A canvas as it is saved: its nodes, edges and (with a scenes store) scenes. */
10
+ /** A canvas as it is saved: its nodes, edges, (with a scenes store) scenes and (with `getViewport`) the view. */
10
11
  export interface CanvasEditorDocument<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdgeLike = CanvasEdge> {
11
12
  nodes: TNode[];
12
13
  edges: TEdge[];
13
14
  scenes?: CanvasScene[];
15
+ /** Where the canvas was looking when it was saved. */
16
+ viewport?: CanvasViewport;
14
17
  }
15
18
  export interface CanvasEditorDocumentOptions<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdgeLike = CanvasEdge> {
16
19
  getNodes: () => TNode[];
@@ -28,12 +31,54 @@ export interface CanvasEditorDocumentOptions<TNode extends CanvasNode = CanvasNo
28
31
  subscribe?: (listener: () => void) => () => void;
29
32
  /** Why the canvas cannot be changed (a sentence), or `false`. */
30
33
  readOnly?: () => string | false;
34
+ /**
35
+ * The view saved with the document and restored on load. Panning and zooming are not
36
+ * document changes: they do not bump the revision, so they alone do not trigger a save.
37
+ */
38
+ getViewport?: () => CanvasViewport;
39
+ setViewport?: (viewport: CanvasViewport) => void;
40
+ /** Node fields left out of the document, besides `selected`, `dragging`, `measured` and `resizing`. */
41
+ omitNodeKeys?: readonly string[];
42
+ /** Edge fields left out of the document, besides `selected`. */
43
+ omitEdgeKeys?: readonly string[];
44
+ /**
45
+ * Checks a node of a document being loaded, after the built-in checks. Return what is wrong
46
+ * (a sentence) to refuse the whole load, or nothing to accept the node.
47
+ */
48
+ validateNode?: (node: TNode, index: number) => string | void;
49
+ /**
50
+ * Turns any document handed to `loadDocument` (a raw one or a migrated file) into the current
51
+ * shape before it is checked, e.g. an older layout the host stored without a file envelope.
52
+ */
53
+ normalize?: (document: unknown) => unknown;
54
+ /** The host's format version of the document (default 1); files with an older one are migrated. */
55
+ formatVersion?: number;
56
+ /** `migrations[v]` turns a version `v` file's document into version `v + 1`. Only files carry a version. */
57
+ migrations?: Record<number, (document: unknown) => unknown>;
58
+ /**
59
+ * Keeps inline binary data in node and edge data (image data URLs, long base64) out of the
60
+ * document as asset references; autosave and the document store save the bytes with it.
61
+ */
62
+ assets?: EditorAssetRegistry;
63
+ }
64
+ /** What to leave out of canvas nodes and edges: the runtime fields plus the host's own. */
65
+ export interface CanvasDocumentOmitOptions {
66
+ omitNodeKeys?: readonly string[];
67
+ omitEdgeKeys?: readonly string[];
31
68
  }
69
+ /**
70
+ * A string that changes only when the saved part of the nodes or edges changes (not selection,
71
+ * measuring or the omitted fields). The React and Svelte document bindings compare it to count
72
+ * changes the history does not see.
73
+ */
74
+ export declare function canvasDocumentContentKey(nodes: readonly CanvasNode[], edges: readonly CanvasEdgeLike[], options?: CanvasDocumentOmitOptions): string;
32
75
  /**
33
76
  * The save / load / undo contract for a canvas whose nodes and edges live in the host (React
34
77
  * Flow or Svelte Flow state), kind `canvas`. The document is the nodes and edges without
35
- * selection or measurement, plus the scenes when a store is given. Recorded loads are one
36
- * undo step in the shared `history`; opening (`history: "reset"`) clears it.
78
+ * selection, measurement or `omitNodeKeys` / `omitEdgeKeys`, plus the scenes when a store is
79
+ * given and the viewport with `getViewport`. Loads are normalized, then checked (built-in
80
+ * checks, then `validateNode`) before anything changes. Recorded loads are one undo step in the
81
+ * shared `history`; opening (`history: "reset"`) clears it.
37
82
  */
38
83
  export declare function createCanvasEditorDocument<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdgeLike = CanvasEdge>(options: CanvasEditorDocumentOptions<TNode, TEdge>): EditorDocument<CanvasEditorDocument<TNode, TEdge>>;
39
84
  export interface CanvasGestureRecorderOptions<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdgeLike = CanvasEdge> {