@overtone-art/canvas-editor-core 0.6.4 → 0.8.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.ts CHANGED
@@ -1,6 +1,6 @@
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, 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-DSu2jbiV.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-DSu2jbiV.js';
1
+ import { Canvas, FabricObject, Point, ImageFormat, Group, Gradient } from 'fabric';
2
+ import { e as EditorEvents, r as LayerType, p as LayerMeta, L as LayerData, _ as SerializedLayer, Q as PatternConfig, R as PatternLocks, a4 as TextCurveConfig, a5 as TextCurveInput, a8 as TextWrapMode, a7 as TextOverflow, w as MaskPresetId, $ as ShapeMaskPresetId, a9 as TextureMaskPresetId, ab as Unit, O as MockupPrintArea, H as MockupConfig, h as FontDefinition, s as LicenseConfig, u as LicenseStatus, W as ProjectState, M as MaskBrushOptions, v as MaskPoint, y as MaskRefinementProvider, x as MaskRefinementPrompt, A as MaskRefinementResult, n as LayerMaskEntry, o as LayerMaskMode, E as EditorConfig, a0 as ShapePlugin, c as BlendMode, a2 as TemplateDefinition, I as ImageAdjustments, f as EditorState, Y as SemanticExportOptions, a1 as SvgExportOptions, U as PrintifyPositioning, P as NormalizedLayerPosition, T as PositioningAdapter, k as ImageProviderResult, l as ImageSearchOptions, m as ImageSearchResult, F as FileAdapter, j as ImageProvider, Z as SerializedBackgroundImageOptions, b as BackgroundImageOptions, X as ResizeOptions, d as DpiIssue, q as LayerShadowConfig, C as CanvasSizePreset, J as MockupDisplacement } from './types-DmintAiu.js';
3
+ export { B as BLEND_GROUPS, a as BLEND_MODES, D as DEFAULT_PATTERN_CONFIG, g as ExportFormat, i as ImageAttribution, t as LicensePayload, z as MaskRefinementRequest, G as MockupBlendMode, K as MockupDisplacementChannel, N as MockupOverlay, S as PatternState, V as ProjectPage, a3 as TemplateParameter, a6 as TextCurveShape, aa as TileMode, ac as blendModeLabel, ad as blendModeOf, ae as blendOperation } from './types-DmintAiu.js';
4
4
 
5
5
  type EventHandler<T> = (data: T) => void;
