@overtone-art/canvas-editor-core 0.0.1

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.
@@ -0,0 +1,250 @@
1
+ import { FabricObject, Canvas, ImageFormat } from 'fabric';
2
+
3
+ type Unit = 'px' | 'mm' | 'in';
4
+ type LayerType = 'image' | 'text' | 'shape' | 'template' | 'group';
5
+ interface LayerData {
6
+ id: string;
7
+ type: LayerType;
8
+ name: string;
9
+ visible: boolean;
10
+ locked: boolean;
11
+ opacity: number;
12
+ }
13
+ interface SerializedLayer extends LayerData {
14
+ fabricObject: Record<string, unknown>;
15
+ }
16
+ interface EditorState {
17
+ version: string;
18
+ canvas: {
19
+ width: number;
20
+ height: number;
21
+ };
22
+ layers: SerializedLayer[];
23
+ background?: string;
24
+ }
25
+ interface EditorConfig {
26
+ width: number;
27
+ height: number;
28
+ backgroundColor?: string;
29
+ fileAdapter?: FileAdapter;
30
+ imageProvider?: ImageProvider;
31
+ preserveObjectStacking?: boolean;
32
+ unit?: Unit;
33
+ dpi?: number;
34
+ }
35
+ type ExportFormat = 'png' | 'svg' | 'json';
36
+ interface FileAdapter {
37
+ save(data: Blob | string, filename: string, format: ExportFormat): Promise<string>;
38
+ }
39
+ interface ImageProviderResult {
40
+ url: string;
41
+ width: number;
42
+ height: number;
43
+ }
44
+ interface ImageProvider {
45
+ upload(file: File): Promise<ImageProviderResult>;
46
+ browse?(): Promise<ImageProviderResult | null>;
47
+ }
48
+ interface ShapePlugin {
49
+ name: string;
50
+ icon: string;
51
+ category?: string;
52
+ create(options?: Record<string, unknown>): FabricObject;
53
+ }
54
+ interface TemplateParameter {
55
+ key: string;
56
+ label: string;
57
+ type: 'text' | 'color' | 'number' | 'font';
58
+ default: string | number;
59
+ }
60
+ interface TemplateDefinition {
61
+ id: string;
62
+ name: string;
63
+ category: string;
64
+ svg: string;
65
+ parameters: TemplateParameter[];
66
+ preview?: string;
67
+ }
68
+ interface PrintifyPositioning {
69
+ x: number;
70
+ y: number;
71
+ scale: number;
72
+ angle: number;
73
+ }
74
+ interface EditorEvents {
75
+ 'layer:added': {
76
+ layer: LayerData;
77
+ };
78
+ 'layer:removed': {
79
+ layerId: string;
80
+ };
81
+ 'layer:selected': {
82
+ layerId: string | null;
83
+ };
84
+ 'layer:modified': {
85
+ layerId: string;
86
+ };
87
+ 'layer:reordered': {
88
+ layerIds: string[];
89
+ };
90
+ 'layers:changed': {
91
+ layers: LayerData[];
92
+ };
93
+ 'selection:changed': {
94
+ selected: string[];
95
+ };
96
+ 'history:changed': {
97
+ canUndo: boolean;
98
+ canRedo: boolean;
99
+ };
100
+ 'canvas:modified': Record<string, never>;
101
+ 'export:start': {
102
+ format: string;
103
+ };
104
+ 'export:complete': {
105
+ format: string;
106
+ url?: string;
107
+ };
108
+ error: {
109
+ message: string;
110
+ error?: unknown;
111
+ };
112
+ }
113
+
114
+ type EventHandler<T> = (data: T) => void;
115
+ declare class EventEmitter<TEvents extends {} = Record<string, unknown>> {
116
+ private listeners;
117
+ on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void;
118
+ off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void;
119
+ once<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void;
120
+ emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void;
121
+ removeAllListeners(event?: keyof TEvents): void;
122
+ }
123
+
124
+ declare class Layer {
125
+ readonly id: string;
126
+ readonly type: LayerType;
127
+ name: string;
128
+ visible: boolean;
129
+ locked: boolean;
130
+ opacity: number;
131
+ readonly fabricObject: FabricObject;
132
+ constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string);
133
+ toData(): LayerData;
134
+ serialize(): SerializedLayer;
135
+ }
136
+ declare class LayerManager {
137
+ private canvas;
138
+ private events;
139
+ private layers;
140
+ constructor(canvas: Canvas, events: EventEmitter<EditorEvents>);
141
+ add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer;
142
+ remove(id: string): boolean;
143
+ reorder(id: string, newIndex: number): boolean;
144
+ select(id: string | null): void;
145
+ get(id: string): Layer | undefined;
146
+ getAll(): Layer[];
147
+ findByObject(obj: FabricObject): Layer | undefined;
148
+ setVisibility(id: string, visible: boolean): void;
149
+ setLocked(id: string, locked: boolean): void;
150
+ setOpacity(id: string, opacity: number): void;
151
+ setName(id: string, name: string): void;
152
+ clear(): void;
153
+ count(): number;
154
+ private emitChanged;
155
+ }
156
+
157
+ declare class HistoryManager {
158
+ private undoStack;
159
+ private redoStack;
160
+ private maxSize;
161
+ private paused;
162
+ private debounceTimer;
163
+ private debounceMs;
164
+ private getState;
165
+ private restoreState;
166
+ private events;
167
+ constructor(opts: {
168
+ getState: () => string;
169
+ restoreState: (state: string) => Promise<void>;
170
+ events: EventEmitter<EditorEvents>;
171
+ maxSize?: number;
172
+ debounceMs?: number;
173
+ });
174
+ save(): void;
175
+ saveImmediate(): void;
176
+ undo(): Promise<void>;
177
+ redo(): Promise<void>;
178
+ pause(): void;
179
+ resume(): void;
180
+ canUndo(): boolean;
181
+ canRedo(): boolean;
182
+ clear(): void;
183
+ dispose(): void;
184
+ private emitChanged;
185
+ }
186
+
187
+ declare class UnitConverter {
188
+ private unit;
189
+ private dpi;
190
+ constructor(unit?: Unit, dpi?: number);
191
+ setUnit(unit: Unit): void;
192
+ setDpi(dpi: number): void;
193
+ getUnit(): Unit;
194
+ getDpi(): number;
195
+ toPixels(value: number, fromUnit?: Unit): number;
196
+ fromPixels(px: number, toUnit?: Unit): number;
197
+ }
198
+
199
+ interface PngExportOptions {
200
+ multiplier?: number;
201
+ format?: ImageFormat;
202
+ quality?: number;
203
+ }
204
+ declare function exportPNG(canvas: Canvas, options?: PngExportOptions): Promise<Blob>;
205
+ declare function exportSVG(canvas: Canvas): string;
206
+ declare function exportDataURL(canvas: Canvas, format?: ImageFormat, multiplier?: number): string;
207
+
208
+ declare class CanvasEditor {
209
+ readonly canvas: Canvas;
210
+ readonly layers: LayerManager;
211
+ readonly history: HistoryManager;
212
+ readonly events: EventEmitter<EditorEvents>;
213
+ readonly units: UnitConverter;
214
+ private fileAdapter?;
215
+ private imageProvider?;
216
+ constructor(canvasElement: HTMLCanvasElement, config: EditorConfig);
217
+ addImage(url: string, options?: Partial<Record<string, unknown>>): Promise<Layer>;
218
+ addText(text: string, options?: Partial<Record<string, unknown>>): Layer;
219
+ addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer;
220
+ addTemplate(template: TemplateDefinition, _params: Record<string, string | number>): Promise<Layer>;
221
+ removeLayer(id: string): void;
222
+ selectLayer(id: string | null): void;
223
+ getSelectedLayer(): Layer | null;
224
+ toJSON(): EditorState;
225
+ fromJSON(state: EditorState): Promise<void>;
226
+ toPNG(options?: PngExportOptions): Promise<Blob>;
227
+ toSVG(): string;
228
+ toDataURL(format?: ImageFormat, multiplier?: number): string;
229
+ toPrintifyPositioning(): Record<string, PrintifyPositioning>;
230
+ save(filename: string, format: 'png' | 'svg' | 'json'): Promise<string | undefined>;
231
+ uploadImage(file: File): Promise<ImageProviderResult | undefined>;
232
+ browseImages(): Promise<ImageProviderResult | null>;
233
+ setFileAdapter(adapter: FileAdapter): void;
234
+ setImageProvider(provider: ImageProvider): void;
235
+ setBackground(color: string): void;
236
+ resize(width: number, height: number): void;
237
+ zoom(level: number): void;
238
+ dispose(): void;
239
+ private setupCanvasEvents;
240
+ }
241
+
242
+ declare const generateId: () => string;
243
+
244
+ declare function clamp(v: number, min: number, max: number): number;
245
+ declare function round2(v: number): number;
246
+
247
+ declare function serializeEditor(editor: CanvasEditor): EditorState;
248
+ declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
249
+
250
+ export { CanvasEditor, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerType, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, type TemplateDefinition, type TemplateParameter, type Unit, UnitConverter, clamp, deserializeEditor, exportDataURL, exportPNG, exportSVG, generateId, round2, serializeEditor };
@@ -0,0 +1,250 @@
1
+ import { FabricObject, Canvas, ImageFormat } from 'fabric';
2
+
3
+ type Unit = 'px' | 'mm' | 'in';
4
+ type LayerType = 'image' | 'text' | 'shape' | 'template' | 'group';
5
+ interface LayerData {
6
+ id: string;
7
+ type: LayerType;
8
+ name: string;
9
+ visible: boolean;
10
+ locked: boolean;
11
+ opacity: number;
12
+ }
13
+ interface SerializedLayer extends LayerData {
14
+ fabricObject: Record<string, unknown>;
15
+ }
16
+ interface EditorState {
17
+ version: string;
18
+ canvas: {
19
+ width: number;
20
+ height: number;
21
+ };
22
+ layers: SerializedLayer[];
23
+ background?: string;
24
+ }
25
+ interface EditorConfig {
26
+ width: number;
27
+ height: number;
28
+ backgroundColor?: string;
29
+ fileAdapter?: FileAdapter;
30
+ imageProvider?: ImageProvider;
31
+ preserveObjectStacking?: boolean;
32
+ unit?: Unit;
33
+ dpi?: number;
34
+ }
35
+ type ExportFormat = 'png' | 'svg' | 'json';
36
+ interface FileAdapter {
37
+ save(data: Blob | string, filename: string, format: ExportFormat): Promise<string>;
38
+ }
39
+ interface ImageProviderResult {
40
+ url: string;
41
+ width: number;
42
+ height: number;
43
+ }
44
+ interface ImageProvider {
45
+ upload(file: File): Promise<ImageProviderResult>;
46
+ browse?(): Promise<ImageProviderResult | null>;
47
+ }
48
+ interface ShapePlugin {
49
+ name: string;
50
+ icon: string;
51
+ category?: string;
52
+ create(options?: Record<string, unknown>): FabricObject;
53
+ }
54
+ interface TemplateParameter {
55
+ key: string;
56
+ label: string;
57
+ type: 'text' | 'color' | 'number' | 'font';
58
+ default: string | number;
59
+ }
60
+ interface TemplateDefinition {
61
+ id: string;
62
+ name: string;
63
+ category: string;
64
+ svg: string;
65
+ parameters: TemplateParameter[];
66
+ preview?: string;
67
+ }
68
+ interface PrintifyPositioning {
69
+ x: number;
70
+ y: number;
71
+ scale: number;
72
+ angle: number;
73
+ }
74
+ interface EditorEvents {
75
+ 'layer:added': {
76
+ layer: LayerData;
77
+ };
78
+ 'layer:removed': {
79
+ layerId: string;
80
+ };
81
+ 'layer:selected': {
82
+ layerId: string | null;
83
+ };
84
+ 'layer:modified': {
85
+ layerId: string;
86
+ };
87
+ 'layer:reordered': {
88
+ layerIds: string[];
89
+ };
90
+ 'layers:changed': {
91
+ layers: LayerData[];
92
+ };
93
+ 'selection:changed': {
94
+ selected: string[];
95
+ };
96
+ 'history:changed': {
97
+ canUndo: boolean;
98
+ canRedo: boolean;
99
+ };
100
+ 'canvas:modified': Record<string, never>;
101
+ 'export:start': {
102
+ format: string;
103
+ };
104
+ 'export:complete': {
105
+ format: string;
106
+ url?: string;
107
+ };
108
+ error: {
109
+ message: string;
110
+ error?: unknown;
111
+ };
112
+ }
113
+
114
+ type EventHandler<T> = (data: T) => void;
115
+ declare class EventEmitter<TEvents extends {} = Record<string, unknown>> {
116
+ private listeners;
117
+ on<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void;
118
+ off<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void;
119
+ once<K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): void;
120
+ emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void;
121
+ removeAllListeners(event?: keyof TEvents): void;
122
+ }
123
+
124
+ declare class Layer {
125
+ readonly id: string;
126
+ readonly type: LayerType;
127
+ name: string;
128
+ visible: boolean;
129
+ locked: boolean;
130
+ opacity: number;
131
+ readonly fabricObject: FabricObject;
132
+ constructor(type: LayerType, fabricObject: FabricObject, name?: string, id?: string);
133
+ toData(): LayerData;
134
+ serialize(): SerializedLayer;
135
+ }
136
+ declare class LayerManager {
137
+ private canvas;
138
+ private events;
139
+ private layers;
140
+ constructor(canvas: Canvas, events: EventEmitter<EditorEvents>);
141
+ add(type: LayerType, fabricObject: FabricObject, name?: string, id?: string): Layer;
142
+ remove(id: string): boolean;
143
+ reorder(id: string, newIndex: number): boolean;
144
+ select(id: string | null): void;
145
+ get(id: string): Layer | undefined;
146
+ getAll(): Layer[];
147
+ findByObject(obj: FabricObject): Layer | undefined;
148
+ setVisibility(id: string, visible: boolean): void;
149
+ setLocked(id: string, locked: boolean): void;
150
+ setOpacity(id: string, opacity: number): void;
151
+ setName(id: string, name: string): void;
152
+ clear(): void;
153
+ count(): number;
154
+ private emitChanged;
155
+ }
156
+
157
+ declare class HistoryManager {
158
+ private undoStack;
159
+ private redoStack;
160
+ private maxSize;
161
+ private paused;
162
+ private debounceTimer;
163
+ private debounceMs;
164
+ private getState;
165
+ private restoreState;
166
+ private events;
167
+ constructor(opts: {
168
+ getState: () => string;
169
+ restoreState: (state: string) => Promise<void>;
170
+ events: EventEmitter<EditorEvents>;
171
+ maxSize?: number;
172
+ debounceMs?: number;
173
+ });
174
+ save(): void;
175
+ saveImmediate(): void;
176
+ undo(): Promise<void>;
177
+ redo(): Promise<void>;
178
+ pause(): void;
179
+ resume(): void;
180
+ canUndo(): boolean;
181
+ canRedo(): boolean;
182
+ clear(): void;
183
+ dispose(): void;
184
+ private emitChanged;
185
+ }
186
+
187
+ declare class UnitConverter {
188
+ private unit;
189
+ private dpi;
190
+ constructor(unit?: Unit, dpi?: number);
191
+ setUnit(unit: Unit): void;
192
+ setDpi(dpi: number): void;
193
+ getUnit(): Unit;
194
+ getDpi(): number;
195
+ toPixels(value: number, fromUnit?: Unit): number;
196
+ fromPixels(px: number, toUnit?: Unit): number;
197
+ }
198
+
199
+ interface PngExportOptions {
200
+ multiplier?: number;
201
+ format?: ImageFormat;
202
+ quality?: number;
203
+ }
204
+ declare function exportPNG(canvas: Canvas, options?: PngExportOptions): Promise<Blob>;
205
+ declare function exportSVG(canvas: Canvas): string;
206
+ declare function exportDataURL(canvas: Canvas, format?: ImageFormat, multiplier?: number): string;
207
+
208
+ declare class CanvasEditor {
209
+ readonly canvas: Canvas;
210
+ readonly layers: LayerManager;
211
+ readonly history: HistoryManager;
212
+ readonly events: EventEmitter<EditorEvents>;
213
+ readonly units: UnitConverter;
214
+ private fileAdapter?;
215
+ private imageProvider?;
216
+ constructor(canvasElement: HTMLCanvasElement, config: EditorConfig);
217
+ addImage(url: string, options?: Partial<Record<string, unknown>>): Promise<Layer>;
218
+ addText(text: string, options?: Partial<Record<string, unknown>>): Layer;
219
+ addShape(plugin: ShapePlugin, options?: Record<string, unknown>): Layer;
220
+ addTemplate(template: TemplateDefinition, _params: Record<string, string | number>): Promise<Layer>;
221
+ removeLayer(id: string): void;
222
+ selectLayer(id: string | null): void;
223
+ getSelectedLayer(): Layer | null;
224
+ toJSON(): EditorState;
225
+ fromJSON(state: EditorState): Promise<void>;
226
+ toPNG(options?: PngExportOptions): Promise<Blob>;
227
+ toSVG(): string;
228
+ toDataURL(format?: ImageFormat, multiplier?: number): string;
229
+ toPrintifyPositioning(): Record<string, PrintifyPositioning>;
230
+ save(filename: string, format: 'png' | 'svg' | 'json'): Promise<string | undefined>;
231
+ uploadImage(file: File): Promise<ImageProviderResult | undefined>;
232
+ browseImages(): Promise<ImageProviderResult | null>;
233
+ setFileAdapter(adapter: FileAdapter): void;
234
+ setImageProvider(provider: ImageProvider): void;
235
+ setBackground(color: string): void;
236
+ resize(width: number, height: number): void;
237
+ zoom(level: number): void;
238
+ dispose(): void;
239
+ private setupCanvasEvents;
240
+ }
241
+
242
+ declare const generateId: () => string;
243
+
244
+ declare function clamp(v: number, min: number, max: number): number;
245
+ declare function round2(v: number): number;
246
+
247
+ declare function serializeEditor(editor: CanvasEditor): EditorState;
248
+ declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
249
+
250
+ export { CanvasEditor, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerType, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, type TemplateDefinition, type TemplateParameter, type Unit, UnitConverter, clamp, deserializeEditor, exportDataURL, exportPNG, exportSVG, generateId, round2, serializeEditor };