@overtone-art/canvas-editor-core 0.2.6 → 0.2.8

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/dist/index.d.mts CHANGED
@@ -1,221 +1,6 @@
1
- import { FabricObject, Canvas, ImageFormat } from 'fabric';
2
-
3
- type Unit = 'px' | 'mm' | 'in';
4
- type LayerType = 'image' | 'text' | 'shape' | 'template' | 'group';
5
- interface LayerData {
6
- id: string;
7
- type: LayerType;
8
- name: string;
9
- visible: boolean;
10
- locked: boolean;
11
- opacity: number;
12
- /** Non-fabric metadata persisted with the layer (pattern config, etc). */
13
- meta?: LayerMeta;
14
- }
15
- interface LayerMeta {
16
- pattern?: PatternState;
17
- [key: string]: unknown;
18
- }
19
- interface SerializedLayer extends LayerData {
20
- fabricObject: Record<string, unknown>;
21
- }
22
- interface EditorState {
23
- version: string;
24
- canvas: {
25
- width: number;
26
- height: number;
27
- };
28
- layers: SerializedLayer[];
29
- background?: string;
30
- mockup?: MockupConfig | null;
31
- }
32
- interface EditorConfig {
33
- width: number;
34
- height: number;
35
- backgroundColor?: string;
36
- fileAdapter?: FileAdapter;
37
- imageProvider?: ImageProvider;
38
- preserveObjectStacking?: boolean;
39
- unit?: Unit;
40
- dpi?: number;
41
- }
42
- type ExportFormat = 'png' | 'svg' | 'json';
43
- interface FileAdapter {
44
- save(data: Blob | string, filename: string, format: ExportFormat): Promise<string>;
45
- }
46
- interface ImageProviderResult {
47
- url: string;
48
- width: number;
49
- height: number;
50
- }
51
- interface ImageProvider {
52
- upload(file: File): Promise<ImageProviderResult>;
53
- browse?(): Promise<ImageProviderResult | null>;
54
- }
55
- interface ShapePlugin {
56
- name: string;
57
- icon: string;
58
- category?: string;
59
- create(options?: Record<string, unknown>): FabricObject;
60
- }
61
- interface TemplateParameter {
62
- key: string;
63
- label: string;
64
- type: 'text' | 'color' | 'number' | 'font';
65
- default: string | number;
66
- }
67
- interface TemplateDefinition {
68
- id: string;
69
- name: string;
70
- category: string;
71
- svg: string;
72
- parameters: TemplateParameter[];
73
- preview?: string;
74
- }
75
- interface PrintifyPositioning {
76
- x: number;
77
- y: number;
78
- scale: number;
79
- angle: number;
80
- }
81
- type TileMode = 'grid' | 'brick-horizontal' | 'brick-vertical';
82
- interface PatternConfig {
83
- mode: TileMode;
84
- /**
85
- * Tile size as a % of the source image's on-canvas size (100 = natural).
86
- * Smaller values pack more repeats across the print area; larger values make
87
- * fewer, bigger tiles. Independent of how the source layer was placed.
88
- */
89
- scale: number;
90
- /** Extra horizontal gap between tiles, as % of tile width. */
91
- horizontalSpacing: number;
92
- /** Extra vertical gap between tiles, as % of tile height. */
93
- verticalSpacing: number;
94
- /** Rotation of the whole pattern, in degrees. */
95
- angle: number;
96
- /** Brick row/column shift, as % of tile size. */
97
- horizontalOffset: number;
98
- /**
99
- * Phase shift of the whole tile grid inside the print area, as % of tile
100
- * width. The pattern object itself is pinned to the print area (it can't be
101
- * dragged — that would expose bare edges), so this is how the tiling is
102
- * nudged into alignment. Clamped to ±100% so coverage is never broken.
103
- */
104
- offsetX: number;
105
- /** Phase shift of the tile grid, as % of tile height. See {@link offsetX}. */
106
- offsetY: number;
107
- /** Added rotation per horizontal step, in degrees. */
108
- rotationStepH: number;
109
- /** Added rotation per vertical step, in degrees. */
110
- rotationStepV: number;
111
- }
112
- /** Fabric interaction flags frozen while a layer is rendered as a pattern. */
113
- interface PatternLocks {
114
- lockMovementX: boolean;
115
- lockMovementY: boolean;
116
- lockScalingX: boolean;
117
- lockScalingY: boolean;
118
- lockRotation: boolean;
119
- hasControls: boolean;
120
- }
121
- /** Persisted on a layer's meta while it is rendered as a repeating pattern. */
122
- interface PatternState {
123
- config: PatternConfig;
124
- /** Original (pre-pattern) image source, as a durable data URL when possible. */
125
- originalSrc: string;
126
- /**
127
- * Serialized `clipPath` the image carried before it became a pattern, or null
128
- * if it had none. A pattern must fill the whole print area, so any pre-pattern
129
- * clip (e.g. a template crop sized to the original box) is stripped while the
130
- * pattern is on and re-applied when it is turned off. Persisted so it survives
131
- * serialization / reload / undo.
132
- */
133
- originalClip?: Record<string, unknown> | null;
134
- /**
135
- * Interaction flags the object carried before it became a pattern. A pattern
136
- * fills the whole print area, so while it is on the object is locked in place
137
- * (a drag/resize would slide the tiled bitmap off the area and leave a bare
138
- * band); the captured flags are restored when the pattern is turned off.
139
- */
140
- originalLocks?: PatternLocks;
141
- /** Original fabric transform, restored when the pattern is turned off. */
142
- original: {
143
- left: number;
144
- top: number;
145
- scaleX: number;
146
- scaleY: number;
147
- width: number;
148
- height: number;
149
- angle: number;
150
- cropX: number;
151
- cropY: number;
152
- };
153
- }
154
- declare const DEFAULT_PATTERN_CONFIG: PatternConfig;
155
- interface MockupPrintArea {
156
- left: number;
157
- top: number;
158
- width: number;
159
- height: number;
160
- }
161
- interface MockupConfig {
162
- /** Garment/product image rendered behind the design (preview only). */
163
- image: string;
164
- /** Print-area rectangle, in canvas pixels. Drawn as a guide. */
165
- printArea?: MockupPrintArea;
166
- }
167
- interface EditorEvents {
168
- 'layer:added': {
169
- layer: LayerData;
170
- };
171
- 'layer:removed': {
172
- layerId: string;
173
- };
174
- 'layer:selected': {
175
- layerId: string | null;
176
- };
177
- 'layer:modified': {
178
- layerId: string;
179
- };
180
- 'layer:reordered': {
181
- layerIds: string[];
182
- };
183
- 'layers:changed': {
184
- layers: LayerData[];
185
- };
186
- 'selection:changed': {
187
- selected: string[];
188
- };
189
- 'history:changed': {
190
- canUndo: boolean;
191
- canRedo: boolean;
192
- };
193
- 'zoom:changed': {
194
- zoom: number;
195
- };
196
- 'snap:changed': {
197
- enabled: boolean;
198
- };
199
- 'crop:changed': {
200
- active: boolean;
201
- layerId: string | null;
202
- };
203
- 'mockup:changed': {
204
- mockup: MockupConfig | null;
205
- };
206
- 'canvas:modified': Record<string, never>;
207
- 'export:start': {
208
- format: string;
209
- };
210
- 'export:complete': {
211
- format: string;
212
- url?: string;
213
- };
214
- error: {
215
- message: string;
216
- error?: unknown;
217
- };
218
- }
1
+ import { Canvas, FabricObject, ImageFormat } from 'fabric';
2
+ import { b as EditorEvents, l as LayerType, k as LayerMeta, L as LayerData, U as SerializedLayer, G as PatternSourceResolver, P as PatternConfig, A as PatternLocks, _ as Unit, z as MockupPrintArea, v as MockupConfig, e as FontDefinition, m as LicenseConfig, o as LicenseStatus, Q as ProjectState, M as MaskBrushOptions, p as MaskPoint, r as MaskRefinementProvider, q as MaskRefinementPrompt, t as MaskRefinementResult, E as EditorConfig, V as ShapePlugin, X as TemplateDefinition, I as ImageAdjustments, c as EditorState, S as SemanticExportOptions, W as SvgExportOptions, K as PrintifyPositioning, N as NormalizedLayerPosition, J as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, T as SerializedBackgroundImageOptions, B as BackgroundImageOptions, R as ResizeOptions, a as DpiIssue, C as CanvasSizePreset, w as MockupDisplacement } from './types-D60CfxL9.mjs';
3
+ export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, n as LicensePayload, s as MaskRefinementRequest, u as MockupBlendMode, x as MockupDisplacementChannel, y as MockupOverlay, H as PatternState, O as ProjectPage, Y as TemplateParameter, Z as TileMode } from './types-D60CfxL9.mjs';
219
4
 