6
6
  declare class EventEmitter<TEvents extends {} = Record<string, unknown>> {
@@ -594,6 +594,80 @@ declare class TextCurveManager {
594
594
  private detach;
595
595
  }
596
596
 
597
+ /** Both properties as they apply to a layer, defaults filled in. */
598
+ interface TextWrapState {
599
+ wrap: TextWrapMode;
600
+ overflow: TextOverflow;
601
+ }
602
+ declare function readTextWrap(meta: LayerMeta | undefined): TextWrapState;
603
+ /** Builds the clip rect. A parameter because a Node renderer's `Rect` (from
604
+ * `fabric/node`) and the browser's `Rect` (from `fabric`) are different
605
+ * classes — a clip built by one does not render on the other's canvas. */
606
+ type RectFactory = (options: Record<string, unknown>) => FabricObject;
607
+ /**
608
+ * The renderer-agnostic half of {@link TextWrapManager.derive}: the fabric
609
+ * state a wrap mode implies once the box's width is already settled.
610
+ *
611
+ * Deliberately does NOT touch width — re-fitting here would let a Node
612
+ * renderer, which sees only the serialized state and never `meta.wrapWidth`,
613
+ * shift a box the browser already fitted at authoring time. Width is the
614
+ * manager's job (`fitTextWidth` for `'none'`, `meta.wrapWidth` restore for
615
+ * everything else); this only sets the levers fabric does not serialize on
616
+ * its own (`wordSplit`) or that depend on the box the manager just settled
617
+ * (the clip). Call it AFTER any width decision, never before — the clip below
618
+ * is sized to `text.width`/`text.height` as they stand when this runs.
619
+ */
620
+ declare function applyTextWrapToObject(object: FabricObject, meta: LayerMeta | undefined, makeRect?: RectFactory): void;
621
+ /**
622
+ * How a text layer breaks its lines, and whether it paints past its box.
623
+ *
624
+ * `meta` is the authoring record; what actually renders — and what a print
625
+ * pipeline that never reads `meta` sees — is the fabric state derived here:
626
+ * `width`, `splitByGrapheme` and `clipPath`, all of which fabric serializes on
627
+ * its own. That is the same division `TextCurveManager` draws between
628
+ * `meta.curve` and the `path` it installs.
629
+ */
630
+ declare class TextWrapManager {
631
+ private canvas;
632
+ private layers;
633
+ private history;
634
+ private events;
635
+ /**
636
+ * Typing changes the run the box was fitted to, so an auto-width layer has to
637
+ * re-fit. No history entry: fabric records the edit when editing exits, and a
638
+ * save per keystroke would bury every earlier step.
639
+ */
640
+ private readonly onTextChanged;
641
+ /**
642
+ * Both mask owners — the preset manager and every mask-stack mutation —
643
+ * install their clip straight onto `clipPath`, dropping whatever was there,
644
+ * and neither knows this layer had a box clip. Re-deriving on the one event
645
+ * they both announce puts it back where it now belongs: nested under the new
646
+ * mask, or at the top level when the last mask leaves. Waiting for the next
647
+ * keystroke instead would leave a layer that should clip inside its box
648
+ * serialized unclipped — which is what the print renderer reads.
649
+ */
650
+ private readonly onMasksChanged;
651
+ constructor(canvas: Canvas, layers: LayerManager, history: HistoryManager, events: EventEmitter<EditorEvents>);
652
+ dispose(): void;
653
+ /** Both properties for a text layer, or null when it is not text. */
654
+ get(layerId: string): TextWrapState | null;
655
+ apply(layerId: string, wrap: TextWrapMode, save?: boolean): boolean;
656
+ setOverflow(layerId: string, overflow: TextOverflow, save?: boolean): boolean;
657
+ /** Back to the defaults: auto-width, unclipped. */
658
+ clear(layerId: string, save?: boolean): boolean;
659
+ /** Re-derive from the stored mode — after a text, font or size change. */
660
+ refresh(layerId: string, save?: boolean): boolean;
661
+ /**
662
+ * Re-derive every text layer — used after a state restore.
663
+ *
664
+ * Recursive because a template inserts as a group of real child layers, and
665
+ * text inside one would otherwise keep whatever box it was restored with.
666
+ */
667
+ refreshAll(): void;
668
+ private derive;
669
+ }
670
+
597
671
  declare function isMaskPresetId(value: unknown): value is MaskPresetId;
598
672
  /**
599
673
  * Clips a layer to a preset silhouette or soft texture.
@@ -892,6 +966,22 @@ declare abstract class MaskStackStore {
892
966
  protected host(target: string, create?: boolean): FabricObject | null;
893
967
  /** Re-pin the design overlay, guarding the reorder that re-triggers this. */
894
968
  protected pin(): void;
969
+ /**
970
+ * Is the host's own `clipPath` the wrap manager's box clip rather than a mask?
971
+ *
972
+ * Adoption rescues a clip whose provenance is UNKNOWN — a mask preset, or a
973
+ * hand-set `clipPath` from before stacks existed — so the first add() cannot
974
+ * silently throw it away. A text layer's box clip is the opposite case: its
975
+ * provenance is known exactly (the wrap manager derives it from
976
+ * `meta.overflow` on every pass and re-installs it under whatever owns the
977
+ * property next), and it is not a mask. Adopted, it becomes a stack row the
978
+ * user never made whose 'add' union covers the whole box — swallowing the mask
979
+ * they actually added.
980
+ *
981
+ * Only meaningful with an empty stack: once there are entries the top-level
982
+ * clip is the composed group and the box clip is nested underneath it.
983
+ */
984
+ protected holdsBoxClip(meta: LayerMeta | undefined): boolean;
895
985
  /**
896
986
  * Take the current geometry back out of the composed clip, entry-aligned.
897
987
  *
@@ -1068,6 +1158,71 @@ declare function fitToBox(object: FabricObject, box: {
1068
1158
  */
1069
1159
  declare function unwrapGroup(group: Group): FabricObject[];
1070
1160
 
1161
+ type GradientKind = 'linear' | 'radial';
1162
+ interface GradientStop {
1163
+ /** `#rrggbb`. Alpha lives in `opacity`, so a colour input stays a colour input. */
1164
+ color: string;
1165
+ /** 0…1. */
1166
+ opacity: number;
1167
+ /** 0…1 along the gradient line. */
1168
+ position: number;
1169
+ }
1170
+ interface GradientConfig {
1171
+ kind: GradientKind;
1172
+ /** Degrees, CSS convention: 0 paints towards the top, 90 towards the right. Linear only. */
1173
+ angle: number;
1174
+ /** 0…1 of the layer box. Radial only. */
1175
+ center: {
1176
+ x: number;
1177
+ y: number;
1178
+ };
1179
+ /** 0…1 of the layer box. An ellipse, because percentage units follow the box. Radial only. */
1180
+ radius: number;
1181
+ /** At least two, ascending by position. */
1182
+ stops: GradientStop[];
1183
+ }
1184
+ /** The layer's UNSCALED box — the space fabric paints a percentage filler in. */
1185
+ interface GradientBox {
1186
+ width: number;
1187
+ height: number;
1188
+ }
1189
+ declare const DEFAULT_GRADIENT_CONFIG: GradientConfig;
1190
+ /**
1191
+ * Endpoints of the gradient line, in percentage space.
1192
+ *
1193
+ * fabric paints a percentage filler through `ctx.transform(width, 0, 0, height, …)`
1194
+ * (`_applyPatternGradientTransform`), so the space is the unit box under a
1195
+ * NON-UNIFORM scale — not a rotation. Drawing the line naively there would put
1196
+ * the gradient's iso-lines at some angle other than the one asked for. These
1197
+ * endpoints are solved the other way round: `v` is the direction whose iso-lines
1198
+ * land perpendicular to `angle` in the box, and `length` is the CSS gradient-line
1199
+ * length, which is what makes the run cover the box corners at every angle.
1200
+ *
1201
+ * A consequence, and the reason the test asserts it: at angles that are not
1202
+ * axis-aligned in a non-square box the endpoints sit OUTSIDE 0…1. Clamping them
1203
+ * would shorten the run and bunch the stops.
1204
+ */
1205
+ declare function linearCoords(angle: number, box: GradientBox): {
1206
+ x1: number;
1207
+ y1: number;
1208
+ x2: number;
1209
+ y2: number;
1210
+ };
1211
+ /** Inverse of {@link linearCoords}. */
1212
+ declare function angleFromCoords(coords: {
1213
+ x1: number;
1214
+ y1: number;
1215
+ x2: number;
1216
+ y2: number;
1217
+ }, box: GradientBox): number;
1218
+ declare function toFabricGradient(config: GradientConfig, box: GradientBox): Gradient<'linear'> | Gradient<'radial'>;
1219
+ /**
1220
+ * The config an object's gradient fill was built from, or null when the fill is
1221
+ * flat. This is the ONLY reader: `fill` is the single source of truth, so a
1222
+ * panel never has to be told what it is already looking at.
1223
+ */
1224
+ declare function readGradientConfig(object: FabricObject): GradientConfig | null;
1225
+
1071
1226
  declare class CanvasEditor {
1072
1227
  readonly canvas: Canvas;
1073
1228
  readonly layers: LayerManager;
@@ -1078,6 +1233,7 @@ declare class CanvasEditor {
1078
1233
  readonly crop: CropController;
1079
1234
  readonly patterns: PatternManager;
1080
1235
  readonly curves: TextCurveManager;
1236
+ readonly wrap: TextWrapManager;
1081
1237
  readonly maskPresets: MaskPresetManager;
1082
1238
  /** Stacked boolean masks, per layer and for the design as a whole. */
1083
1239
  readonly layerMasks: LayerMaskManager;
@@ -1099,6 +1255,23 @@ declare class CanvasEditor {
1099
1255
  replaceImageSource(layerId: string, url: string): Promise<Layer>;
1100
1256
  addText(text: string, options?: Partial<Record<string, unknown>>): Layer;
1101
1257
  addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer;
1258
+ /**
1259
+ * Insert a gradient layer. It is an ordinary `Rect` with a gradient fill —
1260
+ * which is exactly why the Node print compositor needs no code for it.
1261
+ */
1262
+ addGradient(config?: GradientConfig, box?: {
1263
+ left?: number;
1264
+ top?: number;
1265
+ width: number;
1266
+ height: number;
1267
+ }): Layer;
1268
+ /**
1269
+ * Repaint a layer's gradient. `save: false` while a slider is being dragged —
1270
+ * the settled value is the one worth an undo step.
1271
+ */
1272
+ setGradient(layerId: string, config: GradientConfig, save?: boolean): void;
1273
+ /** Set a layer's blend mode. Applied to the render proxy too, like opacity. */
1274
+ setBlend(layerId: string, mode: BlendMode): void;
1102
1275
  addTemplate(template: TemplateDefinition, params: Record<string, string | number>): Promise<Layer>;
1103
1276
  removeLayer(id: string): void;
1104
1277
  selectLayer(id: string | null): void;
@@ -1191,6 +1364,9 @@ declare class CanvasEditor {
1191
1364
  applyTextCurve(layerId: string, config: Partial<TextCurveConfig>): boolean;
1192
1365
  clearTextCurve(layerId: string): boolean;
1193
1366
  getTextCurve(layerId: string): TextCurveConfig | null;
1367
+ applyTextWrap(layerId: string, wrap: TextWrapMode): boolean;
1368
+ setTextOverflow(layerId: string, overflow: TextOverflow): boolean;
1369
+ getTextWrap(layerId: string): TextWrapState | null;
1194
1370
  applyMaskPreset(layerId: string, id: MaskPresetId | null): boolean;
1195
1371
  clearMaskPreset(layerId: string): boolean;
1196
1372
  getMaskPreset(layerId: string): MaskPresetId | null;
@@ -1250,6 +1426,8 @@ declare class CanvasEditor {
1250
1426
  */
1251
1427
  declare function fitTextWidth(object: FabricObject | null | undefined): boolean;
1252
1428
 
1429
+ declare function preWrapWordSplit(value: string): string[];
1430
+
1253
1431
  /**
1254
1432
  * A template is a bootstrap, not a frozen picture: it is taken apart into the
1255
1433
  * layers it is drawn from, so every run of copy stays editable afterwards.
@@ -1406,4 +1584,4 @@ declare class AnnotationOverlay {
1406
1584
  */
1407
1585
  declare function displaceRgba(source: Uint8ClampedArray, map: Uint8ClampedArray, width: number, height: number, options: Omit<MockupDisplacement, 'image'>): Uint8ClampedArray;
1408
1586
 
1409
- 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, fitTextWidth, fitToBox, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, needsAbsoluteSpace, normalizeTextCurve, readLayerShadow, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData, textPathCurve, textPathSpec, toCanvasSpace, toHostSpace, unwrapGroup };
1587
+ export { type AddMaskOptions, AnnotationOverlay, type AnnotationPrimitive, BackgroundImageOptions, BlendMode, CANVAS_MASK_TARGET, CANVAS_SIZE_PRESETS, CanvasEditor, type CanvasRenderingProtocol, CanvasSizePreset, type CoverPlacement, CropController, type CurvePath, DEFAULT_GRADIENT_CONFIG, DEFAULT_LAYER_SHADOW, DEFAULT_SELECTION_STYLE, DEFAULT_TEXT_CURVE, DpiIssue, EditorConfig, EditorEvents, EditorState, EventEmitter, FileAdapter, FontDefinition, FontRegistry, type GradientBox, type GradientConfig, type GradientKind, type GradientStop, 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, TextOverflow, TextWrapManager, TextWrapMode, type TextWrapState, TextureMaskPresetId, type TileDrawSize, type TilePlacement, TiledPatternObject, Unit, UnitConverter, type ViewportTransform, angleFromCoords, applyAspectLock, applyLayerShadow, applyObjectSelectionStyle, applySelectionStyle, applyTextWrapToObject, buildCurveLinePaths, buildCurvePathData, clamp, clearTextureMaskCache, composeMaskGroup, computeCoverPlacement, computePrintAreaClip, computeTilePositions, deserializeEditor, displaceRgba, drawTiles, escapeXml, explodeTemplateSvg, exportDataURL, exportMockup, exportPNG, exportPrintArea, exportSVG, fitTextWidth, fitToBox, generateId, isCssColor, isMaskPresetId, isShapeMaskId, isTextureMaskId, linearCoords, needsAbsoluteSpace, normalizeTextCurve, preWrapWordSplit, readGradientConfig, readLayerShadow, readTextWrap, renderTextureMask, resetTransform, restoreLocks, round2, sanitizeSvg, serializeEditor, shapeMaskPathData, textPathCurve, textPathSpec, toCanvasSpace, toFabricGradient, toHostSpace, unwrapGroup };