@overtone-art/canvas-editor-core 0.2.6 → 0.2.8

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.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 captureLocks,\n applyPatternLocks,\n restoreLocks,\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 PatternLocks,\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 // A pattern must cover the whole print area. States saved before patterns\n // were pinned (or saved at a different display size) restore a tiled bitmap\n // that no longer lines up with the canvas — re-pin without re-rasterising.\n this.patterns.repinAll();\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, PatternLocks } 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 originalLocks: captureLocks(image),\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 /**\n * Stretch every restored pattern layer back over the full print area and\n * re-freeze it, without re-rasterising: the baked bitmap keeps whatever size\n * it was saved at, so it is scaled (not re-tiled) to the current canvas. Used\n * after a state restore, where the canvas may be a different display size\n * than when the pattern was baked — and to repair states saved while a\n * pattern could still be dragged out of the print area.\n */\n repinAll(): void {\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n let changed = false;\n for (const layer of this.layers.getAll()) {\n if (!layer.meta.pattern || layer.type !== 'image') continue;\n const image = layer.fabricObject as FabricImage;\n image.set({\n left: 0,\n top: 0,\n angle: 0,\n scaleX: cw / (image.width || cw),\n scaleY: ch / (image.height || ch),\n });\n applyPatternLocks(image);\n image.setCoords();\n changed = true;\n }\n if (changed) this.canvas.requestRenderAll();\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 // Hand the object back its move/scale/rotate handles (frozen while the\n // pattern was on so it stayed pinned to the print area).\n restoreLocks(image, state.originalLocks);\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 and drawn at 1:1 (scaleX/scaleY=1) from\n // the origin, so it fills the whole print area — on the first enable and on\n // every re-render. Placement is NOT preserved: a moved/rotated/scaled\n // pattern would slide the bitmap off the print area and leave a bare band,\n // which is exactly what a pattern must never do. Use `offsetX`/`offsetY` to\n // nudge the tiling and `angle` to rotate it instead.\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.\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 // Freeze the handles so a drag/resize can't undo the fill. The object stays\n // selectable (the pattern panel needs a selected layer to edit its config).\n applyPatternLocks(image);\n image.setCoords();\n this.canvas.requestRenderAll();\n }\n}\n\n/** Interaction flags to restore when a pattern is turned off. */\nexport function captureLocks(obj: FabricObject): PatternLocks {\n return {\n lockMovementX: obj.lockMovementX ?? false,\n lockMovementY: obj.lockMovementY ?? false,\n lockScalingX: obj.lockScalingX ?? false,\n lockScalingY: obj.lockScalingY ?? false,\n lockRotation: obj.lockRotation ?? false,\n hasControls: obj.hasControls ?? true,\n };\n}\n\n/** Pin a pattern object: no move, scale or rotate while the pattern is on. */\nexport function applyPatternLocks(obj: FabricObject): void {\n obj.set({\n lockMovementX: true,\n lockMovementY: true,\n lockScalingX: true,\n lockScalingY: true,\n lockRotation: true,\n hasControls: false,\n });\n}\n\n/**\n * Restore pre-pattern interaction flags. States saved before locking existed\n * carry no snapshot — fall back to a fully interactive object rather than\n * leaving it frozen forever.\n */\nexport function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void {\n obj.set(\n locks ?? {\n lockMovementX: false,\n lockMovementY: false,\n lockScalingX: false,\n lockScalingY: false,\n lockRotation: false,\n hasControls: true,\n },\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 // Grid phase shift. Clamped to one tile: the loop only carries one spare tile\n // of margin past the diagonal, so a larger shift would uncover an edge — the\n // one thing a pattern must not do. A whole-tile shift is a no-op anyway.\n const shiftX = tileW * (clampOffset(config.offsetX) / 100);\n const shiftY = tileH * (clampOffset(config.offsetY) / 100);\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 + shiftX;\n let y = j * tileH + shiftY;\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/** Grid phase shift, as % of tile size, bounded to ±1 tile (0 if not set). */\nfunction clampOffset(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 0;\n return Math.max(-100, Math.min(100, value));\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 /**\n * Phase shift of the whole tile grid inside the print area, as % of tile\n * width. The pattern object itself is pinned to the print area (it can't be\n * dragged — that would expose bare edges), so this is how the tiling is\n * nudged into alignment. Clamped to ±100% so coverage is never broken.\n */\n offsetX: number;\n /** Phase shift of the tile grid, as % of tile height. See {@link offsetX}. */\n offsetY: 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/** Fabric interaction flags frozen while a layer is rendered as a pattern. */\nexport interface PatternLocks {\n lockMovementX: boolean;\n lockMovementY: boolean;\n lockScalingX: boolean;\n lockScalingY: boolean;\n lockRotation: boolean;\n hasControls: boolean;\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 /**\n * Interaction flags the object carried before it became a pattern. A pattern\n * fills the whole print area, so while it is on the object is locked in place\n * (a drag/resize would slide the tiled bitmap off the area and leave a bare\n * band); the captured flags are restored when the pattern is turned off.\n */\n originalLocks?: PatternLocks;\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 offsetX: 0,\n offsetY: 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/utils/color.ts","../src/export.ts","../src/displacement.ts","../src/utils/svg.ts","../src/fonts.ts","../src/licensing.ts","../src/project.ts","../src/mask.ts","../src/types.ts","../src/presets.ts","../src/annotations.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 captureLocks,\n applyPatternLocks,\n restoreLocks,\n loadPatternImage,\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 { escapeXml, sanitizeSvg } from './utils/svg';\nexport { isCssColor } from './utils/color';\nexport { serializeEditor, deserializeEditor } from './serialization';\nexport {\n exportPNG,\n exportSVG,\n exportDataURL,\n exportMockup,\n computeCoverPlacement,\n computePrintAreaClip,\n} from './export';\nexport { DEFAULT_PATTERN_CONFIG } from './types';\nexport { CANVAS_SIZE_PRESETS } from './presets';\nexport { FontRegistry } from './fonts';\nexport { LicenseManager } from './licensing';\nexport { ProjectManager } from './project';\nexport { MaskController, MaskRefinementError } from './mask';\nexport type { MaskRefinementErrorCode, MaskPerformanceSample } from './mask';\nexport { AnnotationOverlay } from './annotations';\nexport type { AnnotationPrimitive, ViewportTransform } from './annotations';\nexport { displaceRgba } from './displacement';\n\nexport type {\n LayerType,\n LayerData,\n LayerMeta,\n SerializedLayer,\n EditorState,\n EditorConfig,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n ImageAttribution,\n ImageSearchOptions,\n ImageSearchResult,\n ExportFormat,\n ShapePlugin,\n TemplateDefinition,\n TemplateParameter,\n PrintifyPositioning,\n TileMode,\n PatternConfig,\n PatternState,\n PatternLocks,\n MockupConfig,\n MockupPrintArea,\n MockupBlendMode,\n MockupOverlay,\n MockupDisplacement,\n MockupDisplacementChannel,\n EditorEvents,\n Unit,\n ResizeOptions,\n DpiIssue,\n ImageAdjustments,\n CanvasSizePreset,\n PatternSourceResolver,\n BackgroundImageOptions,\n SerializedBackgroundImageOptions,\n SemanticExportOptions,\n MaskPoint,\n MaskBrushOptions,\n MaskRefinementPrompt,\n MaskRefinementRequest,\n MaskRefinementResult,\n MaskRefinementProvider,\n FontDefinition,\n SvgExportOptions,\n LicenseConfig,\n LicensePayload,\n LicenseStatus,\n NormalizedLayerPosition,\n PositioningAdapter,\n ProjectPage,\n ProjectState,\n} from './types';\n\nexport type { PngExportOptions, CoverPlacement } from './export';\n","import { Canvas, FabricImage, Group, Textbox, filters, loadSVGFromString, util } from 'fabric';\nimport type { FabricObject, ImageFormat } from 'fabric';\nimport type {\n EditorConfig,\n DpiIssue,\n BackgroundImageOptions,\n SemanticExportOptions,\n SerializedBackgroundImageOptions,\n SvgExportOptions,\n EditorEvents,\n EditorState,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n ImageSearchOptions,\n ImageSearchResult,\n ImageAdjustments,\n MockupConfig,\n PatternConfig,\n PrintifyPositioning,\n ResizeOptions,\n NormalizedLayerPosition,\n PositioningAdapter,\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, clearPatternImageCache } from './pattern';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL, exportMockup, exportIsolatedPNG } from './export';\nimport type { PngExportOptions } from './export';\nimport { clamp, round2 } from './utils/clamp';\nimport { escapeXml, sanitizeSvg } from './utils/svg';\nimport { isCssColor } from './utils/color';\nimport { FontRegistry } from './fonts';\nimport { LicenseManager } from './licensing';\nimport { ProjectManager } from './project';\nimport { MaskController } from './mask';\n\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 8;\n\nfunction isTaintedCanvasError(error: unknown): boolean {\n return (\n (error instanceof DOMException && error.name === 'SecurityError') ||\n (error instanceof Error && /taint|cross-origin|insecure/i.test(error.message))\n );\n}\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 readonly fonts: FontRegistry;\n readonly licensing: LicenseManager;\n readonly pages: ProjectManager;\n readonly masks: MaskController;\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 private designBackgroundImage: FabricObject | null = null;\n private backgroundImageOptions: SerializedBackgroundImageOptions | null = null;\n\n constructor(canvasElement: HTMLCanvasElement, config: EditorConfig) {\n this.events = new EventEmitter<EditorEvents>();\n this.fonts = new FontRegistry();\n config.fonts?.forEach((font) => this.fonts.register(font));\n this.licensing = new LicenseManager(config.license);\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 this.layers.setHistoryCallback(() => this.history.save());\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(\n this.canvas,\n this.layers,\n this.history,\n this.events,\n config.patternSourceResolver,\n );\n\n this.setupCanvasEvents();\n this.history.saveImmediate();\n this.pages = new ProjectManager(this);\n this.masks = new MaskController(this);\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 try {\n const img = await FabricImage.fromURL(\n url,\n {},\n { originX: 'left', originY: 'top', ...options },\n );\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n } catch (error) {\n this.events.emit('error', { message: 'Failed to add image', error });\n throw error;\n }\n }\n\n /** Replace an image source without changing its layer identity or visual transform. */\n async replaceImageSource(layerId: string, url: string): Promise<Layer> {\n const layer = this.layers.get(layerId);\n if (!layer || (layer.type !== 'image' && layer.type !== 'mask')) {\n throw new Error(`Image or mask layer not found: ${layerId}`);\n }\n if (layer.meta.pattern) {\n throw new Error('Clear the pattern before replacing the image source');\n }\n if (this.crop.activeLayerId() === layerId) this.crop.cancel();\n const previous = layer.fabricObject as FabricImage;\n try {\n const replacement = await FabricImage.fromURL(url, {}, { originX: 'left', originY: 'top' });\n replacement.set({\n left: previous.left,\n top: previous.top,\n originX: previous.originX,\n originY: previous.originY,\n width: previous.width,\n height: previous.height,\n cropX: previous.cropX,\n cropY: previous.cropY,\n scaleX: previous.scaleX,\n scaleY: previous.scaleY,\n angle: previous.angle,\n skewX: previous.skewX,\n skewY: previous.skewY,\n flipX: previous.flipX,\n flipY: previous.flipY,\n });\n replacement.filters = [...previous.filters];\n replacement.applyFilters();\n replacement.setCoords();\n this.layers.replaceObject(layerId, replacement);\n return layer;\n } catch (error) {\n this.events.emit('error', { message: 'Failed to replace image source', error });\n throw error;\n }\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 const values: Record<string, string | number> = {};\n for (const parameter of template.parameters) {\n const value = params[parameter.key] ?? parameter.default;\n if (value === undefined) throw new Error(`Missing template parameter: ${parameter.key}`);\n if (parameter.type === 'number' && !Number.isFinite(Number(value))) {\n throw new Error(`Invalid number for template parameter: ${parameter.key}`);\n }\n if (parameter.type === 'color' && !isCssColor(value)) {\n throw new Error(`Invalid color for template parameter: ${parameter.key}`);\n }\n values[parameter.key] = value;\n }\n const resolved = sanitizeSvg(\n template.svg.replace(/\\{\\{(\\w+)(?:[|:]([^}]*))?\\}\\}/g, (token, key: string, fallback) => {\n const value = values[key] ?? fallback;\n return value === undefined ? token : escapeXml(value);\n }),\n );\n const { objects, options } = await loadSVGFromString(resolved);\n const validObjects = objects.filter((object): object is FabricObject => object !== null);\n if (validObjects.length === 0) throw new Error('Template SVG contains no renderable objects');\n const group = util.groupSVGElements(validObjects, options);\n group.set({\n left: this.canvas.getWidth() / 2,\n top: this.canvas.getHeight() / 2,\n originX: 'center',\n originY: 'center',\n });\n const layer = this.layers.add('template', group, template.name);\n this.history.save();\n return layer;\n }\n\n removeLayer(id: string): void {\n if (this.crop.activeLayerId() === id) this.crop.cancel();\n if (this.masks.activeLayerId() === id) {\n this.masks.detach(id);\n }\n if (this.layers.remove(id)) 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 copy.visible = layer.visible;\n copy.locked = layer.locked;\n copy.opacity = layer.opacity;\n clone.set({\n visible: layer.visible,\n selectable: !layer.locked,\n evented: !layer.locked,\n opacity: layer.opacity,\n });\n\n this.canvas.setActiveObject(clone);\n this.canvas.requestRenderAll();\n this.history.save();\n return copy;\n }\n\n applyImageAdjustments(layerId: string, adjustments: ImageAdjustments): boolean {\n const layer = this.layers.get(layerId);\n if (!layer || layer.type !== 'image') return false;\n const image = layer.fabricObject as FabricImage;\n const previous = (layer.meta.imageAdjustments ?? {}) as ImageAdjustments;\n const next = { ...previous, ...adjustments };\n const clampAdjustment = (value: number | undefined, min = -1) => clamp(value ?? 0, min, 1);\n image.filters = [\n new filters.Brightness({ brightness: clampAdjustment(next.brightness) }),\n new filters.Contrast({ contrast: clampAdjustment(next.contrast) }),\n new filters.Saturation({ saturation: clampAdjustment(next.saturation) }),\n new filters.Blur({ blur: clampAdjustment(next.blur, 0) }),\n ];\n layer.meta.imageAdjustments = next;\n image.applyFilters();\n this.canvas.requestRenderAll();\n this.history.save();\n return true;\n }\n\n /** Combine two or more layers into a single editable group layer. */\n async groupLayers(ids: string[], name = 'Group'): Promise<Layer | null> {\n const uniqueIds = [...new Set(ids)];\n const children = uniqueIds\n .map((id) => this.layers.get(id))\n .filter((layer): layer is Layer => layer !== undefined);\n if (children.length < 2 || children.length !== uniqueIds.length) return null;\n\n return this.history.transaction(() => {\n const childData = children.map((layer) => structuredClone(layer.toData()));\n const objects = children.map((layer) => layer.fabricObject);\n for (const layer of children) this.layers.remove(layer.id);\n const group = new Group(objects);\n const grouped = this.layers.add('group', group, name);\n grouped.meta.groupChildren = childData;\n this.layers.select(grouped.id);\n this.history.save();\n return grouped;\n });\n }\n\n /** Restore a group created by groupLayers back to its original layer records. */\n async ungroupLayer(id: string): Promise<Layer[]> {\n const grouped = this.layers.get(id);\n const childData = grouped?.meta.groupChildren;\n if (!grouped || grouped.type !== 'group' || !Array.isArray(childData)) return [];\n const group = grouped.fabricObject as Group;\n\n return this.history.transaction(() => {\n const transform = group.calcTransformMatrix();\n const objects = group.removeAll();\n this.layers.remove(id);\n const restored = objects.map((object, index) => {\n util.addTransformToObject(object, transform);\n object.setCoords();\n const data = childData[index] as ReturnType<Layer['toData']> | undefined;\n const layer = this.layers.add(data?.type ?? 'group', object, data?.name, data?.id);\n if (data?.meta) layer.meta = structuredClone(data.meta);\n if (data && !data.visible) this.layers.setVisibility(layer.id, false);\n if (data?.locked) this.layers.setLocked(layer.id, true);\n if (data && data.opacity !== 1) this.layers.setOpacity(layer.id, data.opacity);\n return layer;\n });\n this.history.save();\n this.layers.select(restored[0]?.id ?? null);\n return restored;\n });\n }\n\n // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n const managedByHistory = this.history.isRestoring();\n if (!managedByHistory) {\n this.history.saveImmediate();\n this.history.pause();\n }\n try {\n await deserializeEditor(this, state);\n // A pattern must cover the whole print area. States saved before patterns\n // were pinned (or saved at a different display size) restore a tiled bitmap\n // that no longer lines up with the canvas — re-pin without re-rasterising.\n this.patterns.repinAll();\n } catch (error) {\n if (!managedByHistory) {\n this.events.emit('error', { message: 'Failed to load editor state', error });\n }\n throw error;\n } finally {\n if (!managedByHistory) this.history.resume();\n }\n if (!managedByHistory) {\n this.history.clear();\n this.history.saveImmediate();\n }\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n return this.toRaster((options?.format ?? 'png') as 'png' | 'jpeg' | 'webp', options);\n }\n\n async toJPEG(options?: Omit<PngExportOptions, 'format'>): Promise<Blob> {\n return this.toRaster('jpeg', options);\n }\n\n async toWebP(options?: Omit<PngExportOptions, 'format'>): Promise<Blob> {\n return this.toRaster('webp', options);\n }\n\n /** Export one layer in document coordinates or at its native image resolution. */\n async exportLayer(id: string, options: SemanticExportOptions = {}): Promise<Blob> {\n const layer = this.layers.get(id);\n if (!layer) throw new Error(`Layer not found: ${id}`);\n try {\n if (options.resolution === 'source' && layer.fabricObject instanceof FabricImage) {\n const image = await layer.fabricObject.clone();\n image.set({\n left: 0,\n top: 0,\n originX: 'left',\n originY: 'top',\n scaleX: 1,\n scaleY: 1,\n angle: 0,\n flipX: false,\n flipY: false,\n });\n return await exportIsolatedPNG(this.canvas, [image], {\n ...options,\n width: image.width || 1,\n height: image.height || 1,\n cloneObjects: false,\n });\n }\n return await exportIsolatedPNG(this.canvas, [layer.fabricObject], options);\n } catch (error) {\n this.events.emit('error', { message: `Failed to export layer: ${id}`, error });\n throw error;\n }\n }\n\n /** Export only the configured document background, excluding design layers. */\n async exportBackground(options: SemanticExportOptions = {}): Promise<Blob> {\n try {\n return await exportIsolatedPNG(this.canvas, [], {\n ...options,\n backgroundColor: this.designBackground,\n backgroundImage: this.designBackgroundImage,\n });\n } catch (error) {\n this.events.emit('error', { message: 'Failed to export background', error });\n throw error;\n }\n }\n\n private async toRaster(\n format: 'png' | 'jpeg' | 'webp',\n options?: PngExportOptions,\n ): Promise<Blob> {\n this.events.emit('export:start', { format });\n try {\n await this.fonts.ready();\n const blob = await this.withDesignBackground(() =>\n exportPNG(this.canvas, { ...options, format }),\n );\n this.events.emit('export:complete', { format });\n this.licensing.track(`export:${format}`);\n return blob;\n } catch (error) {\n this.events.emit('error', {\n message: isTaintedCanvasError(error)\n ? 'Canvas export was blocked by cross-origin image data; load remote images with CORS enabled'\n : `Failed to export ${format.toUpperCase()}`,\n error,\n });\n throw error;\n }\n }\n\n toSVG(): string {\n this.events.emit('export:start', { format: 'svg' });\n try {\n const svg = this.withDesignBackground(() => exportSVG(this.canvas));\n this.events.emit('export:complete', { format: 'svg' });\n this.licensing.track('export:svg');\n return svg;\n } catch (error) {\n this.events.emit('error', { message: 'Failed to export SVG', error });\n throw error;\n }\n }\n\n async toSVGAsync(options: SvgExportOptions = {}): Promise<string> {\n await this.fonts.ready();\n const svg = this.toSVG();\n if (options.embedFonts === false || this.fonts.getAll().length === 0) return svg;\n try {\n const css = await this.fonts.getEmbeddedCss();\n return svg.replace(/(<svg\\b[^>]*>)/i, `$1<defs><style>${css}</style></defs>`);\n } catch (error) {\n this.events.emit('error', { message: 'Failed to embed fonts in SVG', error });\n throw error;\n }\n }\n\n toDataURL(format: ImageFormat = 'png', multiplier = 1): string {\n return this.withDesignBackground(() => exportDataURL(this.canvas, format, multiplier));\n }\n\n /** Export the current product-preview composite. Advanced warping is host-defined. */\n async toMockupImage(options: PngExportOptions = {}): Promise<Blob> {\n if (!this.mockup) throw new Error('No mockup is configured');\n await this.fonts.ready();\n try {\n const blob = await exportMockup(this.canvas, this.mockup, options);\n this.licensing.track(`export:mockup:${options.format ?? 'png'}`);\n return blob;\n } catch (error) {\n this.events.emit('error', { message: 'Failed to export mockup', error });\n throw error;\n }\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 const previewImage = this.canvas.backgroundImage;\n this.canvas.backgroundColor = this.designBackground;\n this.canvas.backgroundImage = this.designBackgroundImage ?? undefined;\n try {\n return fn();\n } finally {\n this.canvas.backgroundColor = previewBg;\n this.canvas.backgroundImage = previewImage;\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 const key = result[layer.name] ? `${layer.name}-${layer.id}` : layer.name;\n result[key] = {\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 /** Provider-neutral, ID-stable placement data in 0..1 canvas coordinates. */\n toNormalizedPositioning(): NormalizedLayerPosition[] {\n const canvasWidth = this.canvas.getWidth();\n const canvasHeight = this.canvas.getHeight();\n return this.layers.getAll().map((layer) => {\n const object = layer.fabricObject;\n const center = object.getCenterPoint();\n const bounds = object.getBoundingRect();\n return {\n layerId: layer.id,\n name: layer.name,\n type: layer.type,\n centerX: round2(center.x / canvasWidth),\n centerY: round2(center.y / canvasHeight),\n width: round2(bounds.width / canvasWidth),\n height: round2(bounds.height / canvasHeight),\n scaleX: round2(object.scaleX ?? 1),\n scaleY: round2(object.scaleY ?? 1),\n angle: round2(object.angle ?? 0),\n };\n });\n }\n\n toProviderPositioning<T>(adapter: PositioningAdapter<T>): T {\n return adapter.map(this.toNormalizedPositioning(), {\n width: this.canvas.getWidth(),\n height: this.canvas.getHeight(),\n });\n }\n\n // ─── File Operations ────────────────────────────────\n\n async save(\n filename: string,\n format: 'png' | 'jpeg' | 'webp' | 'svg' | 'json',\n ): 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 === 'jpeg') {\n data = await this.toJPEG();\n } else if (format === 'webp') {\n data = await this.toWebP();\n } else if (format === 'svg') {\n data = await this.toSVGAsync();\n } else {\n data = JSON.stringify(this.toJSON(), null, 2);\n }\n\n try {\n return await this.fileAdapter.save(data, filename, format);\n } catch (error) {\n this.events.emit('error', { message: `Failed to save ${format}`, error });\n throw error;\n }\n }\n\n async uploadImage(file: File): Promise<ImageProviderResult | undefined> {\n if (!this.imageProvider?.upload) return undefined;\n try {\n return await this.imageProvider.upload(file);\n } catch (error) {\n this.events.emit('error', { message: 'Failed to upload image', error });\n throw error;\n }\n }\n\n async browseImages(): Promise<ImageProviderResult | null> {\n if (!this.imageProvider?.browse) return null;\n try {\n return await this.imageProvider.browse();\n } catch (error) {\n this.events.emit('error', { message: 'Failed to browse images', error });\n throw error;\n }\n }\n\n async searchImages(\n query: string,\n options?: ImageSearchOptions,\n ): Promise<ImageSearchResult | null> {\n if (!this.imageProvider?.search) return null;\n try {\n return await this.imageProvider.search(query, options);\n } catch (error) {\n this.events.emit('error', { message: 'Failed to search images', error });\n throw error;\n }\n }\n\n /** Track provider usage, then insert its hotlinked image as a normal layer. */\n async addProviderImage(\n image: ImageProviderResult,\n options?: Partial<Record<string, unknown>>,\n ): Promise<Layer> {\n try {\n await this.imageProvider?.trackUse?.(image);\n } catch (error) {\n this.events.emit('error', { message: 'Failed to record image use', error });\n throw error;\n }\n return this.addImage(image.url, options);\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 this.events.emit('canvas:modified', {});\n }\n\n getDesignBackground(): string {\n return this.designBackground;\n }\n\n getDesignBackgroundImage(): FabricObject | null {\n return this.designBackgroundImage;\n }\n\n getBackgroundImageOptions(): SerializedBackgroundImageOptions | null {\n return this.backgroundImageOptions ? { ...this.backgroundImageOptions } : null;\n }\n\n async setBackgroundImage(\n url: string | null,\n options: BackgroundImageOptions = {},\n ): Promise<void> {\n if (url === null) {\n this.setBackgroundImageObject(null);\n return;\n }\n try {\n const image = await FabricImage.fromURL(\n url,\n { crossOrigin: options.crossOrigin ?? null, signal: options.signal },\n { originX: 'left', originY: 'top' },\n );\n const width = image.width || 1;\n const height = image.height || 1;\n const canvasWidth = this.canvas.getWidth();\n const canvasHeight = this.canvas.getHeight();\n const fit = options.fit ?? 'cover';\n const sx = canvasWidth / width;\n const sy = canvasHeight / height;\n const scaleX =\n fit === 'stretch' ? sx : fit === 'contain' ? Math.min(sx, sy) : Math.max(sx, sy);\n const scaleY = fit === 'stretch' ? sy : scaleX;\n image.set({\n left: (canvasWidth - width * scaleX) / 2,\n top: (canvasHeight - height * scaleY) / 2,\n scaleX,\n scaleY,\n opacity: clamp(options.opacity ?? 1, 0, 1),\n selectable: false,\n evented: false,\n });\n const serializableOptions = { ...options };\n delete serializableOptions.signal;\n this.setBackgroundImageObject(image, true, serializableOptions);\n } catch (error) {\n this.events.emit('error', { message: 'Failed to set background image', error });\n throw error;\n }\n }\n\n /** Used by state restoration and advanced integrations with an existing Fabric object. */\n setBackgroundImageObject(\n image: FabricObject | null,\n save = true,\n options: SerializedBackgroundImageOptions | null = null,\n ): void {\n this.designBackgroundImage = image;\n this.backgroundImageOptions = image ? options : null;\n this.canvas.backgroundImage = this.mockup ? undefined : (image ?? undefined);\n this.canvas.requestRenderAll();\n if (save) this.history.save();\n this.events.emit('canvas:modified', {});\n }\n\n setTransparentBackground(): void {\n this.setBackground('');\n }\n\n resize(width: number, height: number, options: ResizeOptions = {}): void {\n const widthPx = this.units.toPixels(width);\n const heightPx = this.units.toPixels(height);\n const oldWidth = this.canvas.getWidth();\n const oldHeight = this.canvas.getHeight();\n const scaleContent = options.scaleContent ?? true;\n if (scaleContent && oldWidth > 0 && oldHeight > 0) {\n const sx = widthPx / oldWidth;\n const sy = heightPx / oldHeight;\n for (const layer of this.layers.getAll()) {\n if (layer.meta.pattern) continue;\n const object = layer.fabricObject;\n object.set({\n left: (object.left ?? 0) * sx,\n top: (object.top ?? 0) * sy,\n scaleX: (object.scaleX ?? 1) * sx,\n scaleY: (object.scaleY ?? 1) * sy,\n });\n object.setCoords();\n }\n if (this.designBackgroundImage) {\n this.designBackgroundImage.set({\n left: (this.designBackgroundImage.left ?? 0) * sx,\n top: (this.designBackgroundImage.top ?? 0) * sy,\n scaleX: (this.designBackgroundImage.scaleX ?? 1) * sx,\n scaleY: (this.designBackgroundImage.scaleY ?? 1) * sy,\n });\n this.designBackgroundImage.setCoords();\n }\n }\n this.canvas.setDimensions({ width: widthPx, height: heightPx });\n this.patterns.repinAll();\n this.canvas.requestRenderAll();\n this.history.save();\n this.events.emit('canvas:modified', {});\n }\n\n /** Effective source resolution for an image at its current physical size. */\n getImageDpi(layerId: string): number | null {\n const layer = this.layers.get(layerId);\n if (!layer || layer.type !== 'image') return null;\n const image = layer.fabricObject as FabricImage;\n const sourcePixels = image.width ?? 0;\n const displayedPixels = image.getScaledWidth();\n if (sourcePixels <= 0 || displayedPixels <= 0) return null;\n return round2(sourcePixels / (displayedPixels / this.units.getDpi()));\n }\n\n validateImageDpi(minimumDpi = 300): DpiIssue[] {\n return this.layers\n .getAll()\n .filter((layer) => layer.type === 'image' && !layer.meta.pattern)\n .flatMap((layer) => {\n const effectiveDpi = this.getImageDpi(layer.id);\n return effectiveDpi !== null && effectiveDpi < minimumDpi\n ? [{ layerId: layer.id, layerName: layer.name, effectiveDpi, minimumDpi }]\n : [];\n });\n }\n\n /** Render a small data-URL preview without changing document dimensions. */\n toThumbnail(maxWidth = 320, maxHeight = 320, format: ImageFormat = 'png'): string {\n const multiplier = Math.min(\n 1,\n maxWidth / this.canvas.getWidth(),\n maxHeight / this.canvas.getHeight(),\n );\n return this.toDataURL(format, multiplier);\n }\n\n bringToFront(id: string): boolean {\n return this.layers.reorder(id, this.layers.count() - 1);\n }\n\n sendToBack(id: string): boolean {\n return this.layers.reorder(id, 0);\n }\n\n bringForward(id: string): boolean {\n const index = this.layers.getAll().findIndex((layer) => layer.id === id);\n return index >= 0 && this.layers.reorder(id, index + 1);\n }\n\n sendBackward(id: string): boolean {\n const index = this.layers.getAll().findIndex((layer) => layer.id === id);\n return index >= 0 && this.layers.reorder(id, index - 1);\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 /** Zoom until the current selection fills the artboard viewport. */\n zoomToSelection(padding = 24): void {\n const active = this.canvas.getActiveObject();\n if (!active) return;\n active.setCoords();\n const bounds = active.getBoundingRect();\n if (bounds.width <= 0 || bounds.height <= 0) return;\n this.setZoom(\n Math.min(\n (this.canvas.getWidth() - padding * 2) / bounds.width,\n (this.canvas.getHeight() - padding * 2) / bounds.height,\n ),\n );\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.backgroundImage = mockup ? undefined : (this.designBackgroundImage ?? undefined);\n this.canvas.requestRenderAll();\n this.events.emit('mockup:changed', { mockup });\n this.history.save();\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.masks.dispose();\n this.snapping.dispose();\n this.crop.dispose();\n this.history.dispose();\n clearPatternImageCache();\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 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 private onPropertyChanged?: () => void;\n\n constructor(\n private canvas: Canvas,\n private events: EventEmitter<EditorEvents>,\n ) {}\n\n setHistoryCallback(callback: () => void): void {\n this.onPropertyChanged = callback;\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 this.onPropertyChanged?.();\n return true;\n }\n\n /** Replace a layer's render object while preserving its immutable ID and panel state. */\n replaceObject(id: string, fabricObject: FabricObject): boolean {\n const layer = this.get(id);\n if (!layer || layer.fabricObject === fabricObject) return false;\n const previous = layer.fabricObject;\n const stackIndex = this.canvas.getObjects().indexOf(previous);\n const wasActive = this.canvas.getActiveObject() === previous;\n this.canvas.remove(previous);\n layer.fabricObject = fabricObject;\n (fabricObject as FabricObject & { _layerId: string })._layerId = id;\n fabricObject.set({\n visible: layer.visible,\n opacity: layer.opacity,\n selectable: !layer.locked,\n evented: !layer.locked,\n });\n this.canvas.insertAt(Math.max(0, stackIndex), fabricObject);\n if (wasActive) this.canvas.setActiveObject(fabricObject);\n this.canvas.requestRenderAll();\n this.events.emit('layer:modified', { layerId: id });\n this.emitChanged();\n this.onPropertyChanged?.();\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 if (!Number.isFinite(newIndex)) return false;\n const clamped = Math.max(0, Math.min(this.layers.length - 1, Math.round(newIndex)));\n if (oldIndex === clamped) return false;\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 this.onPropertyChanged?.();\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 if (layer.visible === visible) return;\n layer.visible = visible;\n layer.fabricObject.visible = visible;\n this.canvas.requestRenderAll();\n this.emitChanged();\n this.onPropertyChanged?.();\n }\n\n setLocked(id: string, locked: boolean): void {\n const layer = this.get(id);\n if (!layer) return;\n if (layer.locked === locked) return;\n layer.locked = locked;\n layer.fabricObject.selectable = !locked;\n layer.fabricObject.evented = !locked;\n this.canvas.requestRenderAll();\n this.emitChanged();\n this.onPropertyChanged?.();\n }\n\n setOpacity(id: string, opacity: number): void {\n const layer = this.get(id);\n if (!layer || !Number.isFinite(opacity)) return;\n const next = Math.max(0, Math.min(1, opacity));\n if (layer.opacity === next) return;\n layer.opacity = next;\n layer.fabricObject.opacity = next;\n this.canvas.requestRenderAll();\n this.emitChanged();\n this.onPropertyChanged?.();\n }\n\n setName(id: string, name: string): void {\n const layer = this.get(id);\n if (!layer) return;\n if (layer.name === name) return;\n layer.name = name;\n this.emitChanged();\n this.onPropertyChanged?.();\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 static readonly ASSET_KEY = '__canvasEditorHistoryAsset';\n private undoStack: string[] = [];\n private redoStack: string[] = [];\n private assets = new Map<string, string>();\n private assetIds = new Map<string, string>();\n private nextAssetId = 1;\n private maxSize: number;\n private maxBytes: 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 private transactionDepth = 0;\n private transactionDirty = false;\n\n constructor(opts: {\n getState: () => string;\n restoreState: (state: string) => Promise<void>;\n events: EventEmitter<EditorEvents>;\n maxSize?: number;\n maxBytes?: 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.maxBytes = opts.maxBytes ?? 50 * 1024 * 1024;\n this.debounceMs = opts.debounceMs ?? 300;\n }\n\n save(): void {\n if (this.paused) return;\n if (this.transactionDepth > 0) {\n this.transactionDirty = true;\n return;\n }\n\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.saveImmediate();\n }, this.debounceMs);\n this.emitChanged();\n }\n\n saveImmediate(): void {\n if (this.paused) {\n this.cancelPending();\n return;\n }\n if (this.transactionDepth > 0) {\n this.transactionDirty = true;\n return;\n }\n this.cancelPending();\n const rawState = this.getState();\n const state = this.compactState(rawState);\n if (this.undoStack.at(-1) === state) {\n this.emitChanged();\n return;\n }\n this.undoStack.push(state);\n while (this.undoStack.length > this.maxSize) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.trimToBudget();\n this.events.emit('history:snapshot', {\n bytes: rawState.length * 2,\n totalBytes: this.snapshotBytes(),\n entries: this.undoStack.length,\n });\n this.emitChanged();\n }\n\n beginTransaction(): void {\n // Commit any prior debounced edit as the transaction's undo baseline.\n if (this.transactionDepth === 0 && this.debounceTimer) this.saveImmediate();\n this.transactionDepth += 1;\n }\n\n endTransaction(): void {\n if (this.transactionDepth === 0) return;\n this.transactionDepth -= 1;\n if (this.transactionDepth === 0 && this.transactionDirty) {\n this.transactionDirty = false;\n this.saveImmediate();\n }\n }\n\n async transaction<T>(operation: () => T | Promise<T>): Promise<T> {\n this.beginTransaction();\n try {\n return await operation();\n } finally {\n this.endTransaction();\n }\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 committed = this.undoStack.at(-1);\n if (!committed) return;\n const current = this.compactState(this.getState());\n\n // The stack's top is the current committed state. If a debounced edit is\n // still dirty, restore that top; otherwise step back to the previous item.\n const currentIsCommitted = current === committed;\n if (currentIsCommitted && this.undoStack.length < 2) return;\n const target = currentIsCommitted ? this.undoStack[this.undoStack.length - 2] : committed;\n\n this.paused = true;\n try {\n await this.restoreState(this.expandState(target));\n if (currentIsCommitted) this.undoStack.pop();\n this.redoStack.push(current);\n this.trimToBudget();\n } catch (error) {\n this.events.emit('error', { message: 'Failed to undo the last change', error });\n throw error;\n } finally {\n this.paused = false;\n this.emitChanged();\n }\n }\n\n async redo(): Promise<void> {\n this.cancelPending();\n const state = this.redoStack.at(-1);\n if (!state) return;\n\n this.paused = true;\n try {\n await this.restoreState(this.expandState(state));\n this.redoStack.pop();\n if (this.undoStack.at(-1) !== state) this.undoStack.push(state);\n this.trimToBudget();\n } catch (error) {\n this.events.emit('error', { message: 'Failed to redo the last change', error });\n throw error;\n } finally {\n this.paused = false;\n this.emitChanged();\n }\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 > 1 || this.debounceTimer !== null;\n }\n\n canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n clear(): void {\n // Drop any debounced save too, otherwise its timer fires after the clear\n // and pushes a snapshot into what the caller believes is empty history.\n this.cancelPending();\n this.undoStack = [];\n this.redoStack = [];\n this.assets.clear();\n this.assetIds.clear();\n this.emitChanged();\n }\n\n getSnapshotBytes(): number {\n return this.snapshotBytes();\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 this.cancelPending();\n this.transactionDepth = 0;\n this.transactionDirty = false;\n this.undoStack = [];\n this.redoStack = [];\n this.assets.clear();\n this.assetIds.clear();\n }\n\n private emitChanged(): void {\n this.events.emit('history:changed', {\n canUndo: this.canUndo(),\n canRedo: this.canRedo(),\n });\n }\n\n private snapshotBytes(): number {\n const stackBytes = [...this.undoStack, ...this.redoStack].reduce(\n (total, state) => total + state.length * 2,\n 0,\n );\n const assetBytes = [...this.assets.values()].reduce(\n (total, asset) => total + asset.length * 2,\n 0,\n );\n return stackBytes + assetBytes;\n }\n\n private trimToBudget(): void {\n // Prune first: entries dropped by the size cap, and every state discarded\n // with the redo stack, orphan their rasters. Without this the orphans stay\n // resident and inflate snapshotBytes until they evict live undo entries.\n this.pruneAssets();\n while (this.undoStack.length > 1 && this.snapshotBytes() > this.maxBytes) {\n this.undoStack.shift();\n this.pruneAssets();\n }\n while (this.redoStack.length > 1 && this.snapshotBytes() > this.maxBytes) {\n this.redoStack.shift();\n this.pruneAssets();\n }\n }\n\n /**\n * History is internal and can content-address large raster strings without\n * changing the public EditorState wire format. Unchanged images are retained\n * once even when dozens of snapshots reference them.\n */\n private compactState(state: string): string {\n let parsed: unknown;\n try {\n parsed = JSON.parse(state);\n } catch {\n return state;\n }\n const visit = (value: unknown): unknown => {\n if (typeof value === 'string' && /^data:image\\/(?:png|jpeg|webp);base64,/i.test(value)) {\n let id = this.assetIds.get(value);\n if (!id) {\n id = `a${this.nextAssetId++}`;\n this.assetIds.set(value, id);\n this.assets.set(id, value);\n }\n return { [HistoryManager.ASSET_KEY]: id };\n }\n if (Array.isArray(value)) return value.map(visit);\n if (!value || typeof value !== 'object') return value;\n return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, visit(entry)]));\n };\n return JSON.stringify(visit(parsed));\n }\n\n private expandState(state: string): string {\n let parsed: unknown;\n try {\n parsed = JSON.parse(state);\n } catch {\n return state;\n }\n const visit = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(visit);\n if (!value || typeof value !== 'object') return value;\n const record = value as Record<string, unknown>;\n const id = record[HistoryManager.ASSET_KEY];\n if (typeof id === 'string' && Object.keys(record).length === 1) {\n const asset = this.assets.get(id);\n if (!asset) throw new Error(`Missing history raster asset: ${id}`);\n return asset;\n }\n return Object.fromEntries(Object.entries(record).map(([key, entry]) => [key, visit(entry)]));\n };\n return JSON.stringify(visit(parsed));\n }\n\n /**\n * Runs on every commit, so it scans for the serialized reference marker\n * instead of re-parsing every snapshot — parsing multi-megabyte raster states\n * per save would cost more than the retention it reclaims. `JSON.stringify`\n * emits the marker verbatim; the same text inside user data is escaped and\n * therefore cannot match.\n */\n private pruneAssets(): void {\n if (this.assets.size === 0) return;\n const states = [...this.undoStack, ...this.redoStack];\n for (const [id, asset] of this.assets) {\n const marker = `\"${HistoryManager.ASSET_KEY}\":\"${id}\"`;\n if (states.some((state) => state.includes(marker))) continue;\n this.assets.delete(id);\n this.assetIds.delete(asset);\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 // Cropping is calculated in the normalized frame, then the original\n // orientation is restored so applying a crop never silently un-rotates it.\n if (s.prevAngle) {\n image.rotate(s.prevAngle);\n image.setCoords();\n }\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 { EditorEvents, PatternConfig, PatternLocks, PatternSourceResolver } from './types';\nimport type { EventEmitter } from './events';\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 private events: EventEmitter<EditorEvents>,\n private sourceResolver?: PatternSourceResolver,\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 originalLocks: captureLocks(image),\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 }).catch((error) => {\n this.events.emit('error', { message: 'Failed to apply image pattern', error });\n throw error;\n });\n }\n\n /**\n * Stretch every restored pattern layer back over the full print area and\n * re-freeze it, without re-rasterising: the baked bitmap keeps whatever size\n * it was saved at, so it is scaled (not re-tiled) to the current canvas. Used\n * after a state restore, where the canvas may be a different display size\n * than when the pattern was baked — and to repair states saved while a\n * pattern could still be dragged out of the print area.\n */\n repinAll(): void {\n const cw = this.canvas.getWidth();\n const ch = this.canvas.getHeight();\n let changed = false;\n for (const layer of this.layers.getAll()) {\n if (!layer.meta.pattern || layer.type !== 'image') continue;\n const image = layer.fabricObject as FabricImage;\n image.set({\n left: 0,\n top: 0,\n angle: 0,\n scaleX: cw / (image.width || cw),\n scaleY: ch / (image.height || ch),\n });\n applyPatternLocks(image);\n image.setCoords();\n changed = true;\n }\n if (changed) this.canvas.requestRenderAll();\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 // Hand the object back its move/scale/rotate handles (frozen while the\n // pattern was on so it stayed pinned to the print area).\n restoreLocks(image, state.originalLocks);\n image.setCoords();\n delete layer.meta.pattern;\n this.canvas.requestRenderAll();\n this.history.save();\n }).catch((error) => {\n this.events.emit('error', { message: 'Failed to clear image pattern', error });\n throw error;\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 this.sourceResolver,\n );\n\n await image.setSrc(dataUrl);\n // The tiled source is exactly cw×ch and drawn at 1:1 (scaleX/scaleY=1) from\n // the origin, so it fills the whole print area — on the first enable and on\n // every re-render. Placement is NOT preserved: a moved/rotated/scaled\n // pattern would slide the bitmap off the print area and leave a bare band,\n // which is exactly what a pattern must never do. Use `offsetX`/`offsetY` to\n // nudge the tiling and `angle` to rotate it instead.\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.\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 // Freeze the handles so a drag/resize can't undo the fill. The object stays\n // selectable (the pattern panel needs a selected layer to edit its config).\n applyPatternLocks(image);\n image.setCoords();\n this.canvas.requestRenderAll();\n }\n}\n\n/** Interaction flags to restore when a pattern is turned off. */\nexport function captureLocks(obj: FabricObject): PatternLocks {\n return {\n lockMovementX: obj.lockMovementX ?? false,\n lockMovementY: obj.lockMovementY ?? false,\n lockScalingX: obj.lockScalingX ?? false,\n lockScalingY: obj.lockScalingY ?? false,\n lockRotation: obj.lockRotation ?? false,\n hasControls: obj.hasControls ?? true,\n };\n}\n\n/** Pin a pattern object: no move, scale or rotate while the pattern is on. */\nexport function applyPatternLocks(obj: FabricObject): void {\n obj.set({\n lockMovementX: true,\n lockMovementY: true,\n lockScalingX: true,\n lockScalingY: true,\n lockRotation: true,\n hasControls: false,\n });\n}\n\n/**\n * Restore pre-pattern interaction flags. States saved before locking existed\n * carry no snapshot — fall back to a fully interactive object rather than\n * leaving it frozen forever.\n */\nexport function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void {\n obj.set(\n locks ?? {\n lockMovementX: false,\n lockMovementY: false,\n lockScalingX: false,\n lockScalingY: false,\n lockRotation: false,\n hasControls: true,\n },\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\nexport function loadPatternImage(\n src: string,\n resolver?: PatternSourceResolver,\n): 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).catch(async (originalError) => {\n if (!resolver) throw originalError;\n const resolved = await resolver(src);\n if (!resolved || resolved === src) {\n throw new Error('Pattern source resolver did not return a usable alternate URL', {\n cause: originalError,\n });\n }\n return decodeImage(resolved);\n });\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 sourceResolver?: PatternSourceResolver,\n): Promise<string> {\n const img = await loadPatternImage(src, sourceResolver);\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 // Grid phase shift. Clamped to one tile: the loop only carries one spare tile\n // of margin past the diagonal, so a larger shift would uncover an edge — the\n // one thing a pattern must not do. A whole-tile shift is a no-op anyway.\n const shiftX = tileW * (clampOffset(config.offsetX) / 100);\n const shiftY = tileH * (clampOffset(config.offsetY) / 100);\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 + shiftX;\n let y = j * tileH + shiftY;\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/** Grid phase shift, as % of tile size, bounded to ±1 tile (0 if not set). */\nfunction clampOffset(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 0;\n return Math.max(-100, Math.min(100, value));\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 private unit: Unit;\n private dpi: number;\n\n constructor(unit: Unit = 'px', dpi: number = 72) {\n this.unit = unit;\n this.dpi = 72;\n this.setDpi(dpi);\n }\n\n setUnit(unit: Unit): void {\n this.unit = unit;\n }\n\n setDpi(dpi: number): void {\n if (!Number.isFinite(dpi) || dpi <= 0) throw new Error('DPI must be a positive number');\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';\nimport { isCssColor } from './utils/color';\n\nconst VERSION = '2.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 unit: editor.units.getUnit(),\n dpi: editor.units.getDpi(),\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 backgroundImage: editor.getDesignBackgroundImage()\n ? (editor.getDesignBackgroundImage()!.toObject() as Record<string, unknown>)\n : null,\n backgroundImageOptions: editor.getBackgroundImageOptions(),\n mockup: editor.getMockup(),\n };\n}\n\nexport async function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void> {\n if (!state || !state.canvas || !Array.isArray(state.layers)) {\n throw new Error('Invalid editor state');\n }\n if (\n !Number.isFinite(state.canvas.width) ||\n !Number.isFinite(state.canvas.height) ||\n state.canvas.width <= 0 ||\n state.canvas.height <= 0 ||\n (state.canvas.unit !== undefined && !['px', 'mm', 'in'].includes(state.canvas.unit)) ||\n (state.canvas.dpi !== undefined &&\n (!Number.isFinite(state.canvas.dpi) || state.canvas.dpi <= 0))\n ) {\n throw new Error('Invalid editor canvas settings');\n }\n const major = Number.parseInt(state.version?.split('.')[0] ?? '1', 10);\n if (!Number.isFinite(major) || major > 2) {\n throw new Error(`Unsupported editor state version: ${state.version}`);\n }\n // The background is copied verbatim into exported SVG, so an untrusted state\n // must not be able to smuggle markup through it. Empty means transparent.\n if (state.background !== undefined && !isCssColor(state.background, true)) {\n throw new Error('Invalid editor background color');\n }\n\n // Enliven everything before touching the live document. A dead image URL or\n // malformed object must not turn a recoverable load failure into data loss.\n const staged = await Promise.all(\n state.layers.map(async (serialized) => {\n const fabricObject = (await util.enlivenObjects([serialized.fabricObject]))[0] as\n | FabricObject\n | undefined;\n if (!fabricObject) {\n const source = serialized.fabricObject.src;\n if (typeof source === 'string' && source.startsWith('blob:')) {\n throw new Error(`Failed to restore expired object URL: ${source}`);\n }\n throw new Error(`Failed to restore layer: ${serialized.id}`);\n }\n return { serialized, fabricObject };\n }),\n );\n const stagedBackground = state.backgroundImage\n ? ((await util.enlivenObjects([state.backgroundImage]))[0] as FabricObject | undefined)\n : null;\n if (state.backgroundImage && !stagedBackground) {\n const source = state.backgroundImage.src;\n if (typeof source === 'string' && source.startsWith('blob:')) {\n throw new Error(`Failed to restore expired background object URL: ${source}`);\n }\n throw new Error('Failed to restore background image');\n }\n\n editor.crop.cancel();\n editor.masks.detach();\n editor.layers.clear();\n if (state.canvas.unit) editor.units.setUnit(state.canvas.unit);\n if (state.canvas.dpi !== undefined) editor.units.setDpi(state.canvas.dpi);\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 !== undefined) {\n editor.setBackground(state.background);\n }\n editor.setBackgroundImageObject(\n stagedBackground ?? null,\n false,\n state.backgroundImageOptions ?? null,\n );\n\n // Missing on legacy states means there was no mockup.\n editor.setMockup(state.mockup ?? null);\n\n for (const item of staged) {\n restoreLayer(editor, item.serialized, item.fabricObject);\n }\n\n editor.canvas.requestRenderAll();\n}\n\nfunction restoreLayer(\n editor: CanvasEditor,\n serialized: SerializedLayer,\n fabricObject: FabricObject,\n): Layer {\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","// Hex, functional (rgb/rgba/hsl/hsla), or a bare keyword such as `red`.\n// Deliberately conservative: values reach canvas fill styles and are copied\n// verbatim into exported SVG, so anything that is not clearly a colour must be\n// rejected rather than passed through.\nconst CSS_COLOR = /^(?:#[\\da-f]{3,8}|(?:rgb|hsl)a?\\([\\d.%+\\-\\s,/]+\\)|[a-z]+)$/i;\n\n/** True for values safe to use as a canvas/SVG colour. Empty means transparent. */\nexport function isCssColor(value: unknown, allowEmpty = false): value is string {\n if (typeof value !== 'string') return false;\n const trimmed = value.trim();\n if (!trimmed) return allowEmpty;\n return CSS_COLOR.test(trimmed);\n}\n","import { StaticCanvas } from 'fabric';\nimport type { Canvas, FabricObject, ImageFormat } from 'fabric';\nimport type { MockupConfig, MockupPrintArea } from './types';\nimport { displaceRgba } from './displacement';\n\nexport interface PngExportOptions {\n multiplier?: number;\n format?: ImageFormat;\n quality?: number;\n}\n\nexport interface CoverPlacement {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\nexport function computePrintAreaClip(\n area: MockupPrintArea,\n scaleX: number,\n scaleY: number,\n targetWidth: number,\n targetHeight: number,\n): MockupPrintArea {\n const left = Math.max(0, Math.min(targetWidth, area.left * scaleX));\n const top = Math.max(0, Math.min(targetHeight, area.top * scaleY));\n const right = Math.max(left, Math.min(targetWidth, (area.left + area.width) * scaleX));\n const bottom = Math.max(top, Math.min(targetHeight, (area.top + area.height) * scaleY));\n return { left, top, width: right - left, height: bottom - top };\n}\n\n/** Object-fit: cover geometry, exported for deterministic preview/composite tests. */\nexport function computeCoverPlacement(\n sourceWidth: number,\n sourceHeight: number,\n targetWidth: number,\n targetHeight: number,\n): CoverPlacement {\n if (sourceWidth <= 0 || sourceHeight <= 0 || targetWidth <= 0 || targetHeight <= 0) {\n throw new Error('Cover dimensions must be positive');\n }\n const scale = Math.max(targetWidth / sourceWidth, targetHeight / sourceHeight);\n const width = sourceWidth * scale;\n const height = sourceHeight * scale;\n return {\n left: (targetWidth - width) / 2,\n top: (targetHeight - height) / 2,\n width,\n height,\n };\n}\n\nfunction canvasElementToBlob(\n output: HTMLCanvasElement,\n format: ImageFormat,\n quality: number,\n): Promise<Blob> {\n const mime = format === 'jpeg' ? 'image/jpeg' : `image/${format}`;\n return new Promise<Blob>((resolve, reject) => {\n output.toBlob(\n (blob) => (blob ? resolve(blob) : reject(new Error(`Failed to export ${format}`))),\n mime,\n quality,\n );\n });\n}\n\nexport async function exportPNG(canvas: Canvas, options: PngExportOptions = {}): Promise<Blob> {\n const { multiplier = 1, format = 'png' as ImageFormat, quality = 1 } = options;\n const output = canvas.toCanvasElement(multiplier);\n return canvasElementToBlob(output, format, quality);\n}\n\n/** Render cloned objects without mutating the live editor canvas. */\nexport async function exportIsolatedPNG(\n source: Canvas,\n objects: FabricObject[],\n options: PngExportOptions & {\n width?: number;\n height?: number;\n backgroundColor?: string;\n backgroundImage?: FabricObject | null;\n cloneObjects?: boolean;\n } = {},\n): Promise<Blob> {\n const element = source.lowerCanvasEl.ownerDocument.createElement('canvas');\n const canvas = new StaticCanvas(element, {\n width: options.width ?? source.getWidth(),\n height: options.height ?? source.getHeight(),\n backgroundColor: options.backgroundColor || undefined,\n });\n try {\n const clones =\n options.cloneObjects === false\n ? objects\n : await Promise.all(objects.map((object) => object.clone()));\n if (clones.length) canvas.add(...clones);\n if (options.backgroundImage) canvas.backgroundImage = await options.backgroundImage.clone();\n canvas.requestRenderAll();\n // Awaited, not returned: `finally` would otherwise dispose the canvas while\n // the export is still reading from it.\n return await exportPNG(canvas as unknown as Canvas, options);\n } finally {\n canvas.dispose();\n }\n}\n\n/** Rasterize the browser mockup preview together with the transparent design. */\nexport async function exportMockup(\n canvas: Canvas,\n mockup: MockupConfig,\n options: PngExportOptions = {},\n): Promise<Blob> {\n const { multiplier = 1, format = 'png' as ImageFormat, quality = 1 } = options;\n const design = canvas.toCanvasElement(multiplier);\n const output = design.ownerDocument.createElement('canvas');\n output.width = design.width;\n output.height = design.height;\n const context = output.getContext('2d');\n if (!context) throw new Error('2D canvas context is unavailable');\n\n const loadImage = (url: string) =>\n new Promise<HTMLImageElement>((resolve, reject) => {\n const element = new Image();\n element.crossOrigin = 'anonymous';\n element.onload = () => resolve(element);\n element.onerror = () => reject(new Error(`Failed to load mockup image: ${url}`));\n element.src = url;\n });\n const drawCover = (\n image: HTMLImageElement,\n targetContext: CanvasRenderingContext2D = context,\n ) => {\n const placement = computeCoverPlacement(\n image.naturalWidth || image.width,\n image.naturalHeight || image.height,\n output.width,\n output.height,\n );\n targetContext.drawImage(\n image,\n placement.left,\n placement.top,\n placement.width,\n placement.height,\n );\n };\n\n // Full-size scratch buffers, released explicitly in the finally below: a\n // detached canvas element can hold its backing store well past its last\n // reference, and a 4K mockup allocates three of them per export.\n const scratch: HTMLCanvasElement[] = [design];\n try {\n drawCover(await loadImage(mockup.image));\n let compositedDesign: CanvasImageSource = design;\n if (mockup.displacement) {\n const sourceContext = design.getContext('2d');\n if (!sourceContext) throw new Error('2D design context is unavailable');\n const mapCanvas = design.ownerDocument.createElement('canvas');\n scratch.push(mapCanvas);\n mapCanvas.width = design.width;\n mapCanvas.height = design.height;\n const mapContext = mapCanvas.getContext('2d');\n if (!mapContext) throw new Error('2D displacement-map context is unavailable');\n drawCover(await loadImage(mockup.displacement.image), mapContext);\n\n const warped = design.ownerDocument.createElement('canvas');\n scratch.push(warped);\n warped.width = design.width;\n warped.height = design.height;\n const warpedContext = warped.getContext('2d');\n if (!warpedContext) throw new Error('2D displaced-design context is unavailable');\n let sourcePixels: Uint8ClampedArray;\n let mapPixels: Uint8ClampedArray;\n try {\n sourcePixels = sourceContext.getImageData(0, 0, design.width, design.height).data;\n mapPixels = mapContext.getImageData(0, 0, design.width, design.height).data;\n } catch (error) {\n throw new Error('Failed to apply mockup displacement map; verify image CORS access', {\n cause: error,\n });\n }\n const pixels = displaceRgba(sourcePixels, mapPixels, design.width, design.height, {\n ...mockup.displacement,\n scaleX: (mockup.displacement.scaleX ?? 10) * multiplier,\n scaleY: (mockup.displacement.scaleY ?? 10) * multiplier,\n });\n const imageData = warpedContext.createImageData(design.width, design.height);\n imageData.data.set(pixels);\n warpedContext.putImageData(imageData, 0, 0);\n compositedDesign = warped;\n }\n context.save();\n if (mockup.printArea && mockup.clipToPrintArea !== false) {\n const clip = computePrintAreaClip(\n mockup.printArea,\n output.width / canvas.getWidth(),\n output.height / canvas.getHeight(),\n output.width,\n output.height,\n );\n context.beginPath();\n context.rect(clip.left, clip.top, clip.width, clip.height);\n context.clip();\n }\n context.globalAlpha = Math.max(0, Math.min(1, mockup.designOpacity ?? 1));\n context.globalCompositeOperation =\n !mockup.designBlendMode || mockup.designBlendMode === 'normal'\n ? 'source-over'\n : mockup.designBlendMode;\n context.drawImage(compositedDesign, 0, 0);\n context.restore();\n\n if (mockup.overlay) {\n context.save();\n context.globalAlpha = Math.max(0, Math.min(1, mockup.overlay.opacity ?? 1));\n context.globalCompositeOperation =\n mockup.overlay.blendMode === 'normal'\n ? 'source-over'\n : (mockup.overlay.blendMode ?? 'multiply');\n drawCover(await loadImage(mockup.overlay.image));\n context.restore();\n }\n return await canvasElementToBlob(output, format, quality);\n } finally {\n for (const element of scratch) {\n element.width = 0;\n element.height = 0;\n }\n }\n}\n\nexport function exportSVG(canvas: Canvas): string {\n return canvas.toSVG();\n}\n\nexport function exportDataURL(canvas: Canvas, format: ImageFormat = 'png', multiplier = 1): string {\n return canvas.toDataURL({ format, multiplier });\n}\n","import type { MockupDisplacement, MockupDisplacementChannel } from './types';\n\nconst CHANNEL_INDEX: Record<MockupDisplacementChannel, number> = {\n red: 0,\n green: 1,\n blue: 2,\n alpha: 3,\n};\n\nfunction finiteScale(value: number | undefined, fallback: number, label: string): number {\n const resolved = value ?? fallback;\n if (!Number.isFinite(resolved)) throw new Error(`${label} must be finite`);\n return resolved;\n}\n\nfunction sample(\n source: Uint8ClampedArray,\n width: number,\n height: number,\n x: number,\n y: number,\n channel: number,\n): number {\n const clampedX = Math.max(0, Math.min(width - 1, x));\n const clampedY = Math.max(0, Math.min(height - 1, y));\n const x0 = Math.floor(clampedX);\n const y0 = Math.floor(clampedY);\n const x1 = Math.min(width - 1, x0 + 1);\n const y1 = Math.min(height - 1, y0 + 1);\n const tx = clampedX - x0;\n const ty = clampedY - y0;\n const top =\n source[(y0 * width + x0) * 4 + channel] * (1 - tx) +\n source[(y0 * width + x1) * 4 + channel] * tx;\n const bottom =\n source[(y1 * width + x0) * 4 + channel] * (1 - tx) +\n source[(y1 * width + x1) * 4 + channel] * tx;\n return top * (1 - ty) + bottom * ty;\n}\n\n/**\n * Warp RGBA pixels with an equally sized channel map. A channel value of 128\n * is neutral; 0 and 255 move by the configured negative/positive maximum.\n */\nexport function displaceRgba(\n source: Uint8ClampedArray,\n map: Uint8ClampedArray,\n width: number,\n height: number,\n options: Omit<MockupDisplacement, 'image'>,\n): Uint8ClampedArray {\n if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {\n throw new Error('Displacement dimensions must be positive integers');\n }\n const expectedLength = width * height * 4;\n if (source.length !== expectedLength || map.length !== expectedLength) {\n throw new Error('Displacement source and map must match the requested dimensions');\n }\n\n const scaleX = finiteScale(options.scaleX, 10, 'Displacement scaleX');\n const scaleY = finiteScale(options.scaleY, 10, 'Displacement scaleY');\n const channelX = CHANNEL_INDEX[options.channelX ?? 'red'];\n const channelY = CHANNEL_INDEX[options.channelY ?? 'green'];\n const output = new Uint8ClampedArray(expectedLength);\n\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const offset = (y * width + x) * 4;\n const sourceX = x + ((map[offset + channelX] - 128) / 127) * scaleX;\n const sourceY = y + ((map[offset + channelY] - 128) / 127) * scaleY;\n for (let channel = 0; channel < 4; channel += 1) {\n output[offset + channel] = Math.round(\n sample(source, width, height, sourceX, sourceY, channel),\n );\n }\n }\n }\n return output;\n}\n","/** XML text/attribute escaping for values interpolated into an SVG document. */\nexport function escapeXml(value: string | number): string {\n return String(value)\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&apos;');\n}\n\n/**\n * Strip active and externally-loaded content from an SVG before it is parsed\n * into canvas objects.\n *\n * This is the single implementation of the control — every path that turns\n * untrusted SVG into layers must call it, so it is exported rather than\n * re-declared per package.\n */\nexport function sanitizeSvg(svg: string): string {\n const document = new DOMParser().parseFromString(svg, 'image/svg+xml');\n if (document.querySelector('parsererror')) throw new Error('Invalid template SVG');\n document\n .querySelectorAll('script, foreignObject, iframe, object, embed, link, style')\n .forEach((node) => node.remove());\n document.querySelectorAll('*').forEach((node) => {\n for (const attribute of [...node.attributes]) {\n const name = attribute.name.toLowerCase();\n const value = attribute.value.trim().toLowerCase();\n const isLink = name === 'href' || name === 'xlink:href' || name === 'src';\n const safeLink =\n value.startsWith('#') || /^data:image\\/(?:png|jpeg|webp|gif);base64,/.test(value);\n if (\n name.startsWith('on') ||\n (isLink && !safeLink) ||\n (/url\\s*\\(/.test(value) && !/url\\s*\\(\\s*['\"]?#/.test(value)) ||\n /(?:javascript:|expression\\s*\\()/.test(value)\n ) {\n node.removeAttribute(attribute.name);\n }\n }\n });\n return new XMLSerializer().serializeToString(document.documentElement);\n}\n","import type { FontDefinition } from './types';\n\nfunction fontSource(source: string): string {\n return /^(?:url|local)\\(/.test(source.trim()) ? source : `url(${JSON.stringify(source)})`;\n}\n\nfunction mimeForSource(source: string): string {\n const path = source.split(/[?#]/)[0].toLowerCase();\n if (path.endsWith('.woff2')) return 'font/woff2';\n if (path.endsWith('.woff')) return 'font/woff';\n if (path.endsWith('.otf')) return 'font/otf';\n return 'font/ttf';\n}\n\nfunction arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let index = 0; index < bytes.length; index += 0x8000) {\n binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000));\n }\n return btoa(binary);\n}\n\n/**\n * Quote a value for a CSS string token. JSON escaping covers quotes and\n * backslashes; `<`/`>`/`&` are additionally emitted as CSS hex escapes so a\n * crafted family name cannot close the `<style>` element when the exported SVG\n * is inlined into an HTML document.\n */\nfunction cssString(value: string): string {\n return JSON.stringify(value).replace(/[<>&]/g, (char) => `\\\\${char.charCodeAt(0).toString(16)} `);\n}\n\nconst SAFE_WEIGHT = /^(?:normal|bold|bolder|lighter|[1-9]\\d{0,2}(?:\\s+[1-9]\\d{0,2})?)$/i;\nconst SAFE_STYLE = /^(?:normal|italic|oblique(?:\\s+-?\\d+(?:\\.\\d+)?deg)?)$/i;\nconst SAFE_DISPLAY = /^(?:auto|block|swap|fallback|optional)$/i;\n\nfunction cssKeyword(value: string | undefined, pattern: RegExp, fallback: string): string {\n const trimmed = value?.trim();\n return trimmed && pattern.test(trimmed) ? trimmed : fallback;\n}\n\nfunction sourceUrl(source: string): string | null {\n const trimmed = source.trim();\n if (trimmed.startsWith('data:')) return trimmed;\n const match = trimmed.match(/^url\\(\\s*(['\"]?)(.*?)\\1\\s*\\)/i);\n if (match) return match[2];\n if (/^local\\(/i.test(trimmed)) return null;\n return trimmed;\n}\n\n/**\n * The font name inside a `local(...)` source, or null for any other form.\n * The name may not contain CSS punctuation, so a source that only *starts*\n * with `local(` cannot smuggle extra declarations through as a \"name\".\n */\nfunction localName(source: string): string | null {\n const match = source.trim().match(/^local\\(\\s*(['\"]?)([^)\"'{};]*)\\1\\s*\\)$/i);\n return match ? match[2].trim() || null : null;\n}\n\n/** Browser font loader and SVG embedding registry. */\nexport class FontRegistry {\n private definitions = new Map<string, FontDefinition>();\n private loads = new Map<string, Promise<FontFace>>();\n\n register(definition: FontDefinition): void {\n if (!definition.family.trim() || !definition.source.trim()) {\n throw new Error('Font family and source are required');\n }\n this.definitions.set(definition.family, { ...definition });\n this.loads.delete(definition.family);\n }\n\n unregister(family: string): boolean {\n this.loads.delete(family);\n return this.definitions.delete(family);\n }\n\n getAll(): FontDefinition[] {\n return [...this.definitions.values()].map((definition) => ({ ...definition }));\n }\n\n load(family: string): Promise<FontFace> {\n const cached = this.loads.get(family);\n if (cached) return cached;\n const definition = this.definitions.get(family);\n if (!definition) return Promise.reject(new Error(`Font is not registered: ${family}`));\n if (typeof FontFace === 'undefined' || typeof document === 'undefined') {\n return Promise.reject(new Error('Font loading requires a browser FontFace API'));\n }\n const promise = new FontFace(definition.family, fontSource(definition.source), {\n weight: definition.weight,\n style: definition.style,\n display: definition.display,\n })\n .load()\n .then((font) => {\n document.fonts.add(font);\n return font;\n });\n promise.catch(() => {\n if (this.loads.get(family) === promise) this.loads.delete(family);\n });\n this.loads.set(family, promise);\n return promise;\n }\n\n async ready(): Promise<void> {\n if (this.definitions.size === 0) return;\n await Promise.all([...this.definitions.keys()].map((family) => this.load(family)));\n await document.fonts.ready;\n }\n\n async getEmbeddedCss(): Promise<string> {\n const rules = await Promise.all(\n this.getAll().map(async (definition) => {\n // Every branch re-quotes the source rather than passing the registered\n // string through, so a definition can never inject raw CSS into the\n // `<style>` block this produces.\n const url = sourceUrl(definition.source);\n let cssSource: string;\n if (url && !url.startsWith('data:')) {\n const response = await fetch(url);\n if (!response.ok) throw new Error(`Failed to fetch font: ${url}`);\n const data = arrayBufferToBase64(await response.arrayBuffer());\n const mime = response.headers.get('content-type') || mimeForSource(url);\n cssSource = `url(${cssString(`data:${mime};base64,${data}`)})`;\n } else if (url) {\n cssSource = `url(${cssString(url)})`;\n } else {\n const name = localName(definition.source);\n if (!name) {\n throw new Error(`Unsupported font source for embedding: ${definition.family}`);\n }\n cssSource = `local(${cssString(name)})`;\n }\n const weight = cssKeyword(definition.weight, SAFE_WEIGHT, 'normal');\n const style = cssKeyword(definition.style, SAFE_STYLE, 'normal');\n const display = cssKeyword(definition.display, SAFE_DISPLAY, 'swap');\n return `@font-face{font-family:${cssString(definition.family)};src:${cssSource};font-weight:${weight};font-style:${style};font-display:${display}}`;\n }),\n );\n return rules.join('\\n');\n }\n}\n","import type { LicenseConfig, LicenseStatus } from './types';\n\nfunction domainMatches(hostname: string, pattern: string): boolean {\n const host = hostname.toLowerCase();\n const expected = pattern.toLowerCase();\n if (expected.startsWith('*.')) {\n const suffix = expected.slice(1);\n return host.endsWith(suffix) && host.length > suffix.length;\n }\n return host === expected;\n}\n\nfunction isLocal(hostname: string): boolean {\n return (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '::1' ||\n hostname.endsWith('.localhost')\n );\n}\n\n/** Inert, honor-system licensing and usage seam; it never performs a network request. */\nexport class LicenseManager {\n private status: LicenseStatus;\n private readyPromise: Promise<LicenseStatus>;\n\n constructor(private config: LicenseConfig = {}) {\n const hostname = config.hostname ?? globalThis.location?.hostname ?? 'localhost';\n const environment = config.environment ?? 'development';\n if (environment !== 'production' || isLocal(hostname)) {\n this.status = { state: 'exempt', payload: null };\n this.readyPromise = Promise.resolve(this.status);\n } else if (!config.key) {\n this.status = { state: 'community', payload: null };\n this.readyPromise = Promise.resolve(this.status);\n } else {\n this.status = { state: 'checking', payload: null };\n this.readyPromise = this.validate(config.key, hostname);\n }\n }\n\n getStatus(): LicenseStatus {\n return this.status;\n }\n\n ready(): Promise<LicenseStatus> {\n return this.readyPromise;\n }\n\n hasFeature(feature: string): boolean {\n return this.status.state === 'valid' && (this.status.payload.features ?? []).includes(feature);\n }\n\n track(name: string): void {\n this.config.onUsage?.({\n name,\n at: new Date().toISOString(),\n licenseId: this.status.state === 'valid' ? this.status.payload.id : undefined,\n });\n }\n\n private async validate(key: string, hostname: string): Promise<LicenseStatus> {\n const payload = (await this.config.verifyOffline?.(key)) ?? null;\n if (!payload) return (this.status = { state: 'invalid', payload: null });\n if (payload.expiresAt && Date.parse(payload.expiresAt) < Date.now()) {\n return (this.status = { state: 'expired', payload });\n }\n if (!payload.domains.some((domain) => domainMatches(hostname, domain))) {\n return (this.status = { state: 'domain-mismatch', payload });\n }\n return (this.status = { state: 'valid', payload });\n }\n}\n","import type { CanvasEditor } from './editor';\nimport type { EditorState, ProjectPage, ProjectState } from './types';\nimport { generateId } from './utils/id';\n\n/** Multi-page document state bound to one live editor canvas. */\nexport class ProjectManager {\n private pages: ProjectPage[];\n private activePageId: string;\n\n constructor(private editor: CanvasEditor) {\n const first = { id: generateId(), name: 'Page 1', state: editor.toJSON() };\n this.pages = [first];\n this.activePageId = first.id;\n }\n\n getAll(): Array<{ id: string; name: string }> {\n return this.pages.map(({ id, name }) => ({ id, name }));\n }\n\n getActivePageId(): string {\n return this.activePageId;\n }\n\n add(name = `Page ${this.pages.length + 1}`, cloneCurrent = false): string {\n this.saveCurrent();\n const source = structuredClone(this.editor.toJSON());\n const state = cloneCurrent ? source : this.blankState(source);\n const page = { id: generateId(), name, state };\n this.pages.push(page);\n this.emitChanged();\n return page.id;\n }\n\n async switchTo(id: string): Promise<boolean> {\n if (id === this.activePageId) return true;\n const page = this.pages.find((candidate) => candidate.id === id);\n if (!page) return false;\n this.saveCurrent();\n await this.editor.fromJSON(structuredClone(page.state));\n this.activePageId = id;\n this.emitChanged();\n return true;\n }\n\n async duplicate(id: string): Promise<string | null> {\n this.saveCurrent();\n const source = this.pages.find((page) => page.id === id);\n if (!source) return null;\n const page = {\n id: generateId(),\n name: `${source.name} copy`,\n state: structuredClone(source.state),\n };\n const index = this.pages.indexOf(source);\n this.pages.splice(index + 1, 0, page);\n this.emitChanged();\n return page.id;\n }\n\n async remove(id: string): Promise<boolean> {\n if (this.pages.length === 1) return false;\n const index = this.pages.findIndex((page) => page.id === id);\n if (index < 0) return false;\n if (id === this.activePageId) {\n const next = this.pages[index + 1] ?? this.pages[index - 1];\n await this.editor.fromJSON(structuredClone(next.state));\n this.activePageId = next.id;\n }\n this.pages.splice(index, 1);\n this.emitChanged();\n return true;\n }\n\n rename(id: string, name: string): boolean {\n const page = this.pages.find((candidate) => candidate.id === id);\n const trimmed = name.trim();\n if (!page || !trimmed || page.name === trimmed) return false;\n page.name = trimmed;\n this.emitChanged();\n return true;\n }\n\n reorder(id: string, newIndex: number): boolean {\n const index = this.pages.findIndex((page) => page.id === id);\n if (index < 0 || !Number.isFinite(newIndex)) return false;\n const target = Math.max(0, Math.min(this.pages.length - 1, Math.round(newIndex)));\n if (target === index) return false;\n const [page] = this.pages.splice(index, 1);\n this.pages.splice(target, 0, page);\n this.emitChanged();\n return true;\n }\n\n toJSON(): ProjectState {\n this.saveCurrent();\n return {\n version: '1.0.0',\n activePageId: this.activePageId,\n pages: structuredClone(this.pages),\n };\n }\n\n async fromJSON(project: ProjectState): Promise<void> {\n if (\n project?.version !== '1.0.0' ||\n !Array.isArray(project.pages) ||\n project.pages.length === 0 ||\n !project.pages.some((page) => page.id === project.activePageId)\n ) {\n throw new Error('Invalid project state');\n }\n const pages = structuredClone(project.pages);\n const active = pages.find((page) => page.id === project.activePageId)!;\n await this.editor.fromJSON(structuredClone(active.state));\n this.pages = pages;\n this.activePageId = active.id;\n this.emitChanged();\n }\n\n private saveCurrent(): void {\n const page = this.pages.find((candidate) => candidate.id === this.activePageId);\n if (page) page.state = structuredClone(this.editor.toJSON());\n }\n\n private blankState(source: EditorState): EditorState {\n return { ...source, layers: [], mockup: null };\n }\n\n private emitChanged(): void {\n this.editor.events.emit('project:changed', {\n activePageId: this.activePageId,\n pages: this.getAll(),\n });\n }\n}\n","import { FabricImage } from 'fabric';\nimport type { CanvasEditor } from './editor';\nimport type {\n MaskBrushOptions,\n MaskPoint,\n MaskRefinementPrompt,\n MaskRefinementProvider,\n MaskRefinementResult,\n} from './types';\nimport type { Layer } from './layer';\nimport { clamp } from './utils/clamp';\n\nexport type MaskRefinementErrorCode = 'not-found' | 'cancelled' | 'provider' | 'invalid-result';\n\nexport class MaskRefinementError extends Error {\n constructor(\n readonly code: MaskRefinementErrorCode,\n message: string,\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = 'MaskRefinementError';\n }\n}\n\nexport interface MaskPerformanceSample {\n width: number;\n height: number;\n backingBytes: number;\n strokeBackupBytes: number;\n estimatedPeakBytes: number;\n historyBytes: number;\n interactionLatencyMs: number;\n usedJsHeapBytes?: number;\n elapsedMs: number;\n}\n\n/** Raster-mask editing without exposing Fabric brush classes in the public contract. */\nexport class MaskController {\n private backing: HTMLCanvasElement | null = null;\n private context: CanvasRenderingContext2D | null = null;\n private layerId: string | null = null;\n private brush: MaskBrushOptions | null = null;\n private previousPoint: MaskPoint | null = null;\n private strokeBackup: ImageData | null = null;\n private strokeStartedAt: number | null = null;\n private lastInteractionLatencyMs = 0;\n private refinement: AbortController | null = null;\n private disposed = false;\n\n constructor(private readonly editor: CanvasEditor) {}\n\n async create(\n width = this.editor.canvas.getWidth(),\n height = this.editor.canvas.getHeight(),\n ): Promise<Layer> {\n this.assertActive();\n if (!Number.isInteger(width) || !Number.isInteger(height) || width <= 0 || height <= 0) {\n throw new Error('Mask dimensions must be positive integers');\n }\n const backing = this.makeCanvas(width, height);\n const image = new FabricImage(backing, {\n left: 0,\n top: 0,\n originX: 'left',\n originY: 'top',\n selectable: false,\n });\n const layer = this.editor.layers.add('mask', image, 'Mask');\n layer.meta.mask = { width, height, revision: 0 };\n this.editor.history.saveImmediate();\n this.attachBacking(layer.id, backing);\n return layer;\n }\n\n attach(layerId: string): void {\n this.assertActive();\n // Swapping the backing store mid-stroke would leave the brush painting into\n // a canvas whose rollback snapshot belongs to the previous attachment.\n this.cancelStroke();\n const layer = this.requireMask(layerId);\n const image = layer.fabricObject as FabricImage;\n const width = layer.meta.mask?.width ?? image.width ?? this.editor.canvas.getWidth();\n const height = layer.meta.mask?.height ?? image.height ?? this.editor.canvas.getHeight();\n const backing = this.makeCanvas(width, height);\n const context = backing.getContext('2d');\n if (!context) throw new Error('2D mask context is unavailable');\n const element = image.getElement();\n if (element) context.drawImage(element, 0, 0, width, height);\n this.attachBacking(layerId, backing);\n }\n\n beginStroke(options: MaskBrushOptions): void {\n this.assertActive();\n if (!this.context || !this.backing || !this.layerId) throw new Error('Attach a mask first');\n if (this.brush) throw new Error('A mask stroke is already active');\n if (!Number.isFinite(options.size) || options.size <= 0) {\n throw new Error('Mask brush size must be positive');\n }\n this.brush = { ...options, hardness: clamp(options.hardness, 0, 1) };\n this.previousPoint = null;\n this.strokeBackup = this.context.getImageData(0, 0, this.backing.width, this.backing.height);\n this.strokeStartedAt = performance.now();\n }\n\n addPoint(point: MaskPoint): void {\n if (!this.brush || !this.context || !this.backing || !this.layerId) {\n throw new Error('No active mask stroke');\n }\n if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) return;\n const previous = this.previousPoint ?? point;\n const distance = Math.hypot(point.x - previous.x, point.y - previous.y);\n const step = Math.max(1, this.brush.size / 4);\n const samples = Math.max(1, Math.ceil(distance / step));\n for (let index = 0; index <= samples; index += 1) {\n const ratio = index / samples;\n this.drawDot({\n x: previous.x + (point.x - previous.x) * ratio,\n y: previous.y + (point.y - previous.y) * ratio,\n });\n }\n this.previousPoint = point;\n (this.requireMask(this.layerId).fabricObject as FabricImage).setElement(this.backing);\n this.editor.canvas.requestRenderAll();\n }\n\n async endStroke(): Promise<void> {\n if (!this.brush || !this.backing || !this.layerId) return;\n const layerId = this.layerId;\n this.brush = null;\n this.previousPoint = null;\n this.strokeBackup = null;\n const dataUrl = this.backing.toDataURL('image/png');\n await this.editor.history.transaction(async () => {\n await this.editor.replaceImageSource(layerId, dataUrl);\n const layer = this.requireMask(layerId);\n if (layer.meta.mask) layer.meta.mask.revision += 1;\n });\n this.attach(layerId);\n if (this.strokeStartedAt !== null) {\n this.lastInteractionLatencyMs = performance.now() - this.strokeStartedAt;\n this.strokeStartedAt = null;\n }\n }\n\n cancelStroke(): void {\n if (this.strokeBackup && this.context && this.backing && this.layerId) {\n this.context.putImageData(this.strokeBackup, 0, 0);\n (this.requireMask(this.layerId).fabricObject as FabricImage).setElement(this.backing);\n this.editor.canvas.requestRenderAll();\n }\n this.brush = null;\n this.previousPoint = null;\n this.strokeBackup = null;\n this.strokeStartedAt = null;\n }\n\n isStrokeActive(): boolean {\n return this.brush !== null;\n }\n\n activeLayerId(): string | null {\n return this.layerId;\n }\n\n detach(layerId?: string): void {\n if (layerId && this.layerId !== layerId) return;\n this.cancelStroke();\n this.cancelRefinement();\n this.backing = null;\n this.context = null;\n this.layerId = null;\n }\n\n async refine(\n layerId: string,\n provider: MaskRefinementProvider,\n prompts: MaskRefinementPrompt[],\n options: { signal?: AbortSignal; onProgress?: (progress: number) => void } = {},\n ): Promise<MaskRefinementResult> {\n this.assertActive();\n const layer = this.editor.layers.get(layerId);\n if (!layer || layer.type !== 'mask') {\n throw new MaskRefinementError('not-found', `Mask layer not found: ${layerId}`);\n }\n this.cancelRefinement();\n const controller = new AbortController();\n this.refinement = controller;\n const abort = () => controller.abort(options.signal?.reason);\n options.signal?.addEventListener('abort', abort, { once: true });\n if (options.signal?.aborted) abort();\n try {\n const image = layer.fabricObject as FabricImage;\n const result = await provider.refine(\n {\n mask: image.getSrc(),\n width: layer.meta.mask?.width ?? image.width ?? 1,\n height: layer.meta.mask?.height ?? image.height ?? 1,\n prompts: structuredClone(prompts),\n },\n { signal: controller.signal, onProgress: options.onProgress },\n );\n if (controller.signal.aborted)\n throw new MaskRefinementError('cancelled', 'Mask refinement cancelled');\n // Raster only: history content-addresses raster data URLs, and a vector\n // result would let a third-party provider inject markup into the document.\n if (!/^data:image\\/(?:png|jpeg|webp);base64,/i.test(result.dataUrl)) {\n throw new MaskRefinementError(\n 'invalid-result',\n 'Mask refinement must return a base64 PNG, JPEG, or WebP data URL',\n );\n }\n await this.editor.history.transaction(async () => {\n await this.editor.replaceImageSource(layerId, result.dataUrl);\n if (layer.meta.mask) layer.meta.mask.revision += 1;\n });\n this.attach(layerId);\n return result;\n } catch (error) {\n if (error instanceof MaskRefinementError) throw error;\n if (controller.signal.aborted) {\n throw new MaskRefinementError('cancelled', 'Mask refinement cancelled', error);\n }\n throw new MaskRefinementError('provider', 'Mask refinement provider failed', error);\n } finally {\n options.signal?.removeEventListener('abort', abort);\n if (this.refinement === controller) this.refinement = null;\n }\n }\n\n cancelRefinement(): void {\n this.refinement?.abort(new DOMException('Cancelled', 'AbortError'));\n this.refinement = null;\n }\n\n measure(): MaskPerformanceSample | null {\n if (!this.backing) return null;\n const started = performance.now();\n this.context?.getImageData(0, 0, 1, 1);\n const backingBytes = this.backing.width * this.backing.height * 4;\n const strokeBackupBytes = this.strokeBackup ? backingBytes : 0;\n const memory = performance as Performance & { memory?: { usedJSHeapSize?: number } };\n return {\n width: this.backing.width,\n height: this.backing.height,\n backingBytes,\n strokeBackupBytes,\n // The backing store and rollback ImageData dominate interactive mask\n // memory. Encoded historical rasters are reported separately below.\n estimatedPeakBytes: backingBytes + strokeBackupBytes,\n historyBytes: this.editor.history.getSnapshotBytes(),\n interactionLatencyMs: this.lastInteractionLatencyMs,\n ...(typeof memory.memory?.usedJSHeapSize === 'number'\n ? { usedJsHeapBytes: memory.memory.usedJSHeapSize }\n : {}),\n elapsedMs: performance.now() - started,\n };\n }\n\n dispose(): void {\n this.detach();\n this.disposed = true;\n }\n\n private drawDot(point: MaskPoint): void {\n const context = this.context!;\n const brush = this.brush!;\n const radius = brush.size / 2;\n context.save();\n context.globalCompositeOperation =\n brush.mode === 'subtract' ? 'destination-out' : 'source-over';\n const gradient = context.createRadialGradient(\n point.x,\n point.y,\n radius * brush.hardness,\n point.x,\n point.y,\n radius,\n );\n const color = brush.mode === 'subtract' ? 'rgba(0,0,0,1)' : 'rgba(255,255,255,1)';\n gradient.addColorStop(0, color);\n gradient.addColorStop(1, 'rgba(255,255,255,0)');\n context.fillStyle = gradient;\n context.beginPath();\n context.arc(point.x, point.y, radius, 0, Math.PI * 2);\n context.fill();\n context.restore();\n }\n\n private makeCanvas(width: number, height: number): HTMLCanvasElement {\n const canvas = this.editor.canvas.lowerCanvasEl.ownerDocument.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return canvas;\n }\n\n private attachBacking(layerId: string, backing: HTMLCanvasElement): void {\n const context = backing.getContext('2d');\n if (!context) throw new Error('2D mask context is unavailable');\n this.layerId = layerId;\n this.backing = backing;\n this.context = context;\n }\n\n private requireMask(layerId: string): Layer {\n const layer = this.editor.layers.get(layerId);\n if (!layer || layer.type !== 'mask') throw new Error(`Mask layer not found: ${layerId}`);\n return layer;\n }\n\n private assertActive(): void {\n if (this.disposed) throw new Error('Mask controller has been disposed');\n }\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' | 'mask' | '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 mask?: { width: number; height: number; revision: number };\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; unit?: Unit; dpi?: number };\n layers: SerializedLayer[];\n background?: string;\n backgroundImage?: Record<string, unknown> | null;\n backgroundImageOptions?: SerializedBackgroundImageOptions | null;\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 /** Resolve a non-CORS image through a trusted proxy or same-origin store for pattern rendering. */\n patternSourceResolver?: PatternSourceResolver;\n fonts?: FontDefinition[];\n license?: LicenseConfig;\n}\n\n// ─── Adapters ────────────────────────────────────────────\n\nexport type ExportFormat = 'png' | 'jpeg' | 'webp' | 'svg' | 'json';\n\nexport interface ResizeOptions {\n /** Scale layer positions and dimensions with the canvas. Defaults to true. */\n scaleContent?: boolean;\n}\n\nexport interface BackgroundImageOptions {\n fit?: 'cover' | 'contain' | 'stretch';\n opacity?: number;\n /** Fabric/HTML image loading mode for remote assets. */\n crossOrigin?: '' | 'anonymous' | 'use-credentials' | null;\n /** Cancels the current load; never serialized. */\n signal?: AbortSignal;\n}\n\nexport type SerializedBackgroundImageOptions = Omit<BackgroundImageOptions, 'signal'>;\n\nexport interface SemanticExportOptions extends PngLikeExportOptions {\n /** Document keeps full-canvas coordinates; source returns native image pixels. */\n resolution?: 'document' | 'source';\n}\n\nexport interface MaskPoint {\n x: number;\n y: number;\n}\n\nexport interface MaskBrushOptions {\n mode: 'add' | 'subtract';\n size: number;\n /** Soft-edge inner radius ratio, from 0 (soft) to 1 (hard). */\n hardness: number;\n}\n\nexport type MaskRefinementPrompt =\n | { type: 'point'; x: number; y: number; label: 'foreground' | 'background' }\n | { type: 'rectangle'; left: number; top: number; width: number; height: number }\n | { type: 'text'; value: string };\n\nexport interface MaskRefinementRequest {\n mask: string;\n width: number;\n height: number;\n prompts: MaskRefinementPrompt[];\n}\n\nexport interface MaskRefinementResult {\n dataUrl: string;\n width?: number;\n height?: number;\n metadata?: Record<string, unknown>;\n}\n\nexport interface MaskRefinementProvider {\n refine(\n request: MaskRefinementRequest,\n options?: {\n signal?: AbortSignal;\n onProgress?: (progress: number) => void;\n },\n ): Promise<MaskRefinementResult>;\n}\n\n/** Kept structural to avoid coupling the public types module to export.ts. */\nexport interface PngLikeExportOptions {\n multiplier?: number;\n quality?: number;\n}\n\nexport interface DpiIssue {\n layerId: string;\n layerName: string;\n effectiveDpi: number;\n minimumDpi: number;\n}\n\nexport interface ImageAdjustments {\n /** Range -1 to 1. */\n brightness?: number;\n /** Range -1 to 1. */\n contrast?: number;\n /** Range -1 to 1. */\n saturation?: number;\n /** Range 0 to 1. */\n blur?: number;\n}\n\nexport type PatternSourceResolver = (source: string) => Promise<string>;\n\nexport interface FontDefinition {\n family: string;\n /** URL, data URL, or a complete CSS FontFace source such as `local(...)`. */\n source: string;\n weight?: string;\n style?: string;\n display?: FontDisplay;\n}\n\nexport interface SvgExportOptions {\n /** Embed registered URL/data fonts into the SVG. Defaults to true. */\n embedFonts?: boolean;\n}\n\nexport interface LicensePayload {\n id: string;\n domains: string[];\n expiresAt?: string;\n features?: string[];\n}\n\nexport type LicenseStatus =\n | { state: 'community' | 'exempt' | 'checking'; payload: null }\n | { state: 'valid'; payload: LicensePayload }\n | { state: 'invalid' | 'expired' | 'domain-mismatch'; payload: LicensePayload | null };\n\nexport interface LicenseConfig {\n key?: string;\n /** Offline signature decoder/verifier supplied by the commercial distribution. */\n verifyOffline?: (key: string) => Promise<LicensePayload | null> | LicensePayload | null;\n hostname?: string;\n environment?: 'development' | 'test' | 'production';\n onUsage?: (event: { name: string; at: string; licenseId?: string }) => void;\n}\n\nexport interface CanvasSizePreset {\n id: string;\n name: string;\n width: number;\n height: number;\n unit: Unit;\n dpi: number;\n}\n\nexport interface FileAdapter {\n save(data: Blob | string, filename: string, format: ExportFormat): Promise<string>;\n}\n\nexport interface ImageProviderResult {\n id?: string;\n url: string;\n width: number;\n height: number;\n previewUrl?: string;\n thumbnailUrl?: string;\n alt?: string;\n sourceUrl?: string;\n attribution?: ImageAttribution;\n /** Provider event endpoint retained for use tracking; never render this as an image. */\n trackingUrl?: string;\n}\n\nexport interface ImageAttribution {\n name: string;\n url: string;\n provider?: string;\n}\n\nexport interface ImageSearchOptions {\n page?: number;\n perPage?: number;\n orientation?: 'landscape' | 'portrait' | 'squarish';\n orderBy?: 'relevant' | 'latest';\n contentFilter?: 'low' | 'high';\n}\n\nexport interface ImageSearchResult {\n items: ImageProviderResult[];\n page: number;\n total: number;\n totalPages: number;\n}\n\nexport interface ImageProvider {\n upload?(file: File): Promise<ImageProviderResult>;\n browse?(): Promise<ImageProviderResult | null>;\n search?(query: string, options?: ImageSearchOptions): Promise<ImageSearchResult>;\n /** Called immediately before a provider asset is inserted into the design. */\n trackUse?(image: ImageProviderResult): Promise<void>;\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\nexport interface NormalizedLayerPosition {\n layerId: string;\n name: string;\n type: LayerType;\n centerX: number;\n centerY: number;\n width: number;\n height: number;\n scaleX: number;\n scaleY: number;\n angle: number;\n}\n\nexport interface PositioningAdapter<T> {\n readonly provider: string;\n map(positions: NormalizedLayerPosition[], canvas: { width: number; height: number }): T;\n}\n\nexport interface ProjectPage {\n id: string;\n name: string;\n state: EditorState;\n}\n\nexport interface ProjectState {\n version: '1.0.0';\n activePageId: string;\n pages: ProjectPage[];\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 /**\n * Phase shift of the whole tile grid inside the print area, as % of tile\n * width. The pattern object itself is pinned to the print area (it can't be\n * dragged — that would expose bare edges), so this is how the tiling is\n * nudged into alignment. Clamped to ±100% so coverage is never broken.\n */\n offsetX: number;\n /** Phase shift of the tile grid, as % of tile height. See {@link offsetX}. */\n offsetY: 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/** Fabric interaction flags frozen while a layer is rendered as a pattern. */\nexport interface PatternLocks {\n lockMovementX: boolean;\n lockMovementY: boolean;\n lockScalingX: boolean;\n lockScalingY: boolean;\n lockRotation: boolean;\n hasControls: boolean;\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 /**\n * Interaction flags the object carried before it became a pattern. A pattern\n * fills the whole print area, so while it is on the object is locked in place\n * (a drag/resize would slide the tiled bitmap off the area and leave a bare\n * band); the captured flags are restored when the pattern is turned off.\n */\n originalLocks?: PatternLocks;\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 offsetX: 0,\n offsetY: 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 type MockupBlendMode =\n | 'normal'\n | 'multiply'\n | 'screen'\n | 'overlay'\n | 'soft-light'\n | 'hard-light';\n\nexport interface MockupOverlay {\n /** Lighting, texture, or shadow image drawn above the design. */\n image: string;\n blendMode?: MockupBlendMode;\n opacity?: number;\n}\n\nexport type MockupDisplacementChannel = 'red' | 'green' | 'blue' | 'alpha';\n\nexport interface MockupDisplacement {\n /** Channel-map image cover-fitted to the mockup canvas. A value of 128 is neutral. */\n image: string;\n /** Maximum horizontal displacement in document pixels. Defaults to 10. */\n scaleX?: number;\n /** Maximum vertical displacement in document pixels. Defaults to 10. */\n scaleY?: number;\n /** Map channel controlling horizontal displacement. Defaults to red. */\n channelX?: MockupDisplacementChannel;\n /** Map channel controlling vertical displacement. Defaults to green. */\n channelY?: MockupDisplacementChannel;\n}\n\nexport interface MockupConfig {\n /** Garment/product image rendered behind the design. */\n image: string;\n /** Print-area rectangle, in canvas pixels. Drawn as a guide. */\n printArea?: MockupPrintArea;\n /** Clip the design to printArea. Defaults to true when printArea exists. */\n clipToPrintArea?: boolean;\n designBlendMode?: MockupBlendMode;\n designOpacity?: number;\n /** Optional channel-map warp applied to the design before compositing. */\n displacement?: MockupDisplacement;\n /** Optional product lighting/shadow pass rendered after the design. */\n overlay?: MockupOverlay;\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 'history:snapshot': { bytes: number; totalBytes: number; entries: number };\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 'project:changed': { activePageId: string; pages: Array<{ id: string; name: string }> };\n 'export:start': { format: string };\n 'export:complete': { format: string; url?: string };\n error: { message: string; error?: unknown };\n}\n","import type { CanvasSizePreset } from './types';\n\n/** Common print and digital canvas sizes. */\nexport const CANVAS_SIZE_PRESETS: readonly CanvasSizePreset[] = [\n { id: 'a4-portrait', name: 'A4 portrait', width: 210, height: 297, unit: 'mm', dpi: 300 },\n { id: 'a4-landscape', name: 'A4 landscape', width: 297, height: 210, unit: 'mm', dpi: 300 },\n { id: 'us-letter', name: 'US Letter', width: 8.5, height: 11, unit: 'in', dpi: 300 },\n { id: 'shirt-front', name: 'Garment front', width: 12, height: 16, unit: 'in', dpi: 300 },\n { id: 'instagram-square', name: 'Social square', width: 1080, height: 1080, unit: 'px', dpi: 72 },\n { id: 'story', name: 'Story', width: 1080, height: 1920, unit: 'px', dpi: 72 },\n];\n","export type AnnotationPrimitive =\n | { id: string; type: 'point'; x: number; y: number; label?: string }\n | {\n id: string;\n type: 'rectangle';\n left: number;\n top: number;\n width: number;\n height: number;\n label?: string;\n }\n | { id: string; type: 'label'; x: number; y: number; text: string }\n | { id: string; type: 'path'; points: Array<{ x: number; y: number }>; label?: string };\n\nexport interface ViewportTransform {\n zoom: number;\n panX: number;\n panY: number;\n devicePixelRatio?: number;\n}\n\n/** Host-owned annotations that never enter editor layers, history, serialization, or exports. */\nexport class AnnotationOverlay {\n private items = new Map<string, AnnotationPrimitive>();\n private transform: ViewportTransform = { zoom: 1, panX: 0, panY: 0, devicePixelRatio: 1 };\n\n set(annotation: AnnotationPrimitive): void {\n this.items.set(annotation.id, structuredClone(annotation));\n }\n\n remove(id: string): boolean {\n return this.items.delete(id);\n }\n\n clear(): void {\n this.items.clear();\n }\n\n getAll(): AnnotationPrimitive[] {\n return [...this.items.values()].map((item) => structuredClone(item));\n }\n\n setTransform(transform: ViewportTransform): void {\n if (!Number.isFinite(transform.zoom) || transform.zoom <= 0) {\n throw new Error('Annotation zoom must be positive');\n }\n this.transform = { ...transform, devicePixelRatio: transform.devicePixelRatio ?? 1 };\n }\n\n documentToViewport(point: { x: number; y: number }): { x: number; y: number } {\n return {\n x: point.x * this.transform.zoom + this.transform.panX,\n y: point.y * this.transform.zoom + this.transform.panY,\n };\n }\n\n viewportToDocument(point: { x: number; y: number }): { x: number; y: number } {\n return {\n x: (point.x - this.transform.panX) / this.transform.zoom,\n y: (point.y - this.transform.panY) / this.transform.zoom,\n };\n }\n\n documentToDevice(point: { x: number; y: number }): { x: number; y: number } {\n const viewport = this.documentToViewport(point);\n const ratio = this.transform.devicePixelRatio ?? 1;\n return { x: viewport.x * ratio, y: viewport.y * ratio };\n }\n}\n"],"mappings":"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","names":["import_fabric","STROKE","import_fabric","import_fabric","import_fabric","document","page","import_fabric"]}