220
5
  type EventHandler<T> = (data: T) => void;
221
6
  declare class EventEmitter<TEvents extends {} = Record<string, unknown>> {
@@ -236,7 +21,7 @@ declare class Layer {
236
21
  opacity: number;
237
22
  /** Non-fabric data (e.g. pattern config) that must persist with the layer. */
238
23
  meta: LayerMeta;
239
- readonly fabricObject: FabricObject;
24
+ fabricObject: FabricObject;
240
25
  constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string);
241
26
  private hasMeta;
242
27
  toData(): LayerData;
@@ -246,9 +31,13 @@ declare class LayerManager {
246
31
  private canvas;
247
32
  private events;
248
33
  private layers;
34
+ private onPropertyChanged?;
249
35
  constructor(canvas: Canvas, events: EventEmitter<EditorEvents>);
36
+ setHistoryCallback(callback: () => void): void;
250
37
  add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer;
251
38
  remove(id: string): boolean;
39
+ /** Replace a layer's render object while preserving its immutable ID and panel state. */
40
+ replaceObject(id: string, fabricObject: FabricObject): boolean;
252
41
  reorder(id: string, newIndex: number): boolean;
253
42
  select(id: string | null): void;
254
43
  get(id: string): Layer | undefined;
@@ -264,24 +53,35 @@ declare class LayerManager {
264
53
  }
265
54
 
266
55
  declare class HistoryManager {
56
+ private static readonly ASSET_KEY;
267
57
  private undoStack;
268
58
  private redoStack;
59
+ private assets;
60
+ private assetIds;
61
+ private nextAssetId;
269
62
  private maxSize;
63
+ private maxBytes;
270
64
  private paused;
271
65
  private debounceTimer;
272
66
  private debounceMs;
273
67
  private getState;
274
68
  private restoreState;
275
69
  private events;
70
+ private transactionDepth;
71
+ private transactionDirty;
276
72
  constructor(opts: {
277
73
  getState: () => string;
278
74
  restoreState: (state: string) => Promise<void>;
279
75
  events: EventEmitter<EditorEvents>;
280
76
  maxSize?: number;
77
+ maxBytes?: number;
281
78
  debounceMs?: number;
282
79
  });
283
80
  save(): void;
284
81
  saveImmediate(): void;
82
+ beginTransaction(): void;
83
+ endTransaction(): void;
84
+ transaction<T>(operation: () => T | Promise<T>): Promise<T>;
285
85
  undo(): Promise<void>;
286
86
  redo(): Promise<void>;
287
87
  /** True while a restore (undo/redo/deserialize) is in flight. Managers that
@@ -292,9 +92,27 @@ declare class HistoryManager {
292
92
  canUndo(): boolean;
293
93
  canRedo(): boolean;
294
94
  clear(): void;
95
+ getSnapshotBytes(): number;
295
96
  private cancelPending;
296
97
  dispose(): void;
297
98
  private emitChanged;
99
+ private snapshotBytes;
100
+ private trimToBudget;
101
+ /**
102
+ * History is internal and can content-address large raster strings without
103
+ * changing the public EditorState wire format. Unchanged images are retained
104
+ * once even when dozens of snapshots reference them.
105
+ */
106
+ private compactState;
107
+ private expandState;
108
+ /**
109
+ * Runs on every commit, so it scans for the serialized reference marker
110
+ * instead of re-parsing every snapshot — parsing multi-megabyte raster states
111
+ * per save would cost more than the retention it reclaims. `JSON.stringify`
112
+ * emits the marker verbatim; the same text inside user data is escaped and
113
+ * therefore cannot match.
114
+ */
115
+ private pruneAssets;
298
116
  }
299
117
 
300
118
  /**
@@ -357,8 +175,10 @@ declare class PatternManager {
357
175
  private canvas;
358
176
  private layers;
359
177
  private history;
178
+ private events;
179
+ private sourceResolver?;
360
180
  private chains;
361
- constructor(canvas: Canvas, layers: LayerManager, history: HistoryManager);
181
+ constructor(canvas: Canvas, layers: LayerManager, history: HistoryManager, events: EventEmitter<EditorEvents>, sourceResolver?: PatternSourceResolver | undefined);
362
182
  isPattern(layerId: string): boolean;
363
183
  getConfig(layerId: string): PatternConfig | null;
364
184
  /** Turn a plain image layer into a pattern, or update an existing one. */
@@ -388,6 +208,7 @@ declare function applyPatternLocks(obj: FabricObject): void;
388
208
  * leaving it frozen forever.
389
209
  */
390
210
  declare function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void;
211
+ declare function loadPatternImage(src: string, resolver?: PatternSourceResolver): Promise<HTMLImageElement>;
391
212
  /** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
392
213
  declare function clearPatternImageCache(): void;
393
214
  /**
@@ -396,7 +217,7 @@ declare function clearPatternImageCache(): void;
396
217
  *
397
218
  * Exported for unit testing of the tile geometry.
398
219
  */
399
- declare function buildPatternDataURL(src: string, config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number): Promise<string>;
220
+ declare function buildPatternDataURL(src: string, config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number, sourceResolver?: PatternSourceResolver): Promise<string>;
400
221
  /** Placement of one tile, relative to the (already rotated) pattern centre. */
401
222
  interface TilePlacement {
402
223
  /** Tile-centre offset from the pattern centre, in px. */
@@ -442,10 +263,124 @@ interface PngExportOptions {
442
263
  format?: ImageFormat;
443
264
  quality?: number;
444
265
  }
266
+ interface CoverPlacement {
267
+ left: number;
268
+ top: number;
269
+ width: number;
270
+ height: number;
271
+ }
272
+ declare function computePrintAreaClip(area: MockupPrintArea, scaleX: number, scaleY: number, targetWidth: number, targetHeight: number): MockupPrintArea;
273
+ /** Object-fit: cover geometry, exported for deterministic preview/composite tests. */
274
+ declare function computeCoverPlacement(sourceWidth: number, sourceHeight: number, targetWidth: number, targetHeight: number): CoverPlacement;
445
275
  declare function exportPNG(canvas: Canvas, options?: PngExportOptions): Promise<Blob>;
276
+ /** Rasterize the browser mockup preview together with the transparent design. */
277
+ declare function exportMockup(canvas: Canvas, mockup: MockupConfig, options?: PngExportOptions): Promise<Blob>;
446
278
  declare function exportSVG(canvas: Canvas): string;
447
279
  declare function exportDataURL(canvas: Canvas, format?: ImageFormat, multiplier?: number): string;
448
280
 
281
+ /** Browser font loader and SVG embedding registry. */
282
+ declare class FontRegistry {
283
+ private definitions;
284
+ private loads;
285
+ register(definition: FontDefinition): void;
286
+ unregister(family: string): boolean;
287
+ getAll(): FontDefinition[];
288
+ load(family: string): Promise<FontFace>;
289
+ ready(): Promise<void>;
290
+ getEmbeddedCss(): Promise<string>;
291
+ }
292
+
293
+ /** Inert, honor-system licensing and usage seam; it never performs a network request. */
294
+ declare class LicenseManager {
295
+ private config;
296
+ private status;
297
+ private readyPromise;
298
+ constructor(config?: LicenseConfig);
299
+ getStatus(): LicenseStatus;
300
+ ready(): Promise<LicenseStatus>;
301
+ hasFeature(feature: string): boolean;
302
+ track(name: string): void;
303
+ private validate;
304
+ }
305
+
306
+ /** Multi-page document state bound to one live editor canvas. */
307
+ declare class ProjectManager {
308
+ private editor;
309
+ private pages;
310
+ private activePageId;
311
+ constructor(editor: CanvasEditor);
312
+ getAll(): Array<{
313
+ id: string;
314
+ name: string;
315
+ }>;
316
+ getActivePageId(): string;
317
+ add(name?: string, cloneCurrent?: boolean): string;
318
+ switchTo(id: string): Promise<boolean>;
319
+ duplicate(id: string): Promise<string | null>;
320
+ remove(id: string): Promise<boolean>;
321
+ rename(id: string, name: string): boolean;
322
+ reorder(id: string, newIndex: number): boolean;
323
+ toJSON(): ProjectState;
324
+ fromJSON(project: ProjectState): Promise<void>;
325
+ private saveCurrent;
326
+ private blankState;
327
+ private emitChanged;
328
+ }
329
+
330
+ type MaskRefinementErrorCode = 'not-found' | 'cancelled' | 'provider' | 'invalid-result';
331
+ declare class MaskRefinementError extends Error {
332
+ readonly code: MaskRefinementErrorCode;
333
+ readonly cause?: unknown | undefined;
334
+ constructor(code: MaskRefinementErrorCode, message: string, cause?: unknown | undefined);
335
+ }
336
+ interface MaskPerformanceSample {
337
+ width: number;
338
+ height: number;
339
+ backingBytes: number;
340
+ strokeBackupBytes: number;
341
+ estimatedPeakBytes: number;
342
+ historyBytes: number;
343
+ interactionLatencyMs: number;
344
+ usedJsHeapBytes?: number;
345
+ elapsedMs: number;
346
+ }
347
+ /** Raster-mask editing without exposing Fabric brush classes in the public contract. */
348
+ declare class MaskController {
349
+ private readonly editor;
350
+ private backing;
351
+ private context;
352
+ private layerId;
353
+ private brush;
354
+ private previousPoint;
355
+ private strokeBackup;
356
+ private strokeStartedAt;
357
+ private lastInteractionLatencyMs;
358
+ private refinement;
359
+ private disposed;
360
+ constructor(editor: CanvasEditor);
361
+ create(width?: number, height?: number): Promise<Layer>;
362
+ attach(layerId: string): void;
363
+ beginStroke(options: MaskBrushOptions): void;
364
+ addPoint(point: MaskPoint): void;
365
+ endStroke(): Promise<void>;
366
+ cancelStroke(): void;
367
+ isStrokeActive(): boolean;
368
+ activeLayerId(): string | null;
369
+ detach(layerId?: string): void;
370
+ refine(layerId: string, provider: MaskRefinementProvider, prompts: MaskRefinementPrompt[], options?: {
371
+ signal?: AbortSignal;
372
+ onProgress?: (progress: number) => void;
373
+ }): Promise<MaskRefinementResult>;
374
+ cancelRefinement(): void;
375
+ measure(): MaskPerformanceSample | null;
376
+ dispose(): void;
377
+ private drawDot;
378
+ private makeCanvas;
379
+ private attachBacking;
380
+ private requireMask;
381
+ private assertActive;
382
+ }
383
+
449
384
  declare class CanvasEditor {
450
385
  readonly canvas: Canvas;
451
386
  readonly layers: LayerManager;
@@ -455,16 +390,24 @@ declare class CanvasEditor {
455
390
  readonly snapping: SnapManager;
456
391
  readonly crop: CropController;
457
392
  readonly patterns: PatternManager;
393
+ readonly fonts: FontRegistry;
394
+ readonly licensing: LicenseManager;
395
+ readonly pages: ProjectManager;
396
+ readonly masks: MaskController;
458
397
  private fileAdapter?;
459
398
  private imageProvider?;
460
399
  private zoomLevel;
461
400
  private mockup;
462
401
  private designBackground;
402
+ private designBackgroundImage;
403
+ private backgroundImageOptions;
463
404
  constructor(canvasElement: HTMLCanvasElement, config: EditorConfig);
464
405
  addImage(url: string, options?: Partial<Record<string, unknown>>): Promise<Layer>;
406
+ /** Replace an image source without changing its layer identity or visual transform. */
407
+ replaceImageSource(layerId: string, url: string): Promise<Layer>;
465
408
  addText(text: string, options?: Partial<Record<string, unknown>>): Layer;
466
409
  addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer;
467
- addTemplate(template: TemplateDefinition, _params: Record<string, string | number>): Promise<Layer>;
410
+ addTemplate(template: TemplateDefinition, params: Record<string, string | number>): Promise<Layer>;
468
411
  removeLayer(id: string): void;
469
412
  selectLayer(id: string | null): void;
470
413
  getSelectedLayer(): Layer | null;
@@ -472,11 +415,26 @@ declare class CanvasEditor {
472
415
  flipLayer(id: string, axis: 'horizontal' | 'vertical'): void;
473
416
  /** Clone a layer (offset slightly) and select the copy. */
474
417
  duplicateLayer(id: string): Promise<Layer | null>;
418
+ applyImageAdjustments(layerId: string, adjustments: ImageAdjustments): boolean;
419
+ /** Combine two or more layers into a single editable group layer. */
420
+ groupLayers(ids: string[], name?: string): Promise<Layer | null>;
421
+ /** Restore a group created by groupLayers back to its original layer records. */
422
+ ungroupLayer(id: string): Promise<Layer[]>;
475
423
  toJSON(): EditorState;
476
424
  fromJSON(state: EditorState): Promise<void>;
477
425
  toPNG(options?: PngExportOptions): Promise<Blob>;
426
+ toJPEG(options?: Omit<PngExportOptions, 'format'>): Promise<Blob>;
427
+ toWebP(options?: Omit<PngExportOptions, 'format'>): Promise<Blob>;
428
+ /** Export one layer in document coordinates or at its native image resolution. */
429
+ exportLayer(id: string, options?: SemanticExportOptions): Promise<Blob>;
430
+ /** Export only the configured document background, excluding design layers. */
431
+ exportBackground(options?: SemanticExportOptions): Promise<Blob>;
432
+ private toRaster;
478
433
  toSVG(): string;
434
+ toSVGAsync(options?: SvgExportOptions): Promise<string>;
479
435
  toDataURL(format?: ImageFormat, multiplier?: number): string;
436
+ /** Export the current product-preview composite. Advanced warping is host-defined. */
437
+ toMockupImage(options?: PngExportOptions): Promise<Blob>;
480
438
  /**
481
439
  * Run an export with the configured design background applied, even when a
482
440
  * mockup preview has forced the live canvas transparent — so exports reflect
@@ -484,14 +442,35 @@ declare class CanvasEditor {
484
442
  */
485
443
  private withDesignBackground;
486
444
  toPrintifyPositioning(): Record<string, PrintifyPositioning>;
487
- save(filename: string, format: 'png' | 'svg' | 'json'): Promise<string | undefined>;
445
+ /** Provider-neutral, ID-stable placement data in 0..1 canvas coordinates. */
446
+ toNormalizedPositioning(): NormalizedLayerPosition[];
447
+ toProviderPositioning<T>(adapter: PositioningAdapter<T>): T;
448
+ save(filename: string, format: 'png' | 'jpeg' | 'webp' | 'svg' | 'json'): Promise<string | undefined>;
488
449
  uploadImage(file: File): Promise<ImageProviderResult | undefined>;
489
450
  browseImages(): Promise<ImageProviderResult | null>;
451
+ searchImages(query: string, options?: ImageSearchOptions): Promise<ImageSearchResult | null>;
452
+ /** Track provider usage, then insert its hotlinked image as a normal layer. */
453
+ addProviderImage(image: ImageProviderResult, options?: Partial<Record<string, unknown>>): Promise<Layer>;
490
454
  setFileAdapter(adapter: FileAdapter): void;
491
455
  setImageProvider(provider: ImageProvider): void;
492
456
  setBackground(color: string): void;
493
457
  getDesignBackground(): string;
494
- resize(width: number, height: number): void;
458
+ getDesignBackgroundImage(): FabricObject | null;
459
+ getBackgroundImageOptions(): SerializedBackgroundImageOptions | null;
460
+ setBackgroundImage(url: string | null, options?: BackgroundImageOptions): Promise<void>;
461
+ /** Used by state restoration and advanced integrations with an existing Fabric object. */
462
+ setBackgroundImageObject(image: FabricObject | null, save?: boolean, options?: SerializedBackgroundImageOptions | null): void;
463
+ setTransparentBackground(): void;
464
+ resize(width: number, height: number, options?: ResizeOptions): void;
465
+ /** Effective source resolution for an image at its current physical size. */
466
+ getImageDpi(layerId: string): number | null;
467
+ validateImageDpi(minimumDpi?: number): DpiIssue[];
468
+ /** Render a small data-URL preview without changing document dimensions. */
469
+ toThumbnail(maxWidth?: number, maxHeight?: number, format?: ImageFormat): string;
470
+ bringToFront(id: string): boolean;
471
+ sendToBack(id: string): boolean;
472
+ bringForward(id: string): boolean;
473
+ sendBackward(id: string): boolean;
495
474
  setZoom(level: number): void;
496
475
  /** @deprecated use setZoom — kept for backward compatibility. */
497
476
  zoom(level: number): void;
@@ -501,6 +480,8 @@ declare class CanvasEditor {
501
480
  resetZoom(): void;
502
481
  /** Fit the canvas inside a viewport (in px), accounting for padding. */
503
482
  zoomToFit(viewportWidth: number, viewportHeight: number, padding?: number): void;
483
+ /** Zoom until the current selection fills the artboard viewport. */
484
+ zoomToSelection(padding?: number): void;
504
485
  applyPattern(layerId: string, config: PatternConfig): Promise<void>;
505
486
  clearPattern(layerId: string): Promise<void>;
506
487
  setMockup(mockup: MockupConfig | null): void;
@@ -515,7 +496,98 @@ declare const generateId: () => string;
515
496
  declare function clamp(v: number, min: number, max: number): number;
516
497
  declare function round2(v: number): number;
517
498
 
499
+ /** XML text/attribute escaping for values interpolated into an SVG document. */
500
+ declare function escapeXml(value: string | number): string;
501
+ /**
502
+ * Strip active and externally-loaded content from an SVG before it is parsed
503
+ * into canvas objects.
504
+ *
505
+ * This is the single implementation of the control — every path that turns
506
+ * untrusted SVG into layers must call it, so it is exported rather than
507
+ * re-declared per package.
508
+ */
509
+ declare function sanitizeSvg(svg: string): string;
510
+
511
+ /** True for values safe to use as a canvas/SVG colour. Empty means transparent. */
512
+ declare function isCssColor(value: unknown, allowEmpty?: boolean): value is string;
513
+
518
514
  declare function serializeEditor(editor: CanvasEditor): EditorState;
519
515
  declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
520
516
 
521
- export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, type PatternLocks, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, applyPatternLocks, buildPatternDataURL, captureLocks, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, restoreLocks, round2, serializeEditor };
517
+ /** Common print and digital canvas sizes. */
518
+ declare const CANVAS_SIZE_PRESETS: readonly CanvasSizePreset[];
519
+
520
+ type AnnotationPrimitive = {
521
+ id: string;
522
+ type: 'point';
523
+ x: number;
524
+ y: number;
525
+ label?: string;
526
+ } | {
527
+ id: string;
528
+ type: 'rectangle';
529
+ left: number;
530
+ top: number;
531
+ width: number;
532
+ height: number;
533
+ label?: string;
534
+ } | {
535
+ id: string;
536
+ type: 'label';
537
+ x: number;
538
+ y: number;
539
+ text: string;
540
+ } | {
541
+ id: string;
542
+ type: 'path';
543
+ points: Array<{
544
+ x: number;
545
+ y: number;
546
+ }>;
547
+ label?: string;
548
+ };
549
+ interface ViewportTransform {
550
+ zoom: number;
551
+ panX: number;
552
+ panY: number;
553
+ devicePixelRatio?: number;
554
+ }
555
+ /** Host-owned annotations that never enter editor layers, history, serialization, or exports. */
556
+ declare class AnnotationOverlay {
557
+ private items;
558
+ private transform;
559
+ set(annotation: AnnotationPrimitive): void;
560
+ remove(id: string): boolean;
561
+ clear(): void;
562
+ getAll(): AnnotationPrimitive[];
563
+ setTransform(transform: ViewportTransform): void;
564
+ documentToViewport(point: {
565
+ x: number;
566
+ y: number;
567
+ }): {
568
+ x: number;
569
+ y: number;
570
+ };
571
+ viewportToDocument(point: {
572
+ x: number;
573
+ y: number;
574
+ }): {
575
+ x: number;
576
+ y: number;
577
+ };
578
+ documentToDevice(point: {
579
+ x: number;
580
+ y: number;
581
+ }): {
582
+ x: number;
583
+ y: number;
584
+ };
585
+ }
586
+
587
+ /**
588
+ * Warp RGBA pixels with an equally sized channel map. A channel value of 128
589
+ * is neutral; 0 and 255 move by the configured negative/positive maximum.
590
+ */
591
+ declare function displaceRgba(source: Uint8ClampedArray, map: Uint8ClampedArray, width: number, height: number, options: Omit<MockupDisplacement, 'image'>): Uint8ClampedArray;
592
+
593
+ export { AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_SIZE_PRESETS, CanvasEditor, CanvasSizePreset, type CoverPlacement, CropController, DpiIssue, EditorConfig, EditorEvents, EditorState, EventEmitter, FileAdapter, FontDefinition, FontRegistry, HistoryManager, ImageAdjustments, ImageProvider, ImageProviderResult, ImageSearchOptions, ImageSearchResult, Layer, LayerData, LayerManager, LayerMeta, LayerType, LicenseConfig, LicenseManager, LicenseStatus, MaskBrushOptions, MaskController, type MaskPerformanceSample, MaskPoint, MaskRefinementError, type MaskRefinementErrorCode, MaskRefinementPrompt, MaskRefinementProvider, MaskRefinementResult, MockupConfig, MockupDisplacement, MockupPrintArea, NormalizedLayerPosition, PatternConfig, PatternLocks, PatternManager, PatternSourceResolver, type PngExportOptions, PositioningAdapter, PrintifyPositioning, ProjectManager, ProjectState, ResizeOptions, SemanticExportOptions, SerializedBackgroundImageOptions, SerializedLayer, ShapePlugin, SnapManager, SvgExportOptions, TemplateDefinition, type TilePlacement, Unit, UnitConverter, type ViewportTransform, applyPatternLocks, buildPatternDataURL, captureLocks, clamp, clearPatternImageCache, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, exportDataURL, exportMockup, exportPNG, exportSVG, generateId, isCssColor, loadPatternImage, restoreLocks, round2, sanitizeSvg, serializeEditor };