@overtone-art/canvas-editor-core 0.2.0 → 0.2.2

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
@@ -81,6 +81,12 @@ interface PrintifyPositioning {
81
81
  type TileMode = 'grid' | 'brick-horizontal' | 'brick-vertical';
82
82
  interface PatternConfig {
83
83
  mode: TileMode;
84
+ /**
85
+ * Tile size as a % of the source image's on-canvas size (100 = natural).
86
+ * Smaller values pack more repeats across the print area; larger values make
87
+ * fewer, bigger tiles. Independent of how the source layer was placed.
88
+ */
89
+ scale: number;
84
90
  /** Extra horizontal gap between tiles, as % of tile width. */
85
91
  horizontalSpacing: number;
86
92
  /** Extra vertical gap between tiles, as % of tile height. */
@@ -99,6 +105,14 @@ interface PatternState {
99
105
  config: PatternConfig;
100
106
  /** Original (pre-pattern) image source, as a durable data URL when possible. */
101
107
  originalSrc: string;
108
+ /**
109
+ * Serialized `clipPath` the image carried before it became a pattern, or null
110
+ * if it had none. A pattern must fill the whole print area, so any pre-pattern
111
+ * clip (e.g. a template crop sized to the original box) is stripped while the
112
+ * pattern is on and re-applied when it is turned off. Persisted so it survives
113
+ * serialization / reload / undo.
114
+ */
115
+ originalClip?: Record<string, unknown> | null;
102
116
  /** Original fabric transform, restored when the pattern is turned off. */
103
117
  original: {
104
118
  left: number;
package/dist/index.d.ts CHANGED
@@ -81,6 +81,12 @@ interface PrintifyPositioning {
81
81
  type TileMode = 'grid' | 'brick-horizontal' | 'brick-vertical';
82
82
  interface PatternConfig {
83
83
  mode: TileMode;
84
+ /**
85
+ * Tile size as a % of the source image's on-canvas size (100 = natural).
86
+ * Smaller values pack more repeats across the print area; larger values make
87
+ * fewer, bigger tiles. Independent of how the source layer was placed.
88
+ */
89
+ scale: number;
84
90
  /** Extra horizontal gap between tiles, as % of tile width. */
85
91
  horizontalSpacing: number;
86
92
  /** Extra vertical gap between tiles, as % of tile height. */
@@ -99,6 +105,14 @@ interface PatternState {
99
105
  config: PatternConfig;
100
106
  /** Original (pre-pattern) image source, as a durable data URL when possible. */
101
107
  originalSrc: string;
108
+ /**
109
+ * Serialized `clipPath` the image carried before it became a pattern, or null
110
+ * if it had none. A pattern must fill the whole print area, so any pre-pattern
111
+ * clip (e.g. a template crop sized to the original box) is stripped while the
112
+ * pattern is on and re-applied when it is turned off. Persisted so it survives
113
+ * serialization / reload / undo.
114
+ */
115
+ originalClip?: Record<string, unknown> | null;
102
116
  /** Original fabric transform, restored when the pattern is turned off. */
103
117
  original: {
104
118
  left: number;
package/dist/index.js CHANGED
@@ -56,7 +56,7 @@ __export(index_exports, {
56
56
  module.exports = __toCommonJS(index_exports);
57
57
 
58
58
  // src/editor.ts
59
- var import_fabric3 = require("fabric");
59
+ var import_fabric4 = require("fabric");
60
60
 
61
61
  // src/events.ts
62
62
  var EventEmitter = class {
@@ -598,6 +598,8 @@ var CropController = class {
598
598
  var STROKE2 = "#22c55e";
599
599
 
600
600
  // src/pattern.ts
601
+ var import_fabric2 = require("fabric");
602
+ var MAX_TILES_PER_AXIS = 200;
601
603
  var PatternManager = class {
602
604
  constructor(canvas, layers, history) {
603
605
  this.canvas = canvas;
@@ -626,9 +628,11 @@ var PatternManager = class {
626
628
  const image = layer.fabricObject;
627
629
  const firstEnable = !layer.meta.pattern;
628
630
  if (!layer.meta.pattern) {
631
+ const clip = image.clipPath;
629
632
  layer.meta.pattern = {
630
633
  config,
631
634
  originalSrc: elementToDataURL(image) ?? image.getSrc(),
635
+ originalClip: clip ? clip.toObject() : null,
632
636
  original: {
633
637
  left: image.left ?? 0,
634
638
  top: image.top ?? 0,
@@ -672,6 +676,7 @@ var PatternManager = class {
672
676
  cropY: state.original.cropY,
673
677
  angle: state.original.angle
674
678
  });
679
+ image.clipPath = state.originalClip ? (await import_fabric2.util.enlivenObjects([state.originalClip]))[0] : void 0;
675
680
  image.setCoords();
676
681
  delete layer.meta.pattern;
677
682
  this.canvas.requestRenderAll();
@@ -694,8 +699,11 @@ var PatternManager = class {
694
699
  const image = layer.fabricObject;
695
700
  const cw = this.canvas.getWidth();
696
701
  const ch = this.canvas.getHeight();
697
- const tileW = state.original.width * state.original.scaleX;
698
- const tileH = state.original.height * state.original.scaleY;
702
+ const scale = Math.max(1, state.config.scale ?? 100) / 100;
703
+ const diag = Math.sqrt(cw * cw + ch * ch);
704
+ const minTile = Math.max(2, diag / MAX_TILES_PER_AXIS);
705
+ const tileW = Math.max(minTile, state.original.width * state.original.scaleX * scale);
706
+ const tileH = Math.max(minTile, state.original.height * state.original.scaleY * scale);
699
707
  const dataUrl = await buildPatternDataURL(
700
708
  state.originalSrc,
701
709
  state.config,
@@ -705,7 +713,18 @@ var PatternManager = class {
705
713
  tileH
706
714
  );
707
715
  await image.setSrc(dataUrl);
708
- image.set({ left: 0, top: 0, scaleX: 1, scaleY: 1, cropX: 0, cropY: 0, angle: 0 });
716
+ image.set({
717
+ left: 0,
718
+ top: 0,
719
+ scaleX: 1,
720
+ scaleY: 1,
721
+ width: cw,
722
+ height: ch,
723
+ cropX: 0,
724
+ cropY: 0,
725
+ angle: 0
726
+ });
727
+ image.clipPath = void 0;
709
728
  image.setCoords();
710
729
  this.canvas.requestRenderAll();
711
730
  }
@@ -770,9 +789,10 @@ async function buildPatternDataURL(src, config, targetW, targetH, baseW, baseH)
770
789
  return off.toDataURL("image/png");
771
790
  }
772
791
  function computeTilePositions(config, targetW, targetH, baseW, baseH) {
773
- const tileW = Math.max(1, baseW * (1 + config.horizontalSpacing / 100));
774
- const tileH = Math.max(1, baseH * (1 + config.verticalSpacing / 100));
775
792
  const diag = Math.sqrt(targetW * targetW + targetH * targetH);
793
+ const minTile = Math.max(1, diag / MAX_TILES_PER_AXIS);
794
+ const tileW = Math.max(minTile, baseW * (1 + config.horizontalSpacing / 100));
795
+ const tileH = Math.max(minTile, baseH * (1 + config.verticalSpacing / 100));
776
796
  const cols = Math.ceil(diag / tileW) + 2;
777
797
  const rows = Math.ceil(diag / tileH) + 2;
778
798
  const halfCols = Math.ceil(cols / 2);
@@ -856,7 +876,7 @@ var UnitConverter = class {
856
876
  };
857
877
 
858
878
  // src/serialization.ts
859
- var import_fabric2 = require("fabric");
879
+ var import_fabric3 = require("fabric");
860
880
  var VERSION = "1.0.0";
861
881
  function serializeEditor(editor) {
862
882
  return {
@@ -887,7 +907,7 @@ async function deserializeEditor(editor, state) {
887
907
  editor.canvas.requestRenderAll();
888
908
  }
889
909
  async function restoreLayer(editor, serialized) {
890
- const objects = await import_fabric2.util.enlivenObjects([serialized.fabricObject]);
910
+ const objects = await import_fabric3.util.enlivenObjects([serialized.fabricObject]);
891
911
  const fabricObject = objects[0];
892
912
  const layer = editor.layers.add(serialized.type, fabricObject, serialized.name, serialized.id);
893
913
  if (serialized.meta) {
@@ -949,7 +969,7 @@ var CanvasEditor = class {
949
969
  const widthPx = this.units.toPixels(config.width);
950
970
  const heightPx = this.units.toPixels(config.height);
951
971
  this.designBackground = config.backgroundColor ?? "#ffffff";
952
- this.canvas = new import_fabric3.Canvas(canvasElement, {
972
+ this.canvas = new import_fabric4.Canvas(canvasElement, {
953
973
  width: widthPx,
954
974
  height: heightPx,
955
975
  backgroundColor: this.designBackground,
@@ -975,17 +995,20 @@ var CanvasEditor = class {
975
995
  }
976
996
  // ─── Layer Operations ────────────────────────────────
977
997
  async addImage(url, options) {
978
- const img = await import_fabric3.FabricImage.fromURL(url, {}, options);
998
+ const img = await import_fabric4.FabricImage.fromURL(url, {}, { originX: "left", originY: "top", ...options });
979
999
  const layer = this.layers.add("image", img);
980
1000
  this.history.save();
981
1001
  return layer;
982
1002
  }
983
1003
  addText(text, options) {
984
- const textbox = new import_fabric3.Textbox(text, {
1004
+ const textbox = new import_fabric4.Textbox(text, {
985
1005
  fontSize: 32,
986
1006
  fontFamily: "Arial",
987
1007
  fill: "#000000",
988
1008
  width: 200,
1009
+ // v7 defaults origin to center; keep top-left placement.
1010
+ originX: "left",
1011
+ originY: "top",
989
1012
  ...options
990
1013
  });
991
1014
  const layer = this.layers.add("text", textbox);
@@ -1004,7 +1027,9 @@ var CanvasEditor = class {
1004
1027
  fontSize: 24,
1005
1028
  fontFamily: "Arial",
1006
1029
  fill: "#666666",
1007
- width: 300
1030
+ width: 300,
1031
+ originX: "left",
1032
+ originY: "top"
1008
1033
  });
1009
1034
  const layer = this.layers.add(
1010
1035
  "template",
@@ -1251,6 +1276,7 @@ var CanvasEditor = class {
1251
1276
  // src/types.ts
1252
1277
  var DEFAULT_PATTERN_CONFIG = {
1253
1278
  mode: "grid",
1279
+ scale: 100,
1254
1280
  horizontalSpacing: 0,
1255
1281
  verticalSpacing: 0,
1256
1282
  angle: 0,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/editor.ts","../src/events.ts","../src/utils/id.ts","../src/layer.ts","../src/history.ts","../src/snapping.ts","../src/crop.ts","../src/utils/clamp.ts","../src/pattern.ts","../src/utils/units.ts","../src/serialization.ts","../src/export.ts","../src/types.ts"],"sourcesContent":["export { CanvasEditor } from './editor';\nexport { Layer, LayerManager } from './layer';\nexport { HistoryManager } from './history';\nexport { SnapManager } from './snapping';\nexport { CropController } from './crop';\nexport {\n PatternManager,\n buildPatternDataURL,\n drawTiles,\n computeTilePositions,\n clearPatternImageCache,\n} from './pattern';\nexport type { TilePlacement } from './pattern';\nexport { EventEmitter } from './events';\nexport { UnitConverter } from './utils/units';\nexport { generateId } from './utils/id';\nexport { clamp, round2 } from './utils/clamp';\nexport { serializeEditor, deserializeEditor } from './serialization';\nexport { exportPNG, exportSVG, exportDataURL } from './export';\nexport { DEFAULT_PATTERN_CONFIG } from './types';\n\nexport type {\n LayerType,\n LayerData,\n LayerMeta,\n SerializedLayer,\n EditorState,\n EditorConfig,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n ExportFormat,\n ShapePlugin,\n TemplateDefinition,\n TemplateParameter,\n PrintifyPositioning,\n TileMode,\n PatternConfig,\n PatternState,\n MockupConfig,\n MockupPrintArea,\n EditorEvents,\n Unit,\n} from './types';\n\nexport type { PngExportOptions } from './export';\n","import { Canvas, FabricImage, Textbox } from 'fabric';\nimport type { FabricObject, ImageFormat } from 'fabric';\nimport type {\n EditorConfig,\n EditorEvents,\n EditorState,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n MockupConfig,\n PatternConfig,\n PrintifyPositioning,\n ShapePlugin,\n TemplateDefinition,\n} from './types';\nimport { EventEmitter } from './events';\nimport { Layer, LayerManager } from './layer';\nimport { HistoryManager } from './history';\nimport { SnapManager } from './snapping';\nimport { CropController } from './crop';\nimport { PatternManager } from './pattern';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL } from './export';\nimport type { PngExportOptions } from './export';\nimport { clamp, round2 } from './utils/clamp';\n\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 8;\n\nexport class CanvasEditor {\n readonly canvas: Canvas;\n readonly layers: LayerManager;\n readonly history: HistoryManager;\n readonly events: EventEmitter<EditorEvents>;\n readonly units: UnitConverter;\n readonly snapping: SnapManager;\n readonly crop: CropController;\n readonly patterns: PatternManager;\n\n private fileAdapter?: FileAdapter;\n private imageProvider?: ImageProvider;\n private zoomLevel = 1;\n private mockup: MockupConfig | null = null;\n // The design's configured background. The live canvas background is forced\n // transparent while a mockup preview is shown, so this is the source of truth\n // for serialization and export — not the (possibly transient) canvas value.\n private designBackground: string;\n\n constructor(canvasElement: HTMLCanvasElement, config: EditorConfig) {\n this.events = new EventEmitter<EditorEvents>();\n this.units = new UnitConverter(config.unit ?? 'px', config.dpi ?? 72);\n\n const widthPx = this.units.toPixels(config.width);\n const heightPx = this.units.toPixels(config.height);\n this.designBackground = config.backgroundColor ?? '#ffffff';\n\n this.canvas = new Canvas(canvasElement, {\n width: widthPx,\n height: heightPx,\n backgroundColor: this.designBackground,\n preserveObjectStacking: config.preserveObjectStacking ?? true,\n selection: true,\n });\n\n this.layers = new LayerManager(this.canvas, this.events);\n this.fileAdapter = config.fileAdapter;\n this.imageProvider = config.imageProvider;\n\n this.history = new HistoryManager({\n getState: () => JSON.stringify(this.toJSON()),\n restoreState: async (state: string) => {\n const parsed = JSON.parse(state) as EditorState;\n await this.fromJSON(parsed);\n },\n events: this.events,\n });\n\n this.snapping = new SnapManager(this.canvas, this.events);\n this.crop = new CropController(this.canvas, this.history, this.events);\n this.patterns = new PatternManager(this.canvas, this.layers, this.history);\n\n this.setupCanvasEvents();\n this.history.saveImmediate();\n }\n\n // ─── Layer Operations ────────────────────────────────\n\n async addImage(url: string, options?: Partial<Record<string, unknown>>): Promise<Layer> {\n // Don't force crossOrigin here: a remote image whose host sends no CORS\n // header would then fail to load at all. The pattern engine requests its own\n // crossOrigin copy when it needs an untainted canvas (see pattern.ts).\n const img = await FabricImage.fromURL(url, {}, options);\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n }\n\n addText(text: string, options?: Partial<Record<string, unknown>>): Layer {\n const textbox = new Textbox(text, {\n fontSize: 32,\n fontFamily: 'Arial',\n fill: '#000000',\n width: 200,\n ...options,\n });\n const layer = this.layers.add('text', textbox as unknown as FabricObject);\n this.history.save();\n return layer;\n }\n\n addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer {\n const obj = plugin.create(options);\n const layer = this.layers.add('shape', obj, plugin.name);\n this.history.save();\n return layer;\n }\n\n async addTemplate(\n template: TemplateDefinition,\n _params: Record<string, string | number>,\n ): Promise<Layer> {\n // Template loading is handled by the templates package\n // This is a placeholder for direct fabric object addition\n const { Textbox: TextboxClass } = await import('fabric');\n const placeholder = new TextboxClass(`[Template: ${template.name}]`, {\n fontSize: 24,\n fontFamily: 'Arial',\n fill: '#666666',\n width: 300,\n });\n const layer = this.layers.add(\n 'template',\n placeholder as unknown as FabricObject,\n template.name,\n );\n this.history.save();\n return layer;\n }\n\n removeLayer(id: string): void {\n this.layers.remove(id);\n this.history.save();\n }\n\n selectLayer(id: string | null): void {\n this.layers.select(id);\n }\n\n getSelectedLayer(): Layer | null {\n const active = this.canvas.getActiveObject();\n if (!active) return null;\n return this.layers.findByObject(active) ?? null;\n }\n\n /** Mirror a layer along the given axis. */\n flipLayer(id: string, axis: 'horizontal' | 'vertical'): void {\n const layer = this.layers.get(id);\n if (!layer) return;\n const obj = layer.fabricObject;\n if (axis === 'horizontal') {\n obj.set('flipX', !obj.flipX);\n } else {\n obj.set('flipY', !obj.flipY);\n }\n obj.setCoords();\n this.canvas.requestRenderAll();\n this.history.save();\n }\n\n /** Clone a layer (offset slightly) and select the copy. */\n async duplicateLayer(id: string): Promise<Layer | null> {\n const layer = this.layers.get(id);\n if (!layer) return null;\n\n const clone = await layer.fabricObject.clone();\n // A pattern layer's image is rasterised to cover the canvas at 0,0; offsetting\n // it would leave a gap at the edges, so only nudge non-pattern duplicates.\n if (!layer.meta.pattern) {\n clone.set({ left: (clone.left ?? 0) + 15, top: (clone.top ?? 0) + 15 });\n }\n clone.setCoords();\n\n const copy = this.layers.add(layer.type, clone, `${layer.name} copy`);\n // Carry non-fabric metadata (e.g. pattern config) onto the copy.\n copy.meta = structuredClone(layer.meta);\n\n this.canvas.setActiveObject(clone);\n this.canvas.requestRenderAll();\n this.history.save();\n return copy;\n }\n\n // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n await deserializeEditor(this, state);\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n this.events.emit('export:start', { format: 'png' });\n const blob = await this.withDesignBackground(() => exportPNG(this.canvas, options));\n this.events.emit('export:complete', { format: 'png' });\n return blob;\n }\n\n toSVG(): string {\n this.events.emit('export:start', { format: 'svg' });\n const svg = this.withDesignBackground(() => exportSVG(this.canvas));\n this.events.emit('export:complete', { format: 'svg' });\n return svg;\n }\n\n toDataURL(format: ImageFormat = 'png', multiplier = 1): string {\n return this.withDesignBackground(() => exportDataURL(this.canvas, format, multiplier));\n }\n\n /**\n * Run an export with the configured design background applied, even when a\n * mockup preview has forced the live canvas transparent — so exports reflect\n * the design, not the preview. Restores the preview state afterwards.\n */\n private withDesignBackground<T>(fn: () => T): T {\n if (!this.mockup) return fn();\n const previewBg = this.canvas.backgroundColor;\n this.canvas.backgroundColor = this.designBackground;\n try {\n return fn();\n } finally {\n this.canvas.backgroundColor = previewBg;\n }\n }\n\n toPrintifyPositioning(): Record<string, PrintifyPositioning> {\n const result: Record<string, PrintifyPositioning> = {};\n const canvasW = this.canvas.getWidth();\n const canvasH = this.canvas.getHeight();\n\n const imageLayers = this.layers.getAll().filter((l) => l.type === 'image');\n\n if (imageLayers.length === 0) {\n result['front'] = { x: 0.5, y: 0.5, scale: 1, angle: 0 };\n return result;\n }\n\n for (const layer of imageLayers) {\n const obj = layer.fabricObject;\n const center = obj.getCenterPoint();\n result[layer.name] = {\n x: round2(center.x / canvasW),\n y: round2(center.y / canvasH),\n scale: round2(obj.scaleX ?? 1),\n angle: round2(obj.angle ?? 0),\n };\n }\n\n return result;\n }\n\n // ─── File Operations ────────────────────────────────\n\n async save(filename: string, format: 'png' | 'svg' | 'json'): Promise<string | undefined> {\n if (!this.fileAdapter) return undefined;\n\n let data: Blob | string;\n if (format === 'png') {\n data = await this.toPNG();\n } else if (format === 'svg') {\n data = this.toSVG();\n } else {\n data = JSON.stringify(this.toJSON(), null, 2);\n }\n\n return this.fileAdapter.save(data, filename, format);\n }\n\n async uploadImage(file: File): Promise<ImageProviderResult | undefined> {\n if (!this.imageProvider) return undefined;\n return this.imageProvider.upload(file);\n }\n\n async browseImages(): Promise<ImageProviderResult | null> {\n if (!this.imageProvider?.browse) return null;\n return this.imageProvider.browse();\n }\n\n setFileAdapter(adapter: FileAdapter): void {\n this.fileAdapter = adapter;\n }\n\n setImageProvider(provider: ImageProvider): void {\n this.imageProvider = provider;\n }\n\n // ─── Canvas Operations ──────────────────────────────\n\n setBackground(color: string): void {\n this.designBackground = color;\n // Don't override the mockup's transparent preview; it will pick up the new\n // colour when the mockup is cleared.\n if (!this.mockup) {\n this.canvas.backgroundColor = color;\n this.canvas.requestRenderAll();\n }\n this.history.save();\n }\n\n getDesignBackground(): string {\n return this.designBackground;\n }\n\n resize(width: number, height: number): void {\n const widthPx = this.units.toPixels(width);\n const heightPx = this.units.toPixels(height);\n this.canvas.setDimensions({ width: widthPx, height: heightPx });\n this.canvas.requestRenderAll();\n }\n\n // ─── Zoom ───────────────────────────────────────────\n //\n // Zoom is a *display* transform (the view layer CSS-scales the canvas), not a\n // fabric viewport zoom — fabric's setZoom bakes into toDataURL/toSVG and would\n // corrupt exports. The editor only tracks the level and emits an event.\n\n setZoom(level: number): void {\n const next = clamp(round2(level), MIN_ZOOM, MAX_ZOOM);\n if (next === this.zoomLevel) return;\n this.zoomLevel = next;\n this.events.emit('zoom:changed', { zoom: next });\n }\n\n /** @deprecated use setZoom — kept for backward compatibility. */\n zoom(level: number): void {\n this.setZoom(level);\n }\n\n getZoom(): number {\n return this.zoomLevel;\n }\n\n zoomIn(step = 0.1): void {\n this.setZoom(this.zoomLevel + step);\n }\n\n zoomOut(step = 0.1): void {\n this.setZoom(this.zoomLevel - step);\n }\n\n resetZoom(): void {\n this.setZoom(1);\n }\n\n /** Fit the canvas inside a viewport (in px), accounting for padding. */\n zoomToFit(viewportWidth: number, viewportHeight: number, padding = 0): void {\n const w = this.canvas.getWidth();\n const h = this.canvas.getHeight();\n if (w <= 0 || h <= 0) return;\n const sx = (viewportWidth - padding * 2) / w;\n const sy = (viewportHeight - padding * 2) / h;\n this.setZoom(Math.min(sx, sy));\n }\n\n // ─── Patterns ───────────────────────────────────────\n\n applyPattern(layerId: string, config: PatternConfig): Promise<void> {\n return this.patterns.apply(layerId, config);\n }\n\n clearPattern(layerId: string): Promise<void> {\n return this.patterns.disable(layerId);\n }\n\n // ─── Mockup (preview-only) ──────────────────────────\n\n setMockup(mockup: MockupConfig | null): void {\n this.mockup = mockup;\n // The mockup renders behind the design, so the canvas must be transparent\n // while it is shown; restore the configured background when it is cleared.\n this.canvas.backgroundColor = mockup ? '' : this.designBackground;\n this.canvas.requestRenderAll();\n this.events.emit('mockup:changed', { mockup });\n }\n\n clearMockup(): void {\n this.setMockup(null);\n }\n\n getMockup(): MockupConfig | null {\n return this.mockup;\n }\n\n // ─── Cleanup ────────────────────────────────────────\n\n dispose(): void {\n this.snapping.dispose();\n this.crop.dispose();\n this.history.dispose();\n this.events.removeAllListeners();\n this.canvas.dispose();\n }\n\n // ─── Private ────────────────────────────────────────\n\n private setupCanvasEvents(): void {\n this.canvas.on('object:modified', (e) => {\n if (!e.target) return;\n const layer = this.layers.findByObject(e.target);\n if (layer) {\n this.events.emit('layer:modified', { layerId: layer.id });\n this.history.save();\n }\n });\n\n this.canvas.on('selection:created', (e) => {\n const selected = (e.selected ?? [])\n .map((obj) => this.layers.findByObject(obj)?.id)\n .filter((id): id is string => id !== undefined);\n this.events.emit('selection:changed', { selected });\n });\n\n this.canvas.on('selection:updated', (e) => {\n const selected = (e.selected ?? [])\n .map((obj) => this.layers.findByObject(obj)?.id)\n .filter((id): id is string => id !== undefined);\n this.events.emit('selection:changed', { selected });\n });\n\n this.canvas.on('selection:cleared', () => {\n this.events.emit('selection:changed', { selected: [] });\n });\n }\n}\n","type EventHandler<T> = (data: T) => void;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport class EventEmitter<TEvents extends {} = Record<string, unknown>> {\n private listeners = new Map<keyof TEvents, Set<EventHandler<unknown>>>();\n\n on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler as EventHandler<unknown>);\n }\n\n off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n this.listeners.get(event)?.delete(handler as EventHandler<unknown>);\n }\n\n once<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n const wrapped: EventHandler<TEvents[K]> = (data) => {\n this.off(event, wrapped);\n handler(data);\n };\n this.on(event, wrapped);\n }\n\n emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n const handlers = this.listeners.get(event);\n if (!handlers) return;\n for (const handler of handlers) {\n handler(data);\n }\n }\n\n removeAllListeners(event?: keyof TEvents): void {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n }\n}\n","import { nanoid } from 'nanoid';\n\nexport const generateId = (): string => nanoid(12);\n","import type { Canvas, FabricObject } from 'fabric';\nimport type { LayerData, LayerMeta, LayerType, SerializedLayer } from './types';\nimport type { EditorEvents } from './types';\nimport type { EventEmitter } from './events';\nimport { generateId } from './utils/id';\n\nexport class Layer {\n readonly id: string;\n readonly type: LayerType;\n name: string;\n visible: boolean;\n locked: boolean;\n opacity: number;\n /** Non-fabric data (e.g. pattern config) that must persist with the layer. */\n meta: LayerMeta;\n readonly fabricObject: FabricObject;\n\n constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string) {\n this.id = id ?? generateId();\n this.type = type;\n this.name = name ?? `${type}-${this.id.slice(0, 4)}`;\n this.visible = true;\n this.locked = false;\n this.opacity = 1;\n this.meta = {};\n this.fabricObject = fabricObject;\n\n (this.fabricObject as FabricObject & { _layerId: string })._layerId = this.id;\n }\n\n private hasMeta(): boolean {\n return Object.keys(this.meta).length > 0;\n }\n\n toData(): LayerData {\n return {\n id: this.id,\n type: this.type,\n name: this.name,\n visible: this.visible,\n locked: this.locked,\n opacity: this.opacity,\n ...(this.hasMeta() ? { meta: this.meta } : {}),\n };\n }\n\n serialize(): SerializedLayer {\n return {\n ...this.toData(),\n fabricObject: this.fabricObject.toObject() as Record<string, unknown>,\n };\n }\n}\n\nexport class LayerManager {\n private layers: Layer[] = [];\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {}\n\n add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer {\n const layer = new Layer(type, fabricObject, name, id);\n this.layers.push(layer);\n this.canvas.add(fabricObject);\n this.events.emit('layer:added', { layer: layer.toData() });\n this.emitChanged();\n return layer;\n }\n\n remove(id: string): boolean {\n const index = this.layers.findIndex((l) => l.id === id);\n if (index === -1) return false;\n const layer = this.layers[index];\n this.canvas.remove(layer.fabricObject);\n this.layers.splice(index, 1);\n this.events.emit('layer:removed', { layerId: id });\n this.emitChanged();\n return true;\n }\n\n reorder(id: string, newIndex: number): boolean {\n const oldIndex = this.layers.findIndex((l) => l.id === id);\n if (oldIndex === -1) return false;\n const clamped = Math.max(0, Math.min(this.layers.length - 1, newIndex));\n const [layer] = this.layers.splice(oldIndex, 1);\n this.layers.splice(clamped, 0, layer);\n // Sync fabric z-order\n this.layers.forEach((l, i) => {\n this.canvas.moveObjectTo(l.fabricObject, i);\n });\n this.events.emit('layer:reordered', { layerIds: this.layers.map((l) => l.id) });\n this.emitChanged();\n return true;\n }\n\n select(id: string | null): void {\n if (id === null) {\n this.canvas.discardActiveObject();\n } else {\n const layer = this.get(id);\n if (layer) {\n this.canvas.setActiveObject(layer.fabricObject);\n }\n }\n this.canvas.requestRenderAll();\n this.events.emit('layer:selected', { layerId: id });\n // Fabric only fires selection:* on user interaction; mirror programmatic\n // selection so React selection state (useSelection) stays in sync.\n const selected = id && this.get(id) ? [id] : [];\n this.events.emit('selection:changed', { selected });\n }\n\n get(id: string): Layer | undefined {\n return this.layers.find((l) => l.id === id);\n }\n\n getAll(): Layer[] {\n return [...this.layers];\n }\n\n findByObject(obj: FabricObject): Layer | undefined {\n const layerId = (obj as FabricObject & { _layerId?: string })._layerId;\n if (layerId) return this.get(layerId);\n return this.layers.find((l) => l.fabricObject === obj);\n }\n\n setVisibility(id: string, visible: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.visible = visible;\n layer.fabricObject.visible = visible;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setLocked(id: string, locked: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.locked = locked;\n layer.fabricObject.selectable = !locked;\n layer.fabricObject.evented = !locked;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setOpacity(id: string, opacity: number): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.opacity = opacity;\n layer.fabricObject.opacity = opacity;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setName(id: string, name: string): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.name = name;\n this.emitChanged();\n }\n\n clear(): void {\n for (const layer of this.layers) {\n this.canvas.remove(layer.fabricObject);\n }\n this.layers = [];\n this.emitChanged();\n }\n\n count(): number {\n return this.layers.length;\n }\n\n private emitChanged(): void {\n this.events.emit('layers:changed', {\n layers: this.layers.map((l) => l.toData()),\n });\n }\n}\n","import type { EditorEvents } from './types';\nimport type { EventEmitter } from './events';\n\nexport class HistoryManager {\n private undoStack: string[] = [];\n private redoStack: string[] = [];\n private maxSize: number;\n private paused = false;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private debounceMs: number;\n private getState: () => string;\n private restoreState: (state: string) => Promise<void>;\n private events: EventEmitter<EditorEvents>;\n\n constructor(opts: {\n getState: () => string;\n restoreState: (state: string) => Promise<void>;\n events: EventEmitter<EditorEvents>;\n maxSize?: number;\n debounceMs?: number;\n }) {\n this.getState = opts.getState;\n this.restoreState = opts.restoreState;\n this.events = opts.events;\n this.maxSize = opts.maxSize ?? 50;\n this.debounceMs = opts.debounceMs ?? 300;\n }\n\n save(): void {\n if (this.paused) return;\n\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.saveImmediate();\n }, this.debounceMs);\n }\n\n saveImmediate(): void {\n if (this.paused) return;\n const state = this.getState();\n this.undoStack.push(state);\n if (this.undoStack.length > this.maxSize) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.emitChanged();\n }\n\n async undo(): Promise<void> {\n // Drop any debounced save still pending from an in-progress edit, otherwise\n // its timer would fire after restore completes and clobber the redo stack.\n this.cancelPending();\n const state = this.undoStack.pop();\n if (!state) return;\n\n this.redoStack.push(this.getState());\n this.paused = true;\n await this.restoreState(state);\n this.paused = false;\n this.emitChanged();\n }\n\n async redo(): Promise<void> {\n this.cancelPending();\n const state = this.redoStack.pop();\n if (!state) return;\n\n this.undoStack.push(this.getState());\n this.paused = true;\n await this.restoreState(state);\n this.paused = false;\n this.emitChanged();\n }\n\n /** True while a restore (undo/redo/deserialize) is in flight. Managers that\n * react to canvas events should skip mutating history during this window. */\n isRestoring(): boolean {\n return this.paused;\n }\n\n pause(): void {\n this.paused = true;\n }\n\n resume(): void {\n this.paused = false;\n }\n\n canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n this.emitChanged();\n }\n\n private cancelPending(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n dispose(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n }\n\n private emitChanged(): void {\n this.events.emit('history:changed', {\n canUndo: this.canUndo(),\n canRedo: this.canRedo(),\n });\n }\n}\n","import type { Canvas, FabricObject, TPointerEvent } from 'fabric';\nimport type { EventEmitter } from './events';\nimport type { EditorEvents } from './types';\n\ninterface SnapGuide {\n /** Vertical guide at this x (canvas px), or undefined for a horizontal one. */\n x?: number;\n y?: number;\n}\n\nconst STROKE = '#22c55e';\n\n/**\n * Snaps a dragged object's edges and centre to the canvas edges/centre and to\n * other objects, drawing transient guide lines while it does. Switchable via\n * {@link setEnabled}; inert (zero overhead beyond a flag check) when disabled.\n */\nexport class SnapManager {\n private enabled = false;\n private threshold = 8;\n private guides: SnapGuide[] = [];\n\n private readonly onMoving: (e: { target?: FabricObject; e: TPointerEvent }) => void;\n private readonly onAfterRender: () => void;\n private readonly onModified: () => void;\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {\n this.onMoving = (e) => this.handleMoving(e.target);\n this.onAfterRender = () => this.drawGuides();\n this.onModified = () => this.clearGuides();\n\n this.canvas.on('object:moving', this.onMoving);\n this.canvas.on('after:render', this.onAfterRender);\n this.canvas.on('mouse:up', this.onModified);\n this.canvas.on('selection:cleared', this.onModified);\n }\n\n setEnabled(enabled: boolean): void {\n if (this.enabled === enabled) return;\n this.enabled = enabled;\n if (!enabled) this.clearGuides();\n this.events.emit('snap:changed', { enabled });\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n setThreshold(px: number): void {\n this.threshold = Math.max(0, px);\n }\n\n dispose(): void {\n this.canvas.off('object:moving', this.onMoving);\n this.canvas.off('after:render', this.onAfterRender);\n this.canvas.off('mouse:up', this.onModified);\n this.canvas.off('selection:cleared', this.onModified);\n this.guides = [];\n }\n\n private handleMoving(target?: FabricObject): void {\n if (!this.enabled || !target) return;\n\n target.setCoords();\n const r = target.getBoundingRect();\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n // Candidate lines for both axes, collected in a single pass over the objects.\n const { xs, ys } = collectCandidates(this.canvas, target, cw, ch);\n\n this.guides = [];\n\n const dx = bestSnap([r.left, r.left + r.width / 2, r.left + r.width], xs, this.threshold);\n if (dx !== null) {\n target.set('left', (target.left ?? 0) + dx.delta);\n this.guides.push({ x: dx.line });\n }\n\n const dy = bestSnap([r.top, r.top + r.height / 2, r.top + r.height], ys, this.threshold);\n if (dy !== null) {\n target.set('top', (target.top ?? 0) + dy.delta);\n this.guides.push({ y: dy.line });\n }\n\n target.setCoords();\n }\n\n private drawGuides(): void {\n if (!this.enabled || this.guides.length === 0) return;\n const ctx = this.canvas.contextTop;\n if (!ctx) return;\n\n const retina = this.canvas.getRetinaScaling();\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n ctx.save();\n ctx.setTransform(retina, 0, 0, retina, 0, 0);\n ctx.lineWidth = 1;\n ctx.strokeStyle = STROKE;\n ctx.setLineDash([4, 4]);\n ctx.beginPath();\n for (const g of this.guides) {\n if (g.x !== undefined) {\n ctx.moveTo(g.x, 0);\n ctx.lineTo(g.x, ch);\n }\n if (g.y !== undefined) {\n ctx.moveTo(0, g.y);\n ctx.lineTo(cw, g.y);\n }\n }\n ctx.stroke();\n ctx.restore();\n }\n\n private clearGuides(): void {\n if (this.guides.length === 0) return;\n this.guides = [];\n this.canvas.clearContext(this.canvas.contextTop);\n }\n}\n\n/**\n * Snap candidate lines (edges + centre) for the canvas and every other object,\n * for both axes, computed in one pass so each object's bounds are read once.\n */\nfunction collectCandidates(\n canvas: Canvas,\n moving: FabricObject,\n cw: number,\n ch: number,\n): { xs: number[]; ys: number[] } {\n const xs = [0, cw / 2, cw];\n const ys = [0, ch / 2, ch];\n for (const obj of canvas.getObjects()) {\n if (obj === moving || !obj.visible) continue;\n obj.setCoords();\n const r = obj.getBoundingRect();\n xs.push(r.left, r.left + r.width / 2, r.left + r.width);\n ys.push(r.top, r.top + r.height / 2, r.top + r.height);\n }\n return { xs, ys };\n}\n\n/**\n * Find the closest (edge, candidate) pair within threshold. Returns the delta\n * to apply and the guide line to draw, or null if nothing is close enough.\n */\nfunction bestSnap(\n edges: number[],\n candidates: number[],\n threshold: number,\n): { delta: number; line: number } | null {\n let best: { delta: number; line: number; dist: number } | null = null;\n for (const edge of edges) {\n for (const c of candidates) {\n const dist = Math.abs(edge - c);\n if (dist <= threshold && (best === null || dist < best.dist)) {\n best = { delta: c - edge, line: c, dist };\n }\n }\n }\n return best ? { delta: best.delta, line: best.line } : null;\n}\n\nexport { bestSnap as _bestSnap };\n","import { Rect } from 'fabric';\nimport type { Canvas, FabricImage } from 'fabric';\nimport type { Layer } from './layer';\nimport type { HistoryManager } from './history';\nimport type { EventEmitter } from './events';\nimport type { EditorEvents } from './types';\nimport { clamp } from './utils/clamp';\n\ninterface CropSession {\n layerId: string;\n image: FabricImage;\n rect: Rect;\n prevSelectable: boolean;\n prevEvented: boolean;\n /** Angle before crop normalised it to 0; restored if the crop is cancelled. */\n prevAngle: number;\n}\n\n/**\n * Interactive crop for image layers. {@link start} drops a draggable/resizable\n * rectangle over the image; {@link apply} converts it into fabric crop props\n * (cropX/cropY/width/height in element pixels) and keeps the displayed size in\n * sync with the rectangle.\n *\n * Rotation is unsupported: an angled image is normalised to 0° on start.\n */\nexport class CropController {\n private session: CropSession | null = null;\n private readonly onSelectionCleared: () => void;\n\n constructor(\n private canvas: Canvas,\n private history: HistoryManager,\n private events: EventEmitter<EditorEvents>,\n ) {\n this.onSelectionCleared = () => {\n // Clicking away from the crop rect abandons the crop.\n if (this.session) this.cancel();\n };\n this.canvas.on('selection:cleared', this.onSelectionCleared);\n }\n\n isActive(): boolean {\n return this.session !== null;\n }\n\n activeLayerId(): string | null {\n return this.session?.layerId ?? null;\n }\n\n start(layer: Layer): void {\n if (layer.type !== 'image') return;\n if (this.session) this.cancel();\n\n const image = layer.fabricObject as FabricImage;\n const prevAngle = image.angle ?? 0;\n if (prevAngle) image.rotate(0);\n image.setCoords();\n const b = image.getBoundingRect();\n\n const rect = new Rect({\n left: b.left,\n top: b.top,\n width: b.width,\n height: b.height,\n originX: 'left',\n originY: 'top',\n fill: 'rgba(34,197,94,0.12)',\n stroke: STROKE,\n strokeWidth: 1,\n strokeDashArray: [4, 4],\n strokeUniform: true,\n lockRotation: true,\n cornerColor: STROKE,\n transparentCorners: false,\n });\n rect.setControlsVisibility({ mtr: false });\n\n const prevSelectable = image.selectable ?? true;\n const prevEvented = image.evented ?? true;\n image.selectable = false;\n image.evented = false;\n\n this.canvas.add(rect);\n this.canvas.setActiveObject(rect);\n this.canvas.requestRenderAll();\n\n this.session = { layerId: layer.id, image, rect, prevSelectable, prevEvented, prevAngle };\n this.events.emit('crop:changed', { active: true, layerId: layer.id });\n }\n\n apply(): void {\n const s = this.session;\n if (!s) return;\n const { image, rect } = s;\n\n rect.setCoords();\n image.setCoords();\n const scaleX = image.scaleX ?? 1;\n const scaleY = image.scaleY ?? 1;\n const imgLeft = image.left ?? 0;\n const imgTop = image.top ?? 0;\n\n const el = image.getElement() as HTMLImageElement;\n const naturalW = el.naturalWidth || image.width || 0;\n const naturalH = el.naturalHeight || image.height || 0;\n\n // Map the rect (canvas px) into the image's element-pixel space.\n let cropX = (image.cropX ?? 0) + (rect.left! - imgLeft) / scaleX;\n let cropY = (image.cropY ?? 0) + (rect.top! - imgTop) / scaleY;\n let cropW = rect.getScaledWidth() / scaleX;\n let cropH = rect.getScaledHeight() / scaleY;\n\n // Keep the crop window inside the source element.\n cropX = clamp(cropX, 0, Math.max(0, naturalW - 1));\n cropY = clamp(cropY, 0, Math.max(0, naturalH - 1));\n cropW = clamp(cropW, 1, naturalW - cropX);\n cropH = clamp(cropH, 1, naturalH - cropY);\n\n image.set({\n cropX,\n cropY,\n width: cropW,\n height: cropH,\n // Anchor the displayed result where the rect sits.\n left: imgLeft + (cropX - (image.cropX ?? 0)) * scaleX,\n top: imgTop + (cropY - (image.cropY ?? 0)) * scaleY,\n });\n image.setCoords();\n\n this.finish();\n this.history.save();\n }\n\n cancel(): void {\n const s = this.session;\n if (!s) return;\n // Cancelling discards the crop, so restore the rotation start() zeroed out.\n if (s.prevAngle) {\n s.image.rotate(s.prevAngle);\n s.image.setCoords();\n }\n this.finish();\n }\n\n dispose(): void {\n this.cancel();\n this.canvas.off('selection:cleared', this.onSelectionCleared);\n }\n\n private finish(): void {\n const s = this.session;\n if (!s) return;\n // Null the session first so the selection:cleared fired by removing the\n // rect / re-selecting does not re-enter cancel().\n this.session = null;\n\n this.canvas.remove(s.rect);\n s.image.selectable = s.prevSelectable;\n s.image.evented = s.prevEvented;\n this.canvas.setActiveObject(s.image);\n this.canvas.requestRenderAll();\n\n this.events.emit('crop:changed', { active: false, layerId: null });\n }\n}\n\nconst STROKE = '#22c55e';\n","export function clamp(v: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, v));\n}\n\nexport function round2(v: number): number {\n return Math.round(v * 100) / 100;\n}\n","import { FabricImage } from 'fabric';\nimport type { Canvas } from 'fabric';\nimport type { Layer, LayerManager } from './layer';\nimport type { HistoryManager } from './history';\nimport type { PatternConfig } from './types';\n\n/**\n * Renders a single image layer as a repeating pattern that fills the canvas.\n *\n * The tiled result is rasterised onto an offscreen canvas and swapped in as the\n * layer's image source, so it survives export, serialization and undo/redo with\n * no extra machinery. The original source + transform are kept in `layer.meta`\n * so the pattern can be turned back off.\n */\nexport class PatternManager {\n // Per-layer task chain. apply()/disable() both await an async setSrc on the\n // same fabric image; running two concurrently lets their setSrc resolutions\n // interleave (wrong image installed, original lost). Serialising per layer\n // guarantees the last-requested operation wins and state stays consistent.\n private chains = new Map<string, Promise<unknown>>();\n\n constructor(\n private canvas: Canvas,\n private layers: LayerManager,\n private history: HistoryManager,\n ) {}\n\n isPattern(layerId: string): boolean {\n return !!this.layers.get(layerId)?.meta.pattern;\n }\n\n getConfig(layerId: string): PatternConfig | null {\n return this.layers.get(layerId)?.meta.pattern?.config ?? null;\n }\n\n /** Turn a plain image layer into a pattern, or update an existing one. */\n apply(layerId: string, config: PatternConfig): Promise<void> {\n return this.enqueue(layerId, async () => {\n const layer = this.layers.get(layerId);\n if (!layer || layer.type !== 'image') return;\n const image = layer.fabricObject as FabricImage;\n\n const firstEnable = !layer.meta.pattern;\n // Check the field directly (not `firstEnable`) so TS narrows the else.\n if (!layer.meta.pattern) {\n // Captured at run time (not enqueue time) so a queued apply always reads\n // the genuine pre-pattern image, never a half-applied one.\n layer.meta.pattern = {\n config,\n originalSrc: elementToDataURL(image) ?? image.getSrc(),\n original: {\n left: image.left ?? 0,\n top: image.top ?? 0,\n scaleX: image.scaleX ?? 1,\n scaleY: image.scaleY ?? 1,\n width: image.width ?? 0,\n height: image.height ?? 0,\n angle: image.angle ?? 0,\n cropX: image.cropX ?? 0,\n cropY: image.cropY ?? 0,\n },\n };\n } else {\n layer.meta.pattern.config = config;\n }\n\n try {\n await this.renderLayer(layer);\n } catch (err) {\n // Roll back a freshly-captured state so a failed first render doesn't\n // leave the layer flagged as a pattern with the original still showing.\n if (firstEnable) delete layer.meta.pattern;\n throw err;\n }\n this.history.save();\n });\n }\n\n /** Restore the original image and drop the pattern. */\n disable(layerId: string): Promise<void> {\n return this.enqueue(layerId, async () => {\n const layer = this.layers.get(layerId);\n const state = layer?.meta.pattern;\n if (!layer || !state) return;\n const image = layer.fabricObject as FabricImage;\n\n await image.setSrc(state.originalSrc);\n // setSrc resets width/height to the full element size, so restore the\n // captured crop window too — otherwise an image that was cropped before\n // being turned into a pattern loses its crop on disable.\n image.set({\n left: state.original.left,\n top: state.original.top,\n scaleX: state.original.scaleX,\n scaleY: state.original.scaleY,\n width: state.original.width,\n height: state.original.height,\n cropX: state.original.cropX,\n cropY: state.original.cropY,\n angle: state.original.angle,\n });\n image.setCoords();\n delete layer.meta.pattern;\n this.canvas.requestRenderAll();\n this.history.save();\n });\n }\n\n /** Run `task` after any in-flight work for this layer, regardless of outcome. */\n private enqueue(layerId: string, task: () => Promise<void>): Promise<void> {\n const prev = this.chains.get(layerId) ?? Promise.resolve();\n const next = prev.then(task, task);\n // Keep the chain alive on rejection so later ops still run.\n this.chains.set(\n layerId,\n next.catch(() => undefined),\n );\n return next;\n }\n\n private async renderLayer(layer: Layer): Promise<void> {\n const state = layer.meta.pattern;\n if (!state) return;\n const image = layer.fabricObject as FabricImage;\n\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n const tileW = state.original.width * state.original.scaleX;\n const tileH = state.original.height * state.original.scaleY;\n\n const dataUrl = await buildPatternDataURL(\n state.originalSrc,\n state.config,\n cw,\n ch,\n tileW,\n tileH,\n );\n\n await image.setSrc(dataUrl);\n // setSrc leaves cropX/cropY untouched — reset them, then position the tiled\n // image to cover the whole canvas at 1:1.\n image.set({ left: 0, top: 0, scaleX: 1, scaleY: 1, cropX: 0, cropY: 0, angle: 0 });\n image.setCoords();\n this.canvas.requestRenderAll();\n }\n}\n\n/** Snapshot an already-loaded image element to a PNG data URL. */\nfunction elementToDataURL(image: FabricImage): string | null {\n try {\n const el = image.getElement() as HTMLImageElement | HTMLCanvasElement;\n const w = (el as HTMLImageElement).naturalWidth || el.width;\n const h = (el as HTMLImageElement).naturalHeight || el.height;\n if (!w || !h) return null;\n const off = document.createElement('canvas');\n off.width = w;\n off.height = h;\n const ctx = off.getContext('2d');\n if (!ctx) return null;\n ctx.drawImage(el, 0, 0);\n return off.toDataURL('image/png');\n } catch {\n // Tainted canvas (remote image without CORS) — fall back to the live src.\n return null;\n }\n}\n\n// Decoding a source (often a large data URL) is the dominant cost of a regen,\n// so cache decoded images. Bounded with simple LRU eviction so a stream of\n// distinct sources can't grow the cache without limit.\nconst IMAGE_CACHE_MAX = 16;\nconst imageCache = new Map<string, Promise<HTMLImageElement>>();\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n const cached = imageCache.get(src);\n if (cached) {\n // Refresh recency (Map preserves insertion order → re-insert = most recent).\n imageCache.delete(src);\n imageCache.set(src, cached);\n return cached;\n }\n\n const promise = decodeImage(src);\n // Don't poison the cache with a failed decode.\n promise.catch(() => {\n if (imageCache.get(src) === promise) imageCache.delete(src);\n });\n\n imageCache.set(src, promise);\n if (imageCache.size > IMAGE_CACHE_MAX) {\n const oldest = imageCache.keys().next().value;\n if (oldest !== undefined) imageCache.delete(oldest);\n }\n return promise;\n}\n\nfunction decodeImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`Failed to load pattern source: ${src}`));\n img.src = src;\n });\n}\n\n/** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */\nexport function clearPatternImageCache(): void {\n imageCache.clear();\n}\n\n/**\n * Draw the source image repeatedly across a `targetW × targetH` offscreen\n * canvas and return the result as a PNG data URL.\n *\n * Exported for unit testing of the tile geometry.\n */\nexport async function buildPatternDataURL(\n src: string,\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): Promise<string> {\n const img = await loadImage(src);\n const off = document.createElement('canvas');\n off.width = Math.max(1, Math.round(targetW));\n off.height = Math.max(1, Math.round(targetH));\n const ctx = off.getContext('2d');\n if (!ctx) return off.toDataURL('image/png');\n\n drawTiles(ctx, img, config, targetW, targetH, baseW, baseH);\n return off.toDataURL('image/png');\n}\n\n/** Placement of one tile, relative to the (already rotated) pattern centre. */\nexport interface TilePlacement {\n /** Tile-centre offset from the pattern centre, in px. */\n x: number;\n y: number;\n /** Per-tile rotation, in radians. */\n rotation: number;\n}\n\n/**\n * Pure tile geometry: where each tile centre lands and how much it is rotated,\n * for a config + target/base size. Covers the rotated bounding diagonal so\n * corners stay filled at any pattern angle. Extracted for unit testing.\n */\nexport function computeTilePositions(\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): TilePlacement[] {\n const tileW = Math.max(1, baseW * (1 + config.horizontalSpacing / 100));\n const tileH = Math.max(1, baseH * (1 + config.verticalSpacing / 100));\n\n const diag = Math.sqrt(targetW * targetW + targetH * targetH);\n const cols = Math.ceil(diag / tileW) + 2;\n const rows = Math.ceil(diag / tileH) + 2;\n const halfCols = Math.ceil(cols / 2);\n const halfRows = Math.ceil(rows / 2);\n\n const placements: TilePlacement[] = [];\n for (let j = -halfRows; j <= halfRows; j++) {\n for (let i = -halfCols; i <= halfCols; i++) {\n let x = i * tileW;\n let y = j * tileH;\n\n if (config.mode === 'brick-horizontal' && mod2(j) === 1) {\n x += tileW * (config.horizontalOffset / 100);\n } else if (config.mode === 'brick-vertical' && mod2(i) === 1) {\n y += tileH * (config.horizontalOffset / 100);\n }\n\n const rotation = ((i * config.rotationStepH + j * config.rotationStepV) * Math.PI) / 180;\n placements.push({ x, y, rotation });\n }\n }\n return placements;\n}\n\n/**\n * Draw the tiled pattern onto a 2D context. The geometry lives in\n * {@link computeTilePositions}; this just plays it out as canvas calls.\n */\nexport function drawTiles(\n ctx: CanvasRenderingProtocol,\n img: CanvasImageSource,\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): void {\n ctx.save();\n // Rotate the whole pattern around the canvas centre.\n ctx.translate(targetW / 2, targetH / 2);\n ctx.rotate((config.angle * Math.PI) / 180);\n\n for (const tile of computeTilePositions(config, targetW, targetH, baseW, baseH)) {\n ctx.save();\n ctx.translate(tile.x, tile.y);\n ctx.rotate(tile.rotation);\n ctx.drawImage(img, -baseW / 2, -baseH / 2, baseW, baseH);\n ctx.restore();\n }\n\n ctx.restore();\n}\n\n/** Non-negative parity (so negative row indices brick-shift consistently). */\nfunction mod2(n: number): number {\n return ((n % 2) + 2) % 2;\n}\n\n/** Minimal 2D-context surface used by {@link drawTiles}; lets tests inject a spy. */\nexport interface CanvasRenderingProtocol {\n save(): void;\n restore(): void;\n translate(x: number, y: number): void;\n rotate(angle: number): void;\n drawImage(image: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;\n}\n","import type { Unit } from '../types';\n\nconst MM_PER_INCH = 25.4;\n\nexport class UnitConverter {\n constructor(\n private unit: Unit = 'px',\n private dpi: number = 72,\n ) {}\n\n setUnit(unit: Unit): void {\n this.unit = unit;\n }\n\n setDpi(dpi: number): void {\n this.dpi = dpi;\n }\n\n getUnit(): Unit {\n return this.unit;\n }\n\n getDpi(): number {\n return this.dpi;\n }\n\n toPixels(value: number, fromUnit?: Unit): number {\n const u = fromUnit ?? this.unit;\n switch (u) {\n case 'px':\n return value;\n case 'in':\n return value * this.dpi;\n case 'mm':\n return (value / MM_PER_INCH) * this.dpi;\n }\n }\n\n fromPixels(px: number, toUnit?: Unit): number {\n const u = toUnit ?? this.unit;\n switch (u) {\n case 'px':\n return px;\n case 'in':\n return px / this.dpi;\n case 'mm':\n return (px / this.dpi) * MM_PER_INCH;\n }\n }\n}\n","import { util } from 'fabric';\nimport type { FabricObject } from 'fabric';\nimport type { EditorState, SerializedLayer } from './types';\nimport type { CanvasEditor } from './editor';\nimport { Layer } from './layer';\n\nconst VERSION = '1.0.0';\n\nexport function serializeEditor(editor: CanvasEditor): EditorState {\n return {\n version: VERSION,\n canvas: {\n width: editor.canvas.getWidth(),\n height: editor.canvas.getHeight(),\n },\n layers: editor.layers.getAll().map((layer) => layer.serialize()),\n // The configured design background, not the live canvas value (which is\n // forced transparent while a mockup preview is active).\n background: editor.getDesignBackground(),\n mockup: editor.getMockup(),\n };\n}\n\nexport async function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void> {\n editor.layers.clear();\n editor.canvas.setDimensions({ width: state.canvas.width, height: state.canvas.height });\n\n // setBackground records the design background; history is paused during\n // restore so this does not push a spurious entry. setMockup (below) then\n // applies the correct live canvas background.\n if (state.background) {\n editor.setBackground(state.background);\n }\n\n // mockup is preview-only (rendered behind the canvas), so it is restored\n // separately from the design layers. `undefined` means \"leave as-is\".\n if (state.mockup !== undefined) {\n editor.setMockup(state.mockup);\n }\n\n for (const serializedLayer of state.layers) {\n await restoreLayer(editor, serializedLayer);\n }\n\n editor.canvas.requestRenderAll();\n}\n\nasync function restoreLayer(editor: CanvasEditor, serialized: SerializedLayer): Promise<Layer> {\n const objects = await util.enlivenObjects([serialized.fabricObject]);\n const fabricObject = objects[0] as FabricObject;\n\n const layer = editor.layers.add(serialized.type, fabricObject, serialized.name, serialized.id);\n\n if (serialized.meta) {\n layer.meta = serialized.meta;\n }\n\n if (!serialized.visible) {\n editor.layers.setVisibility(layer.id, false);\n }\n if (serialized.locked) {\n editor.layers.setLocked(layer.id, true);\n }\n if (serialized.opacity !== 1) {\n editor.layers.setOpacity(layer.id, serialized.opacity);\n }\n\n return layer;\n}\n","import type { Canvas, ImageFormat } from 'fabric';\n\nexport interface PngExportOptions {\n multiplier?: number;\n format?: ImageFormat;\n quality?: number;\n}\n\nexport async function exportPNG(\n canvas: Canvas,\n options: PngExportOptions = {},\n): Promise<Blob> {\n const { multiplier = 1, format = 'png' as ImageFormat, quality = 1 } = options;\n const dataUrl = canvas.toDataURL({\n format,\n multiplier,\n quality,\n });\n const response = await fetch(dataUrl);\n return response.blob();\n}\n\nexport function exportSVG(canvas: Canvas): string {\n return canvas.toSVG();\n}\n\nexport function exportDataURL(\n canvas: Canvas,\n format: ImageFormat = 'png',\n multiplier = 1,\n): string {\n return canvas.toDataURL({ format, multiplier });\n}\n","import type { FabricObject } from 'fabric';\n\n// ─── Units ───────────────────────────────────────────────\n\nexport type Unit = 'px' | 'mm' | 'in';\n\n// ─── Layers ──────────────────────────────────────────────\n\nexport type LayerType = 'image' | 'text' | 'shape' | 'template' | 'group';\n\nexport interface LayerData {\n id: string;\n type: LayerType;\n name: string;\n visible: boolean;\n locked: boolean;\n opacity: number;\n /** Non-fabric metadata persisted with the layer (pattern config, etc). */\n meta?: LayerMeta;\n}\n\nexport interface LayerMeta {\n pattern?: PatternState;\n [key: string]: unknown;\n}\n\nexport interface SerializedLayer extends LayerData {\n fabricObject: Record<string, unknown>;\n}\n\n// ─── Editor ──────────────────────────────────────────────\n\nexport interface EditorState {\n version: string;\n canvas: { width: number; height: number };\n layers: SerializedLayer[];\n background?: string;\n mockup?: MockupConfig | null;\n}\n\nexport interface EditorConfig {\n width: number;\n height: number;\n backgroundColor?: string;\n fileAdapter?: FileAdapter;\n imageProvider?: ImageProvider;\n preserveObjectStacking?: boolean;\n unit?: Unit;\n dpi?: number;\n}\n\n// ─── Adapters ────────────────────────────────────────────\n\nexport type ExportFormat = 'png' | 'svg' | 'json';\n\nexport interface FileAdapter {\n save(data: Blob | string, filename: string, format: ExportFormat): Promise<string>;\n}\n\nexport interface ImageProviderResult {\n url: string;\n width: number;\n height: number;\n}\n\nexport interface ImageProvider {\n upload(file: File): Promise<ImageProviderResult>;\n browse?(): Promise<ImageProviderResult | null>;\n}\n\n// ─── Plugins ─────────────────────────────────────────────\n\nexport interface ShapePlugin {\n name: string;\n icon: string;\n category?: string;\n create(options?: Record<string, unknown>): FabricObject;\n}\n\nexport interface TemplateParameter {\n key: string;\n label: string;\n type: 'text' | 'color' | 'number' | 'font';\n default: string | number;\n}\n\nexport interface TemplateDefinition {\n id: string;\n name: string;\n category: string;\n svg: string;\n parameters: TemplateParameter[];\n preview?: string;\n}\n\n// ─── Printify ────────────────────────────────────────────\n\nexport interface PrintifyPositioning {\n x: number;\n y: number;\n scale: number;\n angle: number;\n}\n\n// ─── Patterns ────────────────────────────────────────────\n\nexport type TileMode = 'grid' | 'brick-horizontal' | 'brick-vertical';\n\nexport interface PatternConfig {\n mode: TileMode;\n /** Extra horizontal gap between tiles, as % of tile width. */\n horizontalSpacing: number;\n /** Extra vertical gap between tiles, as % of tile height. */\n verticalSpacing: number;\n /** Rotation of the whole pattern, in degrees. */\n angle: number;\n /** Brick row/column shift, as % of tile size. */\n horizontalOffset: number;\n /** Added rotation per horizontal step, in degrees. */\n rotationStepH: number;\n /** Added rotation per vertical step, in degrees. */\n rotationStepV: number;\n}\n\n/** Persisted on a layer's meta while it is rendered as a repeating pattern. */\nexport interface PatternState {\n config: PatternConfig;\n /** Original (pre-pattern) image source, as a durable data URL when possible. */\n originalSrc: string;\n /** Original fabric transform, restored when the pattern is turned off. */\n original: {\n left: number;\n top: number;\n scaleX: number;\n scaleY: number;\n width: number;\n height: number;\n angle: number;\n cropX: number;\n cropY: number;\n };\n}\n\nexport const DEFAULT_PATTERN_CONFIG: PatternConfig = {\n mode: 'grid',\n horizontalSpacing: 0,\n verticalSpacing: 0,\n angle: 0,\n horizontalOffset: 0,\n rotationStepH: 0,\n rotationStepV: 0,\n};\n\n// ─── Mockup ──────────────────────────────────────────────\n\nexport interface MockupPrintArea {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\nexport interface MockupConfig {\n /** Garment/product image rendered behind the design (preview only). */\n image: string;\n /** Print-area rectangle, in canvas pixels. Drawn as a guide. */\n printArea?: MockupPrintArea;\n}\n\n// ─── Events ──────────────────────────────────────────────\n\nexport interface EditorEvents {\n 'layer:added': { layer: LayerData };\n 'layer:removed': { layerId: string };\n 'layer:selected': { layerId: string | null };\n 'layer:modified': { layerId: string };\n 'layer:reordered': { layerIds: string[] };\n 'layers:changed': { layers: LayerData[] };\n 'selection:changed': { selected: string[] };\n 'history:changed': { canUndo: boolean; canRedo: boolean };\n 'zoom:changed': { zoom: number };\n 'snap:changed': { enabled: boolean };\n 'crop:changed': { active: boolean; layerId: string | null };\n 'mockup:changed': { mockup: MockupConfig | null };\n 'canvas:modified': Record<string, never>;\n 'export:start': { format: string };\n 'export:complete': { format: string; url?: string };\n error: { message: string; error?: unknown };\n}\n"],"mappings":"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","names":["import_fabric","STROKE","import_fabric"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/editor.ts","../src/events.ts","../src/utils/id.ts","../src/layer.ts","../src/history.ts","../src/snapping.ts","../src/crop.ts","../src/utils/clamp.ts","../src/pattern.ts","../src/utils/units.ts","../src/serialization.ts","../src/export.ts","../src/types.ts"],"sourcesContent":["export { CanvasEditor } from './editor';\nexport { Layer, LayerManager } from './layer';\nexport { HistoryManager } from './history';\nexport { SnapManager } from './snapping';\nexport { CropController } from './crop';\nexport {\n PatternManager,\n buildPatternDataURL,\n drawTiles,\n computeTilePositions,\n clearPatternImageCache,\n} from './pattern';\nexport type { TilePlacement } from './pattern';\nexport { EventEmitter } from './events';\nexport { UnitConverter } from './utils/units';\nexport { generateId } from './utils/id';\nexport { clamp, round2 } from './utils/clamp';\nexport { serializeEditor, deserializeEditor } from './serialization';\nexport { exportPNG, exportSVG, exportDataURL } from './export';\nexport { DEFAULT_PATTERN_CONFIG } from './types';\n\nexport type {\n LayerType,\n LayerData,\n LayerMeta,\n SerializedLayer,\n EditorState,\n EditorConfig,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n ExportFormat,\n ShapePlugin,\n TemplateDefinition,\n TemplateParameter,\n PrintifyPositioning,\n TileMode,\n PatternConfig,\n PatternState,\n MockupConfig,\n MockupPrintArea,\n EditorEvents,\n Unit,\n} from './types';\n\nexport type { PngExportOptions } from './export';\n","import { Canvas, FabricImage, Textbox } from 'fabric';\nimport type { FabricObject, ImageFormat } from 'fabric';\nimport type {\n EditorConfig,\n EditorEvents,\n EditorState,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n MockupConfig,\n PatternConfig,\n PrintifyPositioning,\n ShapePlugin,\n TemplateDefinition,\n} from './types';\nimport { EventEmitter } from './events';\nimport { Layer, LayerManager } from './layer';\nimport { HistoryManager } from './history';\nimport { SnapManager } from './snapping';\nimport { CropController } from './crop';\nimport { PatternManager } from './pattern';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL } from './export';\nimport type { PngExportOptions } from './export';\nimport { clamp, round2 } from './utils/clamp';\n\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 8;\n\nexport class CanvasEditor {\n readonly canvas: Canvas;\n readonly layers: LayerManager;\n readonly history: HistoryManager;\n readonly events: EventEmitter<EditorEvents>;\n readonly units: UnitConverter;\n readonly snapping: SnapManager;\n readonly crop: CropController;\n readonly patterns: PatternManager;\n\n private fileAdapter?: FileAdapter;\n private imageProvider?: ImageProvider;\n private zoomLevel = 1;\n private mockup: MockupConfig | null = null;\n // The design's configured background. The live canvas background is forced\n // transparent while a mockup preview is shown, so this is the source of truth\n // for serialization and export — not the (possibly transient) canvas value.\n private designBackground: string;\n\n constructor(canvasElement: HTMLCanvasElement, config: EditorConfig) {\n this.events = new EventEmitter<EditorEvents>();\n this.units = new UnitConverter(config.unit ?? 'px', config.dpi ?? 72);\n\n const widthPx = this.units.toPixels(config.width);\n const heightPx = this.units.toPixels(config.height);\n this.designBackground = config.backgroundColor ?? '#ffffff';\n\n this.canvas = new Canvas(canvasElement, {\n width: widthPx,\n height: heightPx,\n backgroundColor: this.designBackground,\n preserveObjectStacking: config.preserveObjectStacking ?? true,\n selection: true,\n });\n\n this.layers = new LayerManager(this.canvas, this.events);\n this.fileAdapter = config.fileAdapter;\n this.imageProvider = config.imageProvider;\n\n this.history = new HistoryManager({\n getState: () => JSON.stringify(this.toJSON()),\n restoreState: async (state: string) => {\n const parsed = JSON.parse(state) as EditorState;\n await this.fromJSON(parsed);\n },\n events: this.events,\n });\n\n this.snapping = new SnapManager(this.canvas, this.events);\n this.crop = new CropController(this.canvas, this.history, this.events);\n this.patterns = new PatternManager(this.canvas, this.layers, this.history);\n\n this.setupCanvasEvents();\n this.history.saveImmediate();\n }\n\n // ─── Layer Operations ────────────────────────────────\n\n async addImage(url: string, options?: Partial<Record<string, unknown>>): Promise<Layer> {\n // Don't force crossOrigin here: a remote image whose host sends no CORS\n // header would then fail to load at all. The pattern engine requests its own\n // crossOrigin copy when it needs an untainted canvas (see pattern.ts).\n // Fabric v7 defaults originX/originY to 'center'; pin top-left so images\n // land at their left/top like the rest of the editor's coordinate model.\n const img = await FabricImage.fromURL(url, {}, { originX: 'left', originY: 'top', ...options });\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n }\n\n addText(text: string, options?: Partial<Record<string, unknown>>): Layer {\n const textbox = new Textbox(text, {\n fontSize: 32,\n fontFamily: 'Arial',\n fill: '#000000',\n width: 200,\n // v7 defaults origin to center; keep top-left placement.\n originX: 'left',\n originY: 'top',\n ...options,\n });\n const layer = this.layers.add('text', textbox as unknown as FabricObject);\n this.history.save();\n return layer;\n }\n\n addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer {\n const obj = plugin.create(options);\n const layer = this.layers.add('shape', obj, plugin.name);\n this.history.save();\n return layer;\n }\n\n async addTemplate(\n template: TemplateDefinition,\n _params: Record<string, string | number>,\n ): Promise<Layer> {\n // Template loading is handled by the templates package\n // This is a placeholder for direct fabric object addition\n const { Textbox: TextboxClass } = await import('fabric');\n const placeholder = new TextboxClass(`[Template: ${template.name}]`, {\n fontSize: 24,\n fontFamily: 'Arial',\n fill: '#666666',\n width: 300,\n originX: 'left',\n originY: 'top',\n });\n const layer = this.layers.add(\n 'template',\n placeholder as unknown as FabricObject,\n template.name,\n );\n this.history.save();\n return layer;\n }\n\n removeLayer(id: string): void {\n this.layers.remove(id);\n this.history.save();\n }\n\n selectLayer(id: string | null): void {\n this.layers.select(id);\n }\n\n getSelectedLayer(): Layer | null {\n const active = this.canvas.getActiveObject();\n if (!active) return null;\n return this.layers.findByObject(active) ?? null;\n }\n\n /** Mirror a layer along the given axis. */\n flipLayer(id: string, axis: 'horizontal' | 'vertical'): void {\n const layer = this.layers.get(id);\n if (!layer) return;\n const obj = layer.fabricObject;\n if (axis === 'horizontal') {\n obj.set('flipX', !obj.flipX);\n } else {\n obj.set('flipY', !obj.flipY);\n }\n obj.setCoords();\n this.canvas.requestRenderAll();\n this.history.save();\n }\n\n /** Clone a layer (offset slightly) and select the copy. */\n async duplicateLayer(id: string): Promise<Layer | null> {\n const layer = this.layers.get(id);\n if (!layer) return null;\n\n const clone = await layer.fabricObject.clone();\n // A pattern layer's image is rasterised to cover the canvas at 0,0; offsetting\n // it would leave a gap at the edges, so only nudge non-pattern duplicates.\n if (!layer.meta.pattern) {\n clone.set({ left: (clone.left ?? 0) + 15, top: (clone.top ?? 0) + 15 });\n }\n clone.setCoords();\n\n const copy = this.layers.add(layer.type, clone, `${layer.name} copy`);\n // Carry non-fabric metadata (e.g. pattern config) onto the copy.\n copy.meta = structuredClone(layer.meta);\n\n this.canvas.setActiveObject(clone);\n this.canvas.requestRenderAll();\n this.history.save();\n return copy;\n }\n\n // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n await deserializeEditor(this, state);\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n this.events.emit('export:start', { format: 'png' });\n const blob = await this.withDesignBackground(() => exportPNG(this.canvas, options));\n this.events.emit('export:complete', { format: 'png' });\n return blob;\n }\n\n toSVG(): string {\n this.events.emit('export:start', { format: 'svg' });\n const svg = this.withDesignBackground(() => exportSVG(this.canvas));\n this.events.emit('export:complete', { format: 'svg' });\n return svg;\n }\n\n toDataURL(format: ImageFormat = 'png', multiplier = 1): string {\n return this.withDesignBackground(() => exportDataURL(this.canvas, format, multiplier));\n }\n\n /**\n * Run an export with the configured design background applied, even when a\n * mockup preview has forced the live canvas transparent — so exports reflect\n * the design, not the preview. Restores the preview state afterwards.\n */\n private withDesignBackground<T>(fn: () => T): T {\n if (!this.mockup) return fn();\n const previewBg = this.canvas.backgroundColor;\n this.canvas.backgroundColor = this.designBackground;\n try {\n return fn();\n } finally {\n this.canvas.backgroundColor = previewBg;\n }\n }\n\n toPrintifyPositioning(): Record<string, PrintifyPositioning> {\n const result: Record<string, PrintifyPositioning> = {};\n const canvasW = this.canvas.getWidth();\n const canvasH = this.canvas.getHeight();\n\n const imageLayers = this.layers.getAll().filter((l) => l.type === 'image');\n\n if (imageLayers.length === 0) {\n result['front'] = { x: 0.5, y: 0.5, scale: 1, angle: 0 };\n return result;\n }\n\n for (const layer of imageLayers) {\n const obj = layer.fabricObject;\n const center = obj.getCenterPoint();\n result[layer.name] = {\n x: round2(center.x / canvasW),\n y: round2(center.y / canvasH),\n scale: round2(obj.scaleX ?? 1),\n angle: round2(obj.angle ?? 0),\n };\n }\n\n return result;\n }\n\n // ─── File Operations ────────────────────────────────\n\n async save(filename: string, format: 'png' | 'svg' | 'json'): Promise<string | undefined> {\n if (!this.fileAdapter) return undefined;\n\n let data: Blob | string;\n if (format === 'png') {\n data = await this.toPNG();\n } else if (format === 'svg') {\n data = this.toSVG();\n } else {\n data = JSON.stringify(this.toJSON(), null, 2);\n }\n\n return this.fileAdapter.save(data, filename, format);\n }\n\n async uploadImage(file: File): Promise<ImageProviderResult | undefined> {\n if (!this.imageProvider) return undefined;\n return this.imageProvider.upload(file);\n }\n\n async browseImages(): Promise<ImageProviderResult | null> {\n if (!this.imageProvider?.browse) return null;\n return this.imageProvider.browse();\n }\n\n setFileAdapter(adapter: FileAdapter): void {\n this.fileAdapter = adapter;\n }\n\n setImageProvider(provider: ImageProvider): void {\n this.imageProvider = provider;\n }\n\n // ─── Canvas Operations ──────────────────────────────\n\n setBackground(color: string): void {\n this.designBackground = color;\n // Don't override the mockup's transparent preview; it will pick up the new\n // colour when the mockup is cleared.\n if (!this.mockup) {\n this.canvas.backgroundColor = color;\n this.canvas.requestRenderAll();\n }\n this.history.save();\n }\n\n getDesignBackground(): string {\n return this.designBackground;\n }\n\n resize(width: number, height: number): void {\n const widthPx = this.units.toPixels(width);\n const heightPx = this.units.toPixels(height);\n this.canvas.setDimensions({ width: widthPx, height: heightPx });\n this.canvas.requestRenderAll();\n }\n\n // ─── Zoom ───────────────────────────────────────────\n //\n // Zoom is a *display* transform (the view layer CSS-scales the canvas), not a\n // fabric viewport zoom — fabric's setZoom bakes into toDataURL/toSVG and would\n // corrupt exports. The editor only tracks the level and emits an event.\n\n setZoom(level: number): void {\n const next = clamp(round2(level), MIN_ZOOM, MAX_ZOOM);\n if (next === this.zoomLevel) return;\n this.zoomLevel = next;\n this.events.emit('zoom:changed', { zoom: next });\n }\n\n /** @deprecated use setZoom — kept for backward compatibility. */\n zoom(level: number): void {\n this.setZoom(level);\n }\n\n getZoom(): number {\n return this.zoomLevel;\n }\n\n zoomIn(step = 0.1): void {\n this.setZoom(this.zoomLevel + step);\n }\n\n zoomOut(step = 0.1): void {\n this.setZoom(this.zoomLevel - step);\n }\n\n resetZoom(): void {\n this.setZoom(1);\n }\n\n /** Fit the canvas inside a viewport (in px), accounting for padding. */\n zoomToFit(viewportWidth: number, viewportHeight: number, padding = 0): void {\n const w = this.canvas.getWidth();\n const h = this.canvas.getHeight();\n if (w <= 0 || h <= 0) return;\n const sx = (viewportWidth - padding * 2) / w;\n const sy = (viewportHeight - padding * 2) / h;\n this.setZoom(Math.min(sx, sy));\n }\n\n // ─── Patterns ───────────────────────────────────────\n\n applyPattern(layerId: string, config: PatternConfig): Promise<void> {\n return this.patterns.apply(layerId, config);\n }\n\n clearPattern(layerId: string): Promise<void> {\n return this.patterns.disable(layerId);\n }\n\n // ─── Mockup (preview-only) ──────────────────────────\n\n setMockup(mockup: MockupConfig | null): void {\n this.mockup = mockup;\n // The mockup renders behind the design, so the canvas must be transparent\n // while it is shown; restore the configured background when it is cleared.\n this.canvas.backgroundColor = mockup ? '' : this.designBackground;\n this.canvas.requestRenderAll();\n this.events.emit('mockup:changed', { mockup });\n }\n\n clearMockup(): void {\n this.setMockup(null);\n }\n\n getMockup(): MockupConfig | null {\n return this.mockup;\n }\n\n // ─── Cleanup ────────────────────────────────────────\n\n dispose(): void {\n this.snapping.dispose();\n this.crop.dispose();\n this.history.dispose();\n this.events.removeAllListeners();\n this.canvas.dispose();\n }\n\n // ─── Private ────────────────────────────────────────\n\n private setupCanvasEvents(): void {\n this.canvas.on('object:modified', (e) => {\n if (!e.target) return;\n const layer = this.layers.findByObject(e.target);\n if (layer) {\n this.events.emit('layer:modified', { layerId: layer.id });\n this.history.save();\n }\n });\n\n this.canvas.on('selection:created', (e) => {\n const selected = (e.selected ?? [])\n .map((obj) => this.layers.findByObject(obj)?.id)\n .filter((id): id is string => id !== undefined);\n this.events.emit('selection:changed', { selected });\n });\n\n this.canvas.on('selection:updated', (e) => {\n const selected = (e.selected ?? [])\n .map((obj) => this.layers.findByObject(obj)?.id)\n .filter((id): id is string => id !== undefined);\n this.events.emit('selection:changed', { selected });\n });\n\n this.canvas.on('selection:cleared', () => {\n this.events.emit('selection:changed', { selected: [] });\n });\n }\n}\n","type EventHandler<T> = (data: T) => void;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport class EventEmitter<TEvents extends {} = Record<string, unknown>> {\n private listeners = new Map<keyof TEvents, Set<EventHandler<unknown>>>();\n\n on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler as EventHandler<unknown>);\n }\n\n off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n this.listeners.get(event)?.delete(handler as EventHandler<unknown>);\n }\n\n once<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n const wrapped: EventHandler<TEvents[K]> = (data) => {\n this.off(event, wrapped);\n handler(data);\n };\n this.on(event, wrapped);\n }\n\n emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n const handlers = this.listeners.get(event);\n if (!handlers) return;\n for (const handler of handlers) {\n handler(data);\n }\n }\n\n removeAllListeners(event?: keyof TEvents): void {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n }\n}\n","import { nanoid } from 'nanoid';\n\nexport const generateId = (): string => nanoid(12);\n","import type { Canvas, FabricObject } from 'fabric';\nimport type { LayerData, LayerMeta, LayerType, SerializedLayer } from './types';\nimport type { EditorEvents } from './types';\nimport type { EventEmitter } from './events';\nimport { generateId } from './utils/id';\n\nexport class Layer {\n readonly id: string;\n readonly type: LayerType;\n name: string;\n visible: boolean;\n locked: boolean;\n opacity: number;\n /** Non-fabric data (e.g. pattern config) that must persist with the layer. */\n meta: LayerMeta;\n readonly fabricObject: FabricObject;\n\n constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string) {\n this.id = id ?? generateId();\n this.type = type;\n this.name = name ?? `${type}-${this.id.slice(0, 4)}`;\n this.visible = true;\n this.locked = false;\n this.opacity = 1;\n this.meta = {};\n this.fabricObject = fabricObject;\n\n (this.fabricObject as FabricObject & { _layerId: string })._layerId = this.id;\n }\n\n private hasMeta(): boolean {\n return Object.keys(this.meta).length > 0;\n }\n\n toData(): LayerData {\n return {\n id: this.id,\n type: this.type,\n name: this.name,\n visible: this.visible,\n locked: this.locked,\n opacity: this.opacity,\n ...(this.hasMeta() ? { meta: this.meta } : {}),\n };\n }\n\n serialize(): SerializedLayer {\n return {\n ...this.toData(),\n fabricObject: this.fabricObject.toObject() as Record<string, unknown>,\n };\n }\n}\n\nexport class LayerManager {\n private layers: Layer[] = [];\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {}\n\n add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer {\n const layer = new Layer(type, fabricObject, name, id);\n this.layers.push(layer);\n this.canvas.add(fabricObject);\n this.events.emit('layer:added', { layer: layer.toData() });\n this.emitChanged();\n return layer;\n }\n\n remove(id: string): boolean {\n const index = this.layers.findIndex((l) => l.id === id);\n if (index === -1) return false;\n const layer = this.layers[index];\n this.canvas.remove(layer.fabricObject);\n this.layers.splice(index, 1);\n this.events.emit('layer:removed', { layerId: id });\n this.emitChanged();\n return true;\n }\n\n reorder(id: string, newIndex: number): boolean {\n const oldIndex = this.layers.findIndex((l) => l.id === id);\n if (oldIndex === -1) return false;\n const clamped = Math.max(0, Math.min(this.layers.length - 1, newIndex));\n const [layer] = this.layers.splice(oldIndex, 1);\n this.layers.splice(clamped, 0, layer);\n // Sync fabric z-order\n this.layers.forEach((l, i) => {\n this.canvas.moveObjectTo(l.fabricObject, i);\n });\n this.events.emit('layer:reordered', { layerIds: this.layers.map((l) => l.id) });\n this.emitChanged();\n return true;\n }\n\n select(id: string | null): void {\n if (id === null) {\n this.canvas.discardActiveObject();\n } else {\n const layer = this.get(id);\n if (layer) {\n this.canvas.setActiveObject(layer.fabricObject);\n }\n }\n this.canvas.requestRenderAll();\n this.events.emit('layer:selected', { layerId: id });\n // Fabric only fires selection:* on user interaction; mirror programmatic\n // selection so React selection state (useSelection) stays in sync.\n const selected = id && this.get(id) ? [id] : [];\n this.events.emit('selection:changed', { selected });\n }\n\n get(id: string): Layer | undefined {\n return this.layers.find((l) => l.id === id);\n }\n\n getAll(): Layer[] {\n return [...this.layers];\n }\n\n findByObject(obj: FabricObject): Layer | undefined {\n const layerId = (obj as FabricObject & { _layerId?: string })._layerId;\n if (layerId) return this.get(layerId);\n return this.layers.find((l) => l.fabricObject === obj);\n }\n\n setVisibility(id: string, visible: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.visible = visible;\n layer.fabricObject.visible = visible;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setLocked(id: string, locked: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.locked = locked;\n layer.fabricObject.selectable = !locked;\n layer.fabricObject.evented = !locked;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setOpacity(id: string, opacity: number): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.opacity = opacity;\n layer.fabricObject.opacity = opacity;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setName(id: string, name: string): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.name = name;\n this.emitChanged();\n }\n\n clear(): void {\n for (const layer of this.layers) {\n this.canvas.remove(layer.fabricObject);\n }\n this.layers = [];\n this.emitChanged();\n }\n\n count(): number {\n return this.layers.length;\n }\n\n private emitChanged(): void {\n this.events.emit('layers:changed', {\n layers: this.layers.map((l) => l.toData()),\n });\n }\n}\n","import type { EditorEvents } from './types';\nimport type { EventEmitter } from './events';\n\nexport class HistoryManager {\n private undoStack: string[] = [];\n private redoStack: string[] = [];\n private maxSize: number;\n private paused = false;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private debounceMs: number;\n private getState: () => string;\n private restoreState: (state: string) => Promise<void>;\n private events: EventEmitter<EditorEvents>;\n\n constructor(opts: {\n getState: () => string;\n restoreState: (state: string) => Promise<void>;\n events: EventEmitter<EditorEvents>;\n maxSize?: number;\n debounceMs?: number;\n }) {\n this.getState = opts.getState;\n this.restoreState = opts.restoreState;\n this.events = opts.events;\n this.maxSize = opts.maxSize ?? 50;\n this.debounceMs = opts.debounceMs ?? 300;\n }\n\n save(): void {\n if (this.paused) return;\n\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.saveImmediate();\n }, this.debounceMs);\n }\n\n saveImmediate(): void {\n if (this.paused) return;\n const state = this.getState();\n this.undoStack.push(state);\n if (this.undoStack.length > this.maxSize) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.emitChanged();\n }\n\n async undo(): Promise<void> {\n // Drop any debounced save still pending from an in-progress edit, otherwise\n // its timer would fire after restore completes and clobber the redo stack.\n this.cancelPending();\n const state = this.undoStack.pop();\n if (!state) return;\n\n this.redoStack.push(this.getState());\n this.paused = true;\n await this.restoreState(state);\n this.paused = false;\n this.emitChanged();\n }\n\n async redo(): Promise<void> {\n this.cancelPending();\n const state = this.redoStack.pop();\n if (!state) return;\n\n this.undoStack.push(this.getState());\n this.paused = true;\n await this.restoreState(state);\n this.paused = false;\n this.emitChanged();\n }\n\n /** True while a restore (undo/redo/deserialize) is in flight. Managers that\n * react to canvas events should skip mutating history during this window. */\n isRestoring(): boolean {\n return this.paused;\n }\n\n pause(): void {\n this.paused = true;\n }\n\n resume(): void {\n this.paused = false;\n }\n\n canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n this.emitChanged();\n }\n\n private cancelPending(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n dispose(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n }\n\n private emitChanged(): void {\n this.events.emit('history:changed', {\n canUndo: this.canUndo(),\n canRedo: this.canRedo(),\n });\n }\n}\n","import type { Canvas, FabricObject, TPointerEvent } from 'fabric';\nimport type { EventEmitter } from './events';\nimport type { EditorEvents } from './types';\n\ninterface SnapGuide {\n /** Vertical guide at this x (canvas px), or undefined for a horizontal one. */\n x?: number;\n y?: number;\n}\n\nconst STROKE = '#22c55e';\n\n/**\n * Snaps a dragged object's edges and centre to the canvas edges/centre and to\n * other objects, drawing transient guide lines while it does. Switchable via\n * {@link setEnabled}; inert (zero overhead beyond a flag check) when disabled.\n */\nexport class SnapManager {\n private enabled = false;\n private threshold = 8;\n private guides: SnapGuide[] = [];\n\n private readonly onMoving: (e: { target?: FabricObject; e: TPointerEvent }) => void;\n private readonly onAfterRender: () => void;\n private readonly onModified: () => void;\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {\n this.onMoving = (e) => this.handleMoving(e.target);\n this.onAfterRender = () => this.drawGuides();\n this.onModified = () => this.clearGuides();\n\n this.canvas.on('object:moving', this.onMoving);\n this.canvas.on('after:render', this.onAfterRender);\n this.canvas.on('mouse:up', this.onModified);\n this.canvas.on('selection:cleared', this.onModified);\n }\n\n setEnabled(enabled: boolean): void {\n if (this.enabled === enabled) return;\n this.enabled = enabled;\n if (!enabled) this.clearGuides();\n this.events.emit('snap:changed', { enabled });\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n setThreshold(px: number): void {\n this.threshold = Math.max(0, px);\n }\n\n dispose(): void {\n this.canvas.off('object:moving', this.onMoving);\n this.canvas.off('after:render', this.onAfterRender);\n this.canvas.off('mouse:up', this.onModified);\n this.canvas.off('selection:cleared', this.onModified);\n this.guides = [];\n }\n\n private handleMoving(target?: FabricObject): void {\n if (!this.enabled || !target) return;\n\n target.setCoords();\n const r = target.getBoundingRect();\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n // Candidate lines for both axes, collected in a single pass over the objects.\n const { xs, ys } = collectCandidates(this.canvas, target, cw, ch);\n\n this.guides = [];\n\n const dx = bestSnap([r.left, r.left + r.width / 2, r.left + r.width], xs, this.threshold);\n if (dx !== null) {\n target.set('left', (target.left ?? 0) + dx.delta);\n this.guides.push({ x: dx.line });\n }\n\n const dy = bestSnap([r.top, r.top + r.height / 2, r.top + r.height], ys, this.threshold);\n if (dy !== null) {\n target.set('top', (target.top ?? 0) + dy.delta);\n this.guides.push({ y: dy.line });\n }\n\n target.setCoords();\n }\n\n private drawGuides(): void {\n if (!this.enabled || this.guides.length === 0) return;\n const ctx = this.canvas.contextTop;\n if (!ctx) return;\n\n const retina = this.canvas.getRetinaScaling();\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n ctx.save();\n ctx.setTransform(retina, 0, 0, retina, 0, 0);\n ctx.lineWidth = 1;\n ctx.strokeStyle = STROKE;\n ctx.setLineDash([4, 4]);\n ctx.beginPath();\n for (const g of this.guides) {\n if (g.x !== undefined) {\n ctx.moveTo(g.x, 0);\n ctx.lineTo(g.x, ch);\n }\n if (g.y !== undefined) {\n ctx.moveTo(0, g.y);\n ctx.lineTo(cw, g.y);\n }\n }\n ctx.stroke();\n ctx.restore();\n }\n\n private clearGuides(): void {\n if (this.guides.length === 0) return;\n this.guides = [];\n this.canvas.clearContext(this.canvas.contextTop);\n }\n}\n\n/**\n * Snap candidate lines (edges + centre) for the canvas and every other object,\n * for both axes, computed in one pass so each object's bounds are read once.\n */\nfunction collectCandidates(\n canvas: Canvas,\n moving: FabricObject,\n cw: number,\n ch: number,\n): { xs: number[]; ys: number[] } {\n const xs = [0, cw / 2, cw];\n const ys = [0, ch / 2, ch];\n for (const obj of canvas.getObjects()) {\n if (obj === moving || !obj.visible) continue;\n obj.setCoords();\n const r = obj.getBoundingRect();\n xs.push(r.left, r.left + r.width / 2, r.left + r.width);\n ys.push(r.top, r.top + r.height / 2, r.top + r.height);\n }\n return { xs, ys };\n}\n\n/**\n * Find the closest (edge, candidate) pair within threshold. Returns the delta\n * to apply and the guide line to draw, or null if nothing is close enough.\n */\nfunction bestSnap(\n edges: number[],\n candidates: number[],\n threshold: number,\n): { delta: number; line: number } | null {\n let best: { delta: number; line: number; dist: number } | null = null;\n for (const edge of edges) {\n for (const c of candidates) {\n const dist = Math.abs(edge - c);\n if (dist <= threshold && (best === null || dist < best.dist)) {\n best = { delta: c - edge, line: c, dist };\n }\n }\n }\n return best ? { delta: best.delta, line: best.line } : null;\n}\n\nexport { bestSnap as _bestSnap };\n","import { Rect } from 'fabric';\nimport type { Canvas, FabricImage } from 'fabric';\nimport type { Layer } from './layer';\nimport type { HistoryManager } from './history';\nimport type { EventEmitter } from './events';\nimport type { EditorEvents } from './types';\nimport { clamp } from './utils/clamp';\n\ninterface CropSession {\n layerId: string;\n image: FabricImage;\n rect: Rect;\n prevSelectable: boolean;\n prevEvented: boolean;\n /** Angle before crop normalised it to 0; restored if the crop is cancelled. */\n prevAngle: number;\n}\n\n/**\n * Interactive crop for image layers. {@link start} drops a draggable/resizable\n * rectangle over the image; {@link apply} converts it into fabric crop props\n * (cropX/cropY/width/height in element pixels) and keeps the displayed size in\n * sync with the rectangle.\n *\n * Rotation is unsupported: an angled image is normalised to 0° on start.\n */\nexport class CropController {\n private session: CropSession | null = null;\n private readonly onSelectionCleared: () => void;\n\n constructor(\n private canvas: Canvas,\n private history: HistoryManager,\n private events: EventEmitter<EditorEvents>,\n ) {\n this.onSelectionCleared = () => {\n // Clicking away from the crop rect abandons the crop.\n if (this.session) this.cancel();\n };\n this.canvas.on('selection:cleared', this.onSelectionCleared);\n }\n\n isActive(): boolean {\n return this.session !== null;\n }\n\n activeLayerId(): string | null {\n return this.session?.layerId ?? null;\n }\n\n start(layer: Layer): void {\n if (layer.type !== 'image') return;\n if (this.session) this.cancel();\n\n const image = layer.fabricObject as FabricImage;\n const prevAngle = image.angle ?? 0;\n if (prevAngle) image.rotate(0);\n image.setCoords();\n const b = image.getBoundingRect();\n\n const rect = new Rect({\n left: b.left,\n top: b.top,\n width: b.width,\n height: b.height,\n originX: 'left',\n originY: 'top',\n fill: 'rgba(34,197,94,0.12)',\n stroke: STROKE,\n strokeWidth: 1,\n strokeDashArray: [4, 4],\n strokeUniform: true,\n lockRotation: true,\n cornerColor: STROKE,\n transparentCorners: false,\n });\n rect.setControlsVisibility({ mtr: false });\n\n const prevSelectable = image.selectable ?? true;\n const prevEvented = image.evented ?? true;\n image.selectable = false;\n image.evented = false;\n\n this.canvas.add(rect);\n this.canvas.setActiveObject(rect);\n this.canvas.requestRenderAll();\n\n this.session = { layerId: layer.id, image, rect, prevSelectable, prevEvented, prevAngle };\n this.events.emit('crop:changed', { active: true, layerId: layer.id });\n }\n\n apply(): void {\n const s = this.session;\n if (!s) return;\n const { image, rect } = s;\n\n rect.setCoords();\n image.setCoords();\n const scaleX = image.scaleX ?? 1;\n const scaleY = image.scaleY ?? 1;\n const imgLeft = image.left ?? 0;\n const imgTop = image.top ?? 0;\n\n const el = image.getElement() as HTMLImageElement;\n const naturalW = el.naturalWidth || image.width || 0;\n const naturalH = el.naturalHeight || image.height || 0;\n\n // Map the rect (canvas px) into the image's element-pixel space.\n let cropX = (image.cropX ?? 0) + (rect.left! - imgLeft) / scaleX;\n let cropY = (image.cropY ?? 0) + (rect.top! - imgTop) / scaleY;\n let cropW = rect.getScaledWidth() / scaleX;\n let cropH = rect.getScaledHeight() / scaleY;\n\n // Keep the crop window inside the source element.\n cropX = clamp(cropX, 0, Math.max(0, naturalW - 1));\n cropY = clamp(cropY, 0, Math.max(0, naturalH - 1));\n cropW = clamp(cropW, 1, naturalW - cropX);\n cropH = clamp(cropH, 1, naturalH - cropY);\n\n image.set({\n cropX,\n cropY,\n width: cropW,\n height: cropH,\n // Anchor the displayed result where the rect sits.\n left: imgLeft + (cropX - (image.cropX ?? 0)) * scaleX,\n top: imgTop + (cropY - (image.cropY ?? 0)) * scaleY,\n });\n image.setCoords();\n\n this.finish();\n this.history.save();\n }\n\n cancel(): void {\n const s = this.session;\n if (!s) return;\n // Cancelling discards the crop, so restore the rotation start() zeroed out.\n if (s.prevAngle) {\n s.image.rotate(s.prevAngle);\n s.image.setCoords();\n }\n this.finish();\n }\n\n dispose(): void {\n this.cancel();\n this.canvas.off('selection:cleared', this.onSelectionCleared);\n }\n\n private finish(): void {\n const s = this.session;\n if (!s) return;\n // Null the session first so the selection:cleared fired by removing the\n // rect / re-selecting does not re-enter cancel().\n this.session = null;\n\n this.canvas.remove(s.rect);\n s.image.selectable = s.prevSelectable;\n s.image.evented = s.prevEvented;\n this.canvas.setActiveObject(s.image);\n this.canvas.requestRenderAll();\n\n this.events.emit('crop:changed', { active: false, layerId: null });\n }\n}\n\nconst STROKE = '#22c55e';\n","export function clamp(v: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, v));\n}\n\nexport function round2(v: number): number {\n return Math.round(v * 100) / 100;\n}\n","import { FabricImage, util } from 'fabric';\nimport type { Canvas, FabricObject } from 'fabric';\nimport type { Layer, LayerManager } from './layer';\nimport type { HistoryManager } from './history';\nimport type { PatternConfig } from './types';\n\n/**\n * Upper bound on tiles per axis. Floors the effective tile size so an extreme\n * `scale` (or a tiny source) can't make the draw loop cover the diagonal with a\n * runaway number of tiles.\n */\nconst MAX_TILES_PER_AXIS = 200;\n\n/**\n * Renders a single image layer as a repeating pattern that fills the canvas.\n *\n * The tiled result is rasterised onto an offscreen canvas and swapped in as the\n * layer's image source, so it survives export, serialization and undo/redo with\n * no extra machinery. The original source + transform are kept in `layer.meta`\n * so the pattern can be turned back off.\n */\nexport class PatternManager {\n // Per-layer task chain. apply()/disable() both await an async setSrc on the\n // same fabric image; running two concurrently lets their setSrc resolutions\n // interleave (wrong image installed, original lost). Serialising per layer\n // guarantees the last-requested operation wins and state stays consistent.\n private chains = new Map<string, Promise<unknown>>();\n\n constructor(\n private canvas: Canvas,\n private layers: LayerManager,\n private history: HistoryManager,\n ) {}\n\n isPattern(layerId: string): boolean {\n return !!this.layers.get(layerId)?.meta.pattern;\n }\n\n getConfig(layerId: string): PatternConfig | null {\n return this.layers.get(layerId)?.meta.pattern?.config ?? null;\n }\n\n /** Turn a plain image layer into a pattern, or update an existing one. */\n apply(layerId: string, config: PatternConfig): Promise<void> {\n return this.enqueue(layerId, async () => {\n const layer = this.layers.get(layerId);\n if (!layer || layer.type !== 'image') return;\n const image = layer.fabricObject as FabricImage;\n\n const firstEnable = !layer.meta.pattern;\n // Check the field directly (not `firstEnable`) so TS narrows the else.\n if (!layer.meta.pattern) {\n // Captured at run time (not enqueue time) so a queued apply always reads\n // the genuine pre-pattern image, never a half-applied one.\n const clip = image.clipPath as FabricObject | undefined;\n layer.meta.pattern = {\n config,\n originalSrc: elementToDataURL(image) ?? image.getSrc(),\n originalClip: clip ? (clip.toObject() as Record<string, unknown>) : null,\n original: {\n left: image.left ?? 0,\n top: image.top ?? 0,\n scaleX: image.scaleX ?? 1,\n scaleY: image.scaleY ?? 1,\n width: image.width ?? 0,\n height: image.height ?? 0,\n angle: image.angle ?? 0,\n cropX: image.cropX ?? 0,\n cropY: image.cropY ?? 0,\n },\n };\n } else {\n layer.meta.pattern.config = config;\n }\n\n try {\n await this.renderLayer(layer);\n } catch (err) {\n // Roll back a freshly-captured state so a failed first render doesn't\n // leave the layer flagged as a pattern with the original still showing.\n if (firstEnable) delete layer.meta.pattern;\n throw err;\n }\n this.history.save();\n });\n }\n\n /** Restore the original image and drop the pattern. */\n disable(layerId: string): Promise<void> {\n return this.enqueue(layerId, async () => {\n const layer = this.layers.get(layerId);\n const state = layer?.meta.pattern;\n if (!layer || !state) return;\n const image = layer.fabricObject as FabricImage;\n\n await image.setSrc(state.originalSrc);\n // setSrc resets width/height to the full element size, so restore the\n // captured crop window too — otherwise an image that was cropped before\n // being turned into a pattern loses its crop on disable.\n image.set({\n left: state.original.left,\n top: state.original.top,\n scaleX: state.original.scaleX,\n scaleY: state.original.scaleY,\n width: state.original.width,\n height: state.original.height,\n cropX: state.original.cropX,\n cropY: state.original.cropY,\n angle: state.original.angle,\n });\n // Re-apply the clipPath the image had before it became a pattern (stripped\n // in renderLayer so the pattern could fill the whole print area).\n image.clipPath = state.originalClip\n ? ((await util.enlivenObjects([state.originalClip]))[0] as FabricObject)\n : undefined;\n image.setCoords();\n delete layer.meta.pattern;\n this.canvas.requestRenderAll();\n this.history.save();\n });\n }\n\n /** Run `task` after any in-flight work for this layer, regardless of outcome. */\n private enqueue(layerId: string, task: () => Promise<void>): Promise<void> {\n const prev = this.chains.get(layerId) ?? Promise.resolve();\n const next = prev.then(task, task);\n // Keep the chain alive on rejection so later ops still run.\n this.chains.set(\n layerId,\n next.catch(() => undefined),\n );\n return next;\n }\n\n private async renderLayer(layer: Layer): Promise<void> {\n const state = layer.meta.pattern;\n if (!state) return;\n const image = layer.fabricObject as FabricImage;\n\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n // Base tile = source's on-canvas size × the user's scale. A guard floors the\n // tile so an extreme scale can't spawn a pathological tile count (the draw\n // loop is O(tiles); computeTilePositions covers the diagonal).\n const scale = Math.max(1, state.config.scale ?? 100) / 100;\n const diag = Math.sqrt(cw * cw + ch * ch);\n const minTile = Math.max(2, diag / MAX_TILES_PER_AXIS);\n const tileW = Math.max(minTile, state.original.width * state.original.scaleX * scale);\n const tileH = Math.max(minTile, state.original.height * state.original.scaleY * scale);\n\n const dataUrl = await buildPatternDataURL(\n state.originalSrc,\n state.config,\n cw,\n ch,\n tileW,\n tileH,\n );\n\n await image.setSrc(dataUrl);\n // The tiled source is exactly cw×ch, so set the object to cover the whole\n // canvas 1:1. `width`/`height` are set explicitly (not just via setSrc) and\n // any pre-pattern clipPath is stripped so a leftover crop sized to the\n // original image box can't confine the pattern to that box — the pattern\n // must fill the entire print area. cropX/cropY are reset for the same reason.\n image.set({\n left: 0,\n top: 0,\n scaleX: 1,\n scaleY: 1,\n width: cw,\n height: ch,\n cropX: 0,\n cropY: 0,\n angle: 0,\n });\n image.clipPath = undefined;\n image.setCoords();\n this.canvas.requestRenderAll();\n }\n}\n\n/** Snapshot an already-loaded image element to a PNG data URL. */\nfunction elementToDataURL(image: FabricImage): string | null {\n try {\n const el = image.getElement() as HTMLImageElement | HTMLCanvasElement;\n const w = (el as HTMLImageElement).naturalWidth || el.width;\n const h = (el as HTMLImageElement).naturalHeight || el.height;\n if (!w || !h) return null;\n const off = document.createElement('canvas');\n off.width = w;\n off.height = h;\n const ctx = off.getContext('2d');\n if (!ctx) return null;\n ctx.drawImage(el, 0, 0);\n return off.toDataURL('image/png');\n } catch {\n // Tainted canvas (remote image without CORS) — fall back to the live src.\n return null;\n }\n}\n\n// Decoding a source (often a large data URL) is the dominant cost of a regen,\n// so cache decoded images. Bounded with simple LRU eviction so a stream of\n// distinct sources can't grow the cache without limit.\nconst IMAGE_CACHE_MAX = 16;\nconst imageCache = new Map<string, Promise<HTMLImageElement>>();\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n const cached = imageCache.get(src);\n if (cached) {\n // Refresh recency (Map preserves insertion order → re-insert = most recent).\n imageCache.delete(src);\n imageCache.set(src, cached);\n return cached;\n }\n\n const promise = decodeImage(src);\n // Don't poison the cache with a failed decode.\n promise.catch(() => {\n if (imageCache.get(src) === promise) imageCache.delete(src);\n });\n\n imageCache.set(src, promise);\n if (imageCache.size > IMAGE_CACHE_MAX) {\n const oldest = imageCache.keys().next().value;\n if (oldest !== undefined) imageCache.delete(oldest);\n }\n return promise;\n}\n\nfunction decodeImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`Failed to load pattern source: ${src}`));\n img.src = src;\n });\n}\n\n/** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */\nexport function clearPatternImageCache(): void {\n imageCache.clear();\n}\n\n/**\n * Draw the source image repeatedly across a `targetW × targetH` offscreen\n * canvas and return the result as a PNG data URL.\n *\n * Exported for unit testing of the tile geometry.\n */\nexport async function buildPatternDataURL(\n src: string,\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): Promise<string> {\n const img = await loadImage(src);\n const off = document.createElement('canvas');\n off.width = Math.max(1, Math.round(targetW));\n off.height = Math.max(1, Math.round(targetH));\n const ctx = off.getContext('2d');\n if (!ctx) return off.toDataURL('image/png');\n\n drawTiles(ctx, img, config, targetW, targetH, baseW, baseH);\n return off.toDataURL('image/png');\n}\n\n/** Placement of one tile, relative to the (already rotated) pattern centre. */\nexport interface TilePlacement {\n /** Tile-centre offset from the pattern centre, in px. */\n x: number;\n y: number;\n /** Per-tile rotation, in radians. */\n rotation: number;\n}\n\n/**\n * Pure tile geometry: where each tile centre lands and how much it is rotated,\n * for a config + target/base size. Covers the rotated bounding diagonal so\n * corners stay filled at any pattern angle. Extracted for unit testing.\n */\nexport function computeTilePositions(\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): TilePlacement[] {\n const diag = Math.sqrt(targetW * targetW + targetH * targetH);\n // Floor the spacing-adjusted tile too (not just the base in renderLayer): a\n // negative spacing from a hand-crafted config would otherwise collapse the\n // loop tile to ~1px and spawn ~diag² draw calls (client-side DoS).\n const minTile = Math.max(1, diag / MAX_TILES_PER_AXIS);\n const tileW = Math.max(minTile, baseW * (1 + config.horizontalSpacing / 100));\n const tileH = Math.max(minTile, baseH * (1 + config.verticalSpacing / 100));\n\n const cols = Math.ceil(diag / tileW) + 2;\n const rows = Math.ceil(diag / tileH) + 2;\n const halfCols = Math.ceil(cols / 2);\n const halfRows = Math.ceil(rows / 2);\n\n const placements: TilePlacement[] = [];\n for (let j = -halfRows; j <= halfRows; j++) {\n for (let i = -halfCols; i <= halfCols; i++) {\n let x = i * tileW;\n let y = j * tileH;\n\n if (config.mode === 'brick-horizontal' && mod2(j) === 1) {\n x += tileW * (config.horizontalOffset / 100);\n } else if (config.mode === 'brick-vertical' && mod2(i) === 1) {\n y += tileH * (config.horizontalOffset / 100);\n }\n\n const rotation = ((i * config.rotationStepH + j * config.rotationStepV) * Math.PI) / 180;\n placements.push({ x, y, rotation });\n }\n }\n return placements;\n}\n\n/**\n * Draw the tiled pattern onto a 2D context. The geometry lives in\n * {@link computeTilePositions}; this just plays it out as canvas calls.\n */\nexport function drawTiles(\n ctx: CanvasRenderingProtocol,\n img: CanvasImageSource,\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): void {\n ctx.save();\n // Rotate the whole pattern around the canvas centre.\n ctx.translate(targetW / 2, targetH / 2);\n ctx.rotate((config.angle * Math.PI) / 180);\n\n for (const tile of computeTilePositions(config, targetW, targetH, baseW, baseH)) {\n ctx.save();\n ctx.translate(tile.x, tile.y);\n ctx.rotate(tile.rotation);\n ctx.drawImage(img, -baseW / 2, -baseH / 2, baseW, baseH);\n ctx.restore();\n }\n\n ctx.restore();\n}\n\n/** Non-negative parity (so negative row indices brick-shift consistently). */\nfunction mod2(n: number): number {\n return ((n % 2) + 2) % 2;\n}\n\n/** Minimal 2D-context surface used by {@link drawTiles}; lets tests inject a spy. */\nexport interface CanvasRenderingProtocol {\n save(): void;\n restore(): void;\n translate(x: number, y: number): void;\n rotate(angle: number): void;\n drawImage(image: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;\n}\n","import type { Unit } from '../types';\n\nconst MM_PER_INCH = 25.4;\n\nexport class UnitConverter {\n constructor(\n private unit: Unit = 'px',\n private dpi: number = 72,\n ) {}\n\n setUnit(unit: Unit): void {\n this.unit = unit;\n }\n\n setDpi(dpi: number): void {\n this.dpi = dpi;\n }\n\n getUnit(): Unit {\n return this.unit;\n }\n\n getDpi(): number {\n return this.dpi;\n }\n\n toPixels(value: number, fromUnit?: Unit): number {\n const u = fromUnit ?? this.unit;\n switch (u) {\n case 'px':\n return value;\n case 'in':\n return value * this.dpi;\n case 'mm':\n return (value / MM_PER_INCH) * this.dpi;\n }\n }\n\n fromPixels(px: number, toUnit?: Unit): number {\n const u = toUnit ?? this.unit;\n switch (u) {\n case 'px':\n return px;\n case 'in':\n return px / this.dpi;\n case 'mm':\n return (px / this.dpi) * MM_PER_INCH;\n }\n }\n}\n","import { util } from 'fabric';\nimport type { FabricObject } from 'fabric';\nimport type { EditorState, SerializedLayer } from './types';\nimport type { CanvasEditor } from './editor';\nimport { Layer } from './layer';\n\nconst VERSION = '1.0.0';\n\nexport function serializeEditor(editor: CanvasEditor): EditorState {\n return {\n version: VERSION,\n canvas: {\n width: editor.canvas.getWidth(),\n height: editor.canvas.getHeight(),\n },\n layers: editor.layers.getAll().map((layer) => layer.serialize()),\n // The configured design background, not the live canvas value (which is\n // forced transparent while a mockup preview is active).\n background: editor.getDesignBackground(),\n mockup: editor.getMockup(),\n };\n}\n\nexport async function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void> {\n editor.layers.clear();\n editor.canvas.setDimensions({ width: state.canvas.width, height: state.canvas.height });\n\n // setBackground records the design background; history is paused during\n // restore so this does not push a spurious entry. setMockup (below) then\n // applies the correct live canvas background.\n if (state.background) {\n editor.setBackground(state.background);\n }\n\n // mockup is preview-only (rendered behind the canvas), so it is restored\n // separately from the design layers. `undefined` means \"leave as-is\".\n if (state.mockup !== undefined) {\n editor.setMockup(state.mockup);\n }\n\n for (const serializedLayer of state.layers) {\n await restoreLayer(editor, serializedLayer);\n }\n\n editor.canvas.requestRenderAll();\n}\n\nasync function restoreLayer(editor: CanvasEditor, serialized: SerializedLayer): Promise<Layer> {\n const objects = await util.enlivenObjects([serialized.fabricObject]);\n const fabricObject = objects[0] as FabricObject;\n\n const layer = editor.layers.add(serialized.type, fabricObject, serialized.name, serialized.id);\n\n if (serialized.meta) {\n layer.meta = serialized.meta;\n }\n\n if (!serialized.visible) {\n editor.layers.setVisibility(layer.id, false);\n }\n if (serialized.locked) {\n editor.layers.setLocked(layer.id, true);\n }\n if (serialized.opacity !== 1) {\n editor.layers.setOpacity(layer.id, serialized.opacity);\n }\n\n return layer;\n}\n","import type { Canvas, ImageFormat } from 'fabric';\n\nexport interface PngExportOptions {\n multiplier?: number;\n format?: ImageFormat;\n quality?: number;\n}\n\nexport async function exportPNG(\n canvas: Canvas,\n options: PngExportOptions = {},\n): Promise<Blob> {\n const { multiplier = 1, format = 'png' as ImageFormat, quality = 1 } = options;\n const dataUrl = canvas.toDataURL({\n format,\n multiplier,\n quality,\n });\n const response = await fetch(dataUrl);\n return response.blob();\n}\n\nexport function exportSVG(canvas: Canvas): string {\n return canvas.toSVG();\n}\n\nexport function exportDataURL(\n canvas: Canvas,\n format: ImageFormat = 'png',\n multiplier = 1,\n): string {\n return canvas.toDataURL({ format, multiplier });\n}\n","import type { FabricObject } from 'fabric';\n\n// ─── Units ───────────────────────────────────────────────\n\nexport type Unit = 'px' | 'mm' | 'in';\n\n// ─── Layers ──────────────────────────────────────────────\n\nexport type LayerType = 'image' | 'text' | 'shape' | 'template' | 'group';\n\nexport interface LayerData {\n id: string;\n type: LayerType;\n name: string;\n visible: boolean;\n locked: boolean;\n opacity: number;\n /** Non-fabric metadata persisted with the layer (pattern config, etc). */\n meta?: LayerMeta;\n}\n\nexport interface LayerMeta {\n pattern?: PatternState;\n [key: string]: unknown;\n}\n\nexport interface SerializedLayer extends LayerData {\n fabricObject: Record<string, unknown>;\n}\n\n// ─── Editor ──────────────────────────────────────────────\n\nexport interface EditorState {\n version: string;\n canvas: { width: number; height: number };\n layers: SerializedLayer[];\n background?: string;\n mockup?: MockupConfig | null;\n}\n\nexport interface EditorConfig {\n width: number;\n height: number;\n backgroundColor?: string;\n fileAdapter?: FileAdapter;\n imageProvider?: ImageProvider;\n preserveObjectStacking?: boolean;\n unit?: Unit;\n dpi?: number;\n}\n\n// ─── Adapters ────────────────────────────────────────────\n\nexport type ExportFormat = 'png' | 'svg' | 'json';\n\nexport interface FileAdapter {\n save(data: Blob | string, filename: string, format: ExportFormat): Promise<string>;\n}\n\nexport interface ImageProviderResult {\n url: string;\n width: number;\n height: number;\n}\n\nexport interface ImageProvider {\n upload(file: File): Promise<ImageProviderResult>;\n browse?(): Promise<ImageProviderResult | null>;\n}\n\n// ─── Plugins ─────────────────────────────────────────────\n\nexport interface ShapePlugin {\n name: string;\n icon: string;\n category?: string;\n create(options?: Record<string, unknown>): FabricObject;\n}\n\nexport interface TemplateParameter {\n key: string;\n label: string;\n type: 'text' | 'color' | 'number' | 'font';\n default: string | number;\n}\n\nexport interface TemplateDefinition {\n id: string;\n name: string;\n category: string;\n svg: string;\n parameters: TemplateParameter[];\n preview?: string;\n}\n\n// ─── Printify ────────────────────────────────────────────\n\nexport interface PrintifyPositioning {\n x: number;\n y: number;\n scale: number;\n angle: number;\n}\n\n// ─── Patterns ────────────────────────────────────────────\n\nexport type TileMode = 'grid' | 'brick-horizontal' | 'brick-vertical';\n\nexport interface PatternConfig {\n mode: TileMode;\n /**\n * Tile size as a % of the source image's on-canvas size (100 = natural).\n * Smaller values pack more repeats across the print area; larger values make\n * fewer, bigger tiles. Independent of how the source layer was placed.\n */\n scale: number;\n /** Extra horizontal gap between tiles, as % of tile width. */\n horizontalSpacing: number;\n /** Extra vertical gap between tiles, as % of tile height. */\n verticalSpacing: number;\n /** Rotation of the whole pattern, in degrees. */\n angle: number;\n /** Brick row/column shift, as % of tile size. */\n horizontalOffset: number;\n /** Added rotation per horizontal step, in degrees. */\n rotationStepH: number;\n /** Added rotation per vertical step, in degrees. */\n rotationStepV: number;\n}\n\n/** Persisted on a layer's meta while it is rendered as a repeating pattern. */\nexport interface PatternState {\n config: PatternConfig;\n /** Original (pre-pattern) image source, as a durable data URL when possible. */\n originalSrc: string;\n /**\n * Serialized `clipPath` the image carried before it became a pattern, or null\n * if it had none. A pattern must fill the whole print area, so any pre-pattern\n * clip (e.g. a template crop sized to the original box) is stripped while the\n * pattern is on and re-applied when it is turned off. Persisted so it survives\n * serialization / reload / undo.\n */\n originalClip?: Record<string, unknown> | null;\n /** Original fabric transform, restored when the pattern is turned off. */\n original: {\n left: number;\n top: number;\n scaleX: number;\n scaleY: number;\n width: number;\n height: number;\n angle: number;\n cropX: number;\n cropY: number;\n };\n}\n\nexport const DEFAULT_PATTERN_CONFIG: PatternConfig = {\n mode: 'grid',\n scale: 100,\n horizontalSpacing: 0,\n verticalSpacing: 0,\n angle: 0,\n horizontalOffset: 0,\n rotationStepH: 0,\n rotationStepV: 0,\n};\n\n// ─── Mockup ──────────────────────────────────────────────\n\nexport interface MockupPrintArea {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\nexport interface MockupConfig {\n /** Garment/product image rendered behind the design (preview only). */\n image: string;\n /** Print-area rectangle, in canvas pixels. Drawn as a guide. */\n printArea?: MockupPrintArea;\n}\n\n// ─── Events ──────────────────────────────────────────────\n\nexport interface EditorEvents {\n 'layer:added': { layer: LayerData };\n 'layer:removed': { layerId: string };\n 'layer:selected': { layerId: string | null };\n 'layer:modified': { layerId: string };\n 'layer:reordered': { layerIds: string[] };\n 'layers:changed': { layers: LayerData[] };\n 'selection:changed': { selected: string[] };\n 'history:changed': { canUndo: boolean; canRedo: boolean };\n 'zoom:changed': { zoom: number };\n 'snap:changed': { enabled: boolean };\n 'crop:changed': { active: boolean; layerId: string | null };\n 'mockup:changed': { mockup: MockupConfig | null };\n 'canvas:modified': Record<string, never>;\n 'export:start': { format: string };\n 'export:complete': { format: string; url?: string };\n error: { message: string; error?: unknown };\n}\n"],"mappings":"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","names":["import_fabric","STROKE","import_fabric","import_fabric"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/editor.ts
2
- import { Canvas, FabricImage, Textbox } from "fabric";
2
+ import { Canvas, FabricImage as FabricImage2, Textbox } from "fabric";
3
3
 
4
4
  // src/events.ts
5
5
  var EventEmitter = class {
@@ -541,6 +541,8 @@ var CropController = class {
541
541
  var STROKE2 = "#22c55e";
542
542
 
543
543
  // src/pattern.ts
544
+ import { util } from "fabric";
545
+ var MAX_TILES_PER_AXIS = 200;
544
546
  var PatternManager = class {
545
547
  constructor(canvas, layers, history) {
546
548
  this.canvas = canvas;
@@ -569,9 +571,11 @@ var PatternManager = class {
569
571
  const image = layer.fabricObject;
570
572
  const firstEnable = !layer.meta.pattern;
571
573
  if (!layer.meta.pattern) {
574
+ const clip = image.clipPath;
572
575
  layer.meta.pattern = {
573
576
  config,
574
577
  originalSrc: elementToDataURL(image) ?? image.getSrc(),
578
+ originalClip: clip ? clip.toObject() : null,
575
579
  original: {
576
580
  left: image.left ?? 0,
577
581
  top: image.top ?? 0,
@@ -615,6 +619,7 @@ var PatternManager = class {
615
619
  cropY: state.original.cropY,
616
620
  angle: state.original.angle
617
621
  });
622
+ image.clipPath = state.originalClip ? (await util.enlivenObjects([state.originalClip]))[0] : void 0;
618
623
  image.setCoords();
619
624
  delete layer.meta.pattern;
620
625
  this.canvas.requestRenderAll();
@@ -637,8 +642,11 @@ var PatternManager = class {
637
642
  const image = layer.fabricObject;
638
643
  const cw = this.canvas.getWidth();
639
644
  const ch = this.canvas.getHeight();
640
- const tileW = state.original.width * state.original.scaleX;
641
- const tileH = state.original.height * state.original.scaleY;
645
+ const scale = Math.max(1, state.config.scale ?? 100) / 100;
646
+ const diag = Math.sqrt(cw * cw + ch * ch);
647
+ const minTile = Math.max(2, diag / MAX_TILES_PER_AXIS);
648
+ const tileW = Math.max(minTile, state.original.width * state.original.scaleX * scale);
649
+ const tileH = Math.max(minTile, state.original.height * state.original.scaleY * scale);
642
650
  const dataUrl = await buildPatternDataURL(
643
651
  state.originalSrc,
644
652
  state.config,
@@ -648,7 +656,18 @@ var PatternManager = class {
648
656
  tileH
649
657
  );
650
658
  await image.setSrc(dataUrl);
651
- image.set({ left: 0, top: 0, scaleX: 1, scaleY: 1, cropX: 0, cropY: 0, angle: 0 });
659
+ image.set({
660
+ left: 0,
661
+ top: 0,
662
+ scaleX: 1,
663
+ scaleY: 1,
664
+ width: cw,
665
+ height: ch,
666
+ cropX: 0,
667
+ cropY: 0,
668
+ angle: 0
669
+ });
670
+ image.clipPath = void 0;
652
671
  image.setCoords();
653
672
  this.canvas.requestRenderAll();
654
673
  }
@@ -713,9 +732,10 @@ async function buildPatternDataURL(src, config, targetW, targetH, baseW, baseH)
713
732
  return off.toDataURL("image/png");
714
733
  }
715
734
  function computeTilePositions(config, targetW, targetH, baseW, baseH) {
716
- const tileW = Math.max(1, baseW * (1 + config.horizontalSpacing / 100));
717
- const tileH = Math.max(1, baseH * (1 + config.verticalSpacing / 100));
718
735
  const diag = Math.sqrt(targetW * targetW + targetH * targetH);
736
+ const minTile = Math.max(1, diag / MAX_TILES_PER_AXIS);
737
+ const tileW = Math.max(minTile, baseW * (1 + config.horizontalSpacing / 100));
738
+ const tileH = Math.max(minTile, baseH * (1 + config.verticalSpacing / 100));
719
739
  const cols = Math.ceil(diag / tileW) + 2;
720
740
  const rows = Math.ceil(diag / tileH) + 2;
721
741
  const halfCols = Math.ceil(cols / 2);
@@ -799,7 +819,7 @@ var UnitConverter = class {
799
819
  };
800
820
 
801
821
  // src/serialization.ts
802
- import { util } from "fabric";
822
+ import { util as util2 } from "fabric";
803
823
  var VERSION = "1.0.0";
804
824
  function serializeEditor(editor) {
805
825
  return {
@@ -830,7 +850,7 @@ async function deserializeEditor(editor, state) {
830
850
  editor.canvas.requestRenderAll();
831
851
  }
832
852
  async function restoreLayer(editor, serialized) {
833
- const objects = await util.enlivenObjects([serialized.fabricObject]);
853
+ const objects = await util2.enlivenObjects([serialized.fabricObject]);
834
854
  const fabricObject = objects[0];
835
855
  const layer = editor.layers.add(serialized.type, fabricObject, serialized.name, serialized.id);
836
856
  if (serialized.meta) {
@@ -918,7 +938,7 @@ var CanvasEditor = class {
918
938
  }
919
939
  // ─── Layer Operations ────────────────────────────────
920
940
  async addImage(url, options) {
921
- const img = await FabricImage.fromURL(url, {}, options);
941
+ const img = await FabricImage2.fromURL(url, {}, { originX: "left", originY: "top", ...options });
922
942
  const layer = this.layers.add("image", img);
923
943
  this.history.save();
924
944
  return layer;
@@ -929,6 +949,9 @@ var CanvasEditor = class {
929
949
  fontFamily: "Arial",
930
950
  fill: "#000000",
931
951
  width: 200,
952
+ // v7 defaults origin to center; keep top-left placement.
953
+ originX: "left",
954
+ originY: "top",
932
955
  ...options
933
956
  });
934
957
  const layer = this.layers.add("text", textbox);
@@ -947,7 +970,9 @@ var CanvasEditor = class {
947
970
  fontSize: 24,
948
971
  fontFamily: "Arial",
949
972
  fill: "#666666",
950
- width: 300
973
+ width: 300,
974
+ originX: "left",
975
+ originY: "top"
951
976
  });
952
977
  const layer = this.layers.add(
953
978
  "template",
@@ -1194,6 +1219,7 @@ var CanvasEditor = class {
1194
1219
  // src/types.ts
1195
1220
  var DEFAULT_PATTERN_CONFIG = {
1196
1221
  mode: "grid",
1222
+ scale: 100,
1197
1223
  horizontalSpacing: 0,
1198
1224
  verticalSpacing: 0,
1199
1225
  angle: 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/editor.ts","../src/events.ts","../src/utils/id.ts","../src/layer.ts","../src/history.ts","../src/snapping.ts","../src/crop.ts","../src/utils/clamp.ts","../src/pattern.ts","../src/utils/units.ts","../src/serialization.ts","../src/export.ts","../src/types.ts"],"sourcesContent":["import { Canvas, FabricImage, Textbox } from 'fabric';\nimport type { FabricObject, ImageFormat } from 'fabric';\nimport type {\n EditorConfig,\n EditorEvents,\n EditorState,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n MockupConfig,\n PatternConfig,\n PrintifyPositioning,\n ShapePlugin,\n TemplateDefinition,\n} from './types';\nimport { EventEmitter } from './events';\nimport { Layer, LayerManager } from './layer';\nimport { HistoryManager } from './history';\nimport { SnapManager } from './snapping';\nimport { CropController } from './crop';\nimport { PatternManager } from './pattern';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL } from './export';\nimport type { PngExportOptions } from './export';\nimport { clamp, round2 } from './utils/clamp';\n\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 8;\n\nexport class CanvasEditor {\n readonly canvas: Canvas;\n readonly layers: LayerManager;\n readonly history: HistoryManager;\n readonly events: EventEmitter<EditorEvents>;\n readonly units: UnitConverter;\n readonly snapping: SnapManager;\n readonly crop: CropController;\n readonly patterns: PatternManager;\n\n private fileAdapter?: FileAdapter;\n private imageProvider?: ImageProvider;\n private zoomLevel = 1;\n private mockup: MockupConfig | null = null;\n // The design's configured background. The live canvas background is forced\n // transparent while a mockup preview is shown, so this is the source of truth\n // for serialization and export — not the (possibly transient) canvas value.\n private designBackground: string;\n\n constructor(canvasElement: HTMLCanvasElement, config: EditorConfig) {\n this.events = new EventEmitter<EditorEvents>();\n this.units = new UnitConverter(config.unit ?? 'px', config.dpi ?? 72);\n\n const widthPx = this.units.toPixels(config.width);\n const heightPx = this.units.toPixels(config.height);\n this.designBackground = config.backgroundColor ?? '#ffffff';\n\n this.canvas = new Canvas(canvasElement, {\n width: widthPx,\n height: heightPx,\n backgroundColor: this.designBackground,\n preserveObjectStacking: config.preserveObjectStacking ?? true,\n selection: true,\n });\n\n this.layers = new LayerManager(this.canvas, this.events);\n this.fileAdapter = config.fileAdapter;\n this.imageProvider = config.imageProvider;\n\n this.history = new HistoryManager({\n getState: () => JSON.stringify(this.toJSON()),\n restoreState: async (state: string) => {\n const parsed = JSON.parse(state) as EditorState;\n await this.fromJSON(parsed);\n },\n events: this.events,\n });\n\n this.snapping = new SnapManager(this.canvas, this.events);\n this.crop = new CropController(this.canvas, this.history, this.events);\n this.patterns = new PatternManager(this.canvas, this.layers, this.history);\n\n this.setupCanvasEvents();\n this.history.saveImmediate();\n }\n\n // ─── Layer Operations ────────────────────────────────\n\n async addImage(url: string, options?: Partial<Record<string, unknown>>): Promise<Layer> {\n // Don't force crossOrigin here: a remote image whose host sends no CORS\n // header would then fail to load at all. The pattern engine requests its own\n // crossOrigin copy when it needs an untainted canvas (see pattern.ts).\n const img = await FabricImage.fromURL(url, {}, options);\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n }\n\n addText(text: string, options?: Partial<Record<string, unknown>>): Layer {\n const textbox = new Textbox(text, {\n fontSize: 32,\n fontFamily: 'Arial',\n fill: '#000000',\n width: 200,\n ...options,\n });\n const layer = this.layers.add('text', textbox as unknown as FabricObject);\n this.history.save();\n return layer;\n }\n\n addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer {\n const obj = plugin.create(options);\n const layer = this.layers.add('shape', obj, plugin.name);\n this.history.save();\n return layer;\n }\n\n async addTemplate(\n template: TemplateDefinition,\n _params: Record<string, string | number>,\n ): Promise<Layer> {\n // Template loading is handled by the templates package\n // This is a placeholder for direct fabric object addition\n const { Textbox: TextboxClass } = await import('fabric');\n const placeholder = new TextboxClass(`[Template: ${template.name}]`, {\n fontSize: 24,\n fontFamily: 'Arial',\n fill: '#666666',\n width: 300,\n });\n const layer = this.layers.add(\n 'template',\n placeholder as unknown as FabricObject,\n template.name,\n );\n this.history.save();\n return layer;\n }\n\n removeLayer(id: string): void {\n this.layers.remove(id);\n this.history.save();\n }\n\n selectLayer(id: string | null): void {\n this.layers.select(id);\n }\n\n getSelectedLayer(): Layer | null {\n const active = this.canvas.getActiveObject();\n if (!active) return null;\n return this.layers.findByObject(active) ?? null;\n }\n\n /** Mirror a layer along the given axis. */\n flipLayer(id: string, axis: 'horizontal' | 'vertical'): void {\n const layer = this.layers.get(id);\n if (!layer) return;\n const obj = layer.fabricObject;\n if (axis === 'horizontal') {\n obj.set('flipX', !obj.flipX);\n } else {\n obj.set('flipY', !obj.flipY);\n }\n obj.setCoords();\n this.canvas.requestRenderAll();\n this.history.save();\n }\n\n /** Clone a layer (offset slightly) and select the copy. */\n async duplicateLayer(id: string): Promise<Layer | null> {\n const layer = this.layers.get(id);\n if (!layer) return null;\n\n const clone = await layer.fabricObject.clone();\n // A pattern layer's image is rasterised to cover the canvas at 0,0; offsetting\n // it would leave a gap at the edges, so only nudge non-pattern duplicates.\n if (!layer.meta.pattern) {\n clone.set({ left: (clone.left ?? 0) + 15, top: (clone.top ?? 0) + 15 });\n }\n clone.setCoords();\n\n const copy = this.layers.add(layer.type, clone, `${layer.name} copy`);\n // Carry non-fabric metadata (e.g. pattern config) onto the copy.\n copy.meta = structuredClone(layer.meta);\n\n this.canvas.setActiveObject(clone);\n this.canvas.requestRenderAll();\n this.history.save();\n return copy;\n }\n\n // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n await deserializeEditor(this, state);\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n this.events.emit('export:start', { format: 'png' });\n const blob = await this.withDesignBackground(() => exportPNG(this.canvas, options));\n this.events.emit('export:complete', { format: 'png' });\n return blob;\n }\n\n toSVG(): string {\n this.events.emit('export:start', { format: 'svg' });\n const svg = this.withDesignBackground(() => exportSVG(this.canvas));\n this.events.emit('export:complete', { format: 'svg' });\n return svg;\n }\n\n toDataURL(format: ImageFormat = 'png', multiplier = 1): string {\n return this.withDesignBackground(() => exportDataURL(this.canvas, format, multiplier));\n }\n\n /**\n * Run an export with the configured design background applied, even when a\n * mockup preview has forced the live canvas transparent — so exports reflect\n * the design, not the preview. Restores the preview state afterwards.\n */\n private withDesignBackground<T>(fn: () => T): T {\n if (!this.mockup) return fn();\n const previewBg = this.canvas.backgroundColor;\n this.canvas.backgroundColor = this.designBackground;\n try {\n return fn();\n } finally {\n this.canvas.backgroundColor = previewBg;\n }\n }\n\n toPrintifyPositioning(): Record<string, PrintifyPositioning> {\n const result: Record<string, PrintifyPositioning> = {};\n const canvasW = this.canvas.getWidth();\n const canvasH = this.canvas.getHeight();\n\n const imageLayers = this.layers.getAll().filter((l) => l.type === 'image');\n\n if (imageLayers.length === 0) {\n result['front'] = { x: 0.5, y: 0.5, scale: 1, angle: 0 };\n return result;\n }\n\n for (const layer of imageLayers) {\n const obj = layer.fabricObject;\n const center = obj.getCenterPoint();\n result[layer.name] = {\n x: round2(center.x / canvasW),\n y: round2(center.y / canvasH),\n scale: round2(obj.scaleX ?? 1),\n angle: round2(obj.angle ?? 0),\n };\n }\n\n return result;\n }\n\n // ─── File Operations ────────────────────────────────\n\n async save(filename: string, format: 'png' | 'svg' | 'json'): Promise<string | undefined> {\n if (!this.fileAdapter) return undefined;\n\n let data: Blob | string;\n if (format === 'png') {\n data = await this.toPNG();\n } else if (format === 'svg') {\n data = this.toSVG();\n } else {\n data = JSON.stringify(this.toJSON(), null, 2);\n }\n\n return this.fileAdapter.save(data, filename, format);\n }\n\n async uploadImage(file: File): Promise<ImageProviderResult | undefined> {\n if (!this.imageProvider) return undefined;\n return this.imageProvider.upload(file);\n }\n\n async browseImages(): Promise<ImageProviderResult | null> {\n if (!this.imageProvider?.browse) return null;\n return this.imageProvider.browse();\n }\n\n setFileAdapter(adapter: FileAdapter): void {\n this.fileAdapter = adapter;\n }\n\n setImageProvider(provider: ImageProvider): void {\n this.imageProvider = provider;\n }\n\n // ─── Canvas Operations ──────────────────────────────\n\n setBackground(color: string): void {\n this.designBackground = color;\n // Don't override the mockup's transparent preview; it will pick up the new\n // colour when the mockup is cleared.\n if (!this.mockup) {\n this.canvas.backgroundColor = color;\n this.canvas.requestRenderAll();\n }\n this.history.save();\n }\n\n getDesignBackground(): string {\n return this.designBackground;\n }\n\n resize(width: number, height: number): void {\n const widthPx = this.units.toPixels(width);\n const heightPx = this.units.toPixels(height);\n this.canvas.setDimensions({ width: widthPx, height: heightPx });\n this.canvas.requestRenderAll();\n }\n\n // ─── Zoom ───────────────────────────────────────────\n //\n // Zoom is a *display* transform (the view layer CSS-scales the canvas), not a\n // fabric viewport zoom — fabric's setZoom bakes into toDataURL/toSVG and would\n // corrupt exports. The editor only tracks the level and emits an event.\n\n setZoom(level: number): void {\n const next = clamp(round2(level), MIN_ZOOM, MAX_ZOOM);\n if (next === this.zoomLevel) return;\n this.zoomLevel = next;\n this.events.emit('zoom:changed', { zoom: next });\n }\n\n /** @deprecated use setZoom — kept for backward compatibility. */\n zoom(level: number): void {\n this.setZoom(level);\n }\n\n getZoom(): number {\n return this.zoomLevel;\n }\n\n zoomIn(step = 0.1): void {\n this.setZoom(this.zoomLevel + step);\n }\n\n zoomOut(step = 0.1): void {\n this.setZoom(this.zoomLevel - step);\n }\n\n resetZoom(): void {\n this.setZoom(1);\n }\n\n /** Fit the canvas inside a viewport (in px), accounting for padding. */\n zoomToFit(viewportWidth: number, viewportHeight: number, padding = 0): void {\n const w = this.canvas.getWidth();\n const h = this.canvas.getHeight();\n if (w <= 0 || h <= 0) return;\n const sx = (viewportWidth - padding * 2) / w;\n const sy = (viewportHeight - padding * 2) / h;\n this.setZoom(Math.min(sx, sy));\n }\n\n // ─── Patterns ───────────────────────────────────────\n\n applyPattern(layerId: string, config: PatternConfig): Promise<void> {\n return this.patterns.apply(layerId, config);\n }\n\n clearPattern(layerId: string): Promise<void> {\n return this.patterns.disable(layerId);\n }\n\n // ─── Mockup (preview-only) ──────────────────────────\n\n setMockup(mockup: MockupConfig | null): void {\n this.mockup = mockup;\n // The mockup renders behind the design, so the canvas must be transparent\n // while it is shown; restore the configured background when it is cleared.\n this.canvas.backgroundColor = mockup ? '' : this.designBackground;\n this.canvas.requestRenderAll();\n this.events.emit('mockup:changed', { mockup });\n }\n\n clearMockup(): void {\n this.setMockup(null);\n }\n\n getMockup(): MockupConfig | null {\n return this.mockup;\n }\n\n // ─── Cleanup ────────────────────────────────────────\n\n dispose(): void {\n this.snapping.dispose();\n this.crop.dispose();\n this.history.dispose();\n this.events.removeAllListeners();\n this.canvas.dispose();\n }\n\n // ─── Private ────────────────────────────────────────\n\n private setupCanvasEvents(): void {\n this.canvas.on('object:modified', (e) => {\n if (!e.target) return;\n const layer = this.layers.findByObject(e.target);\n if (layer) {\n this.events.emit('layer:modified', { layerId: layer.id });\n this.history.save();\n }\n });\n\n this.canvas.on('selection:created', (e) => {\n const selected = (e.selected ?? [])\n .map((obj) => this.layers.findByObject(obj)?.id)\n .filter((id): id is string => id !== undefined);\n this.events.emit('selection:changed', { selected });\n });\n\n this.canvas.on('selection:updated', (e) => {\n const selected = (e.selected ?? [])\n .map((obj) => this.layers.findByObject(obj)?.id)\n .filter((id): id is string => id !== undefined);\n this.events.emit('selection:changed', { selected });\n });\n\n this.canvas.on('selection:cleared', () => {\n this.events.emit('selection:changed', { selected: [] });\n });\n }\n}\n","type EventHandler<T> = (data: T) => void;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport class EventEmitter<TEvents extends {} = Record<string, unknown>> {\n private listeners = new Map<keyof TEvents, Set<EventHandler<unknown>>>();\n\n on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler as EventHandler<unknown>);\n }\n\n off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n this.listeners.get(event)?.delete(handler as EventHandler<unknown>);\n }\n\n once<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n const wrapped: EventHandler<TEvents[K]> = (data) => {\n this.off(event, wrapped);\n handler(data);\n };\n this.on(event, wrapped);\n }\n\n emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n const handlers = this.listeners.get(event);\n if (!handlers) return;\n for (const handler of handlers) {\n handler(data);\n }\n }\n\n removeAllListeners(event?: keyof TEvents): void {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n }\n}\n","import { nanoid } from 'nanoid';\n\nexport const generateId = (): string => nanoid(12);\n","import type { Canvas, FabricObject } from 'fabric';\nimport type { LayerData, LayerMeta, LayerType, SerializedLayer } from './types';\nimport type { EditorEvents } from './types';\nimport type { EventEmitter } from './events';\nimport { generateId } from './utils/id';\n\nexport class Layer {\n readonly id: string;\n readonly type: LayerType;\n name: string;\n visible: boolean;\n locked: boolean;\n opacity: number;\n /** Non-fabric data (e.g. pattern config) that must persist with the layer. */\n meta: LayerMeta;\n readonly fabricObject: FabricObject;\n\n constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string) {\n this.id = id ?? generateId();\n this.type = type;\n this.name = name ?? `${type}-${this.id.slice(0, 4)}`;\n this.visible = true;\n this.locked = false;\n this.opacity = 1;\n this.meta = {};\n this.fabricObject = fabricObject;\n\n (this.fabricObject as FabricObject & { _layerId: string })._layerId = this.id;\n }\n\n private hasMeta(): boolean {\n return Object.keys(this.meta).length > 0;\n }\n\n toData(): LayerData {\n return {\n id: this.id,\n type: this.type,\n name: this.name,\n visible: this.visible,\n locked: this.locked,\n opacity: this.opacity,\n ...(this.hasMeta() ? { meta: this.meta } : {}),\n };\n }\n\n serialize(): SerializedLayer {\n return {\n ...this.toData(),\n fabricObject: this.fabricObject.toObject() as Record<string, unknown>,\n };\n }\n}\n\nexport class LayerManager {\n private layers: Layer[] = [];\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {}\n\n add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer {\n const layer = new Layer(type, fabricObject, name, id);\n this.layers.push(layer);\n this.canvas.add(fabricObject);\n this.events.emit('layer:added', { layer: layer.toData() });\n this.emitChanged();\n return layer;\n }\n\n remove(id: string): boolean {\n const index = this.layers.findIndex((l) => l.id === id);\n if (index === -1) return false;\n const layer = this.layers[index];\n this.canvas.remove(layer.fabricObject);\n this.layers.splice(index, 1);\n this.events.emit('layer:removed', { layerId: id });\n this.emitChanged();\n return true;\n }\n\n reorder(id: string, newIndex: number): boolean {\n const oldIndex = this.layers.findIndex((l) => l.id === id);\n if (oldIndex === -1) return false;\n const clamped = Math.max(0, Math.min(this.layers.length - 1, newIndex));\n const [layer] = this.layers.splice(oldIndex, 1);\n this.layers.splice(clamped, 0, layer);\n // Sync fabric z-order\n this.layers.forEach((l, i) => {\n this.canvas.moveObjectTo(l.fabricObject, i);\n });\n this.events.emit('layer:reordered', { layerIds: this.layers.map((l) => l.id) });\n this.emitChanged();\n return true;\n }\n\n select(id: string | null): void {\n if (id === null) {\n this.canvas.discardActiveObject();\n } else {\n const layer = this.get(id);\n if (layer) {\n this.canvas.setActiveObject(layer.fabricObject);\n }\n }\n this.canvas.requestRenderAll();\n this.events.emit('layer:selected', { layerId: id });\n // Fabric only fires selection:* on user interaction; mirror programmatic\n // selection so React selection state (useSelection) stays in sync.\n const selected = id && this.get(id) ? [id] : [];\n this.events.emit('selection:changed', { selected });\n }\n\n get(id: string): Layer | undefined {\n return this.layers.find((l) => l.id === id);\n }\n\n getAll(): Layer[] {\n return [...this.layers];\n }\n\n findByObject(obj: FabricObject): Layer | undefined {\n const layerId = (obj as FabricObject & { _layerId?: string })._layerId;\n if (layerId) return this.get(layerId);\n return this.layers.find((l) => l.fabricObject === obj);\n }\n\n setVisibility(id: string, visible: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.visible = visible;\n layer.fabricObject.visible = visible;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setLocked(id: string, locked: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.locked = locked;\n layer.fabricObject.selectable = !locked;\n layer.fabricObject.evented = !locked;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setOpacity(id: string, opacity: number): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.opacity = opacity;\n layer.fabricObject.opacity = opacity;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setName(id: string, name: string): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.name = name;\n this.emitChanged();\n }\n\n clear(): void {\n for (const layer of this.layers) {\n this.canvas.remove(layer.fabricObject);\n }\n this.layers = [];\n this.emitChanged();\n }\n\n count(): number {\n return this.layers.length;\n }\n\n private emitChanged(): void {\n this.events.emit('layers:changed', {\n layers: this.layers.map((l) => l.toData()),\n });\n }\n}\n","import type { EditorEvents } from './types';\nimport type { EventEmitter } from './events';\n\nexport class HistoryManager {\n private undoStack: string[] = [];\n private redoStack: string[] = [];\n private maxSize: number;\n private paused = false;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private debounceMs: number;\n private getState: () => string;\n private restoreState: (state: string) => Promise<void>;\n private events: EventEmitter<EditorEvents>;\n\n constructor(opts: {\n getState: () => string;\n restoreState: (state: string) => Promise<void>;\n events: EventEmitter<EditorEvents>;\n maxSize?: number;\n debounceMs?: number;\n }) {\n this.getState = opts.getState;\n this.restoreState = opts.restoreState;\n this.events = opts.events;\n this.maxSize = opts.maxSize ?? 50;\n this.debounceMs = opts.debounceMs ?? 300;\n }\n\n save(): void {\n if (this.paused) return;\n\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.saveImmediate();\n }, this.debounceMs);\n }\n\n saveImmediate(): void {\n if (this.paused) return;\n const state = this.getState();\n this.undoStack.push(state);\n if (this.undoStack.length > this.maxSize) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.emitChanged();\n }\n\n async undo(): Promise<void> {\n // Drop any debounced save still pending from an in-progress edit, otherwise\n // its timer would fire after restore completes and clobber the redo stack.\n this.cancelPending();\n const state = this.undoStack.pop();\n if (!state) return;\n\n this.redoStack.push(this.getState());\n this.paused = true;\n await this.restoreState(state);\n this.paused = false;\n this.emitChanged();\n }\n\n async redo(): Promise<void> {\n this.cancelPending();\n const state = this.redoStack.pop();\n if (!state) return;\n\n this.undoStack.push(this.getState());\n this.paused = true;\n await this.restoreState(state);\n this.paused = false;\n this.emitChanged();\n }\n\n /** True while a restore (undo/redo/deserialize) is in flight. Managers that\n * react to canvas events should skip mutating history during this window. */\n isRestoring(): boolean {\n return this.paused;\n }\n\n pause(): void {\n this.paused = true;\n }\n\n resume(): void {\n this.paused = false;\n }\n\n canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n this.emitChanged();\n }\n\n private cancelPending(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n dispose(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n }\n\n private emitChanged(): void {\n this.events.emit('history:changed', {\n canUndo: this.canUndo(),\n canRedo: this.canRedo(),\n });\n }\n}\n","import type { Canvas, FabricObject, TPointerEvent } from 'fabric';\nimport type { EventEmitter } from './events';\nimport type { EditorEvents } from './types';\n\ninterface SnapGuide {\n /** Vertical guide at this x (canvas px), or undefined for a horizontal one. */\n x?: number;\n y?: number;\n}\n\nconst STROKE = '#22c55e';\n\n/**\n * Snaps a dragged object's edges and centre to the canvas edges/centre and to\n * other objects, drawing transient guide lines while it does. Switchable via\n * {@link setEnabled}; inert (zero overhead beyond a flag check) when disabled.\n */\nexport class SnapManager {\n private enabled = false;\n private threshold = 8;\n private guides: SnapGuide[] = [];\n\n private readonly onMoving: (e: { target?: FabricObject; e: TPointerEvent }) => void;\n private readonly onAfterRender: () => void;\n private readonly onModified: () => void;\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {\n this.onMoving = (e) => this.handleMoving(e.target);\n this.onAfterRender = () => this.drawGuides();\n this.onModified = () => this.clearGuides();\n\n this.canvas.on('object:moving', this.onMoving);\n this.canvas.on('after:render', this.onAfterRender);\n this.canvas.on('mouse:up', this.onModified);\n this.canvas.on('selection:cleared', this.onModified);\n }\n\n setEnabled(enabled: boolean): void {\n if (this.enabled === enabled) return;\n this.enabled = enabled;\n if (!enabled) this.clearGuides();\n this.events.emit('snap:changed', { enabled });\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n setThreshold(px: number): void {\n this.threshold = Math.max(0, px);\n }\n\n dispose(): void {\n this.canvas.off('object:moving', this.onMoving);\n this.canvas.off('after:render', this.onAfterRender);\n this.canvas.off('mouse:up', this.onModified);\n this.canvas.off('selection:cleared', this.onModified);\n this.guides = [];\n }\n\n private handleMoving(target?: FabricObject): void {\n if (!this.enabled || !target) return;\n\n target.setCoords();\n const r = target.getBoundingRect();\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n // Candidate lines for both axes, collected in a single pass over the objects.\n const { xs, ys } = collectCandidates(this.canvas, target, cw, ch);\n\n this.guides = [];\n\n const dx = bestSnap([r.left, r.left + r.width / 2, r.left + r.width], xs, this.threshold);\n if (dx !== null) {\n target.set('left', (target.left ?? 0) + dx.delta);\n this.guides.push({ x: dx.line });\n }\n\n const dy = bestSnap([r.top, r.top + r.height / 2, r.top + r.height], ys, this.threshold);\n if (dy !== null) {\n target.set('top', (target.top ?? 0) + dy.delta);\n this.guides.push({ y: dy.line });\n }\n\n target.setCoords();\n }\n\n private drawGuides(): void {\n if (!this.enabled || this.guides.length === 0) return;\n const ctx = this.canvas.contextTop;\n if (!ctx) return;\n\n const retina = this.canvas.getRetinaScaling();\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n ctx.save();\n ctx.setTransform(retina, 0, 0, retina, 0, 0);\n ctx.lineWidth = 1;\n ctx.strokeStyle = STROKE;\n ctx.setLineDash([4, 4]);\n ctx.beginPath();\n for (const g of this.guides) {\n if (g.x !== undefined) {\n ctx.moveTo(g.x, 0);\n ctx.lineTo(g.x, ch);\n }\n if (g.y !== undefined) {\n ctx.moveTo(0, g.y);\n ctx.lineTo(cw, g.y);\n }\n }\n ctx.stroke();\n ctx.restore();\n }\n\n private clearGuides(): void {\n if (this.guides.length === 0) return;\n this.guides = [];\n this.canvas.clearContext(this.canvas.contextTop);\n }\n}\n\n/**\n * Snap candidate lines (edges + centre) for the canvas and every other object,\n * for both axes, computed in one pass so each object's bounds are read once.\n */\nfunction collectCandidates(\n canvas: Canvas,\n moving: FabricObject,\n cw: number,\n ch: number,\n): { xs: number[]; ys: number[] } {\n const xs = [0, cw / 2, cw];\n const ys = [0, ch / 2, ch];\n for (const obj of canvas.getObjects()) {\n if (obj === moving || !obj.visible) continue;\n obj.setCoords();\n const r = obj.getBoundingRect();\n xs.push(r.left, r.left + r.width / 2, r.left + r.width);\n ys.push(r.top, r.top + r.height / 2, r.top + r.height);\n }\n return { xs, ys };\n}\n\n/**\n * Find the closest (edge, candidate) pair within threshold. Returns the delta\n * to apply and the guide line to draw, or null if nothing is close enough.\n */\nfunction bestSnap(\n edges: number[],\n candidates: number[],\n threshold: number,\n): { delta: number; line: number } | null {\n let best: { delta: number; line: number; dist: number } | null = null;\n for (const edge of edges) {\n for (const c of candidates) {\n const dist = Math.abs(edge - c);\n if (dist <= threshold && (best === null || dist < best.dist)) {\n best = { delta: c - edge, line: c, dist };\n }\n }\n }\n return best ? { delta: best.delta, line: best.line } : null;\n}\n\nexport { bestSnap as _bestSnap };\n","import { Rect } from 'fabric';\nimport type { Canvas, FabricImage } from 'fabric';\nimport type { Layer } from './layer';\nimport type { HistoryManager } from './history';\nimport type { EventEmitter } from './events';\nimport type { EditorEvents } from './types';\nimport { clamp } from './utils/clamp';\n\ninterface CropSession {\n layerId: string;\n image: FabricImage;\n rect: Rect;\n prevSelectable: boolean;\n prevEvented: boolean;\n /** Angle before crop normalised it to 0; restored if the crop is cancelled. */\n prevAngle: number;\n}\n\n/**\n * Interactive crop for image layers. {@link start} drops a draggable/resizable\n * rectangle over the image; {@link apply} converts it into fabric crop props\n * (cropX/cropY/width/height in element pixels) and keeps the displayed size in\n * sync with the rectangle.\n *\n * Rotation is unsupported: an angled image is normalised to 0° on start.\n */\nexport class CropController {\n private session: CropSession | null = null;\n private readonly onSelectionCleared: () => void;\n\n constructor(\n private canvas: Canvas,\n private history: HistoryManager,\n private events: EventEmitter<EditorEvents>,\n ) {\n this.onSelectionCleared = () => {\n // Clicking away from the crop rect abandons the crop.\n if (this.session) this.cancel();\n };\n this.canvas.on('selection:cleared', this.onSelectionCleared);\n }\n\n isActive(): boolean {\n return this.session !== null;\n }\n\n activeLayerId(): string | null {\n return this.session?.layerId ?? null;\n }\n\n start(layer: Layer): void {\n if (layer.type !== 'image') return;\n if (this.session) this.cancel();\n\n const image = layer.fabricObject as FabricImage;\n const prevAngle = image.angle ?? 0;\n if (prevAngle) image.rotate(0);\n image.setCoords();\n const b = image.getBoundingRect();\n\n const rect = new Rect({\n left: b.left,\n top: b.top,\n width: b.width,\n height: b.height,\n originX: 'left',\n originY: 'top',\n fill: 'rgba(34,197,94,0.12)',\n stroke: STROKE,\n strokeWidth: 1,\n strokeDashArray: [4, 4],\n strokeUniform: true,\n lockRotation: true,\n cornerColor: STROKE,\n transparentCorners: false,\n });\n rect.setControlsVisibility({ mtr: false });\n\n const prevSelectable = image.selectable ?? true;\n const prevEvented = image.evented ?? true;\n image.selectable = false;\n image.evented = false;\n\n this.canvas.add(rect);\n this.canvas.setActiveObject(rect);\n this.canvas.requestRenderAll();\n\n this.session = { layerId: layer.id, image, rect, prevSelectable, prevEvented, prevAngle };\n this.events.emit('crop:changed', { active: true, layerId: layer.id });\n }\n\n apply(): void {\n const s = this.session;\n if (!s) return;\n const { image, rect } = s;\n\n rect.setCoords();\n image.setCoords();\n const scaleX = image.scaleX ?? 1;\n const scaleY = image.scaleY ?? 1;\n const imgLeft = image.left ?? 0;\n const imgTop = image.top ?? 0;\n\n const el = image.getElement() as HTMLImageElement;\n const naturalW = el.naturalWidth || image.width || 0;\n const naturalH = el.naturalHeight || image.height || 0;\n\n // Map the rect (canvas px) into the image's element-pixel space.\n let cropX = (image.cropX ?? 0) + (rect.left! - imgLeft) / scaleX;\n let cropY = (image.cropY ?? 0) + (rect.top! - imgTop) / scaleY;\n let cropW = rect.getScaledWidth() / scaleX;\n let cropH = rect.getScaledHeight() / scaleY;\n\n // Keep the crop window inside the source element.\n cropX = clamp(cropX, 0, Math.max(0, naturalW - 1));\n cropY = clamp(cropY, 0, Math.max(0, naturalH - 1));\n cropW = clamp(cropW, 1, naturalW - cropX);\n cropH = clamp(cropH, 1, naturalH - cropY);\n\n image.set({\n cropX,\n cropY,\n width: cropW,\n height: cropH,\n // Anchor the displayed result where the rect sits.\n left: imgLeft + (cropX - (image.cropX ?? 0)) * scaleX,\n top: imgTop + (cropY - (image.cropY ?? 0)) * scaleY,\n });\n image.setCoords();\n\n this.finish();\n this.history.save();\n }\n\n cancel(): void {\n const s = this.session;\n if (!s) return;\n // Cancelling discards the crop, so restore the rotation start() zeroed out.\n if (s.prevAngle) {\n s.image.rotate(s.prevAngle);\n s.image.setCoords();\n }\n this.finish();\n }\n\n dispose(): void {\n this.cancel();\n this.canvas.off('selection:cleared', this.onSelectionCleared);\n }\n\n private finish(): void {\n const s = this.session;\n if (!s) return;\n // Null the session first so the selection:cleared fired by removing the\n // rect / re-selecting does not re-enter cancel().\n this.session = null;\n\n this.canvas.remove(s.rect);\n s.image.selectable = s.prevSelectable;\n s.image.evented = s.prevEvented;\n this.canvas.setActiveObject(s.image);\n this.canvas.requestRenderAll();\n\n this.events.emit('crop:changed', { active: false, layerId: null });\n }\n}\n\nconst STROKE = '#22c55e';\n","export function clamp(v: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, v));\n}\n\nexport function round2(v: number): number {\n return Math.round(v * 100) / 100;\n}\n","import { FabricImage } from 'fabric';\nimport type { Canvas } from 'fabric';\nimport type { Layer, LayerManager } from './layer';\nimport type { HistoryManager } from './history';\nimport type { PatternConfig } from './types';\n\n/**\n * Renders a single image layer as a repeating pattern that fills the canvas.\n *\n * The tiled result is rasterised onto an offscreen canvas and swapped in as the\n * layer's image source, so it survives export, serialization and undo/redo with\n * no extra machinery. The original source + transform are kept in `layer.meta`\n * so the pattern can be turned back off.\n */\nexport class PatternManager {\n // Per-layer task chain. apply()/disable() both await an async setSrc on the\n // same fabric image; running two concurrently lets their setSrc resolutions\n // interleave (wrong image installed, original lost). Serialising per layer\n // guarantees the last-requested operation wins and state stays consistent.\n private chains = new Map<string, Promise<unknown>>();\n\n constructor(\n private canvas: Canvas,\n private layers: LayerManager,\n private history: HistoryManager,\n ) {}\n\n isPattern(layerId: string): boolean {\n return !!this.layers.get(layerId)?.meta.pattern;\n }\n\n getConfig(layerId: string): PatternConfig | null {\n return this.layers.get(layerId)?.meta.pattern?.config ?? null;\n }\n\n /** Turn a plain image layer into a pattern, or update an existing one. */\n apply(layerId: string, config: PatternConfig): Promise<void> {\n return this.enqueue(layerId, async () => {\n const layer = this.layers.get(layerId);\n if (!layer || layer.type !== 'image') return;\n const image = layer.fabricObject as FabricImage;\n\n const firstEnable = !layer.meta.pattern;\n // Check the field directly (not `firstEnable`) so TS narrows the else.\n if (!layer.meta.pattern) {\n // Captured at run time (not enqueue time) so a queued apply always reads\n // the genuine pre-pattern image, never a half-applied one.\n layer.meta.pattern = {\n config,\n originalSrc: elementToDataURL(image) ?? image.getSrc(),\n original: {\n left: image.left ?? 0,\n top: image.top ?? 0,\n scaleX: image.scaleX ?? 1,\n scaleY: image.scaleY ?? 1,\n width: image.width ?? 0,\n height: image.height ?? 0,\n angle: image.angle ?? 0,\n cropX: image.cropX ?? 0,\n cropY: image.cropY ?? 0,\n },\n };\n } else {\n layer.meta.pattern.config = config;\n }\n\n try {\n await this.renderLayer(layer);\n } catch (err) {\n // Roll back a freshly-captured state so a failed first render doesn't\n // leave the layer flagged as a pattern with the original still showing.\n if (firstEnable) delete layer.meta.pattern;\n throw err;\n }\n this.history.save();\n });\n }\n\n /** Restore the original image and drop the pattern. */\n disable(layerId: string): Promise<void> {\n return this.enqueue(layerId, async () => {\n const layer = this.layers.get(layerId);\n const state = layer?.meta.pattern;\n if (!layer || !state) return;\n const image = layer.fabricObject as FabricImage;\n\n await image.setSrc(state.originalSrc);\n // setSrc resets width/height to the full element size, so restore the\n // captured crop window too — otherwise an image that was cropped before\n // being turned into a pattern loses its crop on disable.\n image.set({\n left: state.original.left,\n top: state.original.top,\n scaleX: state.original.scaleX,\n scaleY: state.original.scaleY,\n width: state.original.width,\n height: state.original.height,\n cropX: state.original.cropX,\n cropY: state.original.cropY,\n angle: state.original.angle,\n });\n image.setCoords();\n delete layer.meta.pattern;\n this.canvas.requestRenderAll();\n this.history.save();\n });\n }\n\n /** Run `task` after any in-flight work for this layer, regardless of outcome. */\n private enqueue(layerId: string, task: () => Promise<void>): Promise<void> {\n const prev = this.chains.get(layerId) ?? Promise.resolve();\n const next = prev.then(task, task);\n // Keep the chain alive on rejection so later ops still run.\n this.chains.set(\n layerId,\n next.catch(() => undefined),\n );\n return next;\n }\n\n private async renderLayer(layer: Layer): Promise<void> {\n const state = layer.meta.pattern;\n if (!state) return;\n const image = layer.fabricObject as FabricImage;\n\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n const tileW = state.original.width * state.original.scaleX;\n const tileH = state.original.height * state.original.scaleY;\n\n const dataUrl = await buildPatternDataURL(\n state.originalSrc,\n state.config,\n cw,\n ch,\n tileW,\n tileH,\n );\n\n await image.setSrc(dataUrl);\n // setSrc leaves cropX/cropY untouched — reset them, then position the tiled\n // image to cover the whole canvas at 1:1.\n image.set({ left: 0, top: 0, scaleX: 1, scaleY: 1, cropX: 0, cropY: 0, angle: 0 });\n image.setCoords();\n this.canvas.requestRenderAll();\n }\n}\n\n/** Snapshot an already-loaded image element to a PNG data URL. */\nfunction elementToDataURL(image: FabricImage): string | null {\n try {\n const el = image.getElement() as HTMLImageElement | HTMLCanvasElement;\n const w = (el as HTMLImageElement).naturalWidth || el.width;\n const h = (el as HTMLImageElement).naturalHeight || el.height;\n if (!w || !h) return null;\n const off = document.createElement('canvas');\n off.width = w;\n off.height = h;\n const ctx = off.getContext('2d');\n if (!ctx) return null;\n ctx.drawImage(el, 0, 0);\n return off.toDataURL('image/png');\n } catch {\n // Tainted canvas (remote image without CORS) — fall back to the live src.\n return null;\n }\n}\n\n// Decoding a source (often a large data URL) is the dominant cost of a regen,\n// so cache decoded images. Bounded with simple LRU eviction so a stream of\n// distinct sources can't grow the cache without limit.\nconst IMAGE_CACHE_MAX = 16;\nconst imageCache = new Map<string, Promise<HTMLImageElement>>();\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n const cached = imageCache.get(src);\n if (cached) {\n // Refresh recency (Map preserves insertion order → re-insert = most recent).\n imageCache.delete(src);\n imageCache.set(src, cached);\n return cached;\n }\n\n const promise = decodeImage(src);\n // Don't poison the cache with a failed decode.\n promise.catch(() => {\n if (imageCache.get(src) === promise) imageCache.delete(src);\n });\n\n imageCache.set(src, promise);\n if (imageCache.size > IMAGE_CACHE_MAX) {\n const oldest = imageCache.keys().next().value;\n if (oldest !== undefined) imageCache.delete(oldest);\n }\n return promise;\n}\n\nfunction decodeImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`Failed to load pattern source: ${src}`));\n img.src = src;\n });\n}\n\n/** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */\nexport function clearPatternImageCache(): void {\n imageCache.clear();\n}\n\n/**\n * Draw the source image repeatedly across a `targetW × targetH` offscreen\n * canvas and return the result as a PNG data URL.\n *\n * Exported for unit testing of the tile geometry.\n */\nexport async function buildPatternDataURL(\n src: string,\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): Promise<string> {\n const img = await loadImage(src);\n const off = document.createElement('canvas');\n off.width = Math.max(1, Math.round(targetW));\n off.height = Math.max(1, Math.round(targetH));\n const ctx = off.getContext('2d');\n if (!ctx) return off.toDataURL('image/png');\n\n drawTiles(ctx, img, config, targetW, targetH, baseW, baseH);\n return off.toDataURL('image/png');\n}\n\n/** Placement of one tile, relative to the (already rotated) pattern centre. */\nexport interface TilePlacement {\n /** Tile-centre offset from the pattern centre, in px. */\n x: number;\n y: number;\n /** Per-tile rotation, in radians. */\n rotation: number;\n}\n\n/**\n * Pure tile geometry: where each tile centre lands and how much it is rotated,\n * for a config + target/base size. Covers the rotated bounding diagonal so\n * corners stay filled at any pattern angle. Extracted for unit testing.\n */\nexport function computeTilePositions(\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): TilePlacement[] {\n const tileW = Math.max(1, baseW * (1 + config.horizontalSpacing / 100));\n const tileH = Math.max(1, baseH * (1 + config.verticalSpacing / 100));\n\n const diag = Math.sqrt(targetW * targetW + targetH * targetH);\n const cols = Math.ceil(diag / tileW) + 2;\n const rows = Math.ceil(diag / tileH) + 2;\n const halfCols = Math.ceil(cols / 2);\n const halfRows = Math.ceil(rows / 2);\n\n const placements: TilePlacement[] = [];\n for (let j = -halfRows; j <= halfRows; j++) {\n for (let i = -halfCols; i <= halfCols; i++) {\n let x = i * tileW;\n let y = j * tileH;\n\n if (config.mode === 'brick-horizontal' && mod2(j) === 1) {\n x += tileW * (config.horizontalOffset / 100);\n } else if (config.mode === 'brick-vertical' && mod2(i) === 1) {\n y += tileH * (config.horizontalOffset / 100);\n }\n\n const rotation = ((i * config.rotationStepH + j * config.rotationStepV) * Math.PI) / 180;\n placements.push({ x, y, rotation });\n }\n }\n return placements;\n}\n\n/**\n * Draw the tiled pattern onto a 2D context. The geometry lives in\n * {@link computeTilePositions}; this just plays it out as canvas calls.\n */\nexport function drawTiles(\n ctx: CanvasRenderingProtocol,\n img: CanvasImageSource,\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): void {\n ctx.save();\n // Rotate the whole pattern around the canvas centre.\n ctx.translate(targetW / 2, targetH / 2);\n ctx.rotate((config.angle * Math.PI) / 180);\n\n for (const tile of computeTilePositions(config, targetW, targetH, baseW, baseH)) {\n ctx.save();\n ctx.translate(tile.x, tile.y);\n ctx.rotate(tile.rotation);\n ctx.drawImage(img, -baseW / 2, -baseH / 2, baseW, baseH);\n ctx.restore();\n }\n\n ctx.restore();\n}\n\n/** Non-negative parity (so negative row indices brick-shift consistently). */\nfunction mod2(n: number): number {\n return ((n % 2) + 2) % 2;\n}\n\n/** Minimal 2D-context surface used by {@link drawTiles}; lets tests inject a spy. */\nexport interface CanvasRenderingProtocol {\n save(): void;\n restore(): void;\n translate(x: number, y: number): void;\n rotate(angle: number): void;\n drawImage(image: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;\n}\n","import type { Unit } from '../types';\n\nconst MM_PER_INCH = 25.4;\n\nexport class UnitConverter {\n constructor(\n private unit: Unit = 'px',\n private dpi: number = 72,\n ) {}\n\n setUnit(unit: Unit): void {\n this.unit = unit;\n }\n\n setDpi(dpi: number): void {\n this.dpi = dpi;\n }\n\n getUnit(): Unit {\n return this.unit;\n }\n\n getDpi(): number {\n return this.dpi;\n }\n\n toPixels(value: number, fromUnit?: Unit): number {\n const u = fromUnit ?? this.unit;\n switch (u) {\n case 'px':\n return value;\n case 'in':\n return value * this.dpi;\n case 'mm':\n return (value / MM_PER_INCH) * this.dpi;\n }\n }\n\n fromPixels(px: number, toUnit?: Unit): number {\n const u = toUnit ?? this.unit;\n switch (u) {\n case 'px':\n return px;\n case 'in':\n return px / this.dpi;\n case 'mm':\n return (px / this.dpi) * MM_PER_INCH;\n }\n }\n}\n","import { util } from 'fabric';\nimport type { FabricObject } from 'fabric';\nimport type { EditorState, SerializedLayer } from './types';\nimport type { CanvasEditor } from './editor';\nimport { Layer } from './layer';\n\nconst VERSION = '1.0.0';\n\nexport function serializeEditor(editor: CanvasEditor): EditorState {\n return {\n version: VERSION,\n canvas: {\n width: editor.canvas.getWidth(),\n height: editor.canvas.getHeight(),\n },\n layers: editor.layers.getAll().map((layer) => layer.serialize()),\n // The configured design background, not the live canvas value (which is\n // forced transparent while a mockup preview is active).\n background: editor.getDesignBackground(),\n mockup: editor.getMockup(),\n };\n}\n\nexport async function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void> {\n editor.layers.clear();\n editor.canvas.setDimensions({ width: state.canvas.width, height: state.canvas.height });\n\n // setBackground records the design background; history is paused during\n // restore so this does not push a spurious entry. setMockup (below) then\n // applies the correct live canvas background.\n if (state.background) {\n editor.setBackground(state.background);\n }\n\n // mockup is preview-only (rendered behind the canvas), so it is restored\n // separately from the design layers. `undefined` means \"leave as-is\".\n if (state.mockup !== undefined) {\n editor.setMockup(state.mockup);\n }\n\n for (const serializedLayer of state.layers) {\n await restoreLayer(editor, serializedLayer);\n }\n\n editor.canvas.requestRenderAll();\n}\n\nasync function restoreLayer(editor: CanvasEditor, serialized: SerializedLayer): Promise<Layer> {\n const objects = await util.enlivenObjects([serialized.fabricObject]);\n const fabricObject = objects[0] as FabricObject;\n\n const layer = editor.layers.add(serialized.type, fabricObject, serialized.name, serialized.id);\n\n if (serialized.meta) {\n layer.meta = serialized.meta;\n }\n\n if (!serialized.visible) {\n editor.layers.setVisibility(layer.id, false);\n }\n if (serialized.locked) {\n editor.layers.setLocked(layer.id, true);\n }\n if (serialized.opacity !== 1) {\n editor.layers.setOpacity(layer.id, serialized.opacity);\n }\n\n return layer;\n}\n","import type { Canvas, ImageFormat } from 'fabric';\n\nexport interface PngExportOptions {\n multiplier?: number;\n format?: ImageFormat;\n quality?: number;\n}\n\nexport async function exportPNG(\n canvas: Canvas,\n options: PngExportOptions = {},\n): Promise<Blob> {\n const { multiplier = 1, format = 'png' as ImageFormat, quality = 1 } = options;\n const dataUrl = canvas.toDataURL({\n format,\n multiplier,\n quality,\n });\n const response = await fetch(dataUrl);\n return response.blob();\n}\n\nexport function exportSVG(canvas: Canvas): string {\n return canvas.toSVG();\n}\n\nexport function exportDataURL(\n canvas: Canvas,\n format: ImageFormat = 'png',\n multiplier = 1,\n): string {\n return canvas.toDataURL({ format, multiplier });\n}\n","import type { FabricObject } from 'fabric';\n\n// ─── Units ───────────────────────────────────────────────\n\nexport type Unit = 'px' | 'mm' | 'in';\n\n// ─── Layers ──────────────────────────────────────────────\n\nexport type LayerType = 'image' | 'text' | 'shape' | 'template' | 'group';\n\nexport interface LayerData {\n id: string;\n type: LayerType;\n name: string;\n visible: boolean;\n locked: boolean;\n opacity: number;\n /** Non-fabric metadata persisted with the layer (pattern config, etc). */\n meta?: LayerMeta;\n}\n\nexport interface LayerMeta {\n pattern?: PatternState;\n [key: string]: unknown;\n}\n\nexport interface SerializedLayer extends LayerData {\n fabricObject: Record<string, unknown>;\n}\n\n// ─── Editor ──────────────────────────────────────────────\n\nexport interface EditorState {\n version: string;\n canvas: { width: number; height: number };\n layers: SerializedLayer[];\n background?: string;\n mockup?: MockupConfig | null;\n}\n\nexport interface EditorConfig {\n width: number;\n height: number;\n backgroundColor?: string;\n fileAdapter?: FileAdapter;\n imageProvider?: ImageProvider;\n preserveObjectStacking?: boolean;\n unit?: Unit;\n dpi?: number;\n}\n\n// ─── Adapters ────────────────────────────────────────────\n\nexport type ExportFormat = 'png' | 'svg' | 'json';\n\nexport interface FileAdapter {\n save(data: Blob | string, filename: string, format: ExportFormat): Promise<string>;\n}\n\nexport interface ImageProviderResult {\n url: string;\n width: number;\n height: number;\n}\n\nexport interface ImageProvider {\n upload(file: File): Promise<ImageProviderResult>;\n browse?(): Promise<ImageProviderResult | null>;\n}\n\n// ─── Plugins ─────────────────────────────────────────────\n\nexport interface ShapePlugin {\n name: string;\n icon: string;\n category?: string;\n create(options?: Record<string, unknown>): FabricObject;\n}\n\nexport interface TemplateParameter {\n key: string;\n label: string;\n type: 'text' | 'color' | 'number' | 'font';\n default: string | number;\n}\n\nexport interface TemplateDefinition {\n id: string;\n name: string;\n category: string;\n svg: string;\n parameters: TemplateParameter[];\n preview?: string;\n}\n\n// ─── Printify ────────────────────────────────────────────\n\nexport interface PrintifyPositioning {\n x: number;\n y: number;\n scale: number;\n angle: number;\n}\n\n// ─── Patterns ────────────────────────────────────────────\n\nexport type TileMode = 'grid' | 'brick-horizontal' | 'brick-vertical';\n\nexport interface PatternConfig {\n mode: TileMode;\n /** Extra horizontal gap between tiles, as % of tile width. */\n horizontalSpacing: number;\n /** Extra vertical gap between tiles, as % of tile height. */\n verticalSpacing: number;\n /** Rotation of the whole pattern, in degrees. */\n angle: number;\n /** Brick row/column shift, as % of tile size. */\n horizontalOffset: number;\n /** Added rotation per horizontal step, in degrees. */\n rotationStepH: number;\n /** Added rotation per vertical step, in degrees. */\n rotationStepV: number;\n}\n\n/** Persisted on a layer's meta while it is rendered as a repeating pattern. */\nexport interface PatternState {\n config: PatternConfig;\n /** Original (pre-pattern) image source, as a durable data URL when possible. */\n originalSrc: string;\n /** Original fabric transform, restored when the pattern is turned off. */\n original: {\n left: number;\n top: number;\n scaleX: number;\n scaleY: number;\n width: number;\n height: number;\n angle: number;\n cropX: number;\n cropY: number;\n };\n}\n\nexport const DEFAULT_PATTERN_CONFIG: PatternConfig = {\n mode: 'grid',\n horizontalSpacing: 0,\n verticalSpacing: 0,\n angle: 0,\n horizontalOffset: 0,\n rotationStepH: 0,\n rotationStepV: 0,\n};\n\n// ─── Mockup ──────────────────────────────────────────────\n\nexport interface MockupPrintArea {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\nexport interface MockupConfig {\n /** Garment/product image rendered behind the design (preview only). */\n image: string;\n /** Print-area rectangle, in canvas pixels. Drawn as a guide. */\n printArea?: MockupPrintArea;\n}\n\n// ─── Events ──────────────────────────────────────────────\n\nexport interface EditorEvents {\n 'layer:added': { layer: LayerData };\n 'layer:removed': { layerId: string };\n 'layer:selected': { layerId: string | null };\n 'layer:modified': { layerId: string };\n 'layer:reordered': { layerIds: string[] };\n 'layers:changed': { layers: LayerData[] };\n 'selection:changed': { selected: string[] };\n 'history:changed': { canUndo: boolean; canRedo: boolean };\n 'zoom:changed': { zoom: number };\n 'snap:changed': { enabled: boolean };\n 'crop:changed': { active: boolean; layerId: string | null };\n 'mockup:changed': { mockup: MockupConfig | null };\n 'canvas:modified': Record<string, never>;\n 'export:start': { format: string };\n 'export:complete': { format: string; url?: string };\n error: { message: string; error?: unknown };\n}\n"],"mappings":"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","names":["STROKE"]}
1
+ {"version":3,"sources":["../src/editor.ts","../src/events.ts","../src/utils/id.ts","../src/layer.ts","../src/history.ts","../src/snapping.ts","../src/crop.ts","../src/utils/clamp.ts","../src/pattern.ts","../src/utils/units.ts","../src/serialization.ts","../src/export.ts","../src/types.ts"],"sourcesContent":["import { Canvas, FabricImage, Textbox } from 'fabric';\nimport type { FabricObject, ImageFormat } from 'fabric';\nimport type {\n EditorConfig,\n EditorEvents,\n EditorState,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n MockupConfig,\n PatternConfig,\n PrintifyPositioning,\n ShapePlugin,\n TemplateDefinition,\n} from './types';\nimport { EventEmitter } from './events';\nimport { Layer, LayerManager } from './layer';\nimport { HistoryManager } from './history';\nimport { SnapManager } from './snapping';\nimport { CropController } from './crop';\nimport { PatternManager } from './pattern';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL } from './export';\nimport type { PngExportOptions } from './export';\nimport { clamp, round2 } from './utils/clamp';\n\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 8;\n\nexport class CanvasEditor {\n readonly canvas: Canvas;\n readonly layers: LayerManager;\n readonly history: HistoryManager;\n readonly events: EventEmitter<EditorEvents>;\n readonly units: UnitConverter;\n readonly snapping: SnapManager;\n readonly crop: CropController;\n readonly patterns: PatternManager;\n\n private fileAdapter?: FileAdapter;\n private imageProvider?: ImageProvider;\n private zoomLevel = 1;\n private mockup: MockupConfig | null = null;\n // The design's configured background. The live canvas background is forced\n // transparent while a mockup preview is shown, so this is the source of truth\n // for serialization and export — not the (possibly transient) canvas value.\n private designBackground: string;\n\n constructor(canvasElement: HTMLCanvasElement, config: EditorConfig) {\n this.events = new EventEmitter<EditorEvents>();\n this.units = new UnitConverter(config.unit ?? 'px', config.dpi ?? 72);\n\n const widthPx = this.units.toPixels(config.width);\n const heightPx = this.units.toPixels(config.height);\n this.designBackground = config.backgroundColor ?? '#ffffff';\n\n this.canvas = new Canvas(canvasElement, {\n width: widthPx,\n height: heightPx,\n backgroundColor: this.designBackground,\n preserveObjectStacking: config.preserveObjectStacking ?? true,\n selection: true,\n });\n\n this.layers = new LayerManager(this.canvas, this.events);\n this.fileAdapter = config.fileAdapter;\n this.imageProvider = config.imageProvider;\n\n this.history = new HistoryManager({\n getState: () => JSON.stringify(this.toJSON()),\n restoreState: async (state: string) => {\n const parsed = JSON.parse(state) as EditorState;\n await this.fromJSON(parsed);\n },\n events: this.events,\n });\n\n this.snapping = new SnapManager(this.canvas, this.events);\n this.crop = new CropController(this.canvas, this.history, this.events);\n this.patterns = new PatternManager(this.canvas, this.layers, this.history);\n\n this.setupCanvasEvents();\n this.history.saveImmediate();\n }\n\n // ─── Layer Operations ────────────────────────────────\n\n async addImage(url: string, options?: Partial<Record<string, unknown>>): Promise<Layer> {\n // Don't force crossOrigin here: a remote image whose host sends no CORS\n // header would then fail to load at all. The pattern engine requests its own\n // crossOrigin copy when it needs an untainted canvas (see pattern.ts).\n // Fabric v7 defaults originX/originY to 'center'; pin top-left so images\n // land at their left/top like the rest of the editor's coordinate model.\n const img = await FabricImage.fromURL(url, {}, { originX: 'left', originY: 'top', ...options });\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n }\n\n addText(text: string, options?: Partial<Record<string, unknown>>): Layer {\n const textbox = new Textbox(text, {\n fontSize: 32,\n fontFamily: 'Arial',\n fill: '#000000',\n width: 200,\n // v7 defaults origin to center; keep top-left placement.\n originX: 'left',\n originY: 'top',\n ...options,\n });\n const layer = this.layers.add('text', textbox as unknown as FabricObject);\n this.history.save();\n return layer;\n }\n\n addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer {\n const obj = plugin.create(options);\n const layer = this.layers.add('shape', obj, plugin.name);\n this.history.save();\n return layer;\n }\n\n async addTemplate(\n template: TemplateDefinition,\n _params: Record<string, string | number>,\n ): Promise<Layer> {\n // Template loading is handled by the templates package\n // This is a placeholder for direct fabric object addition\n const { Textbox: TextboxClass } = await import('fabric');\n const placeholder = new TextboxClass(`[Template: ${template.name}]`, {\n fontSize: 24,\n fontFamily: 'Arial',\n fill: '#666666',\n width: 300,\n originX: 'left',\n originY: 'top',\n });\n const layer = this.layers.add(\n 'template',\n placeholder as unknown as FabricObject,\n template.name,\n );\n this.history.save();\n return layer;\n }\n\n removeLayer(id: string): void {\n this.layers.remove(id);\n this.history.save();\n }\n\n selectLayer(id: string | null): void {\n this.layers.select(id);\n }\n\n getSelectedLayer(): Layer | null {\n const active = this.canvas.getActiveObject();\n if (!active) return null;\n return this.layers.findByObject(active) ?? null;\n }\n\n /** Mirror a layer along the given axis. */\n flipLayer(id: string, axis: 'horizontal' | 'vertical'): void {\n const layer = this.layers.get(id);\n if (!layer) return;\n const obj = layer.fabricObject;\n if (axis === 'horizontal') {\n obj.set('flipX', !obj.flipX);\n } else {\n obj.set('flipY', !obj.flipY);\n }\n obj.setCoords();\n this.canvas.requestRenderAll();\n this.history.save();\n }\n\n /** Clone a layer (offset slightly) and select the copy. */\n async duplicateLayer(id: string): Promise<Layer | null> {\n const layer = this.layers.get(id);\n if (!layer) return null;\n\n const clone = await layer.fabricObject.clone();\n // A pattern layer's image is rasterised to cover the canvas at 0,0; offsetting\n // it would leave a gap at the edges, so only nudge non-pattern duplicates.\n if (!layer.meta.pattern) {\n clone.set({ left: (clone.left ?? 0) + 15, top: (clone.top ?? 0) + 15 });\n }\n clone.setCoords();\n\n const copy = this.layers.add(layer.type, clone, `${layer.name} copy`);\n // Carry non-fabric metadata (e.g. pattern config) onto the copy.\n copy.meta = structuredClone(layer.meta);\n\n this.canvas.setActiveObject(clone);\n this.canvas.requestRenderAll();\n this.history.save();\n return copy;\n }\n\n // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n await deserializeEditor(this, state);\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n this.events.emit('export:start', { format: 'png' });\n const blob = await this.withDesignBackground(() => exportPNG(this.canvas, options));\n this.events.emit('export:complete', { format: 'png' });\n return blob;\n }\n\n toSVG(): string {\n this.events.emit('export:start', { format: 'svg' });\n const svg = this.withDesignBackground(() => exportSVG(this.canvas));\n this.events.emit('export:complete', { format: 'svg' });\n return svg;\n }\n\n toDataURL(format: ImageFormat = 'png', multiplier = 1): string {\n return this.withDesignBackground(() => exportDataURL(this.canvas, format, multiplier));\n }\n\n /**\n * Run an export with the configured design background applied, even when a\n * mockup preview has forced the live canvas transparent — so exports reflect\n * the design, not the preview. Restores the preview state afterwards.\n */\n private withDesignBackground<T>(fn: () => T): T {\n if (!this.mockup) return fn();\n const previewBg = this.canvas.backgroundColor;\n this.canvas.backgroundColor = this.designBackground;\n try {\n return fn();\n } finally {\n this.canvas.backgroundColor = previewBg;\n }\n }\n\n toPrintifyPositioning(): Record<string, PrintifyPositioning> {\n const result: Record<string, PrintifyPositioning> = {};\n const canvasW = this.canvas.getWidth();\n const canvasH = this.canvas.getHeight();\n\n const imageLayers = this.layers.getAll().filter((l) => l.type === 'image');\n\n if (imageLayers.length === 0) {\n result['front'] = { x: 0.5, y: 0.5, scale: 1, angle: 0 };\n return result;\n }\n\n for (const layer of imageLayers) {\n const obj = layer.fabricObject;\n const center = obj.getCenterPoint();\n result[layer.name] = {\n x: round2(center.x / canvasW),\n y: round2(center.y / canvasH),\n scale: round2(obj.scaleX ?? 1),\n angle: round2(obj.angle ?? 0),\n };\n }\n\n return result;\n }\n\n // ─── File Operations ────────────────────────────────\n\n async save(filename: string, format: 'png' | 'svg' | 'json'): Promise<string | undefined> {\n if (!this.fileAdapter) return undefined;\n\n let data: Blob | string;\n if (format === 'png') {\n data = await this.toPNG();\n } else if (format === 'svg') {\n data = this.toSVG();\n } else {\n data = JSON.stringify(this.toJSON(), null, 2);\n }\n\n return this.fileAdapter.save(data, filename, format);\n }\n\n async uploadImage(file: File): Promise<ImageProviderResult | undefined> {\n if (!this.imageProvider) return undefined;\n return this.imageProvider.upload(file);\n }\n\n async browseImages(): Promise<ImageProviderResult | null> {\n if (!this.imageProvider?.browse) return null;\n return this.imageProvider.browse();\n }\n\n setFileAdapter(adapter: FileAdapter): void {\n this.fileAdapter = adapter;\n }\n\n setImageProvider(provider: ImageProvider): void {\n this.imageProvider = provider;\n }\n\n // ─── Canvas Operations ──────────────────────────────\n\n setBackground(color: string): void {\n this.designBackground = color;\n // Don't override the mockup's transparent preview; it will pick up the new\n // colour when the mockup is cleared.\n if (!this.mockup) {\n this.canvas.backgroundColor = color;\n this.canvas.requestRenderAll();\n }\n this.history.save();\n }\n\n getDesignBackground(): string {\n return this.designBackground;\n }\n\n resize(width: number, height: number): void {\n const widthPx = this.units.toPixels(width);\n const heightPx = this.units.toPixels(height);\n this.canvas.setDimensions({ width: widthPx, height: heightPx });\n this.canvas.requestRenderAll();\n }\n\n // ─── Zoom ───────────────────────────────────────────\n //\n // Zoom is a *display* transform (the view layer CSS-scales the canvas), not a\n // fabric viewport zoom — fabric's setZoom bakes into toDataURL/toSVG and would\n // corrupt exports. The editor only tracks the level and emits an event.\n\n setZoom(level: number): void {\n const next = clamp(round2(level), MIN_ZOOM, MAX_ZOOM);\n if (next === this.zoomLevel) return;\n this.zoomLevel = next;\n this.events.emit('zoom:changed', { zoom: next });\n }\n\n /** @deprecated use setZoom — kept for backward compatibility. */\n zoom(level: number): void {\n this.setZoom(level);\n }\n\n getZoom(): number {\n return this.zoomLevel;\n }\n\n zoomIn(step = 0.1): void {\n this.setZoom(this.zoomLevel + step);\n }\n\n zoomOut(step = 0.1): void {\n this.setZoom(this.zoomLevel - step);\n }\n\n resetZoom(): void {\n this.setZoom(1);\n }\n\n /** Fit the canvas inside a viewport (in px), accounting for padding. */\n zoomToFit(viewportWidth: number, viewportHeight: number, padding = 0): void {\n const w = this.canvas.getWidth();\n const h = this.canvas.getHeight();\n if (w <= 0 || h <= 0) return;\n const sx = (viewportWidth - padding * 2) / w;\n const sy = (viewportHeight - padding * 2) / h;\n this.setZoom(Math.min(sx, sy));\n }\n\n // ─── Patterns ───────────────────────────────────────\n\n applyPattern(layerId: string, config: PatternConfig): Promise<void> {\n return this.patterns.apply(layerId, config);\n }\n\n clearPattern(layerId: string): Promise<void> {\n return this.patterns.disable(layerId);\n }\n\n // ─── Mockup (preview-only) ──────────────────────────\n\n setMockup(mockup: MockupConfig | null): void {\n this.mockup = mockup;\n // The mockup renders behind the design, so the canvas must be transparent\n // while it is shown; restore the configured background when it is cleared.\n this.canvas.backgroundColor = mockup ? '' : this.designBackground;\n this.canvas.requestRenderAll();\n this.events.emit('mockup:changed', { mockup });\n }\n\n clearMockup(): void {\n this.setMockup(null);\n }\n\n getMockup(): MockupConfig | null {\n return this.mockup;\n }\n\n // ─── Cleanup ────────────────────────────────────────\n\n dispose(): void {\n this.snapping.dispose();\n this.crop.dispose();\n this.history.dispose();\n this.events.removeAllListeners();\n this.canvas.dispose();\n }\n\n // ─── Private ────────────────────────────────────────\n\n private setupCanvasEvents(): void {\n this.canvas.on('object:modified', (e) => {\n if (!e.target) return;\n const layer = this.layers.findByObject(e.target);\n if (layer) {\n this.events.emit('layer:modified', { layerId: layer.id });\n this.history.save();\n }\n });\n\n this.canvas.on('selection:created', (e) => {\n const selected = (e.selected ?? [])\n .map((obj) => this.layers.findByObject(obj)?.id)\n .filter((id): id is string => id !== undefined);\n this.events.emit('selection:changed', { selected });\n });\n\n this.canvas.on('selection:updated', (e) => {\n const selected = (e.selected ?? [])\n .map((obj) => this.layers.findByObject(obj)?.id)\n .filter((id): id is string => id !== undefined);\n this.events.emit('selection:changed', { selected });\n });\n\n this.canvas.on('selection:cleared', () => {\n this.events.emit('selection:changed', { selected: [] });\n });\n }\n}\n","type EventHandler<T> = (data: T) => void;\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport class EventEmitter<TEvents extends {} = Record<string, unknown>> {\n private listeners = new Map<keyof TEvents, Set<EventHandler<unknown>>>();\n\n on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(handler as EventHandler<unknown>);\n }\n\n off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n this.listeners.get(event)?.delete(handler as EventHandler<unknown>);\n }\n\n once<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void {\n const wrapped: EventHandler<TEvents[K]> = (data) => {\n this.off(event, wrapped);\n handler(data);\n };\n this.on(event, wrapped);\n }\n\n emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n const handlers = this.listeners.get(event);\n if (!handlers) return;\n for (const handler of handlers) {\n handler(data);\n }\n }\n\n removeAllListeners(event?: keyof TEvents): void {\n if (event) {\n this.listeners.delete(event);\n } else {\n this.listeners.clear();\n }\n }\n}\n","import { nanoid } from 'nanoid';\n\nexport const generateId = (): string => nanoid(12);\n","import type { Canvas, FabricObject } from 'fabric';\nimport type { LayerData, LayerMeta, LayerType, SerializedLayer } from './types';\nimport type { EditorEvents } from './types';\nimport type { EventEmitter } from './events';\nimport { generateId } from './utils/id';\n\nexport class Layer {\n readonly id: string;\n readonly type: LayerType;\n name: string;\n visible: boolean;\n locked: boolean;\n opacity: number;\n /** Non-fabric data (e.g. pattern config) that must persist with the layer. */\n meta: LayerMeta;\n readonly fabricObject: FabricObject;\n\n constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string) {\n this.id = id ?? generateId();\n this.type = type;\n this.name = name ?? `${type}-${this.id.slice(0, 4)}`;\n this.visible = true;\n this.locked = false;\n this.opacity = 1;\n this.meta = {};\n this.fabricObject = fabricObject;\n\n (this.fabricObject as FabricObject & { _layerId: string })._layerId = this.id;\n }\n\n private hasMeta(): boolean {\n return Object.keys(this.meta).length > 0;\n }\n\n toData(): LayerData {\n return {\n id: this.id,\n type: this.type,\n name: this.name,\n visible: this.visible,\n locked: this.locked,\n opacity: this.opacity,\n ...(this.hasMeta() ? { meta: this.meta } : {}),\n };\n }\n\n serialize(): SerializedLayer {\n return {\n ...this.toData(),\n fabricObject: this.fabricObject.toObject() as Record<string, unknown>,\n };\n }\n}\n\nexport class LayerManager {\n private layers: Layer[] = [];\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {}\n\n add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer {\n const layer = new Layer(type, fabricObject, name, id);\n this.layers.push(layer);\n this.canvas.add(fabricObject);\n this.events.emit('layer:added', { layer: layer.toData() });\n this.emitChanged();\n return layer;\n }\n\n remove(id: string): boolean {\n const index = this.layers.findIndex((l) => l.id === id);\n if (index === -1) return false;\n const layer = this.layers[index];\n this.canvas.remove(layer.fabricObject);\n this.layers.splice(index, 1);\n this.events.emit('layer:removed', { layerId: id });\n this.emitChanged();\n return true;\n }\n\n reorder(id: string, newIndex: number): boolean {\n const oldIndex = this.layers.findIndex((l) => l.id === id);\n if (oldIndex === -1) return false;\n const clamped = Math.max(0, Math.min(this.layers.length - 1, newIndex));\n const [layer] = this.layers.splice(oldIndex, 1);\n this.layers.splice(clamped, 0, layer);\n // Sync fabric z-order\n this.layers.forEach((l, i) => {\n this.canvas.moveObjectTo(l.fabricObject, i);\n });\n this.events.emit('layer:reordered', { layerIds: this.layers.map((l) => l.id) });\n this.emitChanged();\n return true;\n }\n\n select(id: string | null): void {\n if (id === null) {\n this.canvas.discardActiveObject();\n } else {\n const layer = this.get(id);\n if (layer) {\n this.canvas.setActiveObject(layer.fabricObject);\n }\n }\n this.canvas.requestRenderAll();\n this.events.emit('layer:selected', { layerId: id });\n // Fabric only fires selection:* on user interaction; mirror programmatic\n // selection so React selection state (useSelection) stays in sync.\n const selected = id && this.get(id) ? [id] : [];\n this.events.emit('selection:changed', { selected });\n }\n\n get(id: string): Layer | undefined {\n return this.layers.find((l) => l.id === id);\n }\n\n getAll(): Layer[] {\n return [...this.layers];\n }\n\n findByObject(obj: FabricObject): Layer | undefined {\n const layerId = (obj as FabricObject & { _layerId?: string })._layerId;\n if (layerId) return this.get(layerId);\n return this.layers.find((l) => l.fabricObject === obj);\n }\n\n setVisibility(id: string, visible: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.visible = visible;\n layer.fabricObject.visible = visible;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setLocked(id: string, locked: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.locked = locked;\n layer.fabricObject.selectable = !locked;\n layer.fabricObject.evented = !locked;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setOpacity(id: string, opacity: number): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.opacity = opacity;\n layer.fabricObject.opacity = opacity;\n this.canvas.requestRenderAll();\n this.emitChanged();\n }\n\n setName(id: string, name: string): void {\n const layer = this.get(id);\n if (!layer) return;\n layer.name = name;\n this.emitChanged();\n }\n\n clear(): void {\n for (const layer of this.layers) {\n this.canvas.remove(layer.fabricObject);\n }\n this.layers = [];\n this.emitChanged();\n }\n\n count(): number {\n return this.layers.length;\n }\n\n private emitChanged(): void {\n this.events.emit('layers:changed', {\n layers: this.layers.map((l) => l.toData()),\n });\n }\n}\n","import type { EditorEvents } from './types';\nimport type { EventEmitter } from './events';\n\nexport class HistoryManager {\n private undoStack: string[] = [];\n private redoStack: string[] = [];\n private maxSize: number;\n private paused = false;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private debounceMs: number;\n private getState: () => string;\n private restoreState: (state: string) => Promise<void>;\n private events: EventEmitter<EditorEvents>;\n\n constructor(opts: {\n getState: () => string;\n restoreState: (state: string) => Promise<void>;\n events: EventEmitter<EditorEvents>;\n maxSize?: number;\n debounceMs?: number;\n }) {\n this.getState = opts.getState;\n this.restoreState = opts.restoreState;\n this.events = opts.events;\n this.maxSize = opts.maxSize ?? 50;\n this.debounceMs = opts.debounceMs ?? 300;\n }\n\n save(): void {\n if (this.paused) return;\n\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.saveImmediate();\n }, this.debounceMs);\n }\n\n saveImmediate(): void {\n if (this.paused) return;\n const state = this.getState();\n this.undoStack.push(state);\n if (this.undoStack.length > this.maxSize) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.emitChanged();\n }\n\n async undo(): Promise<void> {\n // Drop any debounced save still pending from an in-progress edit, otherwise\n // its timer would fire after restore completes and clobber the redo stack.\n this.cancelPending();\n const state = this.undoStack.pop();\n if (!state) return;\n\n this.redoStack.push(this.getState());\n this.paused = true;\n await this.restoreState(state);\n this.paused = false;\n this.emitChanged();\n }\n\n async redo(): Promise<void> {\n this.cancelPending();\n const state = this.redoStack.pop();\n if (!state) return;\n\n this.undoStack.push(this.getState());\n this.paused = true;\n await this.restoreState(state);\n this.paused = false;\n this.emitChanged();\n }\n\n /** True while a restore (undo/redo/deserialize) is in flight. Managers that\n * react to canvas events should skip mutating history during this window. */\n isRestoring(): boolean {\n return this.paused;\n }\n\n pause(): void {\n this.paused = true;\n }\n\n resume(): void {\n this.paused = false;\n }\n\n canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n this.emitChanged();\n }\n\n private cancelPending(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n dispose(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n }\n\n private emitChanged(): void {\n this.events.emit('history:changed', {\n canUndo: this.canUndo(),\n canRedo: this.canRedo(),\n });\n }\n}\n","import type { Canvas, FabricObject, TPointerEvent } from 'fabric';\nimport type { EventEmitter } from './events';\nimport type { EditorEvents } from './types';\n\ninterface SnapGuide {\n /** Vertical guide at this x (canvas px), or undefined for a horizontal one. */\n x?: number;\n y?: number;\n}\n\nconst STROKE = '#22c55e';\n\n/**\n * Snaps a dragged object's edges and centre to the canvas edges/centre and to\n * other objects, drawing transient guide lines while it does. Switchable via\n * {@link setEnabled}; inert (zero overhead beyond a flag check) when disabled.\n */\nexport class SnapManager {\n private enabled = false;\n private threshold = 8;\n private guides: SnapGuide[] = [];\n\n private readonly onMoving: (e: { target?: FabricObject; e: TPointerEvent }) => void;\n private readonly onAfterRender: () => void;\n private readonly onModified: () => void;\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {\n this.onMoving = (e) => this.handleMoving(e.target);\n this.onAfterRender = () => this.drawGuides();\n this.onModified = () => this.clearGuides();\n\n this.canvas.on('object:moving', this.onMoving);\n this.canvas.on('after:render', this.onAfterRender);\n this.canvas.on('mouse:up', this.onModified);\n this.canvas.on('selection:cleared', this.onModified);\n }\n\n setEnabled(enabled: boolean): void {\n if (this.enabled === enabled) return;\n this.enabled = enabled;\n if (!enabled) this.clearGuides();\n this.events.emit('snap:changed', { enabled });\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n setThreshold(px: number): void {\n this.threshold = Math.max(0, px);\n }\n\n dispose(): void {\n this.canvas.off('object:moving', this.onMoving);\n this.canvas.off('after:render', this.onAfterRender);\n this.canvas.off('mouse:up', this.onModified);\n this.canvas.off('selection:cleared', this.onModified);\n this.guides = [];\n }\n\n private handleMoving(target?: FabricObject): void {\n if (!this.enabled || !target) return;\n\n target.setCoords();\n const r = target.getBoundingRect();\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n // Candidate lines for both axes, collected in a single pass over the objects.\n const { xs, ys } = collectCandidates(this.canvas, target, cw, ch);\n\n this.guides = [];\n\n const dx = bestSnap([r.left, r.left + r.width / 2, r.left + r.width], xs, this.threshold);\n if (dx !== null) {\n target.set('left', (target.left ?? 0) + dx.delta);\n this.guides.push({ x: dx.line });\n }\n\n const dy = bestSnap([r.top, r.top + r.height / 2, r.top + r.height], ys, this.threshold);\n if (dy !== null) {\n target.set('top', (target.top ?? 0) + dy.delta);\n this.guides.push({ y: dy.line });\n }\n\n target.setCoords();\n }\n\n private drawGuides(): void {\n if (!this.enabled || this.guides.length === 0) return;\n const ctx = this.canvas.contextTop;\n if (!ctx) return;\n\n const retina = this.canvas.getRetinaScaling();\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n ctx.save();\n ctx.setTransform(retina, 0, 0, retina, 0, 0);\n ctx.lineWidth = 1;\n ctx.strokeStyle = STROKE;\n ctx.setLineDash([4, 4]);\n ctx.beginPath();\n for (const g of this.guides) {\n if (g.x !== undefined) {\n ctx.moveTo(g.x, 0);\n ctx.lineTo(g.x, ch);\n }\n if (g.y !== undefined) {\n ctx.moveTo(0, g.y);\n ctx.lineTo(cw, g.y);\n }\n }\n ctx.stroke();\n ctx.restore();\n }\n\n private clearGuides(): void {\n if (this.guides.length === 0) return;\n this.guides = [];\n this.canvas.clearContext(this.canvas.contextTop);\n }\n}\n\n/**\n * Snap candidate lines (edges + centre) for the canvas and every other object,\n * for both axes, computed in one pass so each object's bounds are read once.\n */\nfunction collectCandidates(\n canvas: Canvas,\n moving: FabricObject,\n cw: number,\n ch: number,\n): { xs: number[]; ys: number[] } {\n const xs = [0, cw / 2, cw];\n const ys = [0, ch / 2, ch];\n for (const obj of canvas.getObjects()) {\n if (obj === moving || !obj.visible) continue;\n obj.setCoords();\n const r = obj.getBoundingRect();\n xs.push(r.left, r.left + r.width / 2, r.left + r.width);\n ys.push(r.top, r.top + r.height / 2, r.top + r.height);\n }\n return { xs, ys };\n}\n\n/**\n * Find the closest (edge, candidate) pair within threshold. Returns the delta\n * to apply and the guide line to draw, or null if nothing is close enough.\n */\nfunction bestSnap(\n edges: number[],\n candidates: number[],\n threshold: number,\n): { delta: number; line: number } | null {\n let best: { delta: number; line: number; dist: number } | null = null;\n for (const edge of edges) {\n for (const c of candidates) {\n const dist = Math.abs(edge - c);\n if (dist <= threshold && (best === null || dist < best.dist)) {\n best = { delta: c - edge, line: c, dist };\n }\n }\n }\n return best ? { delta: best.delta, line: best.line } : null;\n}\n\nexport { bestSnap as _bestSnap };\n","import { Rect } from 'fabric';\nimport type { Canvas, FabricImage } from 'fabric';\nimport type { Layer } from './layer';\nimport type { HistoryManager } from './history';\nimport type { EventEmitter } from './events';\nimport type { EditorEvents } from './types';\nimport { clamp } from './utils/clamp';\n\ninterface CropSession {\n layerId: string;\n image: FabricImage;\n rect: Rect;\n prevSelectable: boolean;\n prevEvented: boolean;\n /** Angle before crop normalised it to 0; restored if the crop is cancelled. */\n prevAngle: number;\n}\n\n/**\n * Interactive crop for image layers. {@link start} drops a draggable/resizable\n * rectangle over the image; {@link apply} converts it into fabric crop props\n * (cropX/cropY/width/height in element pixels) and keeps the displayed size in\n * sync with the rectangle.\n *\n * Rotation is unsupported: an angled image is normalised to 0° on start.\n */\nexport class CropController {\n private session: CropSession | null = null;\n private readonly onSelectionCleared: () => void;\n\n constructor(\n private canvas: Canvas,\n private history: HistoryManager,\n private events: EventEmitter<EditorEvents>,\n ) {\n this.onSelectionCleared = () => {\n // Clicking away from the crop rect abandons the crop.\n if (this.session) this.cancel();\n };\n this.canvas.on('selection:cleared', this.onSelectionCleared);\n }\n\n isActive(): boolean {\n return this.session !== null;\n }\n\n activeLayerId(): string | null {\n return this.session?.layerId ?? null;\n }\n\n start(layer: Layer): void {\n if (layer.type !== 'image') return;\n if (this.session) this.cancel();\n\n const image = layer.fabricObject as FabricImage;\n const prevAngle = image.angle ?? 0;\n if (prevAngle) image.rotate(0);\n image.setCoords();\n const b = image.getBoundingRect();\n\n const rect = new Rect({\n left: b.left,\n top: b.top,\n width: b.width,\n height: b.height,\n originX: 'left',\n originY: 'top',\n fill: 'rgba(34,197,94,0.12)',\n stroke: STROKE,\n strokeWidth: 1,\n strokeDashArray: [4, 4],\n strokeUniform: true,\n lockRotation: true,\n cornerColor: STROKE,\n transparentCorners: false,\n });\n rect.setControlsVisibility({ mtr: false });\n\n const prevSelectable = image.selectable ?? true;\n const prevEvented = image.evented ?? true;\n image.selectable = false;\n image.evented = false;\n\n this.canvas.add(rect);\n this.canvas.setActiveObject(rect);\n this.canvas.requestRenderAll();\n\n this.session = { layerId: layer.id, image, rect, prevSelectable, prevEvented, prevAngle };\n this.events.emit('crop:changed', { active: true, layerId: layer.id });\n }\n\n apply(): void {\n const s = this.session;\n if (!s) return;\n const { image, rect } = s;\n\n rect.setCoords();\n image.setCoords();\n const scaleX = image.scaleX ?? 1;\n const scaleY = image.scaleY ?? 1;\n const imgLeft = image.left ?? 0;\n const imgTop = image.top ?? 0;\n\n const el = image.getElement() as HTMLImageElement;\n const naturalW = el.naturalWidth || image.width || 0;\n const naturalH = el.naturalHeight || image.height || 0;\n\n // Map the rect (canvas px) into the image's element-pixel space.\n let cropX = (image.cropX ?? 0) + (rect.left! - imgLeft) / scaleX;\n let cropY = (image.cropY ?? 0) + (rect.top! - imgTop) / scaleY;\n let cropW = rect.getScaledWidth() / scaleX;\n let cropH = rect.getScaledHeight() / scaleY;\n\n // Keep the crop window inside the source element.\n cropX = clamp(cropX, 0, Math.max(0, naturalW - 1));\n cropY = clamp(cropY, 0, Math.max(0, naturalH - 1));\n cropW = clamp(cropW, 1, naturalW - cropX);\n cropH = clamp(cropH, 1, naturalH - cropY);\n\n image.set({\n cropX,\n cropY,\n width: cropW,\n height: cropH,\n // Anchor the displayed result where the rect sits.\n left: imgLeft + (cropX - (image.cropX ?? 0)) * scaleX,\n top: imgTop + (cropY - (image.cropY ?? 0)) * scaleY,\n });\n image.setCoords();\n\n this.finish();\n this.history.save();\n }\n\n cancel(): void {\n const s = this.session;\n if (!s) return;\n // Cancelling discards the crop, so restore the rotation start() zeroed out.\n if (s.prevAngle) {\n s.image.rotate(s.prevAngle);\n s.image.setCoords();\n }\n this.finish();\n }\n\n dispose(): void {\n this.cancel();\n this.canvas.off('selection:cleared', this.onSelectionCleared);\n }\n\n private finish(): void {\n const s = this.session;\n if (!s) return;\n // Null the session first so the selection:cleared fired by removing the\n // rect / re-selecting does not re-enter cancel().\n this.session = null;\n\n this.canvas.remove(s.rect);\n s.image.selectable = s.prevSelectable;\n s.image.evented = s.prevEvented;\n this.canvas.setActiveObject(s.image);\n this.canvas.requestRenderAll();\n\n this.events.emit('crop:changed', { active: false, layerId: null });\n }\n}\n\nconst STROKE = '#22c55e';\n","export function clamp(v: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, v));\n}\n\nexport function round2(v: number): number {\n return Math.round(v * 100) / 100;\n}\n","import { FabricImage, util } from 'fabric';\nimport type { Canvas, FabricObject } from 'fabric';\nimport type { Layer, LayerManager } from './layer';\nimport type { HistoryManager } from './history';\nimport type { PatternConfig } from './types';\n\n/**\n * Upper bound on tiles per axis. Floors the effective tile size so an extreme\n * `scale` (or a tiny source) can't make the draw loop cover the diagonal with a\n * runaway number of tiles.\n */\nconst MAX_TILES_PER_AXIS = 200;\n\n/**\n * Renders a single image layer as a repeating pattern that fills the canvas.\n *\n * The tiled result is rasterised onto an offscreen canvas and swapped in as the\n * layer's image source, so it survives export, serialization and undo/redo with\n * no extra machinery. The original source + transform are kept in `layer.meta`\n * so the pattern can be turned back off.\n */\nexport class PatternManager {\n // Per-layer task chain. apply()/disable() both await an async setSrc on the\n // same fabric image; running two concurrently lets their setSrc resolutions\n // interleave (wrong image installed, original lost). Serialising per layer\n // guarantees the last-requested operation wins and state stays consistent.\n private chains = new Map<string, Promise<unknown>>();\n\n constructor(\n private canvas: Canvas,\n private layers: LayerManager,\n private history: HistoryManager,\n ) {}\n\n isPattern(layerId: string): boolean {\n return !!this.layers.get(layerId)?.meta.pattern;\n }\n\n getConfig(layerId: string): PatternConfig | null {\n return this.layers.get(layerId)?.meta.pattern?.config ?? null;\n }\n\n /** Turn a plain image layer into a pattern, or update an existing one. */\n apply(layerId: string, config: PatternConfig): Promise<void> {\n return this.enqueue(layerId, async () => {\n const layer = this.layers.get(layerId);\n if (!layer || layer.type !== 'image') return;\n const image = layer.fabricObject as FabricImage;\n\n const firstEnable = !layer.meta.pattern;\n // Check the field directly (not `firstEnable`) so TS narrows the else.\n if (!layer.meta.pattern) {\n // Captured at run time (not enqueue time) so a queued apply always reads\n // the genuine pre-pattern image, never a half-applied one.\n const clip = image.clipPath as FabricObject | undefined;\n layer.meta.pattern = {\n config,\n originalSrc: elementToDataURL(image) ?? image.getSrc(),\n originalClip: clip ? (clip.toObject() as Record<string, unknown>) : null,\n original: {\n left: image.left ?? 0,\n top: image.top ?? 0,\n scaleX: image.scaleX ?? 1,\n scaleY: image.scaleY ?? 1,\n width: image.width ?? 0,\n height: image.height ?? 0,\n angle: image.angle ?? 0,\n cropX: image.cropX ?? 0,\n cropY: image.cropY ?? 0,\n },\n };\n } else {\n layer.meta.pattern.config = config;\n }\n\n try {\n await this.renderLayer(layer);\n } catch (err) {\n // Roll back a freshly-captured state so a failed first render doesn't\n // leave the layer flagged as a pattern with the original still showing.\n if (firstEnable) delete layer.meta.pattern;\n throw err;\n }\n this.history.save();\n });\n }\n\n /** Restore the original image and drop the pattern. */\n disable(layerId: string): Promise<void> {\n return this.enqueue(layerId, async () => {\n const layer = this.layers.get(layerId);\n const state = layer?.meta.pattern;\n if (!layer || !state) return;\n const image = layer.fabricObject as FabricImage;\n\n await image.setSrc(state.originalSrc);\n // setSrc resets width/height to the full element size, so restore the\n // captured crop window too — otherwise an image that was cropped before\n // being turned into a pattern loses its crop on disable.\n image.set({\n left: state.original.left,\n top: state.original.top,\n scaleX: state.original.scaleX,\n scaleY: state.original.scaleY,\n width: state.original.width,\n height: state.original.height,\n cropX: state.original.cropX,\n cropY: state.original.cropY,\n angle: state.original.angle,\n });\n // Re-apply the clipPath the image had before it became a pattern (stripped\n // in renderLayer so the pattern could fill the whole print area).\n image.clipPath = state.originalClip\n ? ((await util.enlivenObjects([state.originalClip]))[0] as FabricObject)\n : undefined;\n image.setCoords();\n delete layer.meta.pattern;\n this.canvas.requestRenderAll();\n this.history.save();\n });\n }\n\n /** Run `task` after any in-flight work for this layer, regardless of outcome. */\n private enqueue(layerId: string, task: () => Promise<void>): Promise<void> {\n const prev = this.chains.get(layerId) ?? Promise.resolve();\n const next = prev.then(task, task);\n // Keep the chain alive on rejection so later ops still run.\n this.chains.set(\n layerId,\n next.catch(() => undefined),\n );\n return next;\n }\n\n private async renderLayer(layer: Layer): Promise<void> {\n const state = layer.meta.pattern;\n if (!state) return;\n const image = layer.fabricObject as FabricImage;\n\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n\n // Base tile = source's on-canvas size × the user's scale. A guard floors the\n // tile so an extreme scale can't spawn a pathological tile count (the draw\n // loop is O(tiles); computeTilePositions covers the diagonal).\n const scale = Math.max(1, state.config.scale ?? 100) / 100;\n const diag = Math.sqrt(cw * cw + ch * ch);\n const minTile = Math.max(2, diag / MAX_TILES_PER_AXIS);\n const tileW = Math.max(minTile, state.original.width * state.original.scaleX * scale);\n const tileH = Math.max(minTile, state.original.height * state.original.scaleY * scale);\n\n const dataUrl = await buildPatternDataURL(\n state.originalSrc,\n state.config,\n cw,\n ch,\n tileW,\n tileH,\n );\n\n await image.setSrc(dataUrl);\n // The tiled source is exactly cw×ch, so set the object to cover the whole\n // canvas 1:1. `width`/`height` are set explicitly (not just via setSrc) and\n // any pre-pattern clipPath is stripped so a leftover crop sized to the\n // original image box can't confine the pattern to that box — the pattern\n // must fill the entire print area. cropX/cropY are reset for the same reason.\n image.set({\n left: 0,\n top: 0,\n scaleX: 1,\n scaleY: 1,\n width: cw,\n height: ch,\n cropX: 0,\n cropY: 0,\n angle: 0,\n });\n image.clipPath = undefined;\n image.setCoords();\n this.canvas.requestRenderAll();\n }\n}\n\n/** Snapshot an already-loaded image element to a PNG data URL. */\nfunction elementToDataURL(image: FabricImage): string | null {\n try {\n const el = image.getElement() as HTMLImageElement | HTMLCanvasElement;\n const w = (el as HTMLImageElement).naturalWidth || el.width;\n const h = (el as HTMLImageElement).naturalHeight || el.height;\n if (!w || !h) return null;\n const off = document.createElement('canvas');\n off.width = w;\n off.height = h;\n const ctx = off.getContext('2d');\n if (!ctx) return null;\n ctx.drawImage(el, 0, 0);\n return off.toDataURL('image/png');\n } catch {\n // Tainted canvas (remote image without CORS) — fall back to the live src.\n return null;\n }\n}\n\n// Decoding a source (often a large data URL) is the dominant cost of a regen,\n// so cache decoded images. Bounded with simple LRU eviction so a stream of\n// distinct sources can't grow the cache without limit.\nconst IMAGE_CACHE_MAX = 16;\nconst imageCache = new Map<string, Promise<HTMLImageElement>>();\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n const cached = imageCache.get(src);\n if (cached) {\n // Refresh recency (Map preserves insertion order → re-insert = most recent).\n imageCache.delete(src);\n imageCache.set(src, cached);\n return cached;\n }\n\n const promise = decodeImage(src);\n // Don't poison the cache with a failed decode.\n promise.catch(() => {\n if (imageCache.get(src) === promise) imageCache.delete(src);\n });\n\n imageCache.set(src, promise);\n if (imageCache.size > IMAGE_CACHE_MAX) {\n const oldest = imageCache.keys().next().value;\n if (oldest !== undefined) imageCache.delete(oldest);\n }\n return promise;\n}\n\nfunction decodeImage(src: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error(`Failed to load pattern source: ${src}`));\n img.src = src;\n });\n}\n\n/** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */\nexport function clearPatternImageCache(): void {\n imageCache.clear();\n}\n\n/**\n * Draw the source image repeatedly across a `targetW × targetH` offscreen\n * canvas and return the result as a PNG data URL.\n *\n * Exported for unit testing of the tile geometry.\n */\nexport async function buildPatternDataURL(\n src: string,\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): Promise<string> {\n const img = await loadImage(src);\n const off = document.createElement('canvas');\n off.width = Math.max(1, Math.round(targetW));\n off.height = Math.max(1, Math.round(targetH));\n const ctx = off.getContext('2d');\n if (!ctx) return off.toDataURL('image/png');\n\n drawTiles(ctx, img, config, targetW, targetH, baseW, baseH);\n return off.toDataURL('image/png');\n}\n\n/** Placement of one tile, relative to the (already rotated) pattern centre. */\nexport interface TilePlacement {\n /** Tile-centre offset from the pattern centre, in px. */\n x: number;\n y: number;\n /** Per-tile rotation, in radians. */\n rotation: number;\n}\n\n/**\n * Pure tile geometry: where each tile centre lands and how much it is rotated,\n * for a config + target/base size. Covers the rotated bounding diagonal so\n * corners stay filled at any pattern angle. Extracted for unit testing.\n */\nexport function computeTilePositions(\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): TilePlacement[] {\n const diag = Math.sqrt(targetW * targetW + targetH * targetH);\n // Floor the spacing-adjusted tile too (not just the base in renderLayer): a\n // negative spacing from a hand-crafted config would otherwise collapse the\n // loop tile to ~1px and spawn ~diag² draw calls (client-side DoS).\n const minTile = Math.max(1, diag / MAX_TILES_PER_AXIS);\n const tileW = Math.max(minTile, baseW * (1 + config.horizontalSpacing / 100));\n const tileH = Math.max(minTile, baseH * (1 + config.verticalSpacing / 100));\n\n const cols = Math.ceil(diag / tileW) + 2;\n const rows = Math.ceil(diag / tileH) + 2;\n const halfCols = Math.ceil(cols / 2);\n const halfRows = Math.ceil(rows / 2);\n\n const placements: TilePlacement[] = [];\n for (let j = -halfRows; j <= halfRows; j++) {\n for (let i = -halfCols; i <= halfCols; i++) {\n let x = i * tileW;\n let y = j * tileH;\n\n if (config.mode === 'brick-horizontal' && mod2(j) === 1) {\n x += tileW * (config.horizontalOffset / 100);\n } else if (config.mode === 'brick-vertical' && mod2(i) === 1) {\n y += tileH * (config.horizontalOffset / 100);\n }\n\n const rotation = ((i * config.rotationStepH + j * config.rotationStepV) * Math.PI) / 180;\n placements.push({ x, y, rotation });\n }\n }\n return placements;\n}\n\n/**\n * Draw the tiled pattern onto a 2D context. The geometry lives in\n * {@link computeTilePositions}; this just plays it out as canvas calls.\n */\nexport function drawTiles(\n ctx: CanvasRenderingProtocol,\n img: CanvasImageSource,\n config: PatternConfig,\n targetW: number,\n targetH: number,\n baseW: number,\n baseH: number,\n): void {\n ctx.save();\n // Rotate the whole pattern around the canvas centre.\n ctx.translate(targetW / 2, targetH / 2);\n ctx.rotate((config.angle * Math.PI) / 180);\n\n for (const tile of computeTilePositions(config, targetW, targetH, baseW, baseH)) {\n ctx.save();\n ctx.translate(tile.x, tile.y);\n ctx.rotate(tile.rotation);\n ctx.drawImage(img, -baseW / 2, -baseH / 2, baseW, baseH);\n ctx.restore();\n }\n\n ctx.restore();\n}\n\n/** Non-negative parity (so negative row indices brick-shift consistently). */\nfunction mod2(n: number): number {\n return ((n % 2) + 2) % 2;\n}\n\n/** Minimal 2D-context surface used by {@link drawTiles}; lets tests inject a spy. */\nexport interface CanvasRenderingProtocol {\n save(): void;\n restore(): void;\n translate(x: number, y: number): void;\n rotate(angle: number): void;\n drawImage(image: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;\n}\n","import type { Unit } from '../types';\n\nconst MM_PER_INCH = 25.4;\n\nexport class UnitConverter {\n constructor(\n private unit: Unit = 'px',\n private dpi: number = 72,\n ) {}\n\n setUnit(unit: Unit): void {\n this.unit = unit;\n }\n\n setDpi(dpi: number): void {\n this.dpi = dpi;\n }\n\n getUnit(): Unit {\n return this.unit;\n }\n\n getDpi(): number {\n return this.dpi;\n }\n\n toPixels(value: number, fromUnit?: Unit): number {\n const u = fromUnit ?? this.unit;\n switch (u) {\n case 'px':\n return value;\n case 'in':\n return value * this.dpi;\n case 'mm':\n return (value / MM_PER_INCH) * this.dpi;\n }\n }\n\n fromPixels(px: number, toUnit?: Unit): number {\n const u = toUnit ?? this.unit;\n switch (u) {\n case 'px':\n return px;\n case 'in':\n return px / this.dpi;\n case 'mm':\n return (px / this.dpi) * MM_PER_INCH;\n }\n }\n}\n","import { util } from 'fabric';\nimport type { FabricObject } from 'fabric';\nimport type { EditorState, SerializedLayer } from './types';\nimport type { CanvasEditor } from './editor';\nimport { Layer } from './layer';\n\nconst VERSION = '1.0.0';\n\nexport function serializeEditor(editor: CanvasEditor): EditorState {\n return {\n version: VERSION,\n canvas: {\n width: editor.canvas.getWidth(),\n height: editor.canvas.getHeight(),\n },\n layers: editor.layers.getAll().map((layer) => layer.serialize()),\n // The configured design background, not the live canvas value (which is\n // forced transparent while a mockup preview is active).\n background: editor.getDesignBackground(),\n mockup: editor.getMockup(),\n };\n}\n\nexport async function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void> {\n editor.layers.clear();\n editor.canvas.setDimensions({ width: state.canvas.width, height: state.canvas.height });\n\n // setBackground records the design background; history is paused during\n // restore so this does not push a spurious entry. setMockup (below) then\n // applies the correct live canvas background.\n if (state.background) {\n editor.setBackground(state.background);\n }\n\n // mockup is preview-only (rendered behind the canvas), so it is restored\n // separately from the design layers. `undefined` means \"leave as-is\".\n if (state.mockup !== undefined) {\n editor.setMockup(state.mockup);\n }\n\n for (const serializedLayer of state.layers) {\n await restoreLayer(editor, serializedLayer);\n }\n\n editor.canvas.requestRenderAll();\n}\n\nasync function restoreLayer(editor: CanvasEditor, serialized: SerializedLayer): Promise<Layer> {\n const objects = await util.enlivenObjects([serialized.fabricObject]);\n const fabricObject = objects[0] as FabricObject;\n\n const layer = editor.layers.add(serialized.type, fabricObject, serialized.name, serialized.id);\n\n if (serialized.meta) {\n layer.meta = serialized.meta;\n }\n\n if (!serialized.visible) {\n editor.layers.setVisibility(layer.id, false);\n }\n if (serialized.locked) {\n editor.layers.setLocked(layer.id, true);\n }\n if (serialized.opacity !== 1) {\n editor.layers.setOpacity(layer.id, serialized.opacity);\n }\n\n return layer;\n}\n","import type { Canvas, ImageFormat } from 'fabric';\n\nexport interface PngExportOptions {\n multiplier?: number;\n format?: ImageFormat;\n quality?: number;\n}\n\nexport async function exportPNG(\n canvas: Canvas,\n options: PngExportOptions = {},\n): Promise<Blob> {\n const { multiplier = 1, format = 'png' as ImageFormat, quality = 1 } = options;\n const dataUrl = canvas.toDataURL({\n format,\n multiplier,\n quality,\n });\n const response = await fetch(dataUrl);\n return response.blob();\n}\n\nexport function exportSVG(canvas: Canvas): string {\n return canvas.toSVG();\n}\n\nexport function exportDataURL(\n canvas: Canvas,\n format: ImageFormat = 'png',\n multiplier = 1,\n): string {\n return canvas.toDataURL({ format, multiplier });\n}\n","import type { FabricObject } from 'fabric';\n\n// ─── Units ───────────────────────────────────────────────\n\nexport type Unit = 'px' | 'mm' | 'in';\n\n// ─── Layers ──────────────────────────────────────────────\n\nexport type LayerType = 'image' | 'text' | 'shape' | 'template' | 'group';\n\nexport interface LayerData {\n id: string;\n type: LayerType;\n name: string;\n visible: boolean;\n locked: boolean;\n opacity: number;\n /** Non-fabric metadata persisted with the layer (pattern config, etc). */\n meta?: LayerMeta;\n}\n\nexport interface LayerMeta {\n pattern?: PatternState;\n [key: string]: unknown;\n}\n\nexport interface SerializedLayer extends LayerData {\n fabricObject: Record<string, unknown>;\n}\n\n// ─── Editor ──────────────────────────────────────────────\n\nexport interface EditorState {\n version: string;\n canvas: { width: number; height: number };\n layers: SerializedLayer[];\n background?: string;\n mockup?: MockupConfig | null;\n}\n\nexport interface EditorConfig {\n width: number;\n height: number;\n backgroundColor?: string;\n fileAdapter?: FileAdapter;\n imageProvider?: ImageProvider;\n preserveObjectStacking?: boolean;\n unit?: Unit;\n dpi?: number;\n}\n\n// ─── Adapters ────────────────────────────────────────────\n\nexport type ExportFormat = 'png' | 'svg' | 'json';\n\nexport interface FileAdapter {\n save(data: Blob | string, filename: string, format: ExportFormat): Promise<string>;\n}\n\nexport interface ImageProviderResult {\n url: string;\n width: number;\n height: number;\n}\n\nexport interface ImageProvider {\n upload(file: File): Promise<ImageProviderResult>;\n browse?(): Promise<ImageProviderResult | null>;\n}\n\n// ─── Plugins ─────────────────────────────────────────────\n\nexport interface ShapePlugin {\n name: string;\n icon: string;\n category?: string;\n create(options?: Record<string, unknown>): FabricObject;\n}\n\nexport interface TemplateParameter {\n key: string;\n label: string;\n type: 'text' | 'color' | 'number' | 'font';\n default: string | number;\n}\n\nexport interface TemplateDefinition {\n id: string;\n name: string;\n category: string;\n svg: string;\n parameters: TemplateParameter[];\n preview?: string;\n}\n\n// ─── Printify ────────────────────────────────────────────\n\nexport interface PrintifyPositioning {\n x: number;\n y: number;\n scale: number;\n angle: number;\n}\n\n// ─── Patterns ────────────────────────────────────────────\n\nexport type TileMode = 'grid' | 'brick-horizontal' | 'brick-vertical';\n\nexport interface PatternConfig {\n mode: TileMode;\n /**\n * Tile size as a % of the source image's on-canvas size (100 = natural).\n * Smaller values pack more repeats across the print area; larger values make\n * fewer, bigger tiles. Independent of how the source layer was placed.\n */\n scale: number;\n /** Extra horizontal gap between tiles, as % of tile width. */\n horizontalSpacing: number;\n /** Extra vertical gap between tiles, as % of tile height. */\n verticalSpacing: number;\n /** Rotation of the whole pattern, in degrees. */\n angle: number;\n /** Brick row/column shift, as % of tile size. */\n horizontalOffset: number;\n /** Added rotation per horizontal step, in degrees. */\n rotationStepH: number;\n /** Added rotation per vertical step, in degrees. */\n rotationStepV: number;\n}\n\n/** Persisted on a layer's meta while it is rendered as a repeating pattern. */\nexport interface PatternState {\n config: PatternConfig;\n /** Original (pre-pattern) image source, as a durable data URL when possible. */\n originalSrc: string;\n /**\n * Serialized `clipPath` the image carried before it became a pattern, or null\n * if it had none. A pattern must fill the whole print area, so any pre-pattern\n * clip (e.g. a template crop sized to the original box) is stripped while the\n * pattern is on and re-applied when it is turned off. Persisted so it survives\n * serialization / reload / undo.\n */\n originalClip?: Record<string, unknown> | null;\n /** Original fabric transform, restored when the pattern is turned off. */\n original: {\n left: number;\n top: number;\n scaleX: number;\n scaleY: number;\n width: number;\n height: number;\n angle: number;\n cropX: number;\n cropY: number;\n };\n}\n\nexport const DEFAULT_PATTERN_CONFIG: PatternConfig = {\n mode: 'grid',\n scale: 100,\n horizontalSpacing: 0,\n verticalSpacing: 0,\n angle: 0,\n horizontalOffset: 0,\n rotationStepH: 0,\n rotationStepV: 0,\n};\n\n// ─── Mockup ──────────────────────────────────────────────\n\nexport interface MockupPrintArea {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\nexport interface MockupConfig {\n /** Garment/product image rendered behind the design (preview only). */\n image: string;\n /** Print-area rectangle, in canvas pixels. Drawn as a guide. */\n printArea?: MockupPrintArea;\n}\n\n// ─── Events ──────────────────────────────────────────────\n\nexport interface EditorEvents {\n 'layer:added': { layer: LayerData };\n 'layer:removed': { layerId: string };\n 'layer:selected': { layerId: string | null };\n 'layer:modified': { layerId: string };\n 'layer:reordered': { layerIds: string[] };\n 'layers:changed': { layers: LayerData[] };\n 'selection:changed': { selected: string[] };\n 'history:changed': { canUndo: boolean; canRedo: boolean };\n 'zoom:changed': { zoom: number };\n 'snap:changed': { enabled: boolean };\n 'crop:changed': { active: boolean; layerId: string | null };\n 'mockup:changed': { mockup: MockupConfig | null };\n 'canvas:modified': Record<string, never>;\n 'export:start': { format: string };\n 'export:complete': { format: string; url?: string };\n error: { message: string; error?: unknown };\n}\n"],"mappings":";AAAA,SAAS,QAAQ,eAAAA,cAAa,eAAe;;;ACGtC,IAAM,eAAN,MAAiE;AAAA,EAC9D,YAAY,oBAAI,IAA+C;AAAA,EAEvE,GAA4B,OAAU,SAAyC;AAC7E,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACrC;AACA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,OAAgC;AAAA,EACjE;AAAA,EAEA,IAA6B,OAAU,SAAyC;AAC9E,SAAK,UAAU,IAAI,KAAK,GAAG,OAAO,OAAgC;AAAA,EACpE;AAAA,EAEA,KAA8B,OAAU,SAAyC;AAC/E,UAAM,UAAoC,CAAC,SAAS;AAClD,WAAK,IAAI,OAAO,OAAO;AACvB,cAAQ,IAAI;AAAA,IACd;AACA,SAAK,GAAG,OAAO,OAAO;AAAA,EACxB;AAAA,EAEA,KAA8B,OAAU,MAAwB;AAC9D,UAAM,WAAW,KAAK,UAAU,IAAI,KAAK;AACzC,QAAI,CAAC,SAAU;AACf,eAAW,WAAW,UAAU;AAC9B,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAAA,EAEA,mBAAmB,OAA6B;AAC9C,QAAI,OAAO;AACT,WAAK,UAAU,OAAO,KAAK;AAAA,IAC7B,OAAO;AACL,WAAK,UAAU,MAAM;AAAA,IACvB;AAAA,EACF;AACF;;;ACxCA,SAAS,cAAc;AAEhB,IAAM,aAAa,MAAc,OAAO,EAAE;;;ACI1C,IAAM,QAAN,MAAY;AAAA,EACR;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACS;AAAA,EAET,YAAY,MAAiB,cAA4B,MAAe,IAAa;AACnF,SAAK,KAAK,MAAM,WAAW;AAC3B,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ,GAAG,IAAI,IAAI,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC;AAClD,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,UAAU;AACf,SAAK,OAAO,CAAC;AACb,SAAK,eAAe;AAEpB,IAAC,KAAK,aAAqD,WAAW,KAAK;AAAA,EAC7E;AAAA,EAEQ,UAAmB;AACzB,WAAO,OAAO,KAAK,KAAK,IAAI,EAAE,SAAS;AAAA,EACzC;AAAA,EAEA,SAAoB;AAClB,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,MACd,GAAI,KAAK,QAAQ,IAAI,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,YAA6B;AAC3B,WAAO;AAAA,MACL,GAAG,KAAK,OAAO;AAAA,MACf,cAAc,KAAK,aAAa,SAAS;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EAGxB,YACU,QACA,QACR;AAFQ;AACA;AAAA,EACP;AAAA,EAFO;AAAA,EACA;AAAA,EAJF,SAAkB,CAAC;AAAA,EAO3B,IAAI,MAAiB,cAA4B,MAAe,IAAoB;AAClF,UAAM,QAAQ,IAAI,MAAM,MAAM,cAAc,MAAM,EAAE;AACpD,SAAK,OAAO,KAAK,KAAK;AACtB,SAAK,OAAO,IAAI,YAAY;AAC5B,SAAK,OAAO,KAAK,eAAe,EAAE,OAAO,MAAM,OAAO,EAAE,CAAC;AACzD,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,IAAqB;AAC1B,UAAM,QAAQ,KAAK,OAAO,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACtD,QAAI,UAAU,GAAI,QAAO;AACzB,UAAM,QAAQ,KAAK,OAAO,KAAK;AAC/B,SAAK,OAAO,OAAO,MAAM,YAAY;AACrC,SAAK,OAAO,OAAO,OAAO,CAAC;AAC3B,SAAK,OAAO,KAAK,iBAAiB,EAAE,SAAS,GAAG,CAAC;AACjD,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,IAAY,UAA2B;AAC7C,UAAM,WAAW,KAAK,OAAO,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACzD,QAAI,aAAa,GAAI,QAAO;AAC5B,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,OAAO,SAAS,GAAG,QAAQ,CAAC;AACtE,UAAM,CAAC,KAAK,IAAI,KAAK,OAAO,OAAO,UAAU,CAAC;AAC9C,SAAK,OAAO,OAAO,SAAS,GAAG,KAAK;AAEpC,SAAK,OAAO,QAAQ,CAAC,GAAG,MAAM;AAC5B,WAAK,OAAO,aAAa,EAAE,cAAc,CAAC;AAAA,IAC5C,CAAC;AACD,SAAK,OAAO,KAAK,mBAAmB,EAAE,UAAU,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC;AAC9E,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,IAAyB;AAC9B,QAAI,OAAO,MAAM;AACf,WAAK,OAAO,oBAAoB;AAAA,IAClC,OAAO;AACL,YAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,UAAI,OAAO;AACT,aAAK,OAAO,gBAAgB,MAAM,YAAY;AAAA,MAChD;AAAA,IACF;AACA,SAAK,OAAO,iBAAiB;AAC7B,SAAK,OAAO,KAAK,kBAAkB,EAAE,SAAS,GAAG,CAAC;AAGlD,UAAM,WAAW,MAAM,KAAK,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAC9C,SAAK,OAAO,KAAK,qBAAqB,EAAE,SAAS,CAAC;AAAA,EACpD;AAAA,EAEA,IAAI,IAA+B;AACjC,WAAO,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,EAC5C;AAAA,EAEA,SAAkB;AAChB,WAAO,CAAC,GAAG,KAAK,MAAM;AAAA,EACxB;AAAA,EAEA,aAAa,KAAsC;AACjD,UAAM,UAAW,IAA6C;AAC9D,QAAI,QAAS,QAAO,KAAK,IAAI,OAAO;AACpC,WAAO,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,iBAAiB,GAAG;AAAA,EACvD;AAAA,EAEA,cAAc,IAAY,SAAwB;AAChD,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,UAAU;AAChB,UAAM,aAAa,UAAU;AAC7B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,UAAU,IAAY,QAAuB;AAC3C,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,SAAS;AACf,UAAM,aAAa,aAAa,CAAC;AACjC,UAAM,aAAa,UAAU,CAAC;AAC9B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,WAAW,IAAY,SAAuB;AAC5C,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,UAAU;AAChB,UAAM,aAAa,UAAU;AAC7B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAQ,IAAY,MAAoB;AACtC,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO;AACb,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAc;AACZ,eAAW,SAAS,KAAK,QAAQ;AAC/B,WAAK,OAAO,OAAO,MAAM,YAAY;AAAA,IACvC;AACA,SAAK,SAAS,CAAC;AACf,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAgB;AACd,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEQ,cAAoB;AAC1B,SAAK,OAAO,KAAK,kBAAkB;AAAA,MACjC,QAAQ,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH;AACF;;;ACjLO,IAAM,iBAAN,MAAqB;AAAA,EAClB,YAAsB,CAAC;AAAA,EACvB,YAAsB,CAAC;AAAA,EACvB;AAAA,EACA,SAAS;AAAA,EACT,gBAAsD;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,MAMT;AACD,SAAK,WAAW,KAAK;AACrB,SAAK,eAAe,KAAK;AACzB,SAAK,SAAS,KAAK;AACnB,SAAK,UAAU,KAAK,WAAW;AAC/B,SAAK,aAAa,KAAK,cAAc;AAAA,EACvC;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,OAAQ;AAEjB,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAAA,IACjC;AAEA,SAAK,gBAAgB,WAAW,MAAM;AACpC,WAAK,cAAc;AAAA,IACrB,GAAG,KAAK,UAAU;AAAA,EACpB;AAAA,EAEA,gBAAsB;AACpB,QAAI,KAAK,OAAQ;AACjB,UAAM,QAAQ,KAAK,SAAS;AAC5B,SAAK,UAAU,KAAK,KAAK;AACzB,QAAI,KAAK,UAAU,SAAS,KAAK,SAAS;AACxC,WAAK,UAAU,MAAM;AAAA,IACvB;AACA,SAAK,YAAY,CAAC;AAClB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,OAAsB;AAG1B,SAAK,cAAc;AACnB,UAAM,QAAQ,KAAK,UAAU,IAAI;AACjC,QAAI,CAAC,MAAO;AAEZ,SAAK,UAAU,KAAK,KAAK,SAAS,CAAC;AACnC,SAAK,SAAS;AACd,UAAM,KAAK,aAAa,KAAK;AAC7B,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,cAAc;AACnB,UAAM,QAAQ,KAAK,UAAU,IAAI;AACjC,QAAI,CAAC,MAAO;AAEZ,SAAK,UAAU,KAAK,KAAK,SAAS,CAAC;AACnC,SAAK,SAAS;AACd,UAAM,KAAK,aAAa,KAAK;AAC7B,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA,EAIA,cAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,SAAe;AACb,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,UAAmB;AACjB,WAAO,KAAK,UAAU,SAAS;AAAA,EACjC;AAAA,EAEA,UAAmB;AACjB,WAAO,KAAK,UAAU,SAAS;AAAA,EACjC;AAAA,EAEA,QAAc;AACZ,SAAK,YAAY,CAAC;AAClB,SAAK,YAAY,CAAC;AAClB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAC/B,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAAA,IACjC;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,SAAK,OAAO,KAAK,mBAAmB;AAAA,MAClC,SAAS,KAAK,QAAQ;AAAA,MACtB,SAAS,KAAK,QAAQ;AAAA,IACxB,CAAC;AAAA,EACH;AACF;;;AClHA,IAAM,SAAS;AAOR,IAAM,cAAN,MAAkB;AAAA,EASvB,YACU,QACA,QACR;AAFQ;AACA;AAER,SAAK,WAAW,CAAC,MAAM,KAAK,aAAa,EAAE,MAAM;AACjD,SAAK,gBAAgB,MAAM,KAAK,WAAW;AAC3C,SAAK,aAAa,MAAM,KAAK,YAAY;AAEzC,SAAK,OAAO,GAAG,iBAAiB,KAAK,QAAQ;AAC7C,SAAK,OAAO,GAAG,gBAAgB,KAAK,aAAa;AACjD,SAAK,OAAO,GAAG,YAAY,KAAK,UAAU;AAC1C,SAAK,OAAO,GAAG,qBAAqB,KAAK,UAAU;AAAA,EACrD;AAAA,EAXU;AAAA,EACA;AAAA,EAVF,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,SAAsB,CAAC;AAAA,EAEd;AAAA,EACA;AAAA,EACA;AAAA,EAgBjB,WAAW,SAAwB;AACjC,QAAI,KAAK,YAAY,QAAS;AAC9B,SAAK,UAAU;AACf,QAAI,CAAC,QAAS,MAAK,YAAY;AAC/B,SAAK,OAAO,KAAK,gBAAgB,EAAE,QAAQ,CAAC;AAAA,EAC9C;AAAA,EAEA,YAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,aAAa,IAAkB;AAC7B,SAAK,YAAY,KAAK,IAAI,GAAG,EAAE;AAAA,EACjC;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO,IAAI,iBAAiB,KAAK,QAAQ;AAC9C,SAAK,OAAO,IAAI,gBAAgB,KAAK,aAAa;AAClD,SAAK,OAAO,IAAI,YAAY,KAAK,UAAU;AAC3C,SAAK,OAAO,IAAI,qBAAqB,KAAK,UAAU;AACpD,SAAK,SAAS,CAAC;AAAA,EACjB;AAAA,EAEQ,aAAa,QAA6B;AAChD,QAAI,CAAC,KAAK,WAAW,CAAC,OAAQ;AAE9B,WAAO,UAAU;AACjB,UAAM,IAAI,OAAO,gBAAgB;AACjC,UAAM,KAAK,KAAK,OAAO,SAAS;AAChC,UAAM,KAAK,KAAK,OAAO,UAAU;AAGjC,UAAM,EAAE,IAAI,GAAG,IAAI,kBAAkB,KAAK,QAAQ,QAAQ,IAAI,EAAE;AAEhE,SAAK,SAAS,CAAC;AAEf,UAAM,KAAK,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,EAAE,OAAO,EAAE,KAAK,GAAG,IAAI,KAAK,SAAS;AACxF,QAAI,OAAO,MAAM;AACf,aAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,GAAG,KAAK;AAChD,WAAK,OAAO,KAAK,EAAE,GAAG,GAAG,KAAK,CAAC;AAAA,IACjC;AAEA,UAAM,KAAK,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,SAAS;AACvF,QAAI,OAAO,MAAM;AACf,aAAO,IAAI,QAAQ,OAAO,OAAO,KAAK,GAAG,KAAK;AAC9C,WAAK,OAAO,KAAK,EAAE,GAAG,GAAG,KAAK,CAAC;AAAA,IACjC;AAEA,WAAO,UAAU;AAAA,EACnB;AAAA,EAEQ,aAAmB;AACzB,QAAI,CAAC,KAAK,WAAW,KAAK,OAAO,WAAW,EAAG;AAC/C,UAAM,MAAM,KAAK,OAAO;AACxB,QAAI,CAAC,IAAK;AAEV,UAAM,SAAS,KAAK,OAAO,iBAAiB;AAC5C,UAAM,KAAK,KAAK,OAAO,SAAS;AAChC,UAAM,KAAK,KAAK,OAAO,UAAU;AAEjC,QAAI,KAAK;AACT,QAAI,aAAa,QAAQ,GAAG,GAAG,QAAQ,GAAG,CAAC;AAC3C,QAAI,YAAY;AAChB,QAAI,cAAc;AAClB,QAAI,YAAY,CAAC,GAAG,CAAC,CAAC;AACtB,QAAI,UAAU;AACd,eAAW,KAAK,KAAK,QAAQ;AAC3B,UAAI,EAAE,MAAM,QAAW;AACrB,YAAI,OAAO,EAAE,GAAG,CAAC;AACjB,YAAI,OAAO,EAAE,GAAG,EAAE;AAAA,MACpB;AACA,UAAI,EAAE,MAAM,QAAW;AACrB,YAAI,OAAO,GAAG,EAAE,CAAC;AACjB,YAAI,OAAO,IAAI,EAAE,CAAC;AAAA,MACpB;AAAA,IACF;AACA,QAAI,OAAO;AACX,QAAI,QAAQ;AAAA,EACd;AAAA,EAEQ,cAAoB;AAC1B,QAAI,KAAK,OAAO,WAAW,EAAG;AAC9B,SAAK,SAAS,CAAC;AACf,SAAK,OAAO,aAAa,KAAK,OAAO,UAAU;AAAA,EACjD;AACF;AAMA,SAAS,kBACP,QACA,QACA,IACA,IACgC;AAChC,QAAM,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE;AACzB,QAAM,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE;AACzB,aAAW,OAAO,OAAO,WAAW,GAAG;AACrC,QAAI,QAAQ,UAAU,CAAC,IAAI,QAAS;AACpC,QAAI,UAAU;AACd,UAAM,IAAI,IAAI,gBAAgB;AAC9B,OAAG,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,EAAE,OAAO,EAAE,KAAK;AACtD,OAAG,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,GAAG,EAAE,MAAM,EAAE,MAAM;AAAA,EACvD;AACA,SAAO,EAAE,IAAI,GAAG;AAClB;AAMA,SAAS,SACP,OACA,YACA,WACwC;AACxC,MAAI,OAA6D;AACjE,aAAW,QAAQ,OAAO;AACxB,eAAW,KAAK,YAAY;AAC1B,YAAM,OAAO,KAAK,IAAI,OAAO,CAAC;AAC9B,UAAI,QAAQ,cAAc,SAAS,QAAQ,OAAO,KAAK,OAAO;AAC5D,eAAO,EAAE,OAAO,IAAI,MAAM,MAAM,GAAG,KAAK;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,EAAE,OAAO,KAAK,OAAO,MAAM,KAAK,KAAK,IAAI;AACzD;;;ACxKA,SAAS,YAAY;;;ACAd,SAAS,MAAM,GAAW,KAAa,KAAqB;AACjE,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC;AACvC;AAEO,SAAS,OAAO,GAAmB;AACxC,SAAO,KAAK,MAAM,IAAI,GAAG,IAAI;AAC/B;;;ADoBO,IAAM,iBAAN,MAAqB;AAAA,EAI1B,YACU,QACA,SACA,QACR;AAHQ;AACA;AACA;AAER,SAAK,qBAAqB,MAAM;AAE9B,UAAI,KAAK,QAAS,MAAK,OAAO;AAAA,IAChC;AACA,SAAK,OAAO,GAAG,qBAAqB,KAAK,kBAAkB;AAAA,EAC7D;AAAA,EATU;AAAA,EACA;AAAA,EACA;AAAA,EANF,UAA8B;AAAA,EACrB;AAAA,EAcjB,WAAoB;AAClB,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA,EAEA,gBAA+B;AAC7B,WAAO,KAAK,SAAS,WAAW;AAAA,EAClC;AAAA,EAEA,MAAM,OAAoB;AACxB,QAAI,MAAM,SAAS,QAAS;AAC5B,QAAI,KAAK,QAAS,MAAK,OAAO;AAE9B,UAAM,QAAQ,MAAM;AACpB,UAAM,YAAY,MAAM,SAAS;AACjC,QAAI,UAAW,OAAM,OAAO,CAAC;AAC7B,UAAM,UAAU;AAChB,UAAM,IAAI,MAAM,gBAAgB;AAEhC,UAAM,OAAO,IAAI,KAAK;AAAA,MACpB,MAAM,EAAE;AAAA,MACR,KAAK,EAAE;AAAA,MACP,OAAO,EAAE;AAAA,MACT,QAAQ,EAAE;AAAA,MACV,SAAS;AAAA,MACT,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQC;AAAA,MACR,aAAa;AAAA,MACb,iBAAiB,CAAC,GAAG,CAAC;AAAA,MACtB,eAAe;AAAA,MACf,cAAc;AAAA,MACd,aAAaA;AAAA,MACb,oBAAoB;AAAA,IACtB,CAAC;AACD,SAAK,sBAAsB,EAAE,KAAK,MAAM,CAAC;AAEzC,UAAM,iBAAiB,MAAM,cAAc;AAC3C,UAAM,cAAc,MAAM,WAAW;AACrC,UAAM,aAAa;AACnB,UAAM,UAAU;AAEhB,SAAK,OAAO,IAAI,IAAI;AACpB,SAAK,OAAO,gBAAgB,IAAI;AAChC,SAAK,OAAO,iBAAiB;AAE7B,SAAK,UAAU,EAAE,SAAS,MAAM,IAAI,OAAO,MAAM,gBAAgB,aAAa,UAAU;AACxF,SAAK,OAAO,KAAK,gBAAgB,EAAE,QAAQ,MAAM,SAAS,MAAM,GAAG,CAAC;AAAA,EACtE;AAAA,EAEA,QAAc;AACZ,UAAM,IAAI,KAAK;AACf,QAAI,CAAC,EAAG;AACR,UAAM,EAAE,OAAO,KAAK,IAAI;AAExB,SAAK,UAAU;AACf,UAAM,UAAU;AAChB,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,UAAU,MAAM,QAAQ;AAC9B,UAAM,SAAS,MAAM,OAAO;AAE5B,UAAM,KAAK,MAAM,WAAW;AAC5B,UAAM,WAAW,GAAG,gBAAgB,MAAM,SAAS;AACnD,UAAM,WAAW,GAAG,iBAAiB,MAAM,UAAU;AAGrD,QAAI,SAAS,MAAM,SAAS,MAAM,KAAK,OAAQ,WAAW;AAC1D,QAAI,SAAS,MAAM,SAAS,MAAM,KAAK,MAAO,UAAU;AACxD,QAAI,QAAQ,KAAK,eAAe,IAAI;AACpC,QAAI,QAAQ,KAAK,gBAAgB,IAAI;AAGrC,YAAQ,MAAM,OAAO,GAAG,KAAK,IAAI,GAAG,WAAW,CAAC,CAAC;AACjD,YAAQ,MAAM,OAAO,GAAG,KAAK,IAAI,GAAG,WAAW,CAAC,CAAC;AACjD,YAAQ,MAAM,OAAO,GAAG,WAAW,KAAK;AACxC,YAAQ,MAAM,OAAO,GAAG,WAAW,KAAK;AAExC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,QAAQ;AAAA;AAAA,MAER,MAAM,WAAW,SAAS,MAAM,SAAS,MAAM;AAAA,MAC/C,KAAK,UAAU,SAAS,MAAM,SAAS,MAAM;AAAA,IAC/C,CAAC;AACD,UAAM,UAAU;AAEhB,SAAK,OAAO;AACZ,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,SAAe;AACb,UAAM,IAAI,KAAK;AACf,QAAI,CAAC,EAAG;AAER,QAAI,EAAE,WAAW;AACf,QAAE,MAAM,OAAO,EAAE,SAAS;AAC1B,QAAE,MAAM,UAAU;AAAA,IACpB;AACA,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO;AACZ,SAAK,OAAO,IAAI,qBAAqB,KAAK,kBAAkB;AAAA,EAC9D;AAAA,EAEQ,SAAe;AACrB,UAAM,IAAI,KAAK;AACf,QAAI,CAAC,EAAG;AAGR,SAAK,UAAU;AAEf,SAAK,OAAO,OAAO,EAAE,IAAI;AACzB,MAAE,MAAM,aAAa,EAAE;AACvB,MAAE,MAAM,UAAU,EAAE;AACpB,SAAK,OAAO,gBAAgB,EAAE,KAAK;AACnC,SAAK,OAAO,iBAAiB;AAE7B,SAAK,OAAO,KAAK,gBAAgB,EAAE,QAAQ,OAAO,SAAS,KAAK,CAAC;AAAA,EACnE;AACF;AAEA,IAAMA,UAAS;;;AEvKf,SAAsB,YAAY;AAWlC,IAAM,qBAAqB;AAUpB,IAAM,iBAAN,MAAqB;AAAA,EAO1B,YACU,QACA,QACA,SACR;AAHQ;AACA;AACA;AAAA,EACP;AAAA,EAHO;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EALF,SAAS,oBAAI,IAA8B;AAAA,EAQnD,UAAU,SAA0B;AAClC,WAAO,CAAC,CAAC,KAAK,OAAO,IAAI,OAAO,GAAG,KAAK;AAAA,EAC1C;AAAA,EAEA,UAAU,SAAuC;AAC/C,WAAO,KAAK,OAAO,IAAI,OAAO,GAAG,KAAK,SAAS,UAAU;AAAA,EAC3D;AAAA;AAAA,EAGA,MAAM,SAAiB,QAAsC;AAC3D,WAAO,KAAK,QAAQ,SAAS,YAAY;AACvC,YAAM,QAAQ,KAAK,OAAO,IAAI,OAAO;AACrC,UAAI,CAAC,SAAS,MAAM,SAAS,QAAS;AACtC,YAAM,QAAQ,MAAM;AAEpB,YAAM,cAAc,CAAC,MAAM,KAAK;AAEhC,UAAI,CAAC,MAAM,KAAK,SAAS;AAGvB,cAAM,OAAO,MAAM;AACnB,cAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA,aAAa,iBAAiB,KAAK,KAAK,MAAM,OAAO;AAAA,UACrD,cAAc,OAAQ,KAAK,SAAS,IAAgC;AAAA,UACpE,UAAU;AAAA,YACR,MAAM,MAAM,QAAQ;AAAA,YACpB,KAAK,MAAM,OAAO;AAAA,YAClB,QAAQ,MAAM,UAAU;AAAA,YACxB,QAAQ,MAAM,UAAU;AAAA,YACxB,OAAO,MAAM,SAAS;AAAA,YACtB,QAAQ,MAAM,UAAU;AAAA,YACxB,OAAO,MAAM,SAAS;AAAA,YACtB,OAAO,MAAM,SAAS;AAAA,YACtB,OAAO,MAAM,SAAS;AAAA,UACxB;AAAA,QACF;AAAA,MACF,OAAO;AACL,cAAM,KAAK,QAAQ,SAAS;AAAA,MAC9B;AAEA,UAAI;AACF,cAAM,KAAK,YAAY,KAAK;AAAA,MAC9B,SAAS,KAAK;AAGZ,YAAI,YAAa,QAAO,MAAM,KAAK;AACnC,cAAM;AAAA,MACR;AACA,WAAK,QAAQ,KAAK;AAAA,IACpB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,QAAQ,SAAgC;AACtC,WAAO,KAAK,QAAQ,SAAS,YAAY;AACvC,YAAM,QAAQ,KAAK,OAAO,IAAI,OAAO;AACrC,YAAM,QAAQ,OAAO,KAAK;AAC1B,UAAI,CAAC,SAAS,CAAC,MAAO;AACtB,YAAM,QAAQ,MAAM;AAEpB,YAAM,MAAM,OAAO,MAAM,WAAW;AAIpC,YAAM,IAAI;AAAA,QACR,MAAM,MAAM,SAAS;AAAA,QACrB,KAAK,MAAM,SAAS;AAAA,QACpB,QAAQ,MAAM,SAAS;AAAA,QACvB,QAAQ,MAAM,SAAS;AAAA,QACvB,OAAO,MAAM,SAAS;AAAA,QACtB,QAAQ,MAAM,SAAS;AAAA,QACvB,OAAO,MAAM,SAAS;AAAA,QACtB,OAAO,MAAM,SAAS;AAAA,QACtB,OAAO,MAAM,SAAS;AAAA,MACxB,CAAC;AAGD,YAAM,WAAW,MAAM,gBACjB,MAAM,KAAK,eAAe,CAAC,MAAM,YAAY,CAAC,GAAG,CAAC,IACpD;AACJ,YAAM,UAAU;AAChB,aAAO,MAAM,KAAK;AAClB,WAAK,OAAO,iBAAiB;AAC7B,WAAK,QAAQ,KAAK;AAAA,IACpB,CAAC;AAAA,EACH;AAAA;AAAA,EAGQ,QAAQ,SAAiB,MAA0C;AACzE,UAAM,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,QAAQ,QAAQ;AACzD,UAAM,OAAO,KAAK,KAAK,MAAM,IAAI;AAEjC,SAAK,OAAO;AAAA,MACV;AAAA,MACA,KAAK,MAAM,MAAM,MAAS;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,YAAY,OAA6B;AACrD,UAAM,QAAQ,MAAM,KAAK;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,QAAQ,MAAM;AAEpB,UAAM,KAAK,KAAK,OAAO,SAAS;AAChC,UAAM,KAAK,KAAK,OAAO,UAAU;AAKjC,UAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,OAAO,SAAS,GAAG,IAAI;AACvD,UAAM,OAAO,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AACxC,UAAM,UAAU,KAAK,IAAI,GAAG,OAAO,kBAAkB;AACrD,UAAM,QAAQ,KAAK,IAAI,SAAS,MAAM,SAAS,QAAQ,MAAM,SAAS,SAAS,KAAK;AACpF,UAAM,QAAQ,KAAK,IAAI,SAAS,MAAM,SAAS,SAAS,MAAM,SAAS,SAAS,KAAK;AAErF,UAAM,UAAU,MAAM;AAAA,MACpB,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,OAAO,OAAO;AAM1B,UAAM,IAAI;AAAA,MACR,MAAM;AAAA,MACN,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACT,CAAC;AACD,UAAM,WAAW;AACjB,UAAM,UAAU;AAChB,SAAK,OAAO,iBAAiB;AAAA,EAC/B;AACF;AAGA,SAAS,iBAAiB,OAAmC;AAC3D,MAAI;AACF,UAAM,KAAK,MAAM,WAAW;AAC5B,UAAM,IAAK,GAAwB,gBAAgB,GAAG;AACtD,UAAM,IAAK,GAAwB,iBAAiB,GAAG;AACvD,QAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,UAAM,MAAM,IAAI,WAAW,IAAI;AAC/B,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,UAAU,IAAI,GAAG,CAAC;AACtB,WAAO,IAAI,UAAU,WAAW;AAAA,EAClC,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAKA,IAAM,kBAAkB;AACxB,IAAM,aAAa,oBAAI,IAAuC;AAE9D,SAAS,UAAU,KAAwC;AACzD,QAAM,SAAS,WAAW,IAAI,GAAG;AACjC,MAAI,QAAQ;AAEV,eAAW,OAAO,GAAG;AACrB,eAAW,IAAI,KAAK,MAAM;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,YAAY,GAAG;AAE/B,UAAQ,MAAM,MAAM;AAClB,QAAI,WAAW,IAAI,GAAG,MAAM,QAAS,YAAW,OAAO,GAAG;AAAA,EAC5D,CAAC;AAED,aAAW,IAAI,KAAK,OAAO;AAC3B,MAAI,WAAW,OAAO,iBAAiB;AACrC,UAAM,SAAS,WAAW,KAAK,EAAE,KAAK,EAAE;AACxC,QAAI,WAAW,OAAW,YAAW,OAAO,MAAM;AAAA,EACpD;AACA,SAAO;AACT;AAEA,SAAS,YAAY,KAAwC;AAC3D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,cAAc;AAClB,QAAI,SAAS,MAAM,QAAQ,GAAG;AAC9B,QAAI,UAAU,MAAM,OAAO,IAAI,MAAM,kCAAkC,GAAG,EAAE,CAAC;AAC7E,QAAI,MAAM;AAAA,EACZ,CAAC;AACH;AAGO,SAAS,yBAA+B;AAC7C,aAAW,MAAM;AACnB;AAQA,eAAsB,oBACpB,KACA,QACA,SACA,SACA,OACA,OACiB;AACjB,QAAM,MAAM,MAAM,UAAU,GAAG;AAC/B,QAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,MAAI,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;AAC3C,MAAI,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;AAC5C,QAAM,MAAM,IAAI,WAAW,IAAI;AAC/B,MAAI,CAAC,IAAK,QAAO,IAAI,UAAU,WAAW;AAE1C,YAAU,KAAK,KAAK,QAAQ,SAAS,SAAS,OAAO,KAAK;AAC1D,SAAO,IAAI,UAAU,WAAW;AAClC;AAgBO,SAAS,qBACd,QACA,SACA,SACA,OACA,OACiB;AACjB,QAAM,OAAO,KAAK,KAAK,UAAU,UAAU,UAAU,OAAO;AAI5D,QAAM,UAAU,KAAK,IAAI,GAAG,OAAO,kBAAkB;AACrD,QAAM,QAAQ,KAAK,IAAI,SAAS,SAAS,IAAI,OAAO,oBAAoB,IAAI;AAC5E,QAAM,QAAQ,KAAK,IAAI,SAAS,SAAS,IAAI,OAAO,kBAAkB,IAAI;AAE1E,QAAM,OAAO,KAAK,KAAK,OAAO,KAAK,IAAI;AACvC,QAAM,OAAO,KAAK,KAAK,OAAO,KAAK,IAAI;AACvC,QAAM,WAAW,KAAK,KAAK,OAAO,CAAC;AACnC,QAAM,WAAW,KAAK,KAAK,OAAO,CAAC;AAEnC,QAAM,aAA8B,CAAC;AACrC,WAAS,IAAI,CAAC,UAAU,KAAK,UAAU,KAAK;AAC1C,aAAS,IAAI,CAAC,UAAU,KAAK,UAAU,KAAK;AAC1C,UAAI,IAAI,IAAI;AACZ,UAAI,IAAI,IAAI;AAEZ,UAAI,OAAO,SAAS,sBAAsB,KAAK,CAAC,MAAM,GAAG;AACvD,aAAK,SAAS,OAAO,mBAAmB;AAAA,MAC1C,WAAW,OAAO,SAAS,oBAAoB,KAAK,CAAC,MAAM,GAAG;AAC5D,aAAK,SAAS,OAAO,mBAAmB;AAAA,MAC1C;AAEA,YAAM,YAAa,IAAI,OAAO,gBAAgB,IAAI,OAAO,iBAAiB,KAAK,KAAM;AACrF,iBAAW,KAAK,EAAE,GAAG,GAAG,SAAS,CAAC;AAAA,IACpC;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,UACd,KACA,KACA,QACA,SACA,SACA,OACA,OACM;AACN,MAAI,KAAK;AAET,MAAI,UAAU,UAAU,GAAG,UAAU,CAAC;AACtC,MAAI,OAAQ,OAAO,QAAQ,KAAK,KAAM,GAAG;AAEzC,aAAW,QAAQ,qBAAqB,QAAQ,SAAS,SAAS,OAAO,KAAK,GAAG;AAC/E,QAAI,KAAK;AACT,QAAI,UAAU,KAAK,GAAG,KAAK,CAAC;AAC5B,QAAI,OAAO,KAAK,QAAQ;AACxB,QAAI,UAAU,KAAK,CAAC,QAAQ,GAAG,CAAC,QAAQ,GAAG,OAAO,KAAK;AACvD,QAAI,QAAQ;AAAA,EACd;AAEA,MAAI,QAAQ;AACd;AAGA,SAAS,KAAK,GAAmB;AAC/B,UAAS,IAAI,IAAK,KAAK;AACzB;;;ACnWA,IAAM,cAAc;AAEb,IAAM,gBAAN,MAAoB;AAAA,EACzB,YACU,OAAa,MACb,MAAc,IACtB;AAFQ;AACA;AAAA,EACP;AAAA,EAFO;AAAA,EACA;AAAA,EAGV,QAAQ,MAAkB;AACxB,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,OAAO,KAAmB;AACxB,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,UAAgB;AACd,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SAAiB;AACf,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SAAS,OAAe,UAAyB;AAC/C,UAAM,IAAI,YAAY,KAAK;AAC3B,YAAQ,GAAG;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO,QAAQ,KAAK;AAAA,MACtB,KAAK;AACH,eAAQ,QAAQ,cAAe,KAAK;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,WAAW,IAAY,QAAuB;AAC5C,UAAM,IAAI,UAAU,KAAK;AACzB,YAAQ,GAAG;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO,KAAK,KAAK;AAAA,MACnB,KAAK;AACH,eAAQ,KAAK,KAAK,MAAO;AAAA,IAC7B;AAAA,EACF;AACF;;;ACjDA,SAAS,QAAAC,aAAY;AAMrB,IAAM,UAAU;AAET,SAAS,gBAAgB,QAAmC;AACjE,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,MACN,OAAO,OAAO,OAAO,SAAS;AAAA,MAC9B,QAAQ,OAAO,OAAO,UAAU;AAAA,IAClC;AAAA,IACA,QAAQ,OAAO,OAAO,OAAO,EAAE,IAAI,CAAC,UAAU,MAAM,UAAU,CAAC;AAAA;AAAA;AAAA,IAG/D,YAAY,OAAO,oBAAoB;AAAA,IACvC,QAAQ,OAAO,UAAU;AAAA,EAC3B;AACF;AAEA,eAAsB,kBAAkB,QAAsB,OAAmC;AAC/F,SAAO,OAAO,MAAM;AACpB,SAAO,OAAO,cAAc,EAAE,OAAO,MAAM,OAAO,OAAO,QAAQ,MAAM,OAAO,OAAO,CAAC;AAKtF,MAAI,MAAM,YAAY;AACpB,WAAO,cAAc,MAAM,UAAU;AAAA,EACvC;AAIA,MAAI,MAAM,WAAW,QAAW;AAC9B,WAAO,UAAU,MAAM,MAAM;AAAA,EAC/B;AAEA,aAAW,mBAAmB,MAAM,QAAQ;AAC1C,UAAM,aAAa,QAAQ,eAAe;AAAA,EAC5C;AAEA,SAAO,OAAO,iBAAiB;AACjC;AAEA,eAAe,aAAa,QAAsB,YAA6C;AAC7F,QAAM,UAAU,MAAMA,MAAK,eAAe,CAAC,WAAW,YAAY,CAAC;AACnE,QAAM,eAAe,QAAQ,CAAC;AAE9B,QAAM,QAAQ,OAAO,OAAO,IAAI,WAAW,MAAM,cAAc,WAAW,MAAM,WAAW,EAAE;AAE7F,MAAI,WAAW,MAAM;AACnB,UAAM,OAAO,WAAW;AAAA,EAC1B;AAEA,MAAI,CAAC,WAAW,SAAS;AACvB,WAAO,OAAO,cAAc,MAAM,IAAI,KAAK;AAAA,EAC7C;AACA,MAAI,WAAW,QAAQ;AACrB,WAAO,OAAO,UAAU,MAAM,IAAI,IAAI;AAAA,EACxC;AACA,MAAI,WAAW,YAAY,GAAG;AAC5B,WAAO,OAAO,WAAW,MAAM,IAAI,WAAW,OAAO;AAAA,EACvD;AAEA,SAAO;AACT;;;AC5DA,eAAsB,UACpB,QACA,UAA4B,CAAC,GACd;AACf,QAAM,EAAE,aAAa,GAAG,SAAS,OAAsB,UAAU,EAAE,IAAI;AACvE,QAAM,UAAU,OAAO,UAAU;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,WAAW,MAAM,MAAM,OAAO;AACpC,SAAO,SAAS,KAAK;AACvB;AAEO,SAAS,UAAU,QAAwB;AAChD,SAAO,OAAO,MAAM;AACtB;AAEO,SAAS,cACd,QACA,SAAsB,OACtB,aAAa,GACL;AACR,SAAO,OAAO,UAAU,EAAE,QAAQ,WAAW,CAAC;AAChD;;;AXLA,IAAM,WAAW;AACjB,IAAM,WAAW;AAEV,IAAM,eAAN,MAAmB;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAED;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,SAA8B;AAAA;AAAA;AAAA;AAAA,EAI9B;AAAA,EAER,YAAY,eAAkC,QAAsB;AAClE,SAAK,SAAS,IAAI,aAA2B;AAC7C,SAAK,QAAQ,IAAI,cAAc,OAAO,QAAQ,MAAM,OAAO,OAAO,EAAE;AAEpE,UAAM,UAAU,KAAK,MAAM,SAAS,OAAO,KAAK;AAChD,UAAM,WAAW,KAAK,MAAM,SAAS,OAAO,MAAM;AAClD,SAAK,mBAAmB,OAAO,mBAAmB;AAElD,SAAK,SAAS,IAAI,OAAO,eAAe;AAAA,MACtC,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,iBAAiB,KAAK;AAAA,MACtB,wBAAwB,OAAO,0BAA0B;AAAA,MACzD,WAAW;AAAA,IACb,CAAC;AAED,SAAK,SAAS,IAAI,aAAa,KAAK,QAAQ,KAAK,MAAM;AACvD,SAAK,cAAc,OAAO;AAC1B,SAAK,gBAAgB,OAAO;AAE5B,SAAK,UAAU,IAAI,eAAe;AAAA,MAChC,UAAU,MAAM,KAAK,UAAU,KAAK,OAAO,CAAC;AAAA,MAC5C,cAAc,OAAO,UAAkB;AACrC,cAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,cAAM,KAAK,SAAS,MAAM;AAAA,MAC5B;AAAA,MACA,QAAQ,KAAK;AAAA,IACf,CAAC;AAED,SAAK,WAAW,IAAI,YAAY,KAAK,QAAQ,KAAK,MAAM;AACxD,SAAK,OAAO,IAAI,eAAe,KAAK,QAAQ,KAAK,SAAS,KAAK,MAAM;AACrE,SAAK,WAAW,IAAI,eAAe,KAAK,QAAQ,KAAK,QAAQ,KAAK,OAAO;AAEzE,SAAK,kBAAkB;AACvB,SAAK,QAAQ,cAAc;AAAA,EAC7B;AAAA;AAAA,EAIA,MAAM,SAAS,KAAa,SAA4D;AAMtF,UAAM,MAAM,MAAMC,aAAY,QAAQ,KAAK,CAAC,GAAG,EAAE,SAAS,QAAQ,SAAS,OAAO,GAAG,QAAQ,CAAC;AAC9F,UAAM,QAAQ,KAAK,OAAO,IAAI,SAAS,GAAG;AAC1C,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,MAAc,SAAmD;AACvE,UAAM,UAAU,IAAI,QAAQ,MAAM;AAAA,MAChC,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,OAAO;AAAA;AAAA,MAEP,SAAS;AAAA,MACT,SAAS;AAAA,MACT,GAAG;AAAA,IACL,CAAC;AACD,UAAM,QAAQ,KAAK,OAAO,IAAI,QAAQ,OAAkC;AACxE,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,QAAqB,SAA0C;AACtE,UAAM,MAAM,OAAO,OAAO,OAAO;AACjC,UAAM,QAAQ,KAAK,OAAO,IAAI,SAAS,KAAK,OAAO,IAAI;AACvD,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YACJ,UACA,SACgB;AAGhB,UAAM,EAAE,SAAS,aAAa,IAAI,MAAM,OAAO,QAAQ;AACvD,UAAM,cAAc,IAAI,aAAa,cAAc,SAAS,IAAI,KAAK;AAAA,MACnE,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AACD,UAAM,QAAQ,KAAK,OAAO;AAAA,MACxB;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX;AACA,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,IAAkB;AAC5B,SAAK,OAAO,OAAO,EAAE;AACrB,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,YAAY,IAAyB;AACnC,SAAK,OAAO,OAAO,EAAE;AAAA,EACvB;AAAA,EAEA,mBAAiC;AAC/B,UAAM,SAAS,KAAK,OAAO,gBAAgB;AAC3C,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,KAAK,OAAO,aAAa,MAAM,KAAK;AAAA,EAC7C;AAAA;AAAA,EAGA,UAAU,IAAY,MAAuC;AAC3D,UAAM,QAAQ,KAAK,OAAO,IAAI,EAAE;AAChC,QAAI,CAAC,MAAO;AACZ,UAAM,MAAM,MAAM;AAClB,QAAI,SAAS,cAAc;AACzB,UAAI,IAAI,SAAS,CAAC,IAAI,KAAK;AAAA,IAC7B,OAAO;AACL,UAAI,IAAI,SAAS,CAAC,IAAI,KAAK;AAAA,IAC7B;AACA,QAAI,UAAU;AACd,SAAK,OAAO,iBAAiB;AAC7B,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA;AAAA,EAGA,MAAM,eAAe,IAAmC;AACtD,UAAM,QAAQ,KAAK,OAAO,IAAI,EAAE;AAChC,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,QAAQ,MAAM,MAAM,aAAa,MAAM;AAG7C,QAAI,CAAC,MAAM,KAAK,SAAS;AACvB,YAAM,IAAI,EAAE,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,MAAM,OAAO,KAAK,GAAG,CAAC;AAAA,IACxE;AACA,UAAM,UAAU;AAEhB,UAAM,OAAO,KAAK,OAAO,IAAI,MAAM,MAAM,OAAO,GAAG,MAAM,IAAI,OAAO;AAEpE,SAAK,OAAO,gBAAgB,MAAM,IAAI;AAEtC,SAAK,OAAO,gBAAgB,KAAK;AACjC,SAAK,OAAO,iBAAiB;AAC7B,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,SAAsB;AACpB,WAAO,gBAAgB,IAAI;AAAA,EAC7B;AAAA,EAEA,MAAM,SAAS,OAAmC;AAChD,UAAM,kBAAkB,MAAM,KAAK;AAAA,EACrC;AAAA;AAAA,EAIA,MAAM,MAAM,SAA2C;AACrD,SAAK,OAAO,KAAK,gBAAgB,EAAE,QAAQ,MAAM,CAAC;AAClD,UAAM,OAAO,MAAM,KAAK,qBAAqB,MAAM,UAAU,KAAK,QAAQ,OAAO,CAAC;AAClF,SAAK,OAAO,KAAK,mBAAmB,EAAE,QAAQ,MAAM,CAAC;AACrD,WAAO;AAAA,EACT;AAAA,EAEA,QAAgB;AACd,SAAK,OAAO,KAAK,gBAAgB,EAAE,QAAQ,MAAM,CAAC;AAClD,UAAM,MAAM,KAAK,qBAAqB,MAAM,UAAU,KAAK,MAAM,CAAC;AAClE,SAAK,OAAO,KAAK,mBAAmB,EAAE,QAAQ,MAAM,CAAC;AACrD,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,SAAsB,OAAO,aAAa,GAAW;AAC7D,WAAO,KAAK,qBAAqB,MAAM,cAAc,KAAK,QAAQ,QAAQ,UAAU,CAAC;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,qBAAwB,IAAgB;AAC9C,QAAI,CAAC,KAAK,OAAQ,QAAO,GAAG;AAC5B,UAAM,YAAY,KAAK,OAAO;AAC9B,SAAK,OAAO,kBAAkB,KAAK;AACnC,QAAI;AACF,aAAO,GAAG;AAAA,IACZ,UAAE;AACA,WAAK,OAAO,kBAAkB;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,wBAA6D;AAC3D,UAAM,SAA8C,CAAC;AACrD,UAAM,UAAU,KAAK,OAAO,SAAS;AACrC,UAAM,UAAU,KAAK,OAAO,UAAU;AAEtC,UAAM,cAAc,KAAK,OAAO,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO;AAEzE,QAAI,YAAY,WAAW,GAAG;AAC5B,aAAO,OAAO,IAAI,EAAE,GAAG,KAAK,GAAG,KAAK,OAAO,GAAG,OAAO,EAAE;AACvD,aAAO;AAAA,IACT;AAEA,eAAW,SAAS,aAAa;AAC/B,YAAM,MAAM,MAAM;AAClB,YAAM,SAAS,IAAI,eAAe;AAClC,aAAO,MAAM,IAAI,IAAI;AAAA,QACnB,GAAG,OAAO,OAAO,IAAI,OAAO;AAAA,QAC5B,GAAG,OAAO,OAAO,IAAI,OAAO;AAAA,QAC5B,OAAO,OAAO,IAAI,UAAU,CAAC;AAAA,QAC7B,OAAO,OAAO,IAAI,SAAS,CAAC;AAAA,MAC9B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,MAAM,KAAK,UAAkB,QAA6D;AACxF,QAAI,CAAC,KAAK,YAAa,QAAO;AAE9B,QAAI;AACJ,QAAI,WAAW,OAAO;AACpB,aAAO,MAAM,KAAK,MAAM;AAAA,IAC1B,WAAW,WAAW,OAAO;AAC3B,aAAO,KAAK,MAAM;AAAA,IACpB,OAAO;AACL,aAAO,KAAK,UAAU,KAAK,OAAO,GAAG,MAAM,CAAC;AAAA,IAC9C;AAEA,WAAO,KAAK,YAAY,KAAK,MAAM,UAAU,MAAM;AAAA,EACrD;AAAA,EAEA,MAAM,YAAY,MAAsD;AACtE,QAAI,CAAC,KAAK,cAAe,QAAO;AAChC,WAAO,KAAK,cAAc,OAAO,IAAI;AAAA,EACvC;AAAA,EAEA,MAAM,eAAoD;AACxD,QAAI,CAAC,KAAK,eAAe,OAAQ,QAAO;AACxC,WAAO,KAAK,cAAc,OAAO;AAAA,EACnC;AAAA,EAEA,eAAe,SAA4B;AACzC,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,iBAAiB,UAA+B;AAC9C,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAIA,cAAc,OAAqB;AACjC,SAAK,mBAAmB;AAGxB,QAAI,CAAC,KAAK,QAAQ;AAChB,WAAK,OAAO,kBAAkB;AAC9B,WAAK,OAAO,iBAAiB;AAAA,IAC/B;AACA,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,sBAA8B;AAC5B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAO,OAAe,QAAsB;AAC1C,UAAM,UAAU,KAAK,MAAM,SAAS,KAAK;AACzC,UAAM,WAAW,KAAK,MAAM,SAAS,MAAM;AAC3C,SAAK,OAAO,cAAc,EAAE,OAAO,SAAS,QAAQ,SAAS,CAAC;AAC9D,SAAK,OAAO,iBAAiB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQ,OAAqB;AAC3B,UAAM,OAAO,MAAM,OAAO,KAAK,GAAG,UAAU,QAAQ;AACpD,QAAI,SAAS,KAAK,UAAW;AAC7B,SAAK,YAAY;AACjB,SAAK,OAAO,KAAK,gBAAgB,EAAE,MAAM,KAAK,CAAC;AAAA,EACjD;AAAA;AAAA,EAGA,KAAK,OAAqB;AACxB,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,UAAkB;AAChB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAO,OAAO,KAAW;AACvB,SAAK,QAAQ,KAAK,YAAY,IAAI;AAAA,EACpC;AAAA,EAEA,QAAQ,OAAO,KAAW;AACxB,SAAK,QAAQ,KAAK,YAAY,IAAI;AAAA,EACpC;AAAA,EAEA,YAAkB;AAChB,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA;AAAA,EAGA,UAAU,eAAuB,gBAAwB,UAAU,GAAS;AAC1E,UAAM,IAAI,KAAK,OAAO,SAAS;AAC/B,UAAM,IAAI,KAAK,OAAO,UAAU;AAChC,QAAI,KAAK,KAAK,KAAK,EAAG;AACtB,UAAM,MAAM,gBAAgB,UAAU,KAAK;AAC3C,UAAM,MAAM,iBAAiB,UAAU,KAAK;AAC5C,SAAK,QAAQ,KAAK,IAAI,IAAI,EAAE,CAAC;AAAA,EAC/B;AAAA;AAAA,EAIA,aAAa,SAAiB,QAAsC;AAClE,WAAO,KAAK,SAAS,MAAM,SAAS,MAAM;AAAA,EAC5C;AAAA,EAEA,aAAa,SAAgC;AAC3C,WAAO,KAAK,SAAS,QAAQ,OAAO;AAAA,EACtC;AAAA;AAAA,EAIA,UAAU,QAAmC;AAC3C,SAAK,SAAS;AAGd,SAAK,OAAO,kBAAkB,SAAS,KAAK,KAAK;AACjD,SAAK,OAAO,iBAAiB;AAC7B,SAAK,OAAO,KAAK,kBAAkB,EAAE,OAAO,CAAC;AAAA,EAC/C;AAAA,EAEA,cAAoB;AAClB,SAAK,UAAU,IAAI;AAAA,EACrB;AAAA,EAEA,YAAiC;AAC/B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAIA,UAAgB;AACd,SAAK,SAAS,QAAQ;AACtB,SAAK,KAAK,QAAQ;AAClB,SAAK,QAAQ,QAAQ;AACrB,SAAK,OAAO,mBAAmB;AAC/B,SAAK,OAAO,QAAQ;AAAA,EACtB;AAAA;AAAA,EAIQ,oBAA0B;AAChC,SAAK,OAAO,GAAG,mBAAmB,CAAC,MAAM;AACvC,UAAI,CAAC,EAAE,OAAQ;AACf,YAAM,QAAQ,KAAK,OAAO,aAAa,EAAE,MAAM;AAC/C,UAAI,OAAO;AACT,aAAK,OAAO,KAAK,kBAAkB,EAAE,SAAS,MAAM,GAAG,CAAC;AACxD,aAAK,QAAQ,KAAK;AAAA,MACpB;AAAA,IACF,CAAC;AAED,SAAK,OAAO,GAAG,qBAAqB,CAAC,MAAM;AACzC,YAAM,YAAY,EAAE,YAAY,CAAC,GAC9B,IAAI,CAAC,QAAQ,KAAK,OAAO,aAAa,GAAG,GAAG,EAAE,EAC9C,OAAO,CAAC,OAAqB,OAAO,MAAS;AAChD,WAAK,OAAO,KAAK,qBAAqB,EAAE,SAAS,CAAC;AAAA,IACpD,CAAC;AAED,SAAK,OAAO,GAAG,qBAAqB,CAAC,MAAM;AACzC,YAAM,YAAY,EAAE,YAAY,CAAC,GAC9B,IAAI,CAAC,QAAQ,KAAK,OAAO,aAAa,GAAG,GAAG,EAAE,EAC9C,OAAO,CAAC,OAAqB,OAAO,MAAS;AAChD,WAAK,OAAO,KAAK,qBAAqB,EAAE,SAAS,CAAC;AAAA,IACpD,CAAC;AAED,SAAK,OAAO,GAAG,qBAAqB,MAAM;AACxC,WAAK,OAAO,KAAK,qBAAqB,EAAE,UAAU,CAAC,EAAE,CAAC;AAAA,IACxD,CAAC;AAAA,EACH;AACF;;;AY/RO,IAAM,yBAAwC;AAAA,EACnD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,OAAO;AAAA,EACP,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,eAAe;AACjB;","names":["FabricImage","STROKE","util","FabricImage"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overtone-art/canvas-editor-core",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Abstract pluggable canvas editor built on Fabric.js",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -24,7 +24,7 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "fabric": "^6.6.1",
27
+ "fabric": "^7.4.0",
28
28
  "nanoid": "^5.1.5"
29
29
  },
30
30
  "devDependencies": {