@overtone-art/canvas-editor-core 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -175,6 +175,8 @@ var LayerManager = class {
175
175
  }
176
176
  this.canvas.requestRenderAll();
177
177
  this.events.emit("layer:selected", { layerId: id });
178
+ const selected = id && this.get(id) ? [id] : [];
179
+ this.events.emit("selection:changed", { selected });
178
180
  }
179
181
  get(id) {
180
182
  return this.layers.find((l) => l.id === id);
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/utils/units.ts","../src/serialization.ts","../src/export.ts","../src/utils/clamp.ts"],"sourcesContent":["export { CanvasEditor } from './editor';\nexport { Layer, LayerManager } from './layer';\nexport { HistoryManager } from './history';\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';\n\nexport type {\n LayerType,\n LayerData,\n SerializedLayer,\n EditorState,\n EditorConfig,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n ExportFormat,\n ShapePlugin,\n TemplateDefinition,\n TemplateParameter,\n PrintifyPositioning,\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 PrintifyPositioning,\n ShapePlugin,\n TemplateDefinition,\n} from './types';\nimport { EventEmitter } from './events';\nimport { Layer, LayerManager } from './layer';\nimport { HistoryManager } from './history';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL } from './export';\nimport type { PngExportOptions } from './export';\nimport { round2 } from './utils/clamp';\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\n private fileAdapter?: FileAdapter;\n private imageProvider?: ImageProvider;\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\n this.canvas = new Canvas(canvasElement, {\n width: widthPx,\n height: heightPx,\n backgroundColor: config.backgroundColor ?? '#ffffff',\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.setupCanvasEvents();\n this.history.saveImmediate();\n }\n\n // ─── Layer Operations ────────────────────────────────\n\n async addImage(\n url: string,\n options?: Partial<Record<string, unknown>>,\n ): Promise<Layer> {\n const img = await FabricImage.fromURL(url, {}, options);\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n }\n\n addText(\n text: string,\n options?: Partial<Record<string, unknown>>,\n ): Layer {\n const textbox = new Textbox(text, {\n fontSize: 32,\n fontFamily: 'Arial',\n fill: '#000000',\n width: 200,\n ...options,\n });\n const layer = this.layers.add('text', textbox as unknown as FabricObject);\n this.history.save();\n return layer;\n }\n\n addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer {\n const obj = plugin.create(options);\n const layer = this.layers.add('shape', obj, plugin.name);\n this.history.save();\n return layer;\n }\n\n async addTemplate(\n template: TemplateDefinition,\n _params: Record<string, string | number>,\n ): Promise<Layer> {\n // Template loading is handled by the templates package\n // This is a placeholder for direct fabric object addition\n const { Textbox: TextboxClass } = await import('fabric');\n const placeholder = new TextboxClass(`[Template: ${template.name}]`, {\n fontSize: 24,\n fontFamily: 'Arial',\n fill: '#666666',\n width: 300,\n });\n const layer = this.layers.add('template', placeholder as unknown as FabricObject, template.name);\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 // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n await deserializeEditor(this, state);\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n this.events.emit('export:start', { format: 'png' });\n const blob = await 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 = 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 exportDataURL(this.canvas, format, multiplier);\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\n .getAll()\n .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(\n filename: string,\n format: 'png' | '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 === '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.canvas.backgroundColor = color;\n this.canvas.requestRenderAll();\n this.history.save();\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(level: number): void {\n this.canvas.setZoom(level);\n this.canvas.requestRenderAll();\n }\n\n // ─── Cleanup ────────────────────────────────────────\n\n dispose(): void {\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, 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 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.fabricObject = fabricObject;\n\n (this.fabricObject as FabricObject & { _layerId: string })._layerId = this.id;\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 };\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 }\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 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 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 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 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 { 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 background: editor.canvas.backgroundColor as string | undefined,\n };\n}\n\nexport async function deserializeEditor(\n editor: CanvasEditor,\n state: EditorState,\n): Promise<void> {\n editor.layers.clear();\n editor.canvas.setDimensions({ width: state.canvas.width, height: state.canvas.height });\n\n if (state.background) {\n editor.canvas.backgroundColor = state.background;\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(\n editor: CanvasEditor,\n serialized: SerializedLayer,\n): Promise<Layer> {\n const objects = await util.enlivenObjects([serialized.fabricObject]);\n const fabricObject = objects[0] as FabricObject;\n\n const layer = editor.layers.add(\n serialized.type,\n fabricObject,\n serialized.name,\n serialized.id,\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","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"],"mappings":"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","names":["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/utils/units.ts","../src/serialization.ts","../src/export.ts","../src/utils/clamp.ts"],"sourcesContent":["export { CanvasEditor } from './editor';\nexport { Layer, LayerManager } from './layer';\nexport { HistoryManager } from './history';\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';\n\nexport type {\n LayerType,\n LayerData,\n SerializedLayer,\n EditorState,\n EditorConfig,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n ExportFormat,\n ShapePlugin,\n TemplateDefinition,\n TemplateParameter,\n PrintifyPositioning,\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 PrintifyPositioning,\n ShapePlugin,\n TemplateDefinition,\n} from './types';\nimport { EventEmitter } from './events';\nimport { Layer, LayerManager } from './layer';\nimport { HistoryManager } from './history';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL } from './export';\nimport type { PngExportOptions } from './export';\nimport { round2 } from './utils/clamp';\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\n private fileAdapter?: FileAdapter;\n private imageProvider?: ImageProvider;\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\n this.canvas = new Canvas(canvasElement, {\n width: widthPx,\n height: heightPx,\n backgroundColor: config.backgroundColor ?? '#ffffff',\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.setupCanvasEvents();\n this.history.saveImmediate();\n }\n\n // ─── Layer Operations ────────────────────────────────\n\n async addImage(\n url: string,\n options?: Partial<Record<string, unknown>>,\n ): Promise<Layer> {\n const img = await FabricImage.fromURL(url, {}, options);\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n }\n\n addText(\n text: string,\n options?: Partial<Record<string, unknown>>,\n ): Layer {\n const textbox = new Textbox(text, {\n fontSize: 32,\n fontFamily: 'Arial',\n fill: '#000000',\n width: 200,\n ...options,\n });\n const layer = this.layers.add('text', textbox as unknown as FabricObject);\n this.history.save();\n return layer;\n }\n\n addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer {\n const obj = plugin.create(options);\n const layer = this.layers.add('shape', obj, plugin.name);\n this.history.save();\n return layer;\n }\n\n async addTemplate(\n template: TemplateDefinition,\n _params: Record<string, string | number>,\n ): Promise<Layer> {\n // Template loading is handled by the templates package\n // This is a placeholder for direct fabric object addition\n const { Textbox: TextboxClass } = await import('fabric');\n const placeholder = new TextboxClass(`[Template: ${template.name}]`, {\n fontSize: 24,\n fontFamily: 'Arial',\n fill: '#666666',\n width: 300,\n });\n const layer = this.layers.add('template', placeholder as unknown as FabricObject, template.name);\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 // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n await deserializeEditor(this, state);\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n this.events.emit('export:start', { format: 'png' });\n const blob = await 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 = 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 exportDataURL(this.canvas, format, multiplier);\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\n .getAll()\n .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(\n filename: string,\n format: 'png' | '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 === '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.canvas.backgroundColor = color;\n this.canvas.requestRenderAll();\n this.history.save();\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(level: number): void {\n this.canvas.setZoom(level);\n this.canvas.requestRenderAll();\n }\n\n // ─── Cleanup ────────────────────────────────────────\n\n dispose(): void {\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, 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 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.fabricObject = fabricObject;\n\n (this.fabricObject as FabricObject & { _layerId: string })._layerId = this.id;\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 };\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 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 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 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 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 { 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 background: editor.canvas.backgroundColor as string | undefined,\n };\n}\n\nexport async function deserializeEditor(\n editor: CanvasEditor,\n state: EditorState,\n): Promise<void> {\n editor.layers.clear();\n editor.canvas.setDimensions({ width: state.canvas.width, height: state.canvas.height });\n\n if (state.background) {\n editor.canvas.backgroundColor = state.background;\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(\n editor: CanvasEditor,\n serialized: SerializedLayer,\n): Promise<Layer> {\n const objects = await util.enlivenObjects([serialized.fabricObject]);\n const fabricObject = objects[0] as FabricObject;\n\n const layer = editor.layers.add(\n serialized.type,\n fabricObject,\n serialized.name,\n serialized.id,\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","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"],"mappings":"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","names":["import_fabric"]}
package/dist/index.mjs CHANGED
@@ -126,6 +126,8 @@ var LayerManager = class {
126
126
  }
127
127
  this.canvas.requestRenderAll();
128
128
  this.events.emit("layer:selected", { layerId: id });
129
+ const selected = id && this.get(id) ? [id] : [];
130
+ this.events.emit("selection:changed", { selected });
129
131
  }
