@overtone-art/canvas-editor-core 0.5.1 → 0.6.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, Point, ImageFormat, Group } from 'fabric';
2
- import { b as EditorEvents, o as LayerType, m as LayerMeta, L as LayerData, X as SerializedLayer, P as PatternConfig, K as PatternLocks, a1 as TextCurveConfig, t as MaskPresetId, Y as ShapeMaskPresetId, a2 as TextureMaskPresetId, a4 as Unit, J as MockupPrintArea, z as MockupConfig, e as FontDefinition, p as LicenseConfig, r as LicenseStatus, T as ProjectState, M as MaskBrushOptions, s as MaskPoint, v as MaskRefinementProvider, u as MaskRefinementPrompt, x as MaskRefinementResult, k as LayerMaskEntry, l as LayerMaskMode, E as EditorConfig, Z as ShapePlugin, $ as TemplateDefinition, I as ImageAdjustments, c as EditorState, V as SemanticExportOptions, _ as SvgExportOptions, R as PrintifyPositioning, N as NormalizedLayerPosition, Q as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, W as SerializedBackgroundImageOptions, B as BackgroundImageOptions, U as ResizeOptions, a as DpiIssue, n as LayerShadowConfig, C as CanvasSizePreset, A as MockupDisplacement } from './types-JP8MZsIs.mjs';
3
- export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, q as LicensePayload, w as MaskRefinementRequest, y as MockupBlendMode, G as MockupDisplacementChannel, H as MockupOverlay, O as PatternState, S as ProjectPage, a0 as TemplateParameter, a3 as TileMode } from './types-JP8MZsIs.mjs';
2
+ import { b as EditorEvents, o as LayerType, m as LayerMeta, L as LayerData, X as SerializedLayer, P as PatternConfig, K as PatternLocks, a1 as TextCurveConfig, a2 as TextCurveInput, t as MaskPresetId, Y as ShapeMaskPresetId, a4 as TextureMaskPresetId, a6 as Unit, J as MockupPrintArea, z as MockupConfig, e as FontDefinition, p as LicenseConfig, r as LicenseStatus, T as ProjectState, M as MaskBrushOptions, s as MaskPoint, v as MaskRefinementProvider, u as MaskRefinementPrompt, x as MaskRefinementResult, k as LayerMaskEntry, l as LayerMaskMode, E as EditorConfig, Z as ShapePlugin, $ as TemplateDefinition, I as ImageAdjustments, c as EditorState, V as SemanticExportOptions, _ as SvgExportOptions, R as PrintifyPositioning, N as NormalizedLayerPosition, Q as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, W as SerializedBackgroundImageOptions, B as BackgroundImageOptions, U as ResizeOptions, a as DpiIssue, n as LayerShadowConfig, C as CanvasSizePreset, A as MockupDisplacement } from './types-LM7pOkkj.mjs';
3
+ export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, q as LicensePayload, w as MaskRefinementRequest, y as MockupBlendMode, G as MockupDisplacementChannel, H as MockupOverlay, O as PatternState, S as ProjectPage, a0 as TemplateParameter, a3 as TextCurveShape, a5 as TileMode } from './types-LM7pOkkj.mjs';
4
4
 
5
5
  type EventHandler<T> = (data: T) => void;
