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

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
@@ -344,6 +344,25 @@ declare class PatternManager {
344
344
  private enqueue;
345
345
  private renderLayer;
346
346
  }
347
+ /**
348
+ * Object placement for a pattern re-render.
349
+ *
350
+ * The FIRST enable pins the pattern to the print-area origin (0,0, unrotated) so
351
+ * it fills the whole area. A later config update (scale/angle/spacing slider)
352
+ * preserves the object's current placement — otherwise tweaking a slider would
353
+ * snap a pattern the user had dragged/rotated back to the top-left corner.
354
+ *
355
+ * Exported for unit testing.
356
+ */
357
+ declare function resolvePatternPlacement(pin: boolean, current: {
358
+ left?: number;
359
+ top?: number;
360
+ angle?: number;
361
+ }): {
362
+ left: number;
363
+ top: number;
364
+ angle: number;
365
+ };
347
366
  /** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
348
367
  declare function clearPatternImageCache(): void;
349
368
  /**
@@ -474,4 +493,4 @@ declare function round2(v: number): number;
474
493
  declare function serializeEditor(editor: CanvasEditor): EditorState;
475
494
  declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
476
495
 
477
- export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, buildPatternDataURL, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, round2, serializeEditor };
496
+ export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, buildPatternDataURL, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, resolvePatternPlacement, round2, serializeEditor };
package/dist/index.d.ts CHANGED
@@ -344,6 +344,25 @@ declare class PatternManager {
344
344
  private enqueue;
345
345
  private renderLayer;
346
346
  }
347
+ /**
348
+ * Object placement for a pattern re-render.
349
+ *
350
+ * The FIRST enable pins the pattern to the print-area origin (0,0, unrotated) so
351
+ * it fills the whole area. A later config update (scale/angle/spacing slider)
352
+ * preserves the object's current placement — otherwise tweaking a slider would
353
+ * snap a pattern the user had dragged/rotated back to the top-left corner.
354
+ *
355
+ * Exported for unit testing.
356
+ */
357
+ declare function resolvePatternPlacement(pin: boolean, current: {
358
+ left?: number;
359
+ top?: number;
360
+ angle?: number;
361
+ }): {
362
+ left: number;
363
+ top: number;
364
+ angle: number;
365
+ };
347
366
  /** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
348
367
  declare function clearPatternImageCache(): void;
349
368
  /**
@@ -474,4 +493,4 @@ declare function round2(v: number): number;
474
493
  declare function serializeEditor(editor: CanvasEditor): EditorState;
475
494
  declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
476
495
 
477
- export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, buildPatternDataURL, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, round2, serializeEditor };
496
+ export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, buildPatternDataURL, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, resolvePatternPlacement, round2, serializeEditor };
package/dist/index.js CHANGED
@@ -50,6 +50,7 @@ __export(index_exports, {
50
50
  exportPNG: () => exportPNG,
51
51
  exportSVG: () => exportSVG,
52
52
  generateId: () => generateId,
53
+ resolvePatternPlacement: () => resolvePatternPlacement,
53
54
  round2: () => round2,
54
55
  serializeEditor: () => serializeEditor
55
56
  });
@@ -649,7 +650,7 @@ var PatternManager = class {
649
650
  layer.meta.pattern.config = config;
650
651
  }
651
652
  try {
652
- await this.renderLayer(layer);
653
+ await this.renderLayer(layer, firstEnable);
653
654
  } catch (err) {
654
655
  if (firstEnable) delete layer.meta.pattern;
655
656
  throw err;
@@ -693,12 +694,13 @@ var PatternManager = class {
693
694
  );
694
695
  return next;
695
696
  }
696
- async renderLayer(layer) {
697
+ async renderLayer(layer, pin) {
697
698
  const state = layer.meta.pattern;
698
699
  if (!state) return;
699
700
  const image = layer.fabricObject;
700
701
  const cw = this.canvas.getWidth();
701
702
  const ch = this.canvas.getHeight();
703
+ const { left, top, angle } = resolvePatternPlacement(pin, image);
702
704
  const scale = Math.max(1, state.config.scale ?? 100) / 100;
703
705
  const diag = Math.sqrt(cw * cw + ch * ch);
704
706
  const minTile = Math.max(2, diag / MAX_TILES_PER_AXIS);
@@ -714,21 +716,29 @@ var PatternManager = class {
714
716
  );
715
717
  await image.setSrc(dataUrl);
716
718
  image.set({
717
- left: 0,
718
- top: 0,
719
+ left,
720
+ top,
719
721
  scaleX: 1,
720
722
  scaleY: 1,
721
723
  width: cw,
722
724
  height: ch,
723
725
  cropX: 0,
724
726
  cropY: 0,
725
- angle: 0
727
+ angle
726
728
  });
727
729
  image.clipPath = void 0;
728
730
  image.setCoords();
729
731
  this.canvas.requestRenderAll();
730
732
  }
731
733
  };
734
+ function resolvePatternPlacement(pin, current) {
735
+ if (pin) return { left: 0, top: 0, angle: 0 };
736
+ return {
737
+ left: current.left ?? 0,
738
+ top: current.top ?? 0,
739
+ angle: current.angle ?? 0
740
+ };
741
+ }
732
742
  function elementToDataURL(image) {
733
743
  try {
734
744
  const el = image.getElement();
@@ -1306,6 +1316,7 @@ var DEFAULT_PATTERN_CONFIG = {
1306
1316
  exportPNG,
1307
1317
  exportSVG,
1308
1318
  generateId,
1319
+ resolvePatternPlacement,
1309
1320
  round2,
1310
1321
  serializeEditor
1311
1322
  });
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 // 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"]}
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 resolvePatternPlacement,\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 // Only the first enable pins the pattern to the full print area; later\n // config updates preserve wherever the user has moved/rotated it.\n await this.renderLayer(layer, firstEnable);\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, pin: boolean): 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 const { left, top, angle } = resolvePatternPlacement(pin, image);\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 and drawn at 1:1 (scaleX/scaleY=1), so on\n // first enable (pin) it fills the whole print area from the origin.\n // `width`/`height` are set explicitly (not just via setSrc) and any\n // pre-pattern clipPath is stripped so a leftover crop sized to the original\n // image box can't confine the pattern to that box. cropX/cropY are reset for\n // the same reason. `left`/`top`/`angle` are preserved on re-render (see above).\n image.set({\n left,\n top,\n scaleX: 1,\n scaleY: 1,\n width: cw,\n height: ch,\n cropX: 0,\n cropY: 0,\n angle,\n });\n image.clipPath = undefined;\n image.setCoords();\n this.canvas.requestRenderAll();\n }\n}\n\n/**\n * Object placement for a pattern re-render.\n *\n * The FIRST enable pins the pattern to the print-area origin (0,0, unrotated) so\n * it fills the whole area. A later config update (scale/angle/spacing slider)\n * preserves the object's current placement — otherwise tweaking a slider would\n * snap a pattern the user had dragged/rotated back to the top-left corner.\n *\n * Exported for unit testing.\n */\nexport function resolvePatternPlacement(\n pin: boolean,\n current: { left?: number; top?: number; angle?: number },\n): { left: number; top: number; angle: number } {\n if (pin) return { left: 0, top: 0, angle: 0 };\n return {\n left: current.left ?? 0,\n top: current.top ?? 0,\n angle: current.angle ?? 0,\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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,iBAA6C;;;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,oBAAuB;AAEhB,IAAM,aAAa,UAAc,sBAAO,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,oBAAqB;;;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,mBAAK;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,IAAAC,iBAAkC;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;AAGF,cAAM,KAAK,YAAY,OAAO,WAAW;AAAA,MAC3C,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,oBAAK,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,OAAc,KAA6B;AACnE,UAAM,QAAQ,MAAM,KAAK;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,QAAQ,MAAM;AAEpB,UAAM,KAAK,KAAK,OAAO,SAAS;AAChC,UAAM,KAAK,KAAK,OAAO,UAAU;AAEjC,UAAM,EAAE,MAAM,KAAK,MAAM,IAAI,wBAAwB,KAAK,KAAK;AAK/D,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;AAO1B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,OAAO;AAAA,MACP;AAAA,IACF,CAAC;AACD,UAAM,WAAW;AACjB,UAAM,UAAU;AAChB,SAAK,OAAO,iBAAiB;AAAA,EAC/B;AACF;AAYO,SAAS,wBACd,KACA,SAC8C;AAC9C,MAAI,IAAK,QAAO,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,EAAE;AAC5C,SAAO;AAAA,IACL,MAAM,QAAQ,QAAQ;AAAA,IACtB,KAAK,QAAQ,OAAO;AAAA,IACpB,OAAO,QAAQ,SAAS;AAAA,EAC1B;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;;;AC9XA,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,IAAAC,iBAAqB;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,MAAM,oBAAK,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,sBAAO,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,MAAM,2BAAY,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,uBAAQ,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":["import_fabric","STROKE","import_fabric","import_fabric"]}
package/dist/index.mjs CHANGED
@@ -592,7 +592,7 @@ var PatternManager = class {
592
592
  layer.meta.pattern.config = config;
593
593
  }
594
594
  try {
595
- await this.renderLayer(layer);
595
+ await this.renderLayer(layer, firstEnable);
596
596
  } catch (err) {
597
597
  if (firstEnable) delete layer.meta.pattern;
598
598
  throw err;
@@ -636,12 +636,13 @@ var PatternManager = class {
636
636
  );
637
637
  return next;
638
638
  }
639
- async renderLayer(layer) {
639
+ async renderLayer(layer, pin) {
640
640
  const state = layer.meta.pattern;
641
641
  if (!state) return;
642
642
  const image = layer.fabricObject;
643
643
  const cw = this.canvas.getWidth();
644
644
  const ch = this.canvas.getHeight();
645
+ const { left, top, angle } = resolvePatternPlacement(pin, image);
645
646
  const scale = Math.max(1, state.config.scale ?? 100) / 100;
646
647
  const diag = Math.sqrt(cw * cw + ch * ch);
647
648
  const minTile = Math.max(2, diag / MAX_TILES_PER_AXIS);
@@ -657,21 +658,29 @@ var PatternManager = class {
657
658
  );
658
659
  await image.setSrc(dataUrl);
659
660
  image.set({
660
- left: 0,
661
- top: 0,
661
+ left,
662
+ top,
662
663
  scaleX: 1,
663
664
  scaleY: 1,
664
665
  width: cw,
665
666
  height: ch,
666
667
  cropX: 0,
667
668
  cropY: 0,
668
- angle: 0
669
+ angle
669
670
  });
670
671
  image.clipPath = void 0;
671
672
  image.setCoords();
672
673
  this.canvas.requestRenderAll();
673
674
  }
674
675
  };
676
+ function resolvePatternPlacement(pin, current) {
677
+ if (pin) return { left: 0, top: 0, angle: 0 };
678
+ return {
679
+ left: current.left ?? 0,
680
+ top: current.top ?? 0,
681
+ angle: current.angle ?? 0
682
+ };
683
+ }
675
684
  function elementToDataURL(image) {
676
685
  try {
677
686
  const el = image.getElement();
@@ -1248,6 +1257,7 @@ export {
1248
1257
  exportPNG,
1249
1258
  exportSVG,
1250
1259
  generateId,
1260
+ resolvePatternPlacement,
1251
1261
  round2,
1252
1262
  serializeEditor
1253
1263
  };
@@ -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 // 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":["FabricImage","STROKE","util","FabricImage"]}
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 // Only the first enable pins the pattern to the full print area; later\n // config updates preserve wherever the user has moved/rotated it.\n await this.renderLayer(layer, firstEnable);\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, pin: boolean): 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 const { left, top, angle } = resolvePatternPlacement(pin, image);\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 and drawn at 1:1 (scaleX/scaleY=1), so on\n // first enable (pin) it fills the whole print area from the origin.\n // `width`/`height` are set explicitly (not just via setSrc) and any\n // pre-pattern clipPath is stripped so a leftover crop sized to the original\n // image box can't confine the pattern to that box. cropX/cropY are reset for\n // the same reason. `left`/`top`/`angle` are preserved on re-render (see above).\n image.set({\n left,\n top,\n scaleX: 1,\n scaleY: 1,\n width: cw,\n height: ch,\n cropX: 0,\n cropY: 0,\n angle,\n });\n image.clipPath = undefined;\n image.setCoords();\n this.canvas.requestRenderAll();\n }\n}\n\n/**\n * Object placement for a pattern re-render.\n *\n * The FIRST enable pins the pattern to the print-area origin (0,0, unrotated) so\n * it fills the whole area. A later config update (scale/angle/spacing slider)\n * preserves the object's current placement — otherwise tweaking a slider would\n * snap a pattern the user had dragged/rotated back to the top-left corner.\n *\n * Exported for unit testing.\n */\nexport function resolvePatternPlacement(\n pin: boolean,\n current: { left?: number; top?: number; angle?: number },\n): { left: number; top: number; angle: number } {\n if (pin) return { left: 0, top: 0, angle: 0 };\n return {\n left: current.left ?? 0,\n top: current.top ?? 0,\n angle: current.angle ?? 0,\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":["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.2",
3
+ "version": "0.2.3",
4
4
  "description": "Abstract pluggable canvas editor built on Fabric.js",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",