130
132
  get(id) {
131
133
  return this.layers.find((l) => l.id === id);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/editor.ts","../src/events.ts","../src/utils/id.ts","../src/layer.ts","../src/history.ts","../src/utils/units.ts","../src/serialization.ts","../src/export.ts","../src/utils/clamp.ts"],"sourcesContent":["import { Canvas, FabricImage, Textbox } from 'fabric';\nimport type { FabricObject, ImageFormat } from 'fabric';\nimport type {\n EditorConfig,\n EditorEvents,\n EditorState,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n PrintifyPositioning,\n ShapePlugin,\n TemplateDefinition,\n} from './types';\nimport { EventEmitter } from './events';\nimport { Layer, LayerManager } from './layer';\nimport { HistoryManager } from './history';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL } from './export';\nimport type { PngExportOptions } from './export';\nimport { round2 } from './utils/clamp';\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\n private fileAdapter?: FileAdapter;\n private imageProvider?: ImageProvider;\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\n this.canvas = new Canvas(canvasElement, {\n width: widthPx,\n height: heightPx,\n backgroundColor: config.backgroundColor ?? '#ffffff',\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.setupCanvasEvents();\n this.history.saveImmediate();\n }\n\n // ─── Layer Operations ────────────────────────────────\n\n async addImage(\n url: string,\n options?: Partial<Record<string, unknown>>,\n ): Promise<Layer> {\n const img = await FabricImage.fromURL(url, {}, options);\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n }\n\n addText(\n text: string,\n options?: Partial<Record<string, unknown>>,\n ): Layer {\n const textbox = new Textbox(text, {\n fontSize: 32,\n fontFamily: 'Arial',\n fill: '#000000',\n width: 200,\n ...options,\n });\n const layer = this.layers.add('text', textbox as unknown as FabricObject);\n this.history.save();\n return layer;\n }\n\n addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer {\n const obj = plugin.create(options);\n const layer = this.layers.add('shape', obj, plugin.name);\n this.history.save();\n return layer;\n }\n\n async addTemplate(\n template: TemplateDefinition,\n _params: Record<string, string | number>,\n ): Promise<Layer> {\n // Template loading is handled by the templates package\n // This is a placeholder for direct fabric object addition\n const { Textbox: TextboxClass } = await import('fabric');\n const placeholder = new TextboxClass(`[Template: ${template.name}]`, {\n fontSize: 24,\n fontFamily: 'Arial',\n fill: '#666666',\n width: 300,\n });\n const layer = this.layers.add('template', placeholder as unknown as FabricObject, template.name);\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 // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n await deserializeEditor(this, state);\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n this.events.emit('export:start', { format: 'png' });\n const blob = await 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 = 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 exportDataURL(this.canvas, format, multiplier);\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\n .getAll()\n .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(\n filename: string,\n format: 'png' | '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 === '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.canvas.backgroundColor = color;\n this.canvas.requestRenderAll();\n this.history.save();\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(level: number): void {\n this.canvas.setZoom(level);\n this.canvas.requestRenderAll();\n }\n\n // ─── Cleanup ────────────────────────────────────────\n\n dispose(): void {\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, 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 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.fabricObject = fabricObject;\n\n (this.fabricObject as FabricObject & { _layerId: string })._layerId = this.id;\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 };\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 }\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 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 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 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 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 { 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 background: editor.canvas.backgroundColor as string | undefined,\n };\n}\n\nexport async function deserializeEditor(\n editor: CanvasEditor,\n state: EditorState,\n): Promise<void> {\n editor.layers.clear();\n editor.canvas.setDimensions({ width: state.canvas.width, height: state.canvas.height });\n\n if (state.background) {\n editor.canvas.backgroundColor = state.background;\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(\n editor: CanvasEditor,\n serialized: SerializedLayer,\n): Promise<Layer> {\n const objects = await util.enlivenObjects([serialized.fabricObject]);\n const fabricObject = objects[0] as FabricObject;\n\n const layer = editor.layers.add(\n serialized.type,\n fabricObject,\n serialized.name,\n serialized.id,\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","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"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/editor.ts","../src/events.ts","../src/utils/id.ts","../src/layer.ts","../src/history.ts","../src/utils/units.ts","../src/serialization.ts","../src/export.ts","../src/utils/clamp.ts"],"sourcesContent":["import { Canvas, FabricImage, Textbox } from 'fabric';\nimport type { FabricObject, ImageFormat } from 'fabric';\nimport type {\n EditorConfig,\n EditorEvents,\n EditorState,\n FileAdapter,\n ImageProvider,\n ImageProviderResult,\n PrintifyPositioning,\n ShapePlugin,\n TemplateDefinition,\n} from './types';\nimport { EventEmitter } from './events';\nimport { Layer, LayerManager } from './layer';\nimport { HistoryManager } from './history';\nimport { UnitConverter } from './utils/units';\nimport { serializeEditor, deserializeEditor } from './serialization';\nimport { exportPNG, exportSVG, exportDataURL } from './export';\nimport type { PngExportOptions } from './export';\nimport { round2 } from './utils/clamp';\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\n private fileAdapter?: FileAdapter;\n private imageProvider?: ImageProvider;\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\n this.canvas = new Canvas(canvasElement, {\n width: widthPx,\n height: heightPx,\n backgroundColor: config.backgroundColor ?? '#ffffff',\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.setupCanvasEvents();\n this.history.saveImmediate();\n }\n\n // ─── Layer Operations ────────────────────────────────\n\n async addImage(\n url: string,\n options?: Partial<Record<string, unknown>>,\n ): Promise<Layer> {\n const img = await FabricImage.fromURL(url, {}, options);\n const layer = this.layers.add('image', img);\n this.history.save();\n return layer;\n }\n\n addText(\n text: string,\n options?: Partial<Record<string, unknown>>,\n ): Layer {\n const textbox = new Textbox(text, {\n fontSize: 32,\n fontFamily: 'Arial',\n fill: '#000000',\n width: 200,\n ...options,\n });\n const layer = this.layers.add('text', textbox as unknown as FabricObject);\n this.history.save();\n return layer;\n }\n\n addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer {\n const obj = plugin.create(options);\n const layer = this.layers.add('shape', obj, plugin.name);\n this.history.save();\n return layer;\n }\n\n async addTemplate(\n template: TemplateDefinition,\n _params: Record<string, string | number>,\n ): Promise<Layer> {\n // Template loading is handled by the templates package\n // This is a placeholder for direct fabric object addition\n const { Textbox: TextboxClass } = await import('fabric');\n const placeholder = new TextboxClass(`[Template: ${template.name}]`, {\n fontSize: 24,\n fontFamily: 'Arial',\n fill: '#666666',\n width: 300,\n });\n const layer = this.layers.add('template', placeholder as unknown as FabricObject, template.name);\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 // ─── Serialization ──────────────────────────────────\n\n toJSON(): EditorState {\n return serializeEditor(this);\n }\n\n async fromJSON(state: EditorState): Promise<void> {\n await deserializeEditor(this, state);\n }\n\n // ─── Export ──────────────────────────────────────────\n\n async toPNG(options?: PngExportOptions): Promise<Blob> {\n this.events.emit('export:start', { format: 'png' });\n const blob = await 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 = 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 exportDataURL(this.canvas, format, multiplier);\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\n .getAll()\n .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(\n filename: string,\n format: 'png' | '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 === '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.canvas.backgroundColor = color;\n this.canvas.requestRenderAll();\n this.history.save();\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(level: number): void {\n this.canvas.setZoom(level);\n this.canvas.requestRenderAll();\n }\n\n // ─── Cleanup ────────────────────────────────────────\n\n dispose(): void {\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, 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 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.fabricObject = fabricObject;\n\n (this.fabricObject as FabricObject & { _layerId: string })._layerId = this.id;\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 };\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 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 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 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 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 { 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 background: editor.canvas.backgroundColor as string | undefined,\n };\n}\n\nexport async function deserializeEditor(\n editor: CanvasEditor,\n state: EditorState,\n): Promise<void> {\n editor.layers.clear();\n editor.canvas.setDimensions({ width: state.canvas.width, height: state.canvas.height });\n\n if (state.background) {\n editor.canvas.backgroundColor = state.background;\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(\n editor: CanvasEditor,\n serialized: SerializedLayer,\n): Promise<Layer> {\n const objects = await util.enlivenObjects([serialized.fabricObject]);\n const fabricObject = objects[0] as FabricObject;\n\n const layer = editor.layers.add(\n serialized.type,\n fabricObject,\n serialized.name,\n serialized.id,\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","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"],"mappings":";AAAA,SAAS,QAAQ,aAAa,eAAe;;;ACGtC,IAAM,eAAN,MAAiE;AAAA,EAC9D,YAAY,oBAAI,IAA+C;AAAA,EAEvE,GAA4B,OAAU,SAAyC;AAC7E,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACrC;AACA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,OAAgC;AAAA,EACjE;AAAA,EAEA,IAA6B,OAAU,SAAyC;AAC9E,SAAK,UAAU,IAAI,KAAK,GAAG,OAAO,OAAgC;AAAA,EACpE;AAAA,EAEA,KAA8B,OAAU,SAAyC;AAC/E,UAAM,UAAoC,CAAC,SAAS;AAClD,WAAK,IAAI,OAAO,OAAO;AACvB,cAAQ,IAAI;AAAA,IACd;AACA,SAAK,GAAG,OAAO,OAAO;AAAA,EACxB;AAAA,EAEA,KAA8B,OAAU,MAAwB;AAC9D,UAAM,WAAW,KAAK,UAAU,IAAI,KAAK;AACzC,QAAI,CAAC,SAAU;AACf,eAAW,WAAW,UAAU;AAC9B,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAAA,EAEA,mBAAmB,OAA6B;AAC9C,QAAI,OAAO;AACT,WAAK,UAAU,OAAO,KAAK;AAAA,IAC7B,OAAO;AACL,WAAK,UAAU,MAAM;AAAA,IACvB;AAAA,EACF;AACF;;;ACxCA,SAAS,cAAc;AAEhB,IAAM,aAAa,MAAc,OAAO,EAAE;;;ACI1C,IAAM,QAAN,MAAY;AAAA,EACR;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACS;AAAA,EAET,YAAY,MAAiB,cAA4B,MAAe,IAAa;AACnF,SAAK,KAAK,MAAM,WAAW;AAC3B,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ,GAAG,IAAI,IAAI,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC;AAClD,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,UAAU;AACf,SAAK,eAAe;AAEpB,IAAC,KAAK,aAAqD,WAAW,KAAK;AAAA,EAC7E;AAAA,EAEA,SAAoB;AAClB,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,YAA6B;AAC3B,WAAO;AAAA,MACL,GAAG,KAAK,OAAO;AAAA,MACf,cAAc,KAAK,aAAa,SAAS;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EAGxB,YACU,QACA,QACR;AAFQ;AACA;AAAA,EACP;AAAA,EAFO;AAAA,EACA;AAAA,EAJF,SAAkB,CAAC;AAAA,EAO3B,IAAI,MAAiB,cAA4B,MAAe,IAAoB;AAClF,UAAM,QAAQ,IAAI,MAAM,MAAM,cAAc,MAAM,EAAE;AACpD,SAAK,OAAO,KAAK,KAAK;AACtB,SAAK,OAAO,IAAI,YAAY;AAC5B,SAAK,OAAO,KAAK,eAAe,EAAE,OAAO,MAAM,OAAO,EAAE,CAAC;AACzD,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,IAAqB;AAC1B,UAAM,QAAQ,KAAK,OAAO,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACtD,QAAI,UAAU,GAAI,QAAO;AACzB,UAAM,QAAQ,KAAK,OAAO,KAAK;AAC/B,SAAK,OAAO,OAAO,MAAM,YAAY;AACrC,SAAK,OAAO,OAAO,OAAO,CAAC;AAC3B,SAAK,OAAO,KAAK,iBAAiB,EAAE,SAAS,GAAG,CAAC;AACjD,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,IAAY,UAA2B;AAC7C,UAAM,WAAW,KAAK,OAAO,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACzD,QAAI,aAAa,GAAI,QAAO;AAC5B,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,OAAO,SAAS,GAAG,QAAQ,CAAC;AACtE,UAAM,CAAC,KAAK,IAAI,KAAK,OAAO,OAAO,UAAU,CAAC;AAC9C,SAAK,OAAO,OAAO,SAAS,GAAG,KAAK;AAEpC,SAAK,OAAO,QAAQ,CAAC,GAAG,MAAM;AAC5B,WAAK,OAAO,aAAa,EAAE,cAAc,CAAC;AAAA,IAC5C,CAAC;AACD,SAAK,OAAO,KAAK,mBAAmB,EAAE,UAAU,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC;AAC9E,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,IAAyB;AAC9B,QAAI,OAAO,MAAM;AACf,WAAK,OAAO,oBAAoB;AAAA,IAClC,OAAO;AACL,YAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,UAAI,OAAO;AACT,aAAK,OAAO,gBAAgB,MAAM,YAAY;AAAA,MAChD;AAAA,IACF;AACA,SAAK,OAAO,iBAAiB;AAC7B,SAAK,OAAO,KAAK,kBAAkB,EAAE,SAAS,GAAG,CAAC;AAGlD,UAAM,WAAW,MAAM,KAAK,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAC9C,SAAK,OAAO,KAAK,qBAAqB,EAAE,SAAS,CAAC;AAAA,EACpD;AAAA,EAEA,IAAI,IAA+B;AACjC,WAAO,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,EAC5C;AAAA,EAEA,SAAkB;AAChB,WAAO,CAAC,GAAG,KAAK,MAAM;AAAA,EACxB;AAAA,EAEA,aAAa,KAAsC;AACjD,UAAM,UAAW,IAA6C;AAC9D,QAAI,QAAS,QAAO,KAAK,IAAI,OAAO;AACpC,WAAO,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,iBAAiB,GAAG;AAAA,EACvD;AAAA,EAEA,cAAc,IAAY,SAAwB;AAChD,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,UAAU;AAChB,UAAM,aAAa,UAAU;AAC7B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,UAAU,IAAY,QAAuB;AAC3C,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,SAAS;AACf,UAAM,aAAa,aAAa,CAAC;AACjC,UAAM,aAAa,UAAU,CAAC;AAC9B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,WAAW,IAAY,SAAuB;AAC5C,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,UAAU;AAChB,UAAM,aAAa,UAAU;AAC7B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAQ,IAAY,MAAoB;AACtC,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO;AACb,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAc;AACZ,eAAW,SAAS,KAAK,QAAQ;AAC/B,WAAK,OAAO,OAAO,MAAM,YAAY;AAAA,IACvC;AACA,SAAK,SAAS,CAAC;AACf,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAgB;AACd,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEQ,cAAoB;AAC1B,SAAK,OAAO,KAAK,kBAAkB;AAAA,MACjC,QAAQ,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,IAC3C,CAAC;AAAA,EACH;AACF;;;ACzKO,IAAM,iBAAN,MAAqB;AAAA,EAClB,YAAsB,CAAC;AAAA,EACvB,YAAsB,CAAC;AAAA,EACvB;AAAA,EACA,SAAS;AAAA,EACT,gBAAsD;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,MAMT;AACD,SAAK,WAAW,KAAK;AACrB,SAAK,eAAe,KAAK;AACzB,SAAK,SAAS,KAAK;AACnB,SAAK,UAAU,KAAK,WAAW;AAC/B,SAAK,aAAa,KAAK,cAAc;AAAA,EACvC;AAAA,EAEA,OAAa;AACX,QAAI,KAAK,OAAQ;AAEjB,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAAA,IACjC;AAEA,SAAK,gBAAgB,WAAW,MAAM;AACpC,WAAK,cAAc;AAAA,IACrB,GAAG,KAAK,UAAU;AAAA,EACpB;AAAA,EAEA,gBAAsB;AACpB,QAAI,KAAK,OAAQ;AACjB,UAAM,QAAQ,KAAK,SAAS;AAC5B,SAAK,UAAU,KAAK,KAAK;AACzB,QAAI,KAAK,UAAU,SAAS,KAAK,SAAS;AACxC,WAAK,UAAU,MAAM;AAAA,IACvB;AACA,SAAK,YAAY,CAAC;AAClB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,QAAQ,KAAK,UAAU,IAAI;AACjC,QAAI,CAAC,MAAO;AAEZ,SAAK,UAAU,KAAK,KAAK,SAAS,CAAC;AACnC,SAAK,SAAS;AACd,UAAM,KAAK,aAAa,KAAK;AAC7B,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,QAAQ,KAAK,UAAU,IAAI;AACjC,QAAI,CAAC,MAAO;AAEZ,SAAK,UAAU,KAAK,KAAK,SAAS,CAAC;AACnC,SAAK,SAAS;AACd,UAAM,KAAK,aAAa,KAAK;AAC7B,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,SAAe;AACb,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,UAAmB;AACjB,WAAO,KAAK,UAAU,SAAS;AAAA,EACjC;AAAA,EAEA,UAAmB;AACjB,WAAO,KAAK,UAAU,SAAS;AAAA,EACjC;AAAA,EAEA,QAAc;AACZ,SAAK,YAAY,CAAC;AAClB,SAAK,YAAY,CAAC;AAClB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAAA,IACjC;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,SAAK,OAAO,KAAK,mBAAmB;AAAA,MAClC,SAAS,KAAK,QAAQ;AAAA,MACtB,SAAS,KAAK,QAAQ;AAAA,IACxB,CAAC;AAAA,EACH;AACF;;;ACzGA,IAAM,cAAc;AAEb,IAAM,gBAAN,MAAoB;AAAA,EACzB,YACU,OAAa,MACb,MAAc,IACtB;AAFQ;AACA;AAAA,EACP;AAAA,EAFO;AAAA,EACA;AAAA,EAGV,QAAQ,MAAkB;AACxB,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,OAAO,KAAmB;AACxB,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,UAAgB;AACd,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SAAiB;AACf,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,SAAS,OAAe,UAAyB;AAC/C,UAAM,IAAI,YAAY,KAAK;AAC3B,YAAQ,GAAG;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO,QAAQ,KAAK;AAAA,MACtB,KAAK;AACH,eAAQ,QAAQ,cAAe,KAAK;AAAA,IACxC;AAAA,EACF;AAAA,EAEA,WAAW,IAAY,QAAuB;AAC5C,UAAM,IAAI,UAAU,KAAK;AACzB,YAAQ,GAAG;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO,KAAK,KAAK;AAAA,MACnB,KAAK;AACH,eAAQ,KAAK,KAAK,MAAO;AAAA,IAC7B;AAAA,EACF;AACF;;;ACjDA,SAAS,YAAY;AAMrB,IAAM,UAAU;AAET,SAAS,gBAAgB,QAAmC;AACjE,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,MACN,OAAO,OAAO,OAAO,SAAS;AAAA,MAC9B,QAAQ,OAAO,OAAO,UAAU;AAAA,IAClC;AAAA,IACA,QAAQ,OAAO,OAAO,OAAO,EAAE,IAAI,CAAC,UAAU,MAAM,UAAU,CAAC;AAAA,IAC/D,YAAY,OAAO,OAAO;AAAA,EAC5B;AACF;AAEA,eAAsB,kBACpB,QACA,OACe;AACf,SAAO,OAAO,MAAM;AACpB,SAAO,OAAO,cAAc,EAAE,OAAO,MAAM,OAAO,OAAO,QAAQ,MAAM,OAAO,OAAO,CAAC;AAEtF,MAAI,MAAM,YAAY;AACpB,WAAO,OAAO,kBAAkB,MAAM;AAAA,EACxC;AAEA,aAAW,mBAAmB,MAAM,QAAQ;AAC1C,UAAM,aAAa,QAAQ,eAAe;AAAA,EAC5C;AAEA,SAAO,OAAO,iBAAiB;AACjC;AAEA,eAAe,aACb,QACA,YACgB;AAChB,QAAM,UAAU,MAAM,KAAK,eAAe,CAAC,WAAW,YAAY,CAAC;AACnE,QAAM,eAAe,QAAQ,CAAC;AAE9B,QAAM,QAAQ,OAAO,OAAO;AAAA,IAC1B,WAAW;AAAA,IACX;AAAA,IACA,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAEA,MAAI,CAAC,WAAW,SAAS;AACvB,WAAO,OAAO,cAAc,MAAM,IAAI,KAAK;AAAA,EAC7C;AACA,MAAI,WAAW,QAAQ;AACrB,WAAO,OAAO,UAAU,MAAM,IAAI,IAAI;AAAA,EACxC;AACA,MAAI,WAAW,YAAY,GAAG;AAC5B,WAAO,OAAO,WAAW,MAAM,IAAI,WAAW,OAAO;AAAA,EACvD;AAEA,SAAO;AACT;;;ACvDA,eAAsB,UACpB,QACA,UAA4B,CAAC,GACd;AACf,QAAM,EAAE,aAAa,GAAG,SAAS,OAAsB,UAAU,EAAE,IAAI;AACvE,QAAM,UAAU,OAAO,UAAU;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,WAAW,MAAM,MAAM,OAAO;AACpC,SAAO,SAAS,KAAK;AACvB;AAEO,SAAS,UAAU,QAAwB;AAChD,SAAO,OAAO,MAAM;AACtB;AAEO,SAAS,cACd,QACA,SAAsB,OACtB,aAAa,GACL;AACR,SAAO,OAAO,UAAU,EAAE,QAAQ,WAAW,CAAC;AAChD;;;AChCO,SAAS,MAAM,GAAW,KAAa,KAAqB;AACjE,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC;AACvC;AAEO,SAAS,OAAO,GAAmB;AACxC,SAAO,KAAK,MAAM,IAAI,GAAG,IAAI;AAC/B;;;ARgBO,IAAM,eAAN,MAAmB;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAED;AAAA,EACA;AAAA,EAER,YAAY,eAAkC,QAAsB;AAClE,SAAK,SAAS,IAAI,aAA2B;AAC7C,SAAK,QAAQ,IAAI,cAAc,OAAO,QAAQ,MAAM,OAAO,OAAO,EAAE;AAEpE,UAAM,UAAU,KAAK,MAAM,SAAS,OAAO,KAAK;AAChD,UAAM,WAAW,KAAK,MAAM,SAAS,OAAO,MAAM;AAElD,SAAK,SAAS,IAAI,OAAO,eAAe;AAAA,MACtC,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,iBAAiB,OAAO,mBAAmB;AAAA,MAC3C,wBAAwB,OAAO,0BAA0B;AAAA,MACzD,WAAW;AAAA,IACb,CAAC;AAED,SAAK,SAAS,IAAI,aAAa,KAAK,QAAQ,KAAK,MAAM;AACvD,SAAK,cAAc,OAAO;AAC1B,SAAK,gBAAgB,OAAO;AAE5B,SAAK,UAAU,IAAI,eAAe;AAAA,MAChC,UAAU,MAAM,KAAK,UAAU,KAAK,OAAO,CAAC;AAAA,MAC5C,cAAc,OAAO,UAAkB;AACrC,cAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,cAAM,KAAK,SAAS,MAAM;AAAA,MAC5B;AAAA,MACA,QAAQ,KAAK;AAAA,IACf,CAAC;AAED,SAAK,kBAAkB;AACvB,SAAK,QAAQ,cAAc;AAAA,EAC7B;AAAA;AAAA,EAIA,MAAM,SACJ,KACA,SACgB;AAChB,UAAM,MAAM,MAAM,YAAY,QAAQ,KAAK,CAAC,GAAG,OAAO;AACtD,UAAM,QAAQ,KAAK,OAAO,IAAI,SAAS,GAAG;AAC1C,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,QACE,MACA,SACO;AACP,UAAM,UAAU,IAAI,QAAQ,MAAM;AAAA,MAChC,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,GAAG;AAAA,IACL,CAAC;AACD,UAAM,QAAQ,KAAK,OAAO,IAAI,QAAQ,OAAkC;AACxE,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,SAAS,QAAqB,SAA0C;AACtE,UAAM,MAAM,OAAO,OAAO,OAAO;AACjC,UAAM,QAAQ,KAAK,OAAO,IAAI,SAAS,KAAK,OAAO,IAAI;AACvD,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YACJ,UACA,SACgB;AAGhB,UAAM,EAAE,SAAS,aAAa,IAAI,MAAM,OAAO,QAAQ;AACvD,UAAM,cAAc,IAAI,aAAa,cAAc,SAAS,IAAI,KAAK;AAAA,MACnE,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,OAAO;AAAA,IACT,CAAC;AACD,UAAM,QAAQ,KAAK,OAAO,IAAI,YAAY,aAAwC,SAAS,IAAI;AAC/F,SAAK,QAAQ,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,IAAkB;AAC5B,SAAK,OAAO,OAAO,EAAE;AACrB,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,YAAY,IAAyB;AACnC,SAAK,OAAO,OAAO,EAAE;AAAA,EACvB;AAAA,EAEA,mBAAiC;AAC/B,UAAM,SAAS,KAAK,OAAO,gBAAgB;AAC3C,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,KAAK,OAAO,aAAa,MAAM,KAAK;AAAA,EAC7C;AAAA;AAAA,EAIA,SAAsB;AACpB,WAAO,gBAAgB,IAAI;AAAA,EAC7B;AAAA,EAEA,MAAM,SAAS,OAAmC;AAChD,UAAM,kBAAkB,MAAM,KAAK;AAAA,EACrC;AAAA;AAAA,EAIA,MAAM,MAAM,SAA2C;AACrD,SAAK,OAAO,KAAK,gBAAgB,EAAE,QAAQ,MAAM,CAAC;AAClD,UAAM,OAAO,MAAM,UAAU,KAAK,QAAQ,OAAO;AACjD,SAAK,OAAO,KAAK,mBAAmB,EAAE,QAAQ,MAAM,CAAC;AACrD,WAAO;AAAA,EACT;AAAA,EAEA,QAAgB;AACd,SAAK,OAAO,KAAK,gBAAgB,EAAE,QAAQ,MAAM,CAAC;AAClD,UAAM,MAAM,UAAU,KAAK,MAAM;AACjC,SAAK,OAAO,KAAK,mBAAmB,EAAE,QAAQ,MAAM,CAAC;AACrD,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,SAAsB,OAAO,aAAa,GAAW;AAC7D,WAAO,cAAc,KAAK,QAAQ,QAAQ,UAAU;AAAA,EACtD;AAAA,EAEA,wBAA6D;AAC3D,UAAM,SAA8C,CAAC;AACrD,UAAM,UAAU,KAAK,OAAO,SAAS;AACrC,UAAM,UAAU,KAAK,OAAO,UAAU;AAEtC,UAAM,cAAc,KAAK,OACtB,OAAO,EACP,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO;AAEnC,QAAI,YAAY,WAAW,GAAG;AAC5B,aAAO,OAAO,IAAI,EAAE,GAAG,KAAK,GAAG,KAAK,OAAO,GAAG,OAAO,EAAE;AACvD,aAAO;AAAA,IACT;AAEA,eAAW,SAAS,aAAa;AAC/B,YAAM,MAAM,MAAM;AAClB,YAAM,SAAS,IAAI,eAAe;AAClC,aAAO,MAAM,IAAI,IAAI;AAAA,QACnB,GAAG,OAAO,OAAO,IAAI,OAAO;AAAA,QAC5B,GAAG,OAAO,OAAO,IAAI,OAAO;AAAA,QAC5B,OAAO,OAAO,IAAI,UAAU,CAAC;AAAA,QAC7B,OAAO,OAAO,IAAI,SAAS,CAAC;AAAA,MAC9B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAIA,MAAM,KACJ,UACA,QAC6B;AAC7B,QAAI,CAAC,KAAK,YAAa,QAAO;AAE9B,QAAI;AACJ,QAAI,WAAW,OAAO;AACpB,aAAO,MAAM,KAAK,MAAM;AAAA,IAC1B,WAAW,WAAW,OAAO;AAC3B,aAAO,KAAK,MAAM;AAAA,IACpB,OAAO;AACL,aAAO,KAAK,UAAU,KAAK,OAAO,GAAG,MAAM,CAAC;AAAA,IAC9C;AAEA,WAAO,KAAK,YAAY,KAAK,MAAM,UAAU,MAAM;AAAA,EACrD;AAAA,EAEA,MAAM,YAAY,MAAsD;AACtE,QAAI,CAAC,KAAK,cAAe,QAAO;AAChC,WAAO,KAAK,cAAc,OAAO,IAAI;AAAA,EACvC;AAAA,EAEA,MAAM,eAAoD;AACxD,QAAI,CAAC,KAAK,eAAe,OAAQ,QAAO;AACxC,WAAO,KAAK,cAAc,OAAO;AAAA,EACnC;AAAA,EAEA,eAAe,SAA4B;AACzC,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,iBAAiB,UAA+B;AAC9C,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAIA,cAAc,OAAqB;AACjC,SAAK,OAAO,kBAAkB;AAC9B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,OAAO,OAAe,QAAsB;AAC1C,UAAM,UAAU,KAAK,MAAM,SAAS,KAAK;AACzC,UAAM,WAAW,KAAK,MAAM,SAAS,MAAM;AAC3C,SAAK,OAAO,cAAc,EAAE,OAAO,SAAS,QAAQ,SAAS,CAAC;AAC9D,SAAK,OAAO,iBAAiB;AAAA,EAC/B;AAAA,EAEA,KAAK,OAAqB;AACxB,SAAK,OAAO,QAAQ,KAAK;AACzB,SAAK,OAAO,iBAAiB;AAAA,EAC/B;AAAA;AAAA,EAIA,UAAgB;AACd,SAAK,QAAQ,QAAQ;AACrB,SAAK,OAAO,mBAAmB;AAC/B,SAAK,OAAO,QAAQ;AAAA,EACtB;AAAA;AAAA,EAIQ,oBAA0B;AAChC,SAAK,OAAO,GAAG,mBAAmB,CAAC,MAAM;AACvC,UAAI,CAAC,EAAE,OAAQ;AACf,YAAM,QAAQ,KAAK,OAAO,aAAa,EAAE,MAAM;AAC/C,UAAI,OAAO;AACT,aAAK,OAAO,KAAK,kBAAkB,EAAE,SAAS,MAAM,GAAG,CAAC;AACxD,aAAK,QAAQ,KAAK;AAAA,MACpB;AAAA,IACF,CAAC;AAED,SAAK,OAAO,GAAG,qBAAqB,CAAC,MAAM;AACzC,YAAM,YAAY,EAAE,YAAY,CAAC,GAC9B,IAAI,CAAC,QAAQ,KAAK,OAAO,aAAa,GAAG,GAAG,EAAE,EAC9C,OAAO,CAAC,OAAqB,OAAO,MAAS;AAChD,WAAK,OAAO,KAAK,qBAAqB,EAAE,SAAS,CAAC;AAAA,IACpD,CAAC;AAED,SAAK,OAAO,GAAG,qBAAqB,CAAC,MAAM;AACzC,YAAM,YAAY,EAAE,YAAY,CAAC,GAC9B,IAAI,CAAC,QAAQ,KAAK,OAAO,aAAa,GAAG,GAAG,EAAE,EAC9C,OAAO,CAAC,OAAqB,OAAO,MAAS;AAChD,WAAK,OAAO,KAAK,qBAAqB,EAAE,SAAS,CAAC;AAAA,IACpD,CAAC;AAED,SAAK,OAAO,GAAG,qBAAqB,MAAM;AACxC,WAAK,OAAO,KAAK,qBAAqB,EAAE,UAAU,CAAC,EAAE,CAAC;AAAA,IACxD,CAAC;AAAA,EACH;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overtone-art/canvas-editor-core",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Abstract pluggable canvas editor built on Fabric.js",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -17,14 +17,11 @@
17
17
  }
18
18
  }
19
19
  },
20
- "files": ["dist"],
21
- "scripts": {
22
- "build": "tsup",
23
- "dev": "tsup --watch",
24
- "lint": "eslint src/",
25
- "type-check": "tsc --noEmit",
26
- "test": "vitest run",
27
- "test:watch": "vitest"
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
28
25
  },
29
26
  "dependencies": {
30
27
  "fabric": "^6.6.1",
@@ -36,5 +33,13 @@
36
33
  "vitest": "^3.2.1",
37
34
  "jsdom": "^26.1.0"
38
35
  },
39
- "license": "MIT"
40
- }
36
+ "license": "MIT",
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "dev": "tsup --watch",
40
+ "lint": "eslint src/",
41
+ "type-check": "tsc --noEmit",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest"
44
+ }
45
+ }