@codingfactory/mediables-vue 2.11.0 → 2.13.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.
Files changed (64) hide show
  1. package/dist/{PixiFrameExporter-apC6SqIv.cjs → PixiFrameExporter-BnAievFi.cjs} +2 -2
  2. package/dist/{PixiFrameExporter-apC6SqIv.cjs.map → PixiFrameExporter-BnAievFi.cjs.map} +1 -1
  3. package/dist/{PixiFrameExporter-D9swI3PO.js → PixiFrameExporter-DwbBy1Iu.js} +2 -2
  4. package/dist/{PixiFrameExporter-D9swI3PO.js.map → PixiFrameExporter-DwbBy1Iu.js.map} +1 -1
  5. package/dist/components/ImageEditorModal.vue.d.ts +9 -2
  6. package/dist/composables/useImageEditorModal.d.ts +1262 -86
  7. package/dist/composables/useRadialMenu.d.ts +1 -1
  8. package/dist/editor-BoDU3YXD.js +10862 -0
  9. package/dist/editor-BoDU3YXD.js.map +1 -0
  10. package/dist/editor-D-cJRASf.cjs +2 -0
  11. package/dist/editor-D-cJRASf.cjs.map +1 -0
  12. package/dist/{index-BFYtEc-Y.js → index-BGC4rPDc.js} +16370 -14565
  13. package/dist/index-BGC4rPDc.js.map +1 -0
  14. package/dist/index-CljyFIEv.cjs +357 -0
  15. package/dist/index-CljyFIEv.cjs.map +1 -0
  16. package/dist/mediables-vanilla.cjs +1 -1
  17. package/dist/mediables-vanilla.mjs +1 -1
  18. package/dist/mediables-vue.cjs +1 -1
  19. package/dist/mediables-vue.mjs +2 -2
  20. package/dist/style.css +1 -1
  21. package/dist/types/editor.d.ts +212 -27
  22. package/dist/vanilla-editor/VanillaImageEditor.d.ts +18 -0
  23. package/dist/vanilla-editor/VanillaImageEditorV2.d.ts +16 -0
  24. package/dist/vanilla-editor/core/EventEmitter.d.ts +12 -0
  25. package/dist/vanilla-editor/core/State.d.ts +98 -0
  26. package/dist/vanilla-editor/filters/categories.d.ts +69 -0
  27. package/dist/vanilla-editor/icons/icons.d.ts +42 -0
  28. package/dist/vanilla-editor/index.d.ts +342 -0
  29. package/dist/vanilla-editor/presets/index.d.ts +114 -0
  30. package/dist/vanilla-editor/renderer/CropManager.d.ts +140 -0
  31. package/dist/vanilla-editor/renderer/FilterManager.d.ts +132 -0
  32. package/dist/vanilla-editor/renderer/PixiRenderer.d.ts +274 -0
  33. package/dist/vanilla-editor/renderer/RemoveBgManager.d.ts +108 -0
  34. package/dist/vanilla-editor/styles/editor-v2.css +1366 -0
  35. package/dist/vanilla-editor/styles/editor.css +1403 -0
  36. package/dist/vanilla-editor/ui/ActiveFiltersPanel.d.ts +93 -0
  37. package/dist/vanilla-editor/ui/CategoryCarousel.d.ts +66 -0
  38. package/dist/vanilla-editor/ui/CropControls.d.ts +65 -0
  39. package/dist/vanilla-editor/ui/FilterAdjustments.d.ts +93 -0
  40. package/dist/vanilla-editor/ui/FilterCarousel.d.ts +74 -0
  41. package/dist/vanilla-editor/ui/MobileActiveFilters.d.ts +21 -0
  42. package/dist/vanilla-editor/ui/MobileFilterDrawer.d.ts +79 -0
  43. package/dist/vanilla-editor/ui/Toolbar.d.ts +35 -0
  44. package/dist/vanilla-editor/ui/UIBuilder.d.ts +87 -0
  45. package/dist/vanilla-editor/ui-v2/ActiveFiltersStack.d.ts +16 -0
  46. package/dist/vanilla-editor/ui-v2/BackgroundPanel.d.ts +16 -0
  47. package/dist/vanilla-editor/ui-v2/BottomDrawer.d.ts +55 -0
  48. package/dist/vanilla-editor/ui-v2/CategoryTabs.d.ts +21 -0
  49. package/dist/vanilla-editor/ui-v2/FilterControlsView.d.ts +16 -0
  50. package/dist/vanilla-editor/ui-v2/FilterListView.d.ts +32 -0
  51. package/dist/vanilla-editor/ui-v2/LayerStackPanel.d.ts +30 -0
  52. package/dist/vanilla-editor/ui-v2/MobileActiveChips.d.ts +23 -0
  53. package/dist/vanilla-editor/ui-v2/MobileNavigation.d.ts +51 -0
  54. package/dist/vanilla-editor/ui-v2/SplitPanelLayout.d.ts +70 -0
  55. package/dist/vanilla-editor/ui-v2/TextPanel.d.ts +58 -0
  56. package/dist/vanilla-exports.d.ts +9 -0
  57. package/package.json +3 -2
  58. package/dist/editor-BTwIhrcA.cjs +0 -2
  59. package/dist/editor-BTwIhrcA.cjs.map +0 -1
  60. package/dist/editor-CYj5y5bp.js +0 -8893
  61. package/dist/editor-CYj5y5bp.js.map +0 -1
  62. package/dist/index-BFYtEc-Y.js.map +0 -1
  63. package/dist/index-CAPdRZVb.cjs +0 -357
  64. package/dist/index-CAPdRZVb.cjs.map +0 -1
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Vanilla Image Editor Type Declarations
3
+ */
4
+
5
+ import { EventEmitter } from './core/EventEmitter';
6
+
7
+ export interface VanillaImageEditorOptions {
8
+ theme?: 'light' | 'dark' | 'auto';
9
+ initialMode?: 'filters' | 'crop' | 'adjust';
10
+ cropShape?: 'free' | 'circle' | 'square';
11
+ initialAspectRatio?: string;
12
+ backgroundRemoval?: {
13
+ enabled?: boolean;
14
+ endpoint?: string;
15
+ optionsEndpoint?: string;
16
+ savedEndpoint?: string;
17
+ fallbackEndpoint?: string | null;
18
+ defaultTier?: 'fast' | 'balanced' | 'best';
19
+ maxMegapixels?: number;
20
+ };
21
+ backgroundReplacement?: BackgroundReplacementConfig | null;
22
+ }
23
+
24
+ export interface BackgroundReplacementConfig {
25
+ assetEndpoint?: string | null;
26
+ materializeEndpoint?: string | null;
27
+ targetMediaUuid?: string | null;
28
+ mediaChoices?: Array<{
29
+ id?: string;
30
+ label?: string;
31
+ source: ImageEditorLayerSource;
32
+ fit?: 'cover' | 'contain' | 'stretch' | 'tile';
33
+ }>;
34
+ }
35
+
36
+ export interface FilterRegistry {
37
+ getAllFilters: () => FilterDefinition[];
38
+ getFilter: (id: string) => FilterDefinition | undefined;
39
+ getFiltersByCategory: (category: string) => FilterDefinition[];
40
+ }
41
+
42
+ export interface ControlDefinition {
43
+ id: string;
44
+ type: 'slider' | 'toggle' | 'color' | 'select' | 'range' | 'button' | 'text';
45
+ label: string;
46
+ property?: string;
47
+ action?: string;
48
+ min?: number;
49
+ max?: number;
50
+ step?: number;
51
+ default?: number | boolean | string;
52
+ options?: Array<{ label: string; value: string | number }>;
53
+ tooltip?: string;
54
+ }
55
+
56
+ export interface FilterDefinition {
57
+ id: string;
58
+ name: string;
59
+ category: string;
60
+ description?: string;
61
+ thumbnail?: string;
62
+ createFilter: (params: Record<string, unknown>) => unknown;
63
+ defaultParams: Record<string, unknown>;
64
+ controls: ControlDefinition[];
65
+ }
66
+
67
+ export interface SaveEventPayload {
68
+ blob: Blob;
69
+ mimeType: string;
70
+ dimensions: { width: number; height: number };
71
+ state: ImageEditorSessionState;
72
+ document?: ImageEditorDocument | null;
73
+ documentId?: string | null;
74
+ documentRevisionId?: string | null;
75
+ documentRevisionNumber?: number | null;
76
+ }
77
+
78
+ export interface ErrorEventPayload {
79
+ error: Error;
80
+ }
81
+
82
+ /** Canonical serializable editor session state (v3.0.0+) */
83
+ export interface ImageEditorSessionState {
84
+ version: 1;
85
+ crop: {
86
+ rect: { x: number; y: number; width: number; height: number } | null;
87
+ aspectRatio: string;
88
+ shape: 'free' | 'square' | 'circle';
89
+ };
90
+ transform?: {
91
+ rotation: number;
92
+ };
93
+ filters: Array<{
94
+ id: string;
95
+ enabled: boolean;
96
+ values: Record<string, string | number | boolean>;
97
+ }>;
98
+ }
99
+
100
+ export type ImageEditorLayerSource =
101
+ | { kind: 'media'; mediaUuid: string; originalWidth: number; originalHeight: number; sourceHash: string; url?: string; previewUrl?: string; sourceUrl?: string }
102
+ | { kind: 'edit-asset'; assetId: string; originalWidth: number; originalHeight: number; sourceHash: string; url?: string; previewUrl?: string; sourceUrl?: string }
103
+ | { kind: 'runtime'; runtimeRef: string; originalWidth: number; originalHeight: number; sourceHash: string | null; url?: string; previewUrl?: string; sourceUrl?: string }
104
+ | { kind: 'color'; color: string };
105
+
106
+ export type ImageEditorBackgroundFill =
107
+ | { kind: 'transparent' }
108
+ | { kind: 'color'; value: string; fit?: 'cover' | 'contain' | 'stretch' | 'tile' }
109
+ | { kind: 'gradient'; gradientType: 'linear' | 'radial'; angle: number; stops: Array<{ offset: number; color: string }>; fit: 'cover' | 'contain' | 'stretch' | 'tile' }
110
+ | { kind: 'media'; source: ImageEditorLayerSource; fit: 'cover' | 'contain' | 'stretch' | 'tile' };
111
+
112
+ export interface ImageEditorTextStyle {
113
+ fontFamily: string;
114
+ fontSize: number;
115
+ fontWeight: number;
116
+ fontStyle: 'normal' | 'italic';
117
+ fill: string;
118
+ align: 'left' | 'center' | 'right';
119
+ lineHeight: number;
120
+ letterSpacing: number;
121
+ }
122
+
123
+ export interface ImageEditorTextEffects {
124
+ stroke: { enabled: boolean; color: string; width: number };
125
+ shadow: { enabled: boolean; color: string; alpha: number; blur: number; distance: number; angle: number };
126
+ glow: { enabled: boolean; color: string; alpha: number; blur: number };
127
+ backdrop: { enabled: boolean; color: string; opacity: number; padding: number; radius: number; blur: number };
128
+ }
129
+
130
+ export interface ImageEditorTextBox {
131
+ x: number;
132
+ y: number;
133
+ width: number;
134
+ height: number;
135
+ }
136
+
137
+ export interface ImageEditorTextPayload {
138
+ content: string;
139
+ box: ImageEditorTextBox;
140
+ style: ImageEditorTextStyle;
141
+ effects: ImageEditorTextEffects;
142
+ }
143
+
144
+ export interface ImageEditorTextLayerPatch {
145
+ content?: string;
146
+ box?: Partial<ImageEditorTextBox>;
147
+ style?: Partial<ImageEditorTextStyle> & Record<string, unknown>;
148
+ effects?: Partial<{
149
+ stroke: Partial<ImageEditorTextEffects['stroke']>;
150
+ shadow: Partial<ImageEditorTextEffects['shadow']>;
151
+ glow: Partial<ImageEditorTextEffects['glow']>;
152
+ backdrop: Partial<ImageEditorTextEffects['backdrop']>;
153
+ }>;
154
+ transform?: Partial<{ x: number; y: number; scaleX: number; scaleY: number; rotation: number }>;
155
+ text?: Partial<ImageEditorTextPayload>;
156
+ opacity?: number;
157
+ blendMode?: ImageEditorLayer['blendMode'];
158
+ }
159
+
160
+ export interface ImageEditorLayer {
161
+ id: string;
162
+ type: 'background' | 'image' | 'text';
163
+ role: 'background' | 'subject' | 'overlay' | 'text';
164
+ name: string;
165
+ visible: boolean;
166
+ locked: boolean;
167
+ opacity: number;
168
+ blendMode: 'normal' | 'multiply' | 'screen' | 'overlay';
169
+ transform: { x: number; y: number; scaleX: number; scaleY: number; rotation: number };
170
+ effects: unknown[];
171
+ metadata: Record<string, unknown>;
172
+ source?: ImageEditorLayerSource;
173
+ fill?: ImageEditorBackgroundFill;
174
+ text?: ImageEditorTextPayload;
175
+ crop: { x: number; y: number; width: number; height: number } | null;
176
+ filters: ImageEditorSessionState['filters'];
177
+ }
178
+
179
+ export interface ImageEditorDocument {
180
+ version: 2;
181
+ id: string;
182
+ sourceMediaUuid: string | null;
183
+ canvas: { width: number; height: number; backgroundPreviewColor: string | null };
184
+ revision: number;
185
+ layers: ImageEditorLayer[];
186
+ activeLayerId: string | null;
187
+ export: {
188
+ mimeType: 'image/png' | 'image/jpeg' | 'image/webp';
189
+ quality: number | null;
190
+ preserveTransparency: boolean;
191
+ matteColor: string | null;
192
+ trimTransparentBounds: boolean;
193
+ };
194
+ createdAt?: string;
195
+ updatedAt?: string;
196
+ }
197
+
198
+ export interface EditorExportOptions {
199
+ maxEdge?: number;
200
+ maxPixels?: number;
201
+ dontUpscale?: boolean;
202
+ }
203
+
204
+ export interface EditorBlobExportResult {
205
+ blob: Blob;
206
+ width: number;
207
+ height: number;
208
+ }
209
+
210
+ export class VanillaImageEditor extends EventEmitter {
211
+ constructor(container: HTMLElement, options?: VanillaImageEditorOptions);
212
+
213
+ loadImage(
214
+ imageSource: string | Blob | File,
215
+ options?: { state?: ImageEditorSessionState | null; document?: ImageEditorDocument | null; documentId?: string | null; documentRevisionId?: string | null; documentRevisionNumber?: number | null; sessionKey?: string | number }
216
+ ): Promise<void>;
217
+ exportImage(format?: 'png' | 'jpeg', quality?: number, options?: EditorExportOptions): string | null;
218
+ exportBlob(format?: 'png' | 'jpeg', quality?: number, options?: EditorExportOptions): Promise<EditorBlobExportResult | null>;
219
+ save(): void | Promise<void>;
220
+ reset(): void;
221
+ rotateBy(degrees: number): boolean;
222
+ setRotationAngle(angle: number): boolean;
223
+ resetRotation(): boolean;
224
+ setTheme(theme: 'light' | 'dark' | 'auto'): void;
225
+ setMode(mode: 'filters' | 'crop'): void;
226
+ setFilterRegistry(registry: FilterRegistry): void;
227
+ removeBackground(options?: { tier?: 'fast' | 'balanced' | 'best'; operationId?: string; sessionKey?: string; sourceHash?: string; model?: string; alpha_matting?: boolean; max_megapixels?: number }): Promise<Record<string, unknown>>;
228
+ canRemoveBackground(): boolean;
229
+ isBackgroundRemovalAvailable(): Promise<boolean>;
230
+ getState(): Record<string, unknown>;
231
+ getSerializableState(): ImageEditorSessionState;
232
+ getSerializableDocument?(): ImageEditorDocument | null;
233
+ getEditorDocumentBinding?(): { documentId: string | null; documentRevisionId: string | null; documentRevisionNumber: number | null } | null;
234
+ setBackgroundColor?(color: string): Promise<string | null>;
235
+ setBackgroundGradient?(gradient: Partial<Extract<ImageEditorBackgroundFill, { kind: 'gradient' }>>): Promise<string | null>;
236
+ setBackgroundMediaSource?(source: ImageEditorLayerSource, options?: { fit?: 'cover' | 'contain' | 'stretch' | 'tile' }): Promise<string | null>;
237
+ setBackgroundImageFromFile?(file: Blob, options?: { fit?: 'cover' | 'contain' | 'stretch' | 'tile' }): Promise<string | null>;
238
+ setBackgroundFit?(fit: 'cover' | 'contain' | 'stretch' | 'tile'): Promise<boolean>;
239
+ setBackgroundBlur?(amount: number): Promise<boolean>;
240
+ removeBackgroundLayer?(): Promise<boolean>;
241
+ selectLayer?(layerId: string): Promise<boolean>;
242
+ renameLayer?(layerId: string, name: string): Promise<boolean>;
243
+ duplicateLayer?(layerId: string): Promise<string | null>;
244
+ deleteLayer?(layerId: string): Promise<boolean>;
245
+ moveLayer?(layerId: string, direction: 'up' | 'down' | 'front' | 'back' | 'bring-forward' | 'send-backward' | 'bring-to-front' | 'send-to-back'): Promise<boolean>;
246
+ setLayerVisibility?(layerId: string, visible: boolean): Promise<boolean>;
247
+ setLayerLocked?(layerId: string, locked: boolean): Promise<boolean>;
248
+ addTextLayer?(payload?: ImageEditorTextLayerPatch): Promise<string | null>;
249
+ updateTextLayer?(layerId: string, patch?: ImageEditorTextLayerPatch): Promise<boolean>;
250
+ destroy(): void;
251
+
252
+ on(event: 'save', callback: (payload: SaveEventPayload) => void): void;
253
+ on(event: 'cancel', callback: () => void): void;
254
+ on(event: 'error', callback: (payload: ErrorEventPayload) => void): void;
255
+ on(event: string, callback: (...args: unknown[]) => void): void;
256
+ }
257
+
258
+ /**
259
+ * v2 editor with persistent inspector panel layout.
260
+ * Mirrors VanillaImageEditor's public API so the Vue wrapper can swap between them.
261
+ */
262
+ export class VanillaImageEditorV2 extends EventEmitter {
263
+ constructor(container: HTMLElement, options?: VanillaImageEditorOptions & { layout?: 'split-panel' });
264
+
265
+ loadImage(
266
+ imageSource: string | Blob | File,
267
+ options?: { state?: ImageEditorSessionState | null; document?: ImageEditorDocument | null; documentId?: string | null; documentRevisionId?: string | null; documentRevisionNumber?: number | null; sessionKey?: string | number }
268
+ ): Promise<void>;
269
+ exportImage(format?: 'png' | 'jpeg', quality?: number, options?: EditorExportOptions): string | null;
270
+ exportBlob(format?: 'png' | 'jpeg', quality?: number, options?: EditorExportOptions): Promise<EditorBlobExportResult | null>;
271
+ save(): void | Promise<void>;
272
+ resetAll(expectedLayerId?: string | null): void;
273
+ rotateBy(degrees: number): boolean;
274
+ setRotationAngle(angle: number): boolean;
275
+ resetRotation(): boolean;
276
+ setTheme(theme: 'light' | 'dark' | 'auto'): void;
277
+ setMode(mode: 'filters' | 'crop'): void;
278
+ setFilterRegistry(registry: FilterRegistry): void;
279
+ removeBackground(options?: { tier?: 'fast' | 'balanced' | 'best'; operationId?: string; sessionKey?: string; sourceHash?: string; model?: string; alpha_matting?: boolean; max_megapixels?: number }): Promise<Record<string, unknown>>;
280
+ canRemoveBackground(): boolean;
281
+ isBackgroundRemovalAvailable(): Promise<boolean>;
282
+ getState(): Record<string, unknown>;
283
+ getSerializableState(): ImageEditorSessionState;
284
+ getSerializableDocument(): ImageEditorDocument | null;
285
+ getEditorDocumentBinding(): { documentId: string | null; documentRevisionId: string | null; documentRevisionNumber: number | null } | null;
286
+ setBackgroundColor(color: string): Promise<string | null>;
287
+ setBackgroundGradient(gradient: Partial<Extract<ImageEditorBackgroundFill, { kind: 'gradient' }>>): Promise<string | null>;
288
+ setBackgroundMediaSource(source: ImageEditorLayerSource, options?: { fit?: 'cover' | 'contain' | 'stretch' | 'tile' }): Promise<string | null>;
289
+ setBackgroundImageFromFile(file: Blob, options?: { fit?: 'cover' | 'contain' | 'stretch' | 'tile' }): Promise<string | null>;
290
+ setBackgroundFit(fit: 'cover' | 'contain' | 'stretch' | 'tile'): Promise<boolean>;
291
+ setBackgroundBlur(amount: number): Promise<boolean>;
292
+ removeBackgroundLayer(): Promise<boolean>;
293
+ selectLayer(layerId: string): Promise<boolean>;
294
+ renameLayer(layerId: string, name: string): Promise<boolean>;
295
+ duplicateLayer(layerId: string): Promise<string | null>;
296
+ deleteLayer(layerId: string): Promise<boolean>;
297
+ moveLayer(layerId: string, direction: 'up' | 'down' | 'front' | 'back' | 'bring-forward' | 'send-backward' | 'bring-to-front' | 'send-to-back'): Promise<boolean>;
298
+ setLayerVisibility(layerId: string, visible: boolean): Promise<boolean>;
299
+ setLayerLocked(layerId: string, locked: boolean): Promise<boolean>;
300
+ addTextLayer(payload?: ImageEditorTextLayerPatch): Promise<string | null>;
301
+ updateTextLayer(layerId: string, patch?: ImageEditorTextLayerPatch): Promise<boolean>;
302
+ destroy(): void;
303
+
304
+ on(event: 'save', callback: (payload: SaveEventPayload) => void): void;
305
+ on(event: 'cancel', callback: () => void): void;
306
+ on(event: 'error', callback: (payload: ErrorEventPayload) => void): void;
307
+ on(event: string, callback: (...args: unknown[]) => void): void;
308
+ }
309
+
310
+ // Core utilities
311
+ export { EventEmitter } from './core/EventEmitter';
312
+ export { State, createState, getState } from './core/State';
313
+
314
+ // Renderer classes
315
+ export { PixiRenderer } from './renderer/PixiRenderer';
316
+ export { FilterManager } from './renderer/FilterManager';
317
+ export { CropManager } from './renderer/CropManager';
318
+
319
+ // UI components
320
+ export { Toolbar } from './ui/Toolbar';
321
+ export { CategoryCarousel } from './ui/CategoryCarousel';
322
+ export { FilterCarousel } from './ui/FilterCarousel';
323
+ export { FilterAdjustments } from './ui/FilterAdjustments';
324
+ export { CropControls } from './ui/CropControls';
325
+ export { ActiveFiltersPanel } from './ui/ActiveFiltersPanel';
326
+
327
+ // UI Builder utilities
328
+ export {
329
+ el,
330
+ createSlider,
331
+ createToggle,
332
+ createColorPicker,
333
+ createSelect,
334
+ createButton,
335
+ createIconButton,
336
+ createChip,
337
+ createCard,
338
+ createScrollContainer
339
+ } from './ui/UIBuilder';
340
+
341
+ // Icons
342
+ export * as icons from './icons/icons';
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Resolve a preset by name or return a custom preset object as-is.
3
+ * Falls back to the 'free' preset if the name is not recognized.
4
+ *
5
+ * @param {string|EditorPreset} preset - Preset name or custom preset object
6
+ * @returns {EditorPreset} Resolved preset
7
+ */
8
+ export function getPreset(preset: string | EditorPreset): EditorPreset;
9
+ /**
10
+ * Merge a preset with explicit option overrides.
11
+ * Options take precedence over preset values.
12
+ *
13
+ * @param {EditorPreset} preset - Base preset
14
+ * @param {Partial<EditorPreset>} overrides - Explicit overrides
15
+ * @returns {EditorPreset} Merged result
16
+ */
17
+ export function mergePresetWithOverrides(preset: EditorPreset, overrides: Partial<EditorPreset>): EditorPreset;
18
+ /**
19
+ * Parse an aspect ratio string into a numeric value.
20
+ * Handles standard ratios ('1:1', '16:9', etc.) and custom strings ('3:1', '21:9').
21
+ * Returns null for 'free' or invalid values.
22
+ *
23
+ * @param {string} ratio - Aspect ratio string
24
+ * @returns {number|null} Numeric ratio (width / height), or null for 'free'
25
+ */
26
+ export function parseAspectRatio(ratio: string): number | null;
27
+ /**
28
+ * Built-in presets.
29
+ * @type {Record<string, EditorPreset>}
30
+ */
31
+ export const PRESETS: Record<string, EditorPreset>;
32
+ export type EditorPreset = {
33
+ /**
34
+ * - Preset identifier (e.g., 'avatar', 'banner', 'product')
35
+ */
36
+ name: string;
37
+ /**
38
+ * - Which tool is active when editor opens
39
+ */
40
+ initialMode: "crop" | "filters";
41
+ /**
42
+ * - Default crop shape
43
+ */
44
+ cropShape: "free" | "square" | "circle";
45
+ /**
46
+ * - Aspect ratio: 'free', '1:1', '4:3', '16:9', '3:2', '2:3', or custom like '3:1'
47
+ */
48
+ aspectRatio: string;
49
+ /**
50
+ * - Smart zoom-out when crop exceeds image bounds
51
+ */
52
+ autoZoomOnCropOverflow: boolean;
53
+ /**
54
+ * - Prevent user from changing crop shape
55
+ */
56
+ lockCropShape: boolean;
57
+ /**
58
+ * - Prevent user from changing aspect ratio
59
+ */
60
+ lockAspectRatio: boolean;
61
+ /**
62
+ * - Whether filter panel is available
63
+ */
64
+ showFilters: boolean;
65
+ /**
66
+ * - Whether crop controls are available
67
+ */
68
+ showCropControls: boolean;
69
+ /**
70
+ * - Optional max output width in pixels
71
+ */
72
+ maxExportWidth?: number | undefined;
73
+ /**
74
+ * - Optional max output height in pixels
75
+ */
76
+ maxExportHeight?: number | undefined;
77
+ };
78
+ /**
79
+ * Editor Preset System
80
+ *
81
+ * Presets configure the image editor for specific use cases (avatars, banners, etc.).
82
+ * Each preset defines initial tool state, crop shape, aspect ratio, and control visibility.
83
+ *
84
+ * @example
85
+ * import { getPreset, PRESETS } from './presets/index.js'
86
+ *
87
+ * // Get a built-in preset by name
88
+ * const avatarPreset = getPreset('avatar')
89
+ *
90
+ * // Use directly
91
+ * const editor = new VanillaImageEditor(container, { preset: avatarPreset })
92
+ *
93
+ * // Or pass by name (resolved later)
94
+ * const editor = new VanillaImageEditor(container, { preset: 'avatar' })
95
+ */
96
+ /**
97
+ * @typedef {Object} EditorPreset
98
+ * @property {string} name - Preset identifier (e.g., 'avatar', 'banner', 'product')
99
+ * @property {'crop'|'filters'} initialMode - Which tool is active when editor opens
100
+ * @property {'free'|'square'|'circle'} cropShape - Default crop shape
101
+ * @property {string} aspectRatio - Aspect ratio: 'free', '1:1', '4:3', '16:9', '3:2', '2:3', or custom like '3:1'
102
+ * @property {boolean} autoZoomOnCropOverflow - Smart zoom-out when crop exceeds image bounds
103
+ * @property {boolean} lockCropShape - Prevent user from changing crop shape
104
+ * @property {boolean} lockAspectRatio - Prevent user from changing aspect ratio
105
+ * @property {boolean} showFilters - Whether filter panel is available
106
+ * @property {boolean} showCropControls - Whether crop controls are available
107
+ * @property {number} [maxExportWidth] - Optional max output width in pixels
108
+ * @property {number} [maxExportHeight] - Optional max output height in pixels
109
+ */
110
+ /**
111
+ * Default values for an EditorPreset. Used as the base when merging.
112
+ * @type {EditorPreset}
113
+ */
114
+ export const DEFAULT_PRESET: EditorPreset;
@@ -0,0 +1,140 @@
1
+ export class CropManager extends EventEmitter {
2
+ /**
3
+ * Padding factor for auto-zoom: image will be ~91% of crop size (1/1.1).
4
+ * Higher values = more aggressive zoom-out, more padding around image.
5
+ */
6
+ static AUTO_ZOOM_PADDING: number;
7
+ /**
8
+ * Minimum interval (ms) between auto-zoom adjustments during drag.
9
+ */
10
+ static AUTO_ZOOM_THROTTLE_MS: number;
11
+ /**
12
+ * Calculate the target zoom level for auto-zoom when crop exceeds image bounds.
13
+ * Pure function — returns the new zoom level, or null if no adjustment is needed.
14
+ *
15
+ * @param {{ width: number, height: number }} cropRect - Current crop dimensions
16
+ * @param {number} spriteWidth - Current sprite width on screen
17
+ * @param {number} spriteHeight - Current sprite height on screen
18
+ * @param {number} texWidth - Original texture width (pixels)
19
+ * @param {number} texHeight - Original texture height (pixels)
20
+ * @param {number} fitScale - Current fit-to-screen scale
21
+ * @param {number} currentZoom - Current zoom level
22
+ * @returns {number|null} Target zoom level, or null if no zoom needed
23
+ */
24
+ static calcAutoZoom(cropRect: {
25
+ width: number;
26
+ height: number;
27
+ }, spriteWidth: number, spriteHeight: number, texWidth: number, texHeight: number, fitScale: number, currentZoom: number): number | null;
28
+ constructor(state: any, pixiRenderer: any);
29
+ state: any;
30
+ renderer: any;
31
+ _overlayCanvas: HTMLCanvasElement | null;
32
+ _isDragging: boolean;
33
+ _dragStart: {
34
+ x: any;
35
+ y: any;
36
+ } | null;
37
+ _dragMode: string | null;
38
+ _startRect: any;
39
+ _hoverMode: string | null;
40
+ _lastAutoZoomCheck: number;
41
+ HANDLE_SIZE: number;
42
+ EDGE_HIT_PAD: number;
43
+ _onPointerDown: (event: any) => void;
44
+ _onPointerMove: (event: any) => void;
45
+ _onPointerUp: () => void;
46
+ /**
47
+ * Set the overlay canvas element
48
+ * @param {HTMLCanvasElement} canvas
49
+ */
50
+ setOverlayCanvas(canvas: HTMLCanvasElement): void;
51
+ /**
52
+ * Get aspect ratio value from string
53
+ * @param {string} aspect
54
+ * @returns {number|null}
55
+ */
56
+ _getAspectRatio(aspect: string): number | null;
57
+ /**
58
+ * Apply aspect ratio constraint to crop rect
59
+ */
60
+ applyAspectRatio(): void;
61
+ /**
62
+ * Constrain crop rect to bounds
63
+ */
64
+ constrainCropRect(): void;
65
+ /**
66
+ * Check if auto-zoom is needed and apply it (throttled).
67
+ * Called during crop resize drags when autoZoomOnCropOverflow is enabled.
68
+ */
69
+ _checkAutoZoom(): void;
70
+ /**
71
+ * Draw crop overlay on canvas
72
+ */
73
+ drawOverlay(): void;
74
+ /**
75
+ * Hit test for handles
76
+ * @param {number} px
77
+ * @param {number} py
78
+ * @returns {string|null}
79
+ */
80
+ _hitHandle(px: number, py: number): string | null;
81
+ /**
82
+ * Handle pointer down event
83
+ */
84
+ _handlePointerDown(event: any): void;
85
+ /**
86
+ * Handle pointer move event
87
+ */
88
+ _handlePointerMove(event: any): void;
89
+ /**
90
+ * Handle pointer up event
91
+ */
92
+ _handlePointerUp(): void;
93
+ /**
94
+ * Enable crop mode
95
+ */
96
+ enable(): void;
97
+ /**
98
+ * Disable crop mode
99
+ */
100
+ disable(): void;
101
+ /**
102
+ * Apply the crop
103
+ * @returns {{ texture: PIXI.Texture, preservedZoom: number }|null}
104
+ */
105
+ apply(): {
106
+ texture: PIXI.Texture;
107
+ preservedZoom: number;
108
+ } | null;
109
+ /**
110
+ * Apply a crop from saved texture-pixel coordinates (for state rehydration).
111
+ * Unlike apply(), this does not read from crop.rect or require the interactive overlay.
112
+ * @param {{ x: number, y: number, width: number, height: number }} pixelRect
113
+ * @param {'free'|'circle'} shape
114
+ * @returns {{ texture: Object, preservedZoom: number }|null}
115
+ */
116
+ applyFromPixelRect(pixelRect: {
117
+ x: number;
118
+ y: number;
119
+ width: number;
120
+ height: number;
121
+ }, shape?: "free" | "circle"): {
122
+ texture: Object;
123
+ preservedZoom: number;
124
+ } | null;
125
+ /**
126
+ * Cancel crop
127
+ */
128
+ cancel(): void;
129
+ /**
130
+ * Set crop shape
131
+ * @param {'free'|'square'|'circle'} shape
132
+ */
133
+ setShape(shape: "free" | "square" | "circle"): void;
134
+ /**
135
+ * Set aspect ratio
136
+ * @param {'free'|'1:1'|'4:3'|'16:9'|'3:2'|'2:3'|string} aspect
137
+ */
138
+ setAspect(aspect: "free" | "1:1" | "4:3" | "16:9" | "3:2" | "2:3" | string): void;
139
+ }
140
+ import { EventEmitter } from '../core/EventEmitter.js';