6
6
  declare class EventEmitter<TEvents extends {} = Record<string, unknown>> {
@@ -28,6 +28,14 @@ declare class Layer {
28
28
  * the layer's real object stays the one every editor API talks to.
29
29
  */
30
30
  renderProxy: FabricObject | null;
31
+ /**
32
+ * Layers nested inside this one, in draw order. Only a `group` layer has any:
33
+ * their fabric objects live inside this layer's fabric `Group`, not on the
34
+ * canvas, which is why the manager keeps them off the canvas stack.
35
+ */
36
+ children: Layer[];
37
+ /** Group this layer sits inside, or null when it is on the canvas stack. */
38
+ parentId: string | null;
31
39
  constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string);
32
40
  private hasMeta;
33
41
  toData(): LayerData;
@@ -42,6 +50,26 @@ declare class LayerManager {
42
50
  setHistoryCallback(callback: () => void): void;
43
51
  add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer;
44
52
  remove(id: string): boolean;
53
+ /**
54
+ * Take a top-level layer off the canvas stack **without** disposing it — it is
55
+ * about to become a group's child, so its object has to stay alive.
56
+ */
57
+ detach(id: string): Layer | undefined;
58
+ /** Put a group's children back on the canvas stack and drop the group. */
59
+ explode(group: Layer): Layer[];
60
+ /**
61
+ * Empty a group onto the canvas stack, in the group's own place, and drop the
62
+ * group record. `removeAll` restores each child's absolute transform, so the
63
+ * group's matrix must NOT be re-applied on top of it.
64
+ */
65
+ private dissolve;
66
+ /**
67
+ * Re-attach child records to an enlivened group. `data[i]` describes
68
+ * `group._objects[i]` — the order fabric serializes and restores them in. A
69
+ * missing record still becomes a child, so a hand-edited state cannot drop
70
+ * artwork off the canvas.
71
+ */
72
+ adoptGroupChildren(group: Layer, data: LayerData[]): void;
45
73
  /** Replace a layer's render object while preserving its immutable ID and panel state. */
46
74
  replaceObject(id: string, fabricObject: FabricObject): boolean;
47
75
  /**
@@ -57,10 +85,26 @@ declare class LayerManager {
57
85
  * front-to-back instead of computed.
58
86
  */
59
87
  private syncZOrder;
88
+ /** The list a layer lives in: its group's children, or the canvas stack. */
89
+ private siblingsOf;
60
90
  reorder(id: string, newIndex: number): boolean;
61
91
  select(id: string | null): void;
92
+ /** Drill into a group so its children can be selected and clicked. */
93
+ private enterGroup;
94
+ /** Leave every entered group — selecting anything else is done drilling in. */
95
+ exitGroups(): void;
96
+ /**
97
+ * Select several layers at once, as one fabric `ActiveSelection`. Children are
98
+ * skipped: a selection spanning in and out of a group has no single transform.
99
+ */
100
+ selectMany(ids: string[]): void;
101
+ /** Depth-first over the whole tree, each layer before its children. */
102
+ private walk;
62
103
  get(id: string): Layer | undefined;
104
+ /** Top-level layers only — the canvas stack. Children hang off their group. */
63
105
  getAll(): Layer[];
106
+ /** Top-level layers, each carrying its children: what the layer panel renders. */
107
+ getTree(): LayerData[];
64
108
  findByObject(obj: FabricObject): Layer | undefined;
65
109
  setVisibility(id: string, visible: boolean): void;
66
110
  setLocked(id: string, locked: boolean): void;
@@ -423,13 +467,31 @@ interface CanvasRenderingProtocol {
423
467
  /** Straight text — the state every text layer starts in. */
424
468
  declare const DEFAULT_TEXT_CURVE: TextCurveConfig;
425
469
  interface CurvePath {
426
- /** SVG path data the text is laid out along. */
470
+ /** SVG path data the line is laid out along. */
427
471
  data: string;
428
- /** Length of that path, used to centre the run on it. */
472
+ /** Length of that path. */
429
473
  length: number;
430
474
  }
431
- /** Path the given curve traces for a text run of `width` px, or null when straight. */
432
- declare function buildCurvePathData(config: TextCurveConfig, width: number, fontSize: number): CurvePath | null;
475
+ /**
476
+ * Fills in a partial curve.
477
+ *
478
+ * States saved before the shape switch existed carry only `arc` and `wave`, and
479
+ * held exactly one of them — so the non-zero one names the shape.
480
+ */
481
+ declare function normalizeTextCurve(config: TextCurveInput): TextCurveConfig;
482
+ /**
483
+ * One path per line of text, in a single shared coordinate frame.
484
+ *
485
+ * Fabric lays every line of a text object along the *same* path, so a
486
+ * multi-line run collapses into one overlapping pile. Each line gets its own
487
+ * path here, and `TextCurveManager` measures each line against its own — arcs
488
+ * concentric around one centre, waves stacked a line-height apart. The block is
489
+ * centred on the single-line baseline so adding a line doesn't shift the design.
490
+ */
491
+ declare function buildCurveLinePaths(config: TextCurveInput, width: number, fontSize: number, lineCount?: number, lineHeight?: number): CurvePath[] | null;
492
+ /** Path the given curve traces for a single-line run, or null when straight. */
493
+ declare function buildCurvePathData(config: TextCurveInput, width: number, fontSize: number): CurvePath | null;
494
+
433
495
  /**
434
496
  * Bends a text layer's baseline along a generated path.
435
497
  *
@@ -447,18 +509,44 @@ declare class TextCurveManager {
447
509
  /** Curve parameters for a layer, or null when it is not curved text. */
448
510
  get(layerId: string): TextCurveConfig | null;
449
511
  isCurved(layerId: string): boolean;
450
- /** Apply (or update) the curve on a text layer. Zeroed config clears it. */
451
- apply(layerId: string, config: Partial<TextCurveConfig>, save?: boolean): boolean;
512
+ /** Apply (or update) the curve on a text layer. A straight config clears it. */
513
+ apply(layerId: string, config: TextCurveInput, save?: boolean): boolean;
452
514
  /** Remove the curve, restoring the authored text box width. */
453
515
  clear(layerId: string, save?: boolean): boolean;
454
516
  /**
455
- * Rebuild the path from the stored parameters. Text content, font family and
456
- * font size all change the run's width, and the path is sized to that width
457
- * without this the curve keeps the geometry of the text it was created from.
517
+ * Rebuild the paths from the stored parameters. Text content, line count,
518
+ * font family and font size all change the geometry the paths are sized to —
519
+ * without this the curve keeps that of the text it was created from.
458
520
  */
459
521
  refresh(layerId: string, save?: boolean): boolean;
460
522
  /** Rebuild every curved layer — used after a state restore. */
461
523
  refreshAll(): void;
524
+ /**
525
+ * Puts every line's path on the object as one shape, and hooks per-line
526
+ * measuring so each line is laid out on its own.
527
+ *
528
+ * The object's own path is the union of the lines: fabric sizes a curved text
529
+ * object from it, and a single line's path would leave the rest of the block
530
+ * outside the layer's box — where object caching clips it away.
531
+ */
532
+ private attach;
533
+ /**
534
+ * Fabric lays every line of a text object along `this.path`, from one
535
+ * `pathStartOffset` — so a multi-line run piles all its lines on top of each
536
+ * other. Each line is measured against its own path instead, by swapping the
537
+ * path in around fabric's own per-line measure pass.
538
+ *
539
+ * The paths all share the first one's `pathOffset`, which is the origin
540
+ * fabric subtracts to place a glyph in the object — without that each line
541
+ * would be re-centred on its own bounding box and land back in the same spot.
542
+ *
543
+ * `textAlign` is switched to centre for the pass, which is how fabric centres
544
+ * a line on its path; the authored value is restored before anything else
545
+ * reads it.
546
+ */
547
+ private patchLineMeasure;
548
+ /** Restore fabric's own per-line measuring, if this manager replaced it. */
549
+ private unpatchLineMeasure;
462
550
  private detach;
463
551
  }
464
552
 
@@ -970,15 +1058,21 @@ declare class CanvasEditor {
970
1058
  addTemplate(template: TemplateDefinition, params: Record<string, string | number>): Promise<Layer>;
971
1059
  removeLayer(id: string): void;
972
1060
  selectLayer(id: string | null): void;
1061
+ /** Select several layers at once — what a panel's shift/⌘ click drives. */
1062
+ selectLayers(ids: string[]): void;
973
1063
  getSelectedLayer(): Layer | null;
974
1064
  /** Mirror a layer along the given axis. */
975
1065
  flipLayer(id: string, axis: 'horizontal' | 'vertical'): void;
976
1066
  /** Clone a layer (offset slightly) and select the copy. */
977
1067
  duplicateLayer(id: string): Promise<Layer | null>;
978
1068
  applyImageAdjustments(layerId: string, adjustments: ImageAdjustments): boolean;
979
- /** Combine two or more layers into a single editable group layer. */
1069
+ /**
1070
+ * Combine two or more layers into one group layer. Picking a group among them
1071
+ * **absorbs** it — its children join the new group and the old group record is
1072
+ * dropped — because groups are exactly one level deep.
1073
+ */
980
1074
  groupLayers(ids: string[], name?: string): Promise<Layer | null>;
981
- /** Restore a group created by groupLayers back to its original layer records. */
1075
+ /** Split a group back into the layers it holds, in place. */
982
1076
  ungroupLayer(id: string): Promise<Layer[]>;
983
1077
  toJSON(): EditorState;
984
1078
  fromJSON(state: EditorState): Promise<void>;
@@ -1090,6 +1184,37 @@ declare class CanvasEditor {
1090
1184
  private setupCanvasEvents;
1091
1185
  }
1092
1186
 
1187
+ /**
1188
+ * A template is a bootstrap, not a frozen picture: it is taken apart into the
1189
+ * layers it is drawn from, so every run of copy stays editable afterwards.
1190
+ */
1191
+ interface TemplateLayerSpec {
1192
+ type: 'text' | 'shape';
1193
+ name: string;
1194
+ object: FabricObject;
1195
+ /**
1196
+ * Baseline curve to apply once the layer exists (`<textPath>` templates).
1197
+ * Partial on purpose: a template states only the parameters it cares about
1198
+ * and `TextCurveManager.apply` normalizes the rest.
1199
+ */
1200
+ curve?: Partial<TextCurveConfig>;
1201
+ }
1202
+ /**
1203
+ * Curve a `<textPath>` template asks for.
1204
+ *
1205
+ * Fabric's SVG parser cannot read `<textPath>` at all, so these templates are
1206
+ * rebuilt from their parameters instead of parsed — the curve is the engine's
1207
+ * own, which keeps the text editable rather than rasterized.
1208
+ */
1209
+ declare function textPathCurve(template: TemplateDefinition, values: Record<string, string | number>, runWidth: number): Partial<TextCurveConfig>;
1210
+ /** The single text layer a `<textPath>` template stands for. */
1211
+ declare function textPathSpec(template: TemplateDefinition, values: Record<string, string | number>): TemplateLayerSpec;
1212
+ /**
1213
+ * The layers a resolved template SVG is made of, in draw order: every `<text>`
1214
+ * an editable text layer, every other top-level element a vector layer.
1215
+ */
1216
+ declare function explodeTemplateSvg(template: TemplateDefinition, resolvedSvg: string, values: Record<string, string | number>): Promise<TemplateLayerSpec[]>;
1217
+
1093
1218
  declare const DEFAULT_LAYER_SHADOW: LayerShadowConfig;
1094
1219
  /**
1095
1220
  * Read a layer's drop shadow back off its fabric object.
@@ -1215,4 +1340,4 @@ declare class AnnotationOverlay {
1215
1340
  */
1216
1341
  declare function displaceRgba(source: Uint8ClampedArray, map: Uint8ClampedArray, width: number, height: number, options: Omit<MockupDisplacement, 'image'>): Uint8ClampedArray;
1217
1342
 
1218
- export { type AddMaskOptions, AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_MASK_TARGET, 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, LayerMaskEntry, LayerMaskManager, LayerMaskMode, 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 TileDrawSize, type TilePlacement, TiledPatternObject, Unit, UnitConverter, type ViewportTransform, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applySelectionStyle, buildCurvePathData, clamp, clearTextureMaskCache, composeMaskGroup, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, fitToBox, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, needsAbsoluteSpace, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData, toCanvasSpace, toHostSpace, unwrapGroup };
1343
+ export { type AddMaskOptions, AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_MASK_TARGET, CANVAS_SIZE_PRESETS, CanvasEditor, type CanvasRenderingProtocol, CanvasSizePreset, type CoverPlacement, CropController, type CurvePath, 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, LayerMaskEntry, LayerMaskManager, LayerMaskMode, 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, type TemplateLayerSpec, TextCurveConfig, TextCurveInput, TextCurveManager, TextureMaskPresetId, type TileDrawSize, type TilePlacement, TiledPatternObject, Unit, UnitConverter, type ViewportTransform, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applySelectionStyle, buildCurveLinePaths, buildCurvePathData, clamp, clearTextureMaskCache, composeMaskGroup, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, explodeTemplateSvg, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, fitToBox, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, needsAbsoluteSpace, normalizeTextCurve, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData, textPathCurve, textPathSpec, toCanvasSpace, toHostSpace, unwrapGroup };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Canvas, FabricObject, Point, ImageFormat, Group } from 'fabric';
2
- import { b as EditorEvents, o as LayerType, m as LayerMeta, L as LayerData, X as SerializedLayer, P as PatternConfig, K as PatternLocks, a1 as TextCurveConfig, t as MaskPresetId, Y as ShapeMaskPresetId, a2 as TextureMaskPresetId, a4 as Unit, J as MockupPrintArea, z as MockupConfig, e as FontDefinition, p as LicenseConfig, r as LicenseStatus, T as ProjectState, M as MaskBrushOptions, s as MaskPoint, v as MaskRefinementProvider, u as MaskRefinementPrompt, x as MaskRefinementResult, k as LayerMaskEntry, l as LayerMaskMode, E as EditorConfig, Z as ShapePlugin, $ as TemplateDefinition, I as ImageAdjustments, c as EditorState, V as SemanticExportOptions, _ as SvgExportOptions, R as PrintifyPositioning, N as NormalizedLayerPosition, Q as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, W as SerializedBackgroundImageOptions, B as BackgroundImageOptions, U as ResizeOptions, a as DpiIssue, n as LayerShadowConfig, C as CanvasSizePreset, A as MockupDisplacement } from './types-JP8MZsIs.js';
3
- export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, q as LicensePayload, w as MaskRefinementRequest, y as MockupBlendMode, G as MockupDisplacementChannel, H as MockupOverlay, O as PatternState, S as ProjectPage, a0 as TemplateParameter, a3 as TileMode } from './types-JP8MZsIs.js';
2
+ import { b as EditorEvents, o as LayerType, m as LayerMeta, L as LayerData, X as SerializedLayer, P as PatternConfig, K as PatternLocks, a1 as TextCurveConfig, a2 as TextCurveInput, t as MaskPresetId, Y as ShapeMaskPresetId, a4 as TextureMaskPresetId, a6 as Unit, J as MockupPrintArea, z as MockupConfig, e as FontDefinition, p as LicenseConfig, r as LicenseStatus, T as ProjectState, M as MaskBrushOptions, s as MaskPoint, v as MaskRefinementProvider, u as MaskRefinementPrompt, x as MaskRefinementResult, k as LayerMaskEntry, l as LayerMaskMode, E as EditorConfig, Z as ShapePlugin, $ as TemplateDefinition, I as ImageAdjustments, c as EditorState, V as SemanticExportOptions, _ as SvgExportOptions, R as PrintifyPositioning, N as NormalizedLayerPosition, Q as PositioningAdapter, h as ImageProviderResult, i as ImageSearchOptions, j as ImageSearchResult, F as FileAdapter, g as ImageProvider, W as SerializedBackgroundImageOptions, B as BackgroundImageOptions, U as ResizeOptions, a as DpiIssue, n as LayerShadowConfig, C as CanvasSizePreset, A as MockupDisplacement } from './types-LM7pOkkj.js';
3
+ export { D as DEFAULT_PATTERN_CONFIG, d as ExportFormat, f as ImageAttribution, q as LicensePayload, w as MaskRefinementRequest, y as MockupBlendMode, G as MockupDisplacementChannel, H as MockupOverlay, O as PatternState, S as ProjectPage, a0 as TemplateParameter, a3 as TextCurveShape, a5 as TileMode } from './types-LM7pOkkj.js';
4
4
 
5
5
  type EventHandler<T> = (data: T) => void;
6
6
  declare class EventEmitter<TEvents extends {} = Record<string, unknown>> {
@@ -28,6 +28,14 @@ declare class Layer {
28
28
  * the layer's real object stays the one every editor API talks to.
29
29
  */
30
30
  renderProxy: FabricObject | null;
31
+ /**
32
+ * Layers nested inside this one, in draw order. Only a `group` layer has any:
33
+ * their fabric objects live inside this layer's fabric `Group`, not on the
34
+ * canvas, which is why the manager keeps them off the canvas stack.
35
+ */
36
+ children: Layer[];
37
+ /** Group this layer sits inside, or null when it is on the canvas stack. */
38
+ parentId: string | null;
31
39
  constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string);
32
40
  private hasMeta;
33
41
  toData(): LayerData;
@@ -42,6 +50,26 @@ declare class LayerManager {
42
50
  setHistoryCallback(callback: () => void): void;
43
51
  add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer;
44
52
  remove(id: string): boolean;
53
+ /**
54
+ * Take a top-level layer off the canvas stack **without** disposing it — it is
55
+ * about to become a group's child, so its object has to stay alive.
56
+ */
57
+ detach(id: string): Layer | undefined;
58
+ /** Put a group's children back on the canvas stack and drop the group. */
59
+ explode(group: Layer): Layer[];
60
+ /**
61
+ * Empty a group onto the canvas stack, in the group's own place, and drop the
62
+ * group record. `removeAll` restores each child's absolute transform, so the
63
+ * group's matrix must NOT be re-applied on top of it.
64
+ */
65
+ private dissolve;
66
+ /**
67
+ * Re-attach child records to an enlivened group. `data[i]` describes
68
+ * `group._objects[i]` — the order fabric serializes and restores them in. A
69
+ * missing record still becomes a child, so a hand-edited state cannot drop
70
+ * artwork off the canvas.
71
+ */
72
+ adoptGroupChildren(group: Layer, data: LayerData[]): void;
45
73
  /** Replace a layer's render object while preserving its immutable ID and panel state. */
46
74
  replaceObject(id: string, fabricObject: FabricObject): boolean;
47
75
  /**
@@ -57,10 +85,26 @@ declare class LayerManager {
57
85
  * front-to-back instead of computed.
58
86
  */
59
87
  private syncZOrder;
88
+ /** The list a layer lives in: its group's children, or the canvas stack. */
89
+ private siblingsOf;
60
90
  reorder(id: string, newIndex: number): boolean;
61
91
  select(id: string | null): void;
92
+ /** Drill into a group so its children can be selected and clicked. */
93
+ private enterGroup;
94
+ /** Leave every entered group — selecting anything else is done drilling in. */
95
+ exitGroups(): void;
96
+ /**
97
+ * Select several layers at once, as one fabric `ActiveSelection`. Children are
98
+ * skipped: a selection spanning in and out of a group has no single transform.
99
+ */
100
+ selectMany(ids: string[]): void;
101
+ /** Depth-first over the whole tree, each layer before its children. */
102
+ private walk;
62
103
  get(id: string): Layer | undefined;
104
+ /** Top-level layers only — the canvas stack. Children hang off their group. */
63
105
  getAll(): Layer[];
106
+ /** Top-level layers, each carrying its children: what the layer panel renders. */
107
+ getTree(): LayerData[];
64
108
  findByObject(obj: FabricObject): Layer | undefined;
65
109
  setVisibility(id: string, visible: boolean): void;
66
110
  setLocked(id: string, locked: boolean): void;
@@ -423,13 +467,31 @@ interface CanvasRenderingProtocol {
423
467
  /** Straight text — the state every text layer starts in. */
424
468
  declare const DEFAULT_TEXT_CURVE: TextCurveConfig;
425
469
  interface CurvePath {
426
- /** SVG path data the text is laid out along. */
470
+ /** SVG path data the line is laid out along. */
427
471
  data: string;
428
- /** Length of that path, used to centre the run on it. */
472
+ /** Length of that path. */
429
473
  length: number;
430
474
  }
431
- /** Path the given curve traces for a text run of `width` px, or null when straight. */
432
- declare function buildCurvePathData(config: TextCurveConfig, width: number, fontSize: number): CurvePath | null;
475
+ /**
476
+ * Fills in a partial curve.
477
+ *
478
+ * States saved before the shape switch existed carry only `arc` and `wave`, and
479
+ * held exactly one of them — so the non-zero one names the shape.
480
+ */
481
+ declare function normalizeTextCurve(config: TextCurveInput): TextCurveConfig;
482
+ /**
483
+ * One path per line of text, in a single shared coordinate frame.
484
+ *
485
+ * Fabric lays every line of a text object along the *same* path, so a
486
+ * multi-line run collapses into one overlapping pile. Each line gets its own
487
+ * path here, and `TextCurveManager` measures each line against its own — arcs
488
+ * concentric around one centre, waves stacked a line-height apart. The block is
489
+ * centred on the single-line baseline so adding a line doesn't shift the design.
490
+ */
491
+ declare function buildCurveLinePaths(config: TextCurveInput, width: number, fontSize: number, lineCount?: number, lineHeight?: number): CurvePath[] | null;
492
+ /** Path the given curve traces for a single-line run, or null when straight. */
493
+ declare function buildCurvePathData(config: TextCurveInput, width: number, fontSize: number): CurvePath | null;
494
+
433
495
  /**
434
496
  * Bends a text layer's baseline along a generated path.
435
497
  *
@@ -447,18 +509,44 @@ declare class TextCurveManager {
447
509
  /** Curve parameters for a layer, or null when it is not curved text. */
448
510
  get(layerId: string): TextCurveConfig | null;
449
511
  isCurved(layerId: string): boolean;
450
- /** Apply (or update) the curve on a text layer. Zeroed config clears it. */
451
- apply(layerId: string, config: Partial<TextCurveConfig>, save?: boolean): boolean;
512
+ /** Apply (or update) the curve on a text layer. A straight config clears it. */
513
+ apply(layerId: string, config: TextCurveInput, save?: boolean): boolean;
452
514
  /** Remove the curve, restoring the authored text box width. */
453
515
  clear(layerId: string, save?: boolean): boolean;
454
516
  /**
455
- * Rebuild the path from the stored parameters. Text content, font family and
456
- * font size all change the run's width, and the path is sized to that width
457
- * without this the curve keeps the geometry of the text it was created from.
517
+ * Rebuild the paths from the stored parameters. Text content, line count,
518
+ * font family and font size all change the geometry the paths are sized to —
519
+ * without this the curve keeps that of the text it was created from.
458
520
  */
459
521
  refresh(layerId: string, save?: boolean): boolean;
460
522
  /** Rebuild every curved layer — used after a state restore. */
461
523
  refreshAll(): void;
524
+ /**
525
+ * Puts every line's path on the object as one shape, and hooks per-line
526
+ * measuring so each line is laid out on its own.
527
+ *
528
+ * The object's own path is the union of the lines: fabric sizes a curved text
529
+ * object from it, and a single line's path would leave the rest of the block
530
+ * outside the layer's box — where object caching clips it away.
531
+ */
532
+ private attach;
533
+ /**
534
+ * Fabric lays every line of a text object along `this.path`, from one
535
+ * `pathStartOffset` — so a multi-line run piles all its lines on top of each
536
+ * other. Each line is measured against its own path instead, by swapping the
537
+ * path in around fabric's own per-line measure pass.
538
+ *
539
+ * The paths all share the first one's `pathOffset`, which is the origin
540
+ * fabric subtracts to place a glyph in the object — without that each line
541
+ * would be re-centred on its own bounding box and land back in the same spot.
542
+ *
543
+ * `textAlign` is switched to centre for the pass, which is how fabric centres
544
+ * a line on its path; the authored value is restored before anything else
545
+ * reads it.
546
+ */
547
+ private patchLineMeasure;
548
+ /** Restore fabric's own per-line measuring, if this manager replaced it. */
549
+ private unpatchLineMeasure;
462
550
  private detach;
463
551
  }
464
552
 
@@ -970,15 +1058,21 @@ declare class CanvasEditor {
970
1058
  addTemplate(template: TemplateDefinition, params: Record<string, string | number>): Promise<Layer>;
971
1059
  removeLayer(id: string): void;
972
1060
  selectLayer(id: string | null): void;
1061
+ /** Select several layers at once — what a panel's shift/⌘ click drives. */
1062
+ selectLayers(ids: string[]): void;
973
1063
  getSelectedLayer(): Layer | null;
974
1064
  /** Mirror a layer along the given axis. */
975
1065
  flipLayer(id: string, axis: 'horizontal' | 'vertical'): void;
976
1066
  /** Clone a layer (offset slightly) and select the copy. */
977
1067
  duplicateLayer(id: string): Promise<Layer | null>;
978
1068
  applyImageAdjustments(layerId: string, adjustments: ImageAdjustments): boolean;
979
- /** Combine two or more layers into a single editable group layer. */
1069
+ /**
1070
+ * Combine two or more layers into one group layer. Picking a group among them
1071
+ * **absorbs** it — its children join the new group and the old group record is
1072
+ * dropped — because groups are exactly one level deep.
1073
+ */
980
1074
  groupLayers(ids: string[], name?: string): Promise<Layer | null>;
981
- /** Restore a group created by groupLayers back to its original layer records. */
1075
+ /** Split a group back into the layers it holds, in place. */
982
1076
  ungroupLayer(id: string): Promise<Layer[]>;
983
1077
  toJSON(): EditorState;
984
1078
  fromJSON(state: EditorState): Promise<void>;
@@ -1090,6 +1184,37 @@ declare class CanvasEditor {
1090
1184
  private setupCanvasEvents;
1091
1185
  }
1092
1186
 
1187
+ /**
1188
+ * A template is a bootstrap, not a frozen picture: it is taken apart into the
1189
+ * layers it is drawn from, so every run of copy stays editable afterwards.
1190
+ */
1191
+ interface TemplateLayerSpec {
1192
+ type: 'text' | 'shape';
1193
+ name: string;
1194
+ object: FabricObject;
1195
+ /**
1196
+ * Baseline curve to apply once the layer exists (`<textPath>` templates).
1197
+ * Partial on purpose: a template states only the parameters it cares about
1198
+ * and `TextCurveManager.apply` normalizes the rest.
1199
+ */
1200
+ curve?: Partial<TextCurveConfig>;
1201
+ }
1202
+ /**
1203
+ * Curve a `<textPath>` template asks for.
1204
+ *
1205
+ * Fabric's SVG parser cannot read `<textPath>` at all, so these templates are
1206
+ * rebuilt from their parameters instead of parsed — the curve is the engine's
1207
+ * own, which keeps the text editable rather than rasterized.
1208
+ */
1209
+ declare function textPathCurve(template: TemplateDefinition, values: Record<string, string | number>, runWidth: number): Partial<TextCurveConfig>;
1210
+ /** The single text layer a `<textPath>` template stands for. */
1211
+ declare function textPathSpec(template: TemplateDefinition, values: Record<string, string | number>): TemplateLayerSpec;
1212
+ /**
1213
+ * The layers a resolved template SVG is made of, in draw order: every `<text>`
1214
+ * an editable text layer, every other top-level element a vector layer.
1215
+ */
1216
+ declare function explodeTemplateSvg(template: TemplateDefinition, resolvedSvg: string, values: Record<string, string | number>): Promise<TemplateLayerSpec[]>;
1217
+
1093
1218
  declare const DEFAULT_LAYER_SHADOW: LayerShadowConfig;
1094
1219
  /**
1095
1220
  * Read a layer's drop shadow back off its fabric object.
@@ -1215,4 +1340,4 @@ declare class AnnotationOverlay {
1215
1340
  */
1216
1341
  declare function displaceRgba(source: Uint8ClampedArray, map: Uint8ClampedArray, width: number, height: number, options: Omit<MockupDisplacement, 'image'>): Uint8ClampedArray;
1217
1342
 
1218
- export { type AddMaskOptions, AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_MASK_TARGET, 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, LayerMaskEntry, LayerMaskManager, LayerMaskMode, 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 TileDrawSize, type TilePlacement, TiledPatternObject, Unit, UnitConverter, type ViewportTransform, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applySelectionStyle, buildCurvePathData, clamp, clearTextureMaskCache, composeMaskGroup, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, fitToBox, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, needsAbsoluteSpace, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData, toCanvasSpace, toHostSpace, unwrapGroup };
1343
+ export { type AddMaskOptions, AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, CANVAS_MASK_TARGET, CANVAS_SIZE_PRESETS, CanvasEditor, type CanvasRenderingProtocol, CanvasSizePreset, type CoverPlacement, CropController, type CurvePath, 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, LayerMaskEntry, LayerMaskManager, LayerMaskMode, 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, type TemplateLayerSpec, TextCurveConfig, TextCurveInput, TextCurveManager, TextureMaskPresetId, type TileDrawSize, type TilePlacement, TiledPatternObject, Unit, UnitConverter, type ViewportTransform, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applySelectionStyle, buildCurveLinePaths, buildCurvePathData, clamp, clearTextureMaskCache, composeMaskGroup, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, explodeTemplateSvg, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, fitToBox, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, needsAbsoluteSpace, normalizeTextCurve, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData, textPathCurve, textPathSpec, toCanvasSpace, toHostSpace, unwrapGroup };