@overtone-art/canvas-editor-core 0.3.2 → 0.4.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.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Canvas, FabricObject, ImageFormat } from 'fabric';
2
- import { b as EditorEvents, m as LayerType, k as LayerMeta, L as LayerData, W as SerializedLayer, J as PatternSourceResolver, P as PatternConfig, H as PatternLocks, a0 as TextCurveConfig, r as MaskPresetId, X as ShapeMaskPresetId, a1 as TextureMaskPresetId, a3 as Unit, G as MockupPrintArea, x as MockupConfig, e as FontDefinition, n as LicenseConfig, p as LicenseStatus, S as ProjectState, M as MaskBrushOptions, q as MaskPoint, t as MaskRefinementProvider, s as MaskRefinementPrompt, v as MaskRefinementResult, E as EditorConfig, Y as ShapePlugin, _ as TemplateDefinition, I as ImageAdjustments, c as EditorState, U as SemanticExportOptions, Z as SvgExportOptions, Q as PrintifyPositioning, N as NormalizedLayerPosition, O as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, V as SerializedBackgroundImageOptions, B as BackgroundImageOptions, T as ResizeOptions, a as DpiIssue, l as LayerShadowConfig, C as CanvasSizePreset, y as MockupDisplacement } from './types-Dh1unrzT.mjs';
3
- export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, o as LicensePayload, u as MaskRefinementRequest, w as MockupBlendMode, z as MockupDisplacementChannel, A as MockupOverlay, K as PatternState, R as ProjectPage, $ as TemplateParameter, a2 as TileMode } from './types-Dh1unrzT.mjs';
2
+ import { b as EditorEvents, m as LayerType, k as LayerMeta, L as LayerData, V as SerializedLayer, P as PatternConfig, H as PatternLocks, $ as TextCurveConfig, r as MaskPresetId, W as ShapeMaskPresetId, a0 as TextureMaskPresetId, a2 as Unit, G as MockupPrintArea, x as MockupConfig, e as FontDefinition, n as LicenseConfig, p as LicenseStatus, R as ProjectState, M as MaskBrushOptions, q as MaskPoint, t as MaskRefinementProvider, s as MaskRefinementPrompt, v as MaskRefinementResult, E as EditorConfig, X as ShapePlugin, Z as TemplateDefinition, I as ImageAdjustments, c as EditorState, T as SemanticExportOptions, Y as SvgExportOptions, O as PrintifyPositioning, N as NormalizedLayerPosition, K as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, U as SerializedBackgroundImageOptions, B as BackgroundImageOptions, S as ResizeOptions, a as DpiIssue, l as LayerShadowConfig, C as CanvasSizePreset, y as MockupDisplacement } from './types-BNzE_X5M.mjs';
3
+ export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, o as LicensePayload, u as MaskRefinementRequest, w as MockupBlendMode, z as MockupDisplacementChannel, A as MockupOverlay, J as PatternState, Q as ProjectPage, _ as TemplateParameter, a1 as TileMode } from './types-BNzE_X5M.mjs';
4
4
 
5
5
  type EventHandler<T> = (data: T) => void;
6
6
  declare class EventEmitter<TEvents extends {} = Record<string, unknown>> {
@@ -22,6 +22,12 @@ declare class Layer {
22
22
  /** Non-fabric data (e.g. pattern config) that must persist with the layer. */
23
23
  meta: LayerMeta;
24
24
  fabricObject: FabricObject;
25
+ /**
26
+ * Stand-in object drawn in place of `fabricObject` (the tiled pattern). It is
27
+ * part of the canvas but never part of the document: it is not serialized, and
28
+ * the layer's real object stays the one every editor API talks to.
29
+ */
30
+ renderProxy: FabricObject | null;
25
31
  constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string);
26
32
  private hasMeta;
27
33
  toData(): LayerData;
@@ -38,6 +44,19 @@ declare class LayerManager {
38
44
  remove(id: string): boolean;
39
45
  /** Replace a layer's render object while preserving its immutable ID and panel state. */
40
46
  replaceObject(id: string, fabricObject: FabricObject): boolean;
47
+ /**
48
+ * Attach (or clear) the object drawn in place of a layer's own object. The
49
+ * proxy tracks the layer's stacking position, visibility and opacity, and is
50
+ * removed with the layer — it must never outlive or drift from its source.
51
+ */
52
+ setRenderProxy(id: string, proxy: FabricObject | null): void;
53
+ /**
54
+ * Re-stack every canvas object to match layer order, keeping each proxy
55
+ * directly above the source it stands in for. Canvas indices can't be derived
56
+ * from layer indices once proxies are in the array, so the order is rebuilt
57
+ * front-to-back instead of computed.
58
+ */
59
+ private syncZOrder;
41
60
  reorder(id: string, newIndex: number): boolean;
42
61
  select(id: string | null): void;
43
62
  get(id: string): Layer | undefined;
@@ -176,79 +195,141 @@ declare class CropController {
176
195
  }
177
196
 
178
197
  /**
179
- * Renders a single image layer as a repeating pattern that fills the canvas.
198
+ * Repeats a layer's own object across the print area.
180
199
  *
181
- * The tiled result is rasterised onto an offscreen canvas and swapped in as the
182
- * layer's image source, so it survives export, serialization and undo/redo with
183
- * no extra machinery. The original source + transform are kept in `layer.meta`
184
- * so the pattern can be turned back off.
200
+ * The layer keeps its real fabric object as the tile *source*: it stays in the
201
+ * document, stays selectable and stays editable, and is simply parked at
202
+ * `opacity: 0` while a {@link TiledPatternObject} render proxy draws the tiling
203
+ * over the print area. Nothing is baked into the layer, so a source edit (text,
204
+ * font, colour, drag, scale) shows up on the very next render, and the saved
205
+ * state stores a `Textbox` rather than a screenshot of one.
185
206
  */
186
207
  declare class PatternManager {
187
208
  private canvas;
188
209
  private layers;
189
210
  private history;
190
211
  private events;
191
- private sourceResolver?;
192
- private chains;
193
- constructor(canvas: Canvas, layers: LayerManager, history: HistoryManager, events: EventEmitter<EditorEvents>, sourceResolver?: PatternSourceResolver | undefined);
212
+ private proxies;
213
+ private readonly onSourceModified;
214
+ private readonly onLayerRemoved;
215
+ constructor(canvas: Canvas, layers: LayerManager, history: HistoryManager, events: EventEmitter<EditorEvents>);
194
216
  isPattern(layerId: string): boolean;
195
217
  getConfig(layerId: string): PatternConfig | null;
196
- /** Turn a plain image layer into a pattern, or update an existing one. */
218
+ /** Turn a layer into a repeating pattern, or update an existing one. */
197
219
  apply(layerId: string, config: PatternConfig): Promise<void>;
220
+ /** Drop the tiling and show the source again. */
221
+ disable(layerId: string): Promise<void>;
198
222
  /**
199
- * Stretch every restored pattern layer back over the full print area and
200
- * re-freeze it, without re-rasterising: the baked bitmap keeps whatever size
201
- * it was saved at, so it is scaled (not re-tiled) to the current canvas. Used
202
- * after a state restore, where the canvas may be a different display size
203
- * than when the pattern was baked — and to repair states saved while a
204
- * pattern could still be dragged out of the print area.
223
+ * Rebuild every proxy after a state restore, migrating any layer that was
224
+ * saved by the old bake-into-the-layer engine.
205
225
  */
206
- repinAll(): void;
207
- /** Restore the original image and drop the pattern. */
208
- disable(layerId: string): Promise<void>;
209
- /** Run `task` after any in-flight work for this layer, regardless of outcome. */
210
- private enqueue;
211
- private renderLayer;
226
+ rehydrateAll(): Promise<void>;
227
+ /** Re-fit every proxy to the print area (canvas resize). */
228
+ syncArea(): void;
229
+ /** Drop a layer's cached source snapshot (its content changed). */
230
+ invalidate(layerId: string): void;
231
+ /** Give a freshly cloned layer its own proxy (duplicating a pattern layer). */
232
+ attachTo(layer: Layer): void;
233
+ dispose(): void;
234
+ /** Release every proxy and the offscreen snapshot it holds. */
235
+ private clearProxies;
236
+ private attach;
237
+ private detach;
238
+ private invalidateFor;
212
239
  }
213
- /** Interaction flags to restore when a pattern is turned off. */
214
- declare function captureLocks(obj: FabricObject): PatternLocks;
215
- /** Pin a pattern object: no move, scale or rotate while the pattern is on. */
216
- declare function applyPatternLocks(obj: FabricObject): void;
217
240
  /**
218
- * Restore pre-pattern interaction flags. States saved before locking existed
219
- * carry no snapshot fall back to a fully interactive object rather than
220
- * leaving it frozen forever.
241
+ * Restore pre-pattern interaction flags. Only legacy states carry a snapshot —
242
+ * the live-source engine never freezes anything so a missing snapshot means
243
+ * "fully interactive", not "leave it frozen".
221
244
  */
222
245
  declare function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void;
223
- declare function loadPatternImage(src: string, resolver?: PatternSourceResolver): Promise<HTMLImageElement>;
224
- /** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
225
- declare function clearPatternImageCache(): void;
246
+
226
247
  /**
227
- * Draw the source image repeatedly across a `targetW × targetH` offscreen
228
- * canvas and return the result as a PNG data URL.
248
+ * Renders a layer's source object repeated across the print area.
249
+ *
250
+ * This is a *render proxy*, not a layer: it is attached to the layer that owns
251
+ * the source (see `LayerManager.setRenderProxy`) and never serialized. The
252
+ * document keeps the real object — a `Textbox` stays a `Textbox` — so tiling is
253
+ * non-destructive and every source edit simply shows up on the next render.
229
254
  *
230
- * Exported for unit testing of the tile geometry.
255
+ * The source is snapshotted to an offscreen canvas and blitted once per tile.
256
+ * The snapshot is rebuilt whenever the source changes or the drawing context
257
+ * gets sharper (a print export), so tiles are rasterised at export resolution
258
+ * instead of being upscaled from a screen-sized bitmap.
231
259
  */
232
- declare function buildPatternDataURL(src: string, config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number, sourceResolver?: PatternSourceResolver): Promise<string>;
233
- /** Placement of one tile, relative to the (already rotated) pattern centre. */
260
+ declare class TiledPatternObject extends FabricObject {
261
+ static type: string;
262
+ /** The layer's real object. Never rendered directly (it is kept at opacity 0). */
263
+ source: FabricObject;
264
+ config: PatternConfig;
265
+ private snapshotEl;
266
+ private snapshotScale;
267
+ constructor(source: FabricObject, config: PatternConfig, width: number, height: number);
268
+ /** Swap in a new config; the next render picks it up. */
269
+ setConfig(config: PatternConfig): void;
270
+ /** Resize to a new print area. */
271
+ setArea(width: number, height: number): void;
272
+ /** Drop the cached source snapshot (e.g. the source was edited). */
273
+ invalidate(): void;
274
+ /** Free the offscreen snapshot. */
275
+ dispose(): void;
276
+ _render(ctx: CanvasRenderingContext2D): void;
277
+ /** Raster fallback for SVG export — one `<image>` covering the print area. */
278
+ _toSVG(): string[];
279
+ /**
280
+ * Fabric's generic clone round-trips through `toObject()` + the class
281
+ * registry, which cannot carry a live source reference. Export paths clone
282
+ * every canvas object, so without this a print export would silently lose the
283
+ * tiling. The copy shares the source (it only ever reads from it).
284
+ */
285
+ clone(): Promise<this>;
286
+ /**
287
+ * The proxy holds a live reference to its source, which would make a
288
+ * serialized canvas circular. Nothing persists this object (only layers are
289
+ * serialized) — this keeps an accidental `canvas.toObject()` from throwing.
290
+ */
291
+ toObject(): Record<string, unknown>;
292
+ /** Tile size in canvas units, floored so a tiny tile can't spawn a huge loop. */
293
+ private tileSize;
294
+ /** Grid anchor: the source's centre, in this object's coordinates. */
295
+ private gridOrigin;
296
+ /**
297
+ * Snapshot the source at (at least) `scale`, reusing the cached one when it is
298
+ * still sharp enough and the source has not changed.
299
+ */
300
+ private ensureSnapshot;
301
+ /** Bound the snapshot by both a linear scale and a total pixel budget. */
302
+ private clampScale;
303
+ }
304
+
305
+ /** Placement of one tile, relative to the (already rotated) pattern origin. */
234
306
  interface TilePlacement {
235
- /** Tile-centre offset from the pattern centre, in px. */
307
+ /** Tile-centre offset from the pattern origin, in px. */
236
308
  x: number;
237
309
  y: number;
238
310
  /** Per-tile rotation, in radians. */
239
311
  rotation: number;
240
312
  }
313
+ /** Where the tile grid is anchored inside the target, in target px. */
314
+ interface PatternOrigin {
315
+ x: number;
316
+ y: number;
317
+ }
241
318
  /**
242
319
  * Pure tile geometry: where each tile centre lands and how much it is rotated,
243
- * for a config + target/base size. Covers the rotated bounding diagonal so
244
- * corners stay filled at any pattern angle. Extracted for unit testing.
320
+ * for a config + target/base size. Covers every corner of the target from the
321
+ * grid origin, so no rotation or origin can uncover an edge. Extracted for unit
322
+ * testing.
323
+ *
324
+ * `origin` defaults to the centre of the target.
245
325
  */
246
- declare function computeTilePositions(config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number): TilePlacement[];
326
+ declare function computeTilePositions(config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number, origin?: PatternOrigin): TilePlacement[];
247
327
  /**
248
- * Draw the tiled pattern onto a 2D context. The geometry lives in
249
- * {@link computeTilePositions}; this just plays it out as canvas calls.
328
+ * Draw the tiled pattern onto a 2D context, in target coordinates (0,0 = the
329
+ * target's top-left corner). The geometry lives in {@link computeTilePositions};
330
+ * this just plays it out as canvas calls.
250
331
  */
251
- declare function drawTiles(ctx: CanvasRenderingProtocol, img: CanvasImageSource, config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number): void;
332
+ declare function drawTiles(ctx: CanvasRenderingProtocol, img: CanvasImageSource, config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number, origin?: PatternOrigin): void;
252
333
  /** Minimal 2D-context surface used by {@link drawTiles}; lets tests inject a spy. */
253
334
  interface CanvasRenderingProtocol {
254
335
  save(): void;
@@ -820,4 +901,4 @@ declare class AnnotationOverlay {
820
901
  */
821
902
  declare function displaceRgba(source: Uint8ClampedArray, map: Uint8ClampedArray, width: number, height: number, options: Omit<MockupDisplacement, 'image'>): Uint8ClampedArray;
822
903
 
823
- export { AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_SIZE_PRESETS, CanvasEditor, CanvasSizePreset, type CoverPlacement, CropController, DEFAULT_LAYER_SHADOW, DEFAULT_SELECTION_STYLE, DEFAULT_TEXT_CURVE, DpiIssue, EditorConfig, EditorEvents, EditorState, EventEmitter, FileAdapter, FontDefinition, FontRegistry, HistoryManager, ImageAdjustments, ImageProvider, ImageProviderResult, ImageSearchOptions, ImageSearchResult, Layer, LayerData, LayerManager, LayerMeta, LayerShadowConfig, LayerType, LicenseConfig, LicenseManager, LicenseStatus, MaskBrushOptions, MaskController, type MaskPerformanceSample, MaskPoint, MaskPresetId, MaskPresetManager, MaskRefinementError, type MaskRefinementErrorCode, MaskRefinementPrompt, MaskRefinementProvider, MaskRefinementResult, MockupConfig, MockupDisplacement, MockupPrintArea, NormalizedLayerPosition, PatternConfig, PatternLocks, PatternManager, PatternSourceResolver, type PngExportOptions, PositioningAdapter, PrintifyPositioning, ProjectManager, ProjectState, ResizeOptions, SHAPE_MASK_BOX, SHAPE_MASK_IDS, type SelectionStyle, SemanticExportOptions, SerializedBackgroundImageOptions, SerializedLayer, ShapeMaskPresetId, ShapePlugin, SnapManager, SvgExportOptions, TEXTURE_MASK_IDS, TEXTURE_MASK_SIZE, TemplateDefinition, TextCurveConfig, TextCurveManager, TextureMaskPresetId, type TilePlacement, Unit, UnitConverter, type ViewportTransform, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applyPatternLocks, applySelectionStyle, buildCurvePathData, buildPatternDataURL, captureLocks, clamp, clearPatternImageCache, clearTextureMaskCache, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, loadPatternImage, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData };
904
+ export { AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_SIZE_PRESETS, CanvasEditor, type CanvasRenderingProtocol, CanvasSizePreset, type CoverPlacement, CropController, DEFAULT_LAYER_SHADOW, DEFAULT_SELECTION_STYLE, DEFAULT_TEXT_CURVE, DpiIssue, EditorConfig, EditorEvents, EditorState, EventEmitter, FileAdapter, FontDefinition, FontRegistry, HistoryManager, ImageAdjustments, ImageProvider, ImageProviderResult, ImageSearchOptions, ImageSearchResult, Layer, LayerData, LayerManager, LayerMeta, LayerShadowConfig, LayerType, LicenseConfig, LicenseManager, LicenseStatus, MaskBrushOptions, MaskController, type MaskPerformanceSample, MaskPoint, MaskPresetId, MaskPresetManager, MaskRefinementError, type MaskRefinementErrorCode, MaskRefinementPrompt, MaskRefinementProvider, MaskRefinementResult, MockupConfig, MockupDisplacement, MockupPrintArea, NormalizedLayerPosition, PatternConfig, PatternLocks, PatternManager, type PatternOrigin, type PngExportOptions, PositioningAdapter, PrintifyPositioning, ProjectManager, ProjectState, ResizeOptions, SHAPE_MASK_BOX, SHAPE_MASK_IDS, type SelectionStyle, SemanticExportOptions, SerializedBackgroundImageOptions, SerializedLayer, ShapeMaskPresetId, ShapePlugin, SnapManager, SvgExportOptions, TEXTURE_MASK_IDS, TEXTURE_MASK_SIZE, TemplateDefinition, TextCurveConfig, TextCurveManager, TextureMaskPresetId, type TilePlacement, TiledPatternObject, Unit, UnitConverter, type ViewportTransform, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applySelectionStyle, buildCurvePathData, clamp, clearTextureMaskCache, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Canvas, FabricObject, ImageFormat } from 'fabric';
2
- import { b as EditorEvents, m as LayerType, k as LayerMeta, L as LayerData, W as SerializedLayer, J as PatternSourceResolver, P as PatternConfig, H as PatternLocks, a0 as TextCurveConfig, r as MaskPresetId, X as ShapeMaskPresetId, a1 as TextureMaskPresetId, a3 as Unit, G as MockupPrintArea, x as MockupConfig, e as FontDefinition, n as LicenseConfig, p as LicenseStatus, S as ProjectState, M as MaskBrushOptions, q as MaskPoint, t as MaskRefinementProvider, s as MaskRefinementPrompt, v as MaskRefinementResult, E as EditorConfig, Y as ShapePlugin, _ as TemplateDefinition, I as ImageAdjustments, c as EditorState, U as SemanticExportOptions, Z as SvgExportOptions, Q as PrintifyPositioning, N as NormalizedLayerPosition, O as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, V as SerializedBackgroundImageOptions, B as BackgroundImageOptions, T as ResizeOptions, a as DpiIssue, l as LayerShadowConfig, C as CanvasSizePreset, y as MockupDisplacement } from './types-Dh1unrzT.js';
3
- export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, o as LicensePayload, u as MaskRefinementRequest, w as MockupBlendMode, z as MockupDisplacementChannel, A as MockupOverlay, K as PatternState, R as ProjectPage, $ as TemplateParameter, a2 as TileMode } from './types-Dh1unrzT.js';
2
+ import { b as EditorEvents, m as LayerType, k as LayerMeta, L as LayerData, V as SerializedLayer, P as PatternConfig, H as PatternLocks, $ as TextCurveConfig, r as MaskPresetId, W as ShapeMaskPresetId, a0 as TextureMaskPresetId, a2 as Unit, G as MockupPrintArea, x as MockupConfig, e as FontDefinition, n as LicenseConfig, p as LicenseStatus, R as ProjectState, M as MaskBrushOptions, q as MaskPoint, t as MaskRefinementProvider, s as MaskRefinementPrompt, v as MaskRefinementResult, E as EditorConfig, X as ShapePlugin, Z as TemplateDefinition, I as ImageAdjustments, c as EditorState, T as SemanticExportOptions, Y as SvgExportOptions, O as PrintifyPositioning, N as NormalizedLayerPosition, K as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, U as SerializedBackgroundImageOptions, B as BackgroundImageOptions, S as ResizeOptions, a as DpiIssue, l as LayerShadowConfig, C as CanvasSizePreset, y as MockupDisplacement } from './types-BNzE_X5M.js';
3
+ export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, o as LicensePayload, u as MaskRefinementRequest, w as MockupBlendMode, z as MockupDisplacementChannel, A as MockupOverlay, J as PatternState, Q as ProjectPage, _ as TemplateParameter, a1 as TileMode } from './types-BNzE_X5M.js';
4
4
 
5
5
  type EventHandler<T> = (data: T) => void;
6
6
  declare class EventEmitter<TEvents extends {} = Record<string, unknown>> {
@@ -22,6 +22,12 @@ declare class Layer {
22
22
  /** Non-fabric data (e.g. pattern config) that must persist with the layer. */
23
23
  meta: LayerMeta;
24
24
  fabricObject: FabricObject;
25
+ /**
26
+ * Stand-in object drawn in place of `fabricObject` (the tiled pattern). It is
27
+ * part of the canvas but never part of the document: it is not serialized, and
28
+ * the layer's real object stays the one every editor API talks to.
29
+ */
30
+ renderProxy: FabricObject | null;
25
31
  constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string);
26
32
  private hasMeta;
27
33
  toData(): LayerData;
@@ -38,6 +44,19 @@ declare class LayerManager {
38
44
  remove(id: string): boolean;
39
45
  /** Replace a layer's render object while preserving its immutable ID and panel state. */
40
46
  replaceObject(id: string, fabricObject: FabricObject): boolean;
47
+ /**
48
+ * Attach (or clear) the object drawn in place of a layer's own object. The
49
+ * proxy tracks the layer's stacking position, visibility and opacity, and is
50
+ * removed with the layer — it must never outlive or drift from its source.
51
+ */
52
+ setRenderProxy(id: string, proxy: FabricObject | null): void;
53
+ /**
54
+ * Re-stack every canvas object to match layer order, keeping each proxy
55
+ * directly above the source it stands in for. Canvas indices can't be derived
56
+ * from layer indices once proxies are in the array, so the order is rebuilt
57
+ * front-to-back instead of computed.
58
+ */
59
+ private syncZOrder;
41
60
  reorder(id: string, newIndex: number): boolean;
42
61
  select(id: string | null): void;
43
62
  get(id: string): Layer | undefined;
@@ -176,79 +195,141 @@ declare class CropController {
176
195
  }
177
196
 
178
197
  /**
179
- * Renders a single image layer as a repeating pattern that fills the canvas.
198
+ * Repeats a layer's own object across the print area.
180
199
  *
181
- * The tiled result is rasterised onto an offscreen canvas and swapped in as the
182
- * layer's image source, so it survives export, serialization and undo/redo with
183
- * no extra machinery. The original source + transform are kept in `layer.meta`
184
- * so the pattern can be turned back off.
200
+ * The layer keeps its real fabric object as the tile *source*: it stays in the
201
+ * document, stays selectable and stays editable, and is simply parked at
202
+ * `opacity: 0` while a {@link TiledPatternObject} render proxy draws the tiling
203
+ * over the print area. Nothing is baked into the layer, so a source edit (text,
204
+ * font, colour, drag, scale) shows up on the very next render, and the saved
205
+ * state stores a `Textbox` rather than a screenshot of one.
185
206
  */
186
207
  declare class PatternManager {
187
208
  private canvas;
188
209
  private layers;
189
210
  private history;
190
211
  private events;
191
- private sourceResolver?;
192
- private chains;
193
- constructor(canvas: Canvas, layers: LayerManager, history: HistoryManager, events: EventEmitter<EditorEvents>, sourceResolver?: PatternSourceResolver | undefined);
212
+ private proxies;
213
+ private readonly onSourceModified;
214
+ private readonly onLayerRemoved;
215
+ constructor(canvas: Canvas, layers: LayerManager, history: HistoryManager, events: EventEmitter<EditorEvents>);
194
216
  isPattern(layerId: string): boolean;
195
217
  getConfig(layerId: string): PatternConfig | null;
196
- /** Turn a plain image layer into a pattern, or update an existing one. */
218
+ /** Turn a layer into a repeating pattern, or update an existing one. */
197
219
  apply(layerId: string, config: PatternConfig): Promise<void>;
220
+ /** Drop the tiling and show the source again. */
221
+ disable(layerId: string): Promise<void>;
198
222
  /**
199
- * Stretch every restored pattern layer back over the full print area and
200
- * re-freeze it, without re-rasterising: the baked bitmap keeps whatever size
201
- * it was saved at, so it is scaled (not re-tiled) to the current canvas. Used
202
- * after a state restore, where the canvas may be a different display size
203
- * than when the pattern was baked — and to repair states saved while a
204
- * pattern could still be dragged out of the print area.
223
+ * Rebuild every proxy after a state restore, migrating any layer that was
224
+ * saved by the old bake-into-the-layer engine.
205
225
  */
206
- repinAll(): void;
207
- /** Restore the original image and drop the pattern. */
208
- disable(layerId: string): Promise<void>;
209
- /** Run `task` after any in-flight work for this layer, regardless of outcome. */
210
- private enqueue;
211
- private renderLayer;
226
+ rehydrateAll(): Promise<void>;
227
+ /** Re-fit every proxy to the print area (canvas resize). */
228
+ syncArea(): void;
229
+ /** Drop a layer's cached source snapshot (its content changed). */
230
+ invalidate(layerId: string): void;
231
+ /** Give a freshly cloned layer its own proxy (duplicating a pattern layer). */
232
+ attachTo(layer: Layer): void;
233
+ dispose(): void;
234
+ /** Release every proxy and the offscreen snapshot it holds. */
235
+ private clearProxies;
236
+ private attach;
237
+ private detach;
238
+ private invalidateFor;
212
239
  }
213
- /** Interaction flags to restore when a pattern is turned off. */
214
- declare function captureLocks(obj: FabricObject): PatternLocks;
215
- /** Pin a pattern object: no move, scale or rotate while the pattern is on. */
216
- declare function applyPatternLocks(obj: FabricObject): void;
217
240
  /**
218
- * Restore pre-pattern interaction flags. States saved before locking existed
219
- * carry no snapshot fall back to a fully interactive object rather than
220
- * leaving it frozen forever.
241
+ * Restore pre-pattern interaction flags. Only legacy states carry a snapshot —
242
+ * the live-source engine never freezes anything so a missing snapshot means
243
+ * "fully interactive", not "leave it frozen".
221
244
  */
222
245
  declare function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void;
223
- declare function loadPatternImage(src: string, resolver?: PatternSourceResolver): Promise<HTMLImageElement>;
224
- /** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
225
- declare function clearPatternImageCache(): void;
246
+
226
247
  /**
227
- * Draw the source image repeatedly across a `targetW × targetH` offscreen
228
- * canvas and return the result as a PNG data URL.
248
+ * Renders a layer's source object repeated across the print area.
249
+ *
250
+ * This is a *render proxy*, not a layer: it is attached to the layer that owns
251
+ * the source (see `LayerManager.setRenderProxy`) and never serialized. The
252
+ * document keeps the real object — a `Textbox` stays a `Textbox` — so tiling is
253
+ * non-destructive and every source edit simply shows up on the next render.
229
254
  *
230
- * Exported for unit testing of the tile geometry.
255
+ * The source is snapshotted to an offscreen canvas and blitted once per tile.
256
+ * The snapshot is rebuilt whenever the source changes or the drawing context
257
+ * gets sharper (a print export), so tiles are rasterised at export resolution
258
+ * instead of being upscaled from a screen-sized bitmap.
231
259
  */
232
- declare function buildPatternDataURL(src: string, config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number, sourceResolver?: PatternSourceResolver): Promise<string>;
233
- /** Placement of one tile, relative to the (already rotated) pattern centre. */
260
+ declare class TiledPatternObject extends FabricObject {
261
+ static type: string;
262
+ /** The layer's real object. Never rendered directly (it is kept at opacity 0). */
263
+ source: FabricObject;
264
+ config: PatternConfig;
265
+ private snapshotEl;
266
+ private snapshotScale;
267
+ constructor(source: FabricObject, config: PatternConfig, width: number, height: number);
268
+ /** Swap in a new config; the next render picks it up. */
269
+ setConfig(config: PatternConfig): void;
270
+ /** Resize to a new print area. */
271
+ setArea(width: number, height: number): void;
272
+ /** Drop the cached source snapshot (e.g. the source was edited). */
273
+ invalidate(): void;
274
+ /** Free the offscreen snapshot. */
275
+ dispose(): void;
276
+ _render(ctx: CanvasRenderingContext2D): void;
277
+ /** Raster fallback for SVG export — one `<image>` covering the print area. */
278
+ _toSVG(): string[];
279
+ /**
280
+ * Fabric's generic clone round-trips through `toObject()` + the class
281
+ * registry, which cannot carry a live source reference. Export paths clone
282
+ * every canvas object, so without this a print export would silently lose the
283
+ * tiling. The copy shares the source (it only ever reads from it).
284
+ */
285
+ clone(): Promise<this>;
286
+ /**
287
+ * The proxy holds a live reference to its source, which would make a
288
+ * serialized canvas circular. Nothing persists this object (only layers are
289
+ * serialized) — this keeps an accidental `canvas.toObject()` from throwing.
290
+ */
291
+ toObject(): Record<string, unknown>;
292
+ /** Tile size in canvas units, floored so a tiny tile can't spawn a huge loop. */
293
+ private tileSize;
294
+ /** Grid anchor: the source's centre, in this object's coordinates. */
295
+ private gridOrigin;
296
+ /**
297
+ * Snapshot the source at (at least) `scale`, reusing the cached one when it is
298
+ * still sharp enough and the source has not changed.
299
+ */
300
+ private ensureSnapshot;
301
+ /** Bound the snapshot by both a linear scale and a total pixel budget. */
302
+ private clampScale;
303
+ }
304
+
305
+ /** Placement of one tile, relative to the (already rotated) pattern origin. */
234
306
  interface TilePlacement {
235
- /** Tile-centre offset from the pattern centre, in px. */
307
+ /** Tile-centre offset from the pattern origin, in px. */
236
308
  x: number;
237
309
  y: number;
238
310
  /** Per-tile rotation, in radians. */
239
311
  rotation: number;
240
312
  }
313
+ /** Where the tile grid is anchored inside the target, in target px. */
314
+ interface PatternOrigin {
315
+ x: number;
316
+ y: number;
317
+ }
241
318
  /**
242
319
  * Pure tile geometry: where each tile centre lands and how much it is rotated,
243
- * for a config + target/base size. Covers the rotated bounding diagonal so
244
- * corners stay filled at any pattern angle. Extracted for unit testing.
320
+ * for a config + target/base size. Covers every corner of the target from the
321
+ * grid origin, so no rotation or origin can uncover an edge. Extracted for unit
322
+ * testing.
323
+ *
324
+ * `origin` defaults to the centre of the target.
245
325
  */
246
- declare function computeTilePositions(config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number): TilePlacement[];
326
+ declare function computeTilePositions(config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number, origin?: PatternOrigin): TilePlacement[];
247
327
  /**
248
- * Draw the tiled pattern onto a 2D context. The geometry lives in
249
- * {@link computeTilePositions}; this just plays it out as canvas calls.
328
+ * Draw the tiled pattern onto a 2D context, in target coordinates (0,0 = the
329
+ * target's top-left corner). The geometry lives in {@link computeTilePositions};
330
+ * this just plays it out as canvas calls.
250
331
  */
251
- declare function drawTiles(ctx: CanvasRenderingProtocol, img: CanvasImageSource, config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number): void;
332
+ declare function drawTiles(ctx: CanvasRenderingProtocol, img: CanvasImageSource, config: PatternConfig, targetW: number, targetH: number, baseW: number, baseH: number, origin?: PatternOrigin): void;
252
333
  /** Minimal 2D-context surface used by {@link drawTiles}; lets tests inject a spy. */
253
334
  interface CanvasRenderingProtocol {
254
335
  save(): void;
@@ -820,4 +901,4 @@ declare class AnnotationOverlay {
820
901
  */
821
902
  declare function displaceRgba(source: Uint8ClampedArray, map: Uint8ClampedArray, width: number, height: number, options: Omit<MockupDisplacement, 'image'>): Uint8ClampedArray;
822
903
 
823
- export { AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_SIZE_PRESETS, CanvasEditor, CanvasSizePreset, type CoverPlacement, CropController, DEFAULT_LAYER_SHADOW, DEFAULT_SELECTION_STYLE, DEFAULT_TEXT_CURVE, DpiIssue, EditorConfig, EditorEvents, EditorState, EventEmitter, FileAdapter, FontDefinition, FontRegistry, HistoryManager, ImageAdjustments, ImageProvider, ImageProviderResult, ImageSearchOptions, ImageSearchResult, Layer, LayerData, LayerManager, LayerMeta, LayerShadowConfig, LayerType, LicenseConfig, LicenseManager, LicenseStatus, MaskBrushOptions, MaskController, type MaskPerformanceSample, MaskPoint, MaskPresetId, MaskPresetManager, MaskRefinementError, type MaskRefinementErrorCode, MaskRefinementPrompt, MaskRefinementProvider, MaskRefinementResult, MockupConfig, MockupDisplacement, MockupPrintArea, NormalizedLayerPosition, PatternConfig, PatternLocks, PatternManager, PatternSourceResolver, type PngExportOptions, PositioningAdapter, PrintifyPositioning, ProjectManager, ProjectState, ResizeOptions, SHAPE_MASK_BOX, SHAPE_MASK_IDS, type SelectionStyle, SemanticExportOptions, SerializedBackgroundImageOptions, SerializedLayer, ShapeMaskPresetId, ShapePlugin, SnapManager, SvgExportOptions, TEXTURE_MASK_IDS, TEXTURE_MASK_SIZE, TemplateDefinition, TextCurveConfig, TextCurveManager, TextureMaskPresetId, type TilePlacement, Unit, UnitConverter, type ViewportTransform, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applyPatternLocks, applySelectionStyle, buildCurvePathData, buildPatternDataURL, captureLocks, clamp, clearPatternImageCache, clearTextureMaskCache, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, loadPatternImage, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData };
904
+ export { AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_SIZE_PRESETS, CanvasEditor, type CanvasRenderingProtocol, CanvasSizePreset, type CoverPlacement, CropController, DEFAULT_LAYER_SHADOW, DEFAULT_SELECTION_STYLE, DEFAULT_TEXT_CURVE, DpiIssue, EditorConfig, EditorEvents, EditorState, EventEmitter, FileAdapter, FontDefinition, FontRegistry, HistoryManager, ImageAdjustments, ImageProvider, ImageProviderResult, ImageSearchOptions, ImageSearchResult, Layer, LayerData, LayerManager, LayerMeta, LayerShadowConfig, LayerType, LicenseConfig, LicenseManager, LicenseStatus, MaskBrushOptions, MaskController, type MaskPerformanceSample, MaskPoint, MaskPresetId, MaskPresetManager, MaskRefinementError, type MaskRefinementErrorCode, MaskRefinementPrompt, MaskRefinementProvider, MaskRefinementResult, MockupConfig, MockupDisplacement, MockupPrintArea, NormalizedLayerPosition, PatternConfig, PatternLocks, PatternManager, type PatternOrigin, type PngExportOptions, PositioningAdapter, PrintifyPositioning, ProjectManager, ProjectState, ResizeOptions, SHAPE_MASK_BOX, SHAPE_MASK_IDS, type SelectionStyle, SemanticExportOptions, SerializedBackgroundImageOptions, SerializedLayer, ShapeMaskPresetId, ShapePlugin, SnapManager, SvgExportOptions, TEXTURE_MASK_IDS, TEXTURE_MASK_SIZE, TemplateDefinition, TextCurveConfig, TextCurveManager, TextureMaskPresetId, type TilePlacement, TiledPatternObject, Unit, UnitConverter, type ViewportTransform, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applySelectionStyle, buildCurvePathData, clamp, clearTextureMaskCache, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData };