vuedit-image-editor 0.1.0 → 0.2.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.
- package/README.md +222 -0
- package/dist/style.css +1 -0
- package/dist/types/components/VueditImageEditor.vue.d.ts +91 -0
- package/dist/types/components/VueditImageEditorModal.vue.d.ts +71 -0
- package/dist/types/components/canvas/FabricCanvas.vue.d.ts +5 -0
- package/dist/types/components/panels/CropPanel.vue.d.ts +32 -0
- package/dist/types/components/panels/DrawPanel.vue.d.ts +24 -0
- package/dist/types/components/panels/FilterPanel.vue.d.ts +28 -0
- package/dist/types/components/panels/ResizePanel.vue.d.ts +35 -0
- package/dist/types/components/panels/RotateFlipPanel.vue.d.ts +31 -0
- package/dist/types/components/panels/ShapePanel.vue.d.ts +26 -0
- package/dist/types/components/panels/TextPanel.vue.d.ts +26 -0
- package/dist/types/components/shared/ColorPicker.vue.d.ts +21 -0
- package/dist/types/components/shared/ConfirmBar.vue.d.ts +23 -0
- package/dist/types/components/shared/RangeSlider.vue.d.ts +27 -0
- package/dist/types/components/toolbar/EditorToolbar.vue.d.ts +26 -0
- package/dist/types/components/toolbar/ToolButton.vue.d.ts +30 -0
- package/dist/types/composables/useCrop.d.ts +18 -0
- package/dist/types/composables/useDraw.d.ts +12 -0
- package/dist/types/composables/useEditorState.d.ts +7 -0
- package/dist/types/composables/useFabricCanvas.d.ts +16 -0
- package/dist/types/composables/useFilters.d.ts +18 -0
- package/dist/types/composables/useHistory.d.ts +11 -0
- package/dist/types/composables/useResize.d.ts +16 -0
- package/dist/types/composables/useRotateFlip.d.ts +12 -0
- package/dist/types/composables/useShapes.d.ts +12 -0
- package/dist/types/composables/useText.d.ts +16 -0
- package/dist/types/composables/useZoom.d.ts +11 -0
- package/dist/types/i18n/ar.d.ts +54 -0
- package/dist/types/i18n/en.d.ts +55 -0
- package/dist/types/i18n/index.d.ts +7 -0
- package/dist/types/icons/CropIcon.vue.d.ts +2 -0
- package/dist/types/icons/DrawIcon.vue.d.ts +2 -0
- package/dist/types/icons/FilterIcon.vue.d.ts +2 -0
- package/dist/types/icons/FlipHIcon.vue.d.ts +2 -0
- package/dist/types/icons/FlipVIcon.vue.d.ts +2 -0
- package/dist/types/icons/RedoIcon.vue.d.ts +2 -0
- package/dist/types/icons/ResizeIcon.vue.d.ts +2 -0
- package/dist/types/icons/RotateIcon.vue.d.ts +2 -0
- package/dist/types/icons/RotateLeftIcon.vue.d.ts +2 -0
- package/dist/types/icons/RotateRightIcon.vue.d.ts +2 -0
- package/dist/types/icons/ShapeIcon.vue.d.ts +2 -0
- package/dist/types/icons/TextIcon.vue.d.ts +2 -0
- package/dist/types/icons/UndoIcon.vue.d.ts +2 -0
- package/dist/types/icons/ZoomInIcon.vue.d.ts +2 -0
- package/dist/types/icons/ZoomOutIcon.vue.d.ts +2 -0
- package/dist/types/icons/index.d.ts +15 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/types/index.d.ts +43 -0
- package/dist/vuedit-image-editor.es.js +8898 -3298
- package/dist/vuedit-image-editor.es.js.map +1 -1
- package/dist/vuedit-image-editor.umd.js +504 -35
- package/dist/vuedit-image-editor.umd.js.map +1 -1
- package/package.json +50 -31
- package/LICENSE +0 -21
- package/dist/index.d.ts +0 -663
- package/dist/vuedit-image-editor.css +0 -1
package/dist/index.d.ts
DELETED
|
@@ -1,663 +0,0 @@
|
|
|
1
|
-
import { App } from 'vue';
|
|
2
|
-
import { Component } from 'vue';
|
|
3
|
-
import { ComponentOptionsMixin } from 'vue';
|
|
4
|
-
import { ComponentProvideOptions } from 'vue';
|
|
5
|
-
import { DeepReadonly } from 'vue';
|
|
6
|
-
import { DefineComponent } from 'vue';
|
|
7
|
-
import { ExtractPropTypes } from 'vue';
|
|
8
|
-
import { InjectionKey } from 'vue';
|
|
9
|
-
import { PropType } from 'vue';
|
|
10
|
-
import { PublicProps } from 'vue';
|
|
11
|
-
import { Ref } from 'vue';
|
|
12
|
-
|
|
13
|
-
declare type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
14
|
-
|
|
15
|
-
declare type __VLS_NonUndefinedable_2<T> = T extends undefined ? never : T;
|
|
16
|
-
|
|
17
|
-
declare type __VLS_Prettify<T> = {
|
|
18
|
-
[K in keyof T]: T[K];
|
|
19
|
-
} & {};
|
|
20
|
-
|
|
21
|
-
declare type __VLS_TypePropsToRuntimeProps<T> = {
|
|
22
|
-
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
23
|
-
type: PropType<__VLS_NonUndefinedable<T[K]>>;
|
|
24
|
-
} : {
|
|
25
|
-
type: PropType<T[K]>;
|
|
26
|
-
required: true;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
declare type __VLS_TypePropsToRuntimeProps_2<T> = {
|
|
31
|
-
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
32
|
-
type: PropType<__VLS_NonUndefinedable_2<T[K]>>;
|
|
33
|
-
} : {
|
|
34
|
-
type: PropType<T[K]>;
|
|
35
|
-
required: true;
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
declare type __VLS_WithDefaults<P, D> = {
|
|
40
|
-
[K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
|
|
41
|
-
default: D[K];
|
|
42
|
-
}> : P[K];
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Apply clarity (local contrast / unsharp mask) to ImageData in-place.
|
|
47
|
-
* strength: -0.25 to 0.25, base 0. Positive sharpens, negative softens.
|
|
48
|
-
* Uses a 3x3 Laplacian kernel blended with the original.
|
|
49
|
-
*/
|
|
50
|
-
export declare function applyClarity(imageData: ImageData, strength: number): void;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Apply a 5x4 color matrix to ImageData in-place.
|
|
54
|
-
* Offsets (m4, m9, m14, m19) are in 0-1 range and scaled to 0-255 internally.
|
|
55
|
-
*/
|
|
56
|
-
export declare function applyColorMatrix(imageData: ImageData, matrix: number[]): void;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Apply gamma correction to ImageData in-place.
|
|
60
|
-
* gamma: 0.2 to 5.0, base 1.0 (1 = no change)
|
|
61
|
-
*/
|
|
62
|
-
export declare function applyGamma(imageData: ImageData, gamma: number): void;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Apply vignette effect to ImageData in-place.
|
|
66
|
-
* strength: -1 to 1 (negative = lighten edges, positive = darken edges)
|
|
67
|
-
*/
|
|
68
|
-
export declare function applyVignette(imageData: ImageData, strength: number, width: number, height: number): void;
|
|
69
|
-
|
|
70
|
-
export declare const ar: LocaleStrings;
|
|
71
|
-
|
|
72
|
-
/** Brightness adjustment matrix. v: -0.25 to 0.25 */
|
|
73
|
-
export declare function brightnessMatrix(v: number): number[];
|
|
74
|
-
|
|
75
|
-
export declare class CanvasRenderer {
|
|
76
|
-
private canvas;
|
|
77
|
-
private ctx;
|
|
78
|
-
private sourceImage;
|
|
79
|
-
private animFrameId;
|
|
80
|
-
private needsRender;
|
|
81
|
-
/** Viewport transform */
|
|
82
|
-
viewport: ViewportTransform;
|
|
83
|
-
/** Active plugin ID — used to determine whether to show cropped preview */
|
|
84
|
-
activePluginId: string | null;
|
|
85
|
-
/**
|
|
86
|
-
* The full image display rect in CSS pixels.
|
|
87
|
-
* Used by the crop overlay to position handles on top of the image.
|
|
88
|
-
*/
|
|
89
|
-
private fullImageRect;
|
|
90
|
-
/** Cached offscreen canvas for color-adjusted preview */
|
|
91
|
-
private previewCanvas;
|
|
92
|
-
private previewCtx;
|
|
93
|
-
private previewCacheKey;
|
|
94
|
-
/** Max preview dimension for color processing performance */
|
|
95
|
-
private readonly previewMaxSize;
|
|
96
|
-
/** Cache for image-based sticker elements */
|
|
97
|
-
private stickerImageCache;
|
|
98
|
-
constructor(canvas: HTMLCanvasElement);
|
|
99
|
-
setSource(image: HTMLImageElement | null): void;
|
|
100
|
-
/** Resize canvas to match container (handles DPR) */
|
|
101
|
-
resize(width: number, height: number): void;
|
|
102
|
-
/** Request a render on the next animation frame */
|
|
103
|
-
requestRender(): void;
|
|
104
|
-
/** Callback called after each render with context for plugin drawing */
|
|
105
|
-
afterRender: ((ctx: CanvasRenderingContext2D, state: ImageState, imageRect: {
|
|
106
|
-
x: number;
|
|
107
|
-
y: number;
|
|
108
|
-
width: number;
|
|
109
|
-
height: number;
|
|
110
|
-
}) => void) | null;
|
|
111
|
-
/** Cached getState function for render loop */
|
|
112
|
-
private getState;
|
|
113
|
-
/** Start the render loop */
|
|
114
|
-
startRenderLoop(getState: () => ImageState): void;
|
|
115
|
-
/** Schedule a single render frame (only when needed) */
|
|
116
|
-
private scheduleRender;
|
|
117
|
-
/** Stop the render loop */
|
|
118
|
-
stopRenderLoop(): void;
|
|
119
|
-
/**
|
|
120
|
-
* Build a cache key from the finetune + filter state.
|
|
121
|
-
* Only regenerates the preview canvas when these values change.
|
|
122
|
-
*/
|
|
123
|
-
private buildEffectsCacheKey;
|
|
124
|
-
/**
|
|
125
|
-
* Check if any color effects are active.
|
|
126
|
-
*/
|
|
127
|
-
private hasColorEffects;
|
|
128
|
-
/**
|
|
129
|
-
* Get a color-adjusted preview image.
|
|
130
|
-
* Returns the source image if no effects are active,
|
|
131
|
-
* otherwise returns a cached offscreen canvas with effects applied.
|
|
132
|
-
*/
|
|
133
|
-
private getPreviewSource;
|
|
134
|
-
/** Check if crop is not at full-image defaults */
|
|
135
|
-
private isCropActive;
|
|
136
|
-
/**
|
|
137
|
-
* Main render method.
|
|
138
|
-
* Draws the image with color adjustments fitted to the canvas.
|
|
139
|
-
* On crop tab: renders full image (CropOverlay handles crop visualization).
|
|
140
|
-
* On other tabs: renders only the cropped region fitted to canvas.
|
|
141
|
-
*/
|
|
142
|
-
render(state: ImageState): void;
|
|
143
|
-
/**
|
|
144
|
-
* Render full image view (used on the crop tab).
|
|
145
|
-
*/
|
|
146
|
-
private renderFullView;
|
|
147
|
-
/**
|
|
148
|
-
* Render cropped view (used on non-crop tabs).
|
|
149
|
-
* Shows only the crop region fitted to the canvas.
|
|
150
|
-
*/
|
|
151
|
-
private renderCroppedView;
|
|
152
|
-
/**
|
|
153
|
-
* Render frame preview overlay on the canvas.
|
|
154
|
-
*/
|
|
155
|
-
private renderFramePreview;
|
|
156
|
-
/**
|
|
157
|
-
* Get the full image display rect in CSS pixels.
|
|
158
|
-
* The CropOverlay uses this to position crop handles correctly.
|
|
159
|
-
*/
|
|
160
|
-
getImageRect(): {
|
|
161
|
-
x: number;
|
|
162
|
-
y: number;
|
|
163
|
-
width: number;
|
|
164
|
-
height: number;
|
|
165
|
-
};
|
|
166
|
-
/**
|
|
167
|
-
* Render stickers on the canvas viewport.
|
|
168
|
-
*/
|
|
169
|
-
private renderStickers;
|
|
170
|
-
/**
|
|
171
|
-
* Render stickers with positions remapped relative to the crop region.
|
|
172
|
-
*/
|
|
173
|
-
private renderStickersInCrop;
|
|
174
|
-
destroy(): void;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/** Compose multiple matrices into one (left to right application order) */
|
|
178
|
-
export declare function composeMatrices(...matrices: number[][]): number[];
|
|
179
|
-
|
|
180
|
-
/** Contrast adjustment matrix. v: -0.25 to 0.25 */
|
|
181
|
-
export declare function contrastMatrix(v: number): number[];
|
|
182
|
-
|
|
183
|
-
export declare function createDefaultImageState(): ImageState;
|
|
184
|
-
|
|
185
|
-
export declare function createEditorEngine(options: EditorOptions): EditorEngine;
|
|
186
|
-
|
|
187
|
-
export declare function CropPlugin(): EditorPlugin;
|
|
188
|
-
|
|
189
|
-
export declare interface CropState {
|
|
190
|
-
/** Normalized 0-1 X position of crop region */
|
|
191
|
-
x: number;
|
|
192
|
-
/** Normalized 0-1 Y position of crop region */
|
|
193
|
-
y: number;
|
|
194
|
-
/** Normalized 0-1 width of crop region */
|
|
195
|
-
width: number;
|
|
196
|
-
/** Normalized 0-1 height of crop region */
|
|
197
|
-
height: number;
|
|
198
|
-
/** Locked aspect ratio (width/height), null = free */
|
|
199
|
-
aspectRatio: number | null;
|
|
200
|
-
/** Rotation in radians, -PI/4 to PI/4 for free rotation */
|
|
201
|
-
rotation: number;
|
|
202
|
-
/** Flip horizontally */
|
|
203
|
-
flipX: boolean;
|
|
204
|
-
/** Flip vertically */
|
|
205
|
-
flipY: boolean;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
declare const _default: {
|
|
209
|
-
install: typeof install;
|
|
210
|
-
};
|
|
211
|
-
export default _default;
|
|
212
|
-
|
|
213
|
-
export declare const defaultStickerCategories: StickerCategory[];
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Deserialize a JSON string back to an ImageState.
|
|
217
|
-
* Validates the basic structure before returning.
|
|
218
|
-
*/
|
|
219
|
-
export declare function deserializeState(json: string): Partial<ImageState>;
|
|
220
|
-
|
|
221
|
-
export declare interface EditorEngine {
|
|
222
|
-
/** Reactive editor state */
|
|
223
|
-
state: ImageState;
|
|
224
|
-
/** Read-only reactive state for consumers */
|
|
225
|
-
readonlyState: DeepReadonly<ImageState>;
|
|
226
|
-
/** Source image element */
|
|
227
|
-
sourceImage: Ref<HTMLImageElement | null>;
|
|
228
|
-
/** Whether the source image is loading */
|
|
229
|
-
loading: Ref<boolean>;
|
|
230
|
-
/** Error message if loading fails */
|
|
231
|
-
error: Ref<string | null>;
|
|
232
|
-
/** Registered plugins */
|
|
233
|
-
plugins: Ref<EditorPlugin[]>;
|
|
234
|
-
/** Currently active plugin ID */
|
|
235
|
-
activePluginId: Ref<string | null>;
|
|
236
|
-
/** Currently active plugin */
|
|
237
|
-
activePlugin: Ref<EditorPlugin | null>;
|
|
238
|
-
/** Whether any edits have been made */
|
|
239
|
-
hasEdits: Ref<boolean>;
|
|
240
|
-
/** History */
|
|
241
|
-
canUndo: Ref<boolean>;
|
|
242
|
-
canRedo: Ref<boolean>;
|
|
243
|
-
/** Zoom level (1 = fit to container) */
|
|
244
|
-
zoom: Ref<number>;
|
|
245
|
-
/** Editor options */
|
|
246
|
-
options: EditorOptions;
|
|
247
|
-
/** Load the source image */
|
|
248
|
-
load: (src: string | File | Blob) => Promise<void>;
|
|
249
|
-
/** Register a plugin */
|
|
250
|
-
registerPlugin: (plugin: EditorPlugin) => void;
|
|
251
|
-
/** Unregister a plugin by ID */
|
|
252
|
-
unregisterPlugin: (id: string) => void;
|
|
253
|
-
/** Set active plugin by ID */
|
|
254
|
-
setActivePlugin: (id: string) => void;
|
|
255
|
-
/** Update state and push to history */
|
|
256
|
-
updateState: (updater: (state: ImageState) => void) => void;
|
|
257
|
-
/** Undo last change */
|
|
258
|
-
undo: () => void;
|
|
259
|
-
/** Redo last undone change */
|
|
260
|
-
redo: () => void;
|
|
261
|
-
/** Revert to initial state */
|
|
262
|
-
revert: () => void;
|
|
263
|
-
/** Set zoom level */
|
|
264
|
-
setZoom: (level: number) => void;
|
|
265
|
-
/** Request a re-render of the canvas */
|
|
266
|
-
requestRender: () => void;
|
|
267
|
-
/** Render callback — set by EditorCanvas */
|
|
268
|
-
onRender: Ref<(() => void) | null>;
|
|
269
|
-
/** Temporary viewport pan during crop drag (pixels) */
|
|
270
|
-
cropDragPan: Ref<{
|
|
271
|
-
x: number;
|
|
272
|
-
y: number;
|
|
273
|
-
}>;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
export declare interface EditorOptions {
|
|
277
|
-
/** Image source: URL, File, or Blob */
|
|
278
|
-
src: string | File | Blob;
|
|
279
|
-
/** List of plugin IDs to enable (e.g., ['crop', 'filter', 'finetune']) */
|
|
280
|
-
plugins?: string[];
|
|
281
|
-
/** Locale code */
|
|
282
|
-
locale?: 'en' | 'ar';
|
|
283
|
-
/** Text direction */
|
|
284
|
-
dir?: 'ltr' | 'rtl';
|
|
285
|
-
/** Theme: 'light' (default) or 'dark' */
|
|
286
|
-
theme?: 'dark' | 'light';
|
|
287
|
-
/** Output format */
|
|
288
|
-
outputMimeType?: 'image/jpeg' | 'image/png' | 'image/webp';
|
|
289
|
-
/** Output quality (0-1), for JPEG/WebP */
|
|
290
|
-
outputQuality?: number;
|
|
291
|
-
/** Max canvas pixels for export (iOS limit ~16M) */
|
|
292
|
-
maxCanvasPixels?: number;
|
|
293
|
-
/** Initial image state (for re-editing) */
|
|
294
|
-
initialState?: Partial<ImageState>;
|
|
295
|
-
/** Custom plugins to register in addition to built-in plugins */
|
|
296
|
-
customPlugins?: EditorPlugin[];
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export declare interface EditorPlugin {
|
|
300
|
-
/** Unique plugin ID (e.g., 'crop', 'filter') */
|
|
301
|
-
id: string;
|
|
302
|
-
/** SVG icon as HTML string or component */
|
|
303
|
-
icon: string | Component;
|
|
304
|
-
/** Display label key (used for locale lookup) */
|
|
305
|
-
labelKey: string;
|
|
306
|
-
/** Controls component rendered in the control panel */
|
|
307
|
-
controls: Component;
|
|
308
|
-
/** Overlay component rendered on top of the canvas viewport */
|
|
309
|
-
overlay?: Component;
|
|
310
|
-
/** Called when the plugin tab is activated */
|
|
311
|
-
onActivate?: () => void;
|
|
312
|
-
/** Called when the plugin tab is deactivated */
|
|
313
|
-
onDeactivate?: () => void;
|
|
314
|
-
/** Called after the base canvas render to draw plugin-specific preview content */
|
|
315
|
-
onRender?: (context: PluginRenderContext) => void;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export declare const en: {
|
|
319
|
-
undo: string;
|
|
320
|
-
redo: string;
|
|
321
|
-
revert: string;
|
|
322
|
-
zoomIn: string;
|
|
323
|
-
zoomOut: string;
|
|
324
|
-
zoomToFit: string;
|
|
325
|
-
done: string;
|
|
326
|
-
cancel: string;
|
|
327
|
-
export: string;
|
|
328
|
-
close: string;
|
|
329
|
-
crop: string;
|
|
330
|
-
filter: string;
|
|
331
|
-
finetune: string;
|
|
332
|
-
sticker: string;
|
|
333
|
-
frame: string;
|
|
334
|
-
resize: string;
|
|
335
|
-
aspectRatio: string;
|
|
336
|
-
custom: string;
|
|
337
|
-
square: string;
|
|
338
|
-
landscape: string;
|
|
339
|
-
portrait: string;
|
|
340
|
-
rotation: string;
|
|
341
|
-
rotateLeft: string;
|
|
342
|
-
rotateRight: string;
|
|
343
|
-
flipHorizontal: string;
|
|
344
|
-
flipVertical: string;
|
|
345
|
-
brightness: string;
|
|
346
|
-
contrast: string;
|
|
347
|
-
saturation: string;
|
|
348
|
-
exposure: string;
|
|
349
|
-
temperature: string;
|
|
350
|
-
gamma: string;
|
|
351
|
-
clarity: string;
|
|
352
|
-
vignette: string;
|
|
353
|
-
original: string;
|
|
354
|
-
intensity: string;
|
|
355
|
-
chrome: string;
|
|
356
|
-
fade: string;
|
|
357
|
-
pastel: string;
|
|
358
|
-
cold: string;
|
|
359
|
-
warm: string;
|
|
360
|
-
mono: string;
|
|
361
|
-
noir: string;
|
|
362
|
-
wash: string;
|
|
363
|
-
stark: string;
|
|
364
|
-
sepia: string;
|
|
365
|
-
rust: string;
|
|
366
|
-
blues: string;
|
|
367
|
-
color: string;
|
|
368
|
-
solidSharp: string;
|
|
369
|
-
solidRound: string;
|
|
370
|
-
lineSingle: string;
|
|
371
|
-
lineMultiple: string;
|
|
372
|
-
hook: string;
|
|
373
|
-
polaroid: string;
|
|
374
|
-
edgeSeparate: string;
|
|
375
|
-
edgeCross: string;
|
|
376
|
-
edgeOverlap: string;
|
|
377
|
-
fill: string;
|
|
378
|
-
transparent: string;
|
|
379
|
-
emoji: string;
|
|
380
|
-
shapes: string;
|
|
381
|
-
none: string;
|
|
382
|
-
frameSize: string;
|
|
383
|
-
frameColor: string;
|
|
384
|
-
width: string;
|
|
385
|
-
height: string;
|
|
386
|
-
lockAspectRatio: string;
|
|
387
|
-
loading: string;
|
|
388
|
-
loadError: string;
|
|
389
|
-
processing: string;
|
|
390
|
-
cropApply: string;
|
|
391
|
-
unsavedChanges: string;
|
|
392
|
-
confirm: string;
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
/** Exposure adjustment matrix. v: -0.25 to 0.25 */
|
|
396
|
-
export declare function exposureMatrix(v: number): number[];
|
|
397
|
-
|
|
398
|
-
export declare function FillPlugin(): EditorPlugin;
|
|
399
|
-
|
|
400
|
-
export declare function FilterPlugin(): EditorPlugin;
|
|
401
|
-
|
|
402
|
-
declare interface FilterPreset {
|
|
403
|
-
id: string;
|
|
404
|
-
labelKey: string;
|
|
405
|
-
matrix: number[];
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
export declare const filterPresets: FilterPreset[];
|
|
409
|
-
|
|
410
|
-
export declare interface FilterState {
|
|
411
|
-
/** Filter preset ID, null = no filter */
|
|
412
|
-
id: string | null;
|
|
413
|
-
/** Filter intensity 0 to 1 */
|
|
414
|
-
intensity: number;
|
|
415
|
-
/** Optional 5x4 color matrix (20 elements) */
|
|
416
|
-
matrix?: number[];
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
declare interface FinetuneEffect {
|
|
420
|
-
key: keyof FinetuneState;
|
|
421
|
-
labelKey: keyof LocaleStrings;
|
|
422
|
-
min: number;
|
|
423
|
-
max: number;
|
|
424
|
-
base: number;
|
|
425
|
-
step: number;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
export declare const finetuneEffects: FinetuneEffect[];
|
|
429
|
-
|
|
430
|
-
export declare function FinetunePlugin(): EditorPlugin;
|
|
431
|
-
|
|
432
|
-
export declare interface FinetuneState {
|
|
433
|
-
brightness: number;
|
|
434
|
-
contrast: number;
|
|
435
|
-
saturation: number;
|
|
436
|
-
exposure: number;
|
|
437
|
-
temperature: number;
|
|
438
|
-
gamma: number;
|
|
439
|
-
clarity: number;
|
|
440
|
-
vignette: number;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
export declare function FramePlugin(): EditorPlugin;
|
|
444
|
-
|
|
445
|
-
export declare interface FrameState {
|
|
446
|
-
/** Frame style ID, null = no frame */
|
|
447
|
-
style: string | null;
|
|
448
|
-
/** Frame thickness as percentage of image size */
|
|
449
|
-
size: number;
|
|
450
|
-
/** RGBA color, each 0-1 */
|
|
451
|
-
color: [number, number, number, number];
|
|
452
|
-
/** Corner radius */
|
|
453
|
-
radius: number;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
declare interface FrameStyleDef {
|
|
457
|
-
id: string;
|
|
458
|
-
labelKey: keyof LocaleStrings;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
export declare const frameStyles: FrameStyleDef[];
|
|
462
|
-
|
|
463
|
-
declare class History_2 {
|
|
464
|
-
private states;
|
|
465
|
-
private pointer;
|
|
466
|
-
private maxSize;
|
|
467
|
-
constructor(maxSize?: number);
|
|
468
|
-
/** Push a new state snapshot. Truncates any future (redo) states. */
|
|
469
|
-
push(state: ImageState): void;
|
|
470
|
-
/** Undo: return the previous state, or null if at the beginning. */
|
|
471
|
-
undo(): ImageState | null;
|
|
472
|
-
/** Redo: return the next state, or null if at the end. */
|
|
473
|
-
redo(): ImageState | null;
|
|
474
|
-
/** Reset history with a single initial state. */
|
|
475
|
-
reset(state: ImageState): void;
|
|
476
|
-
get canUndo(): boolean;
|
|
477
|
-
get canRedo(): boolean;
|
|
478
|
-
get currentIndex(): number;
|
|
479
|
-
get length(): number;
|
|
480
|
-
}
|
|
481
|
-
export { History_2 as History }
|
|
482
|
-
|
|
483
|
-
/** Identity matrix — no transformation */
|
|
484
|
-
export declare const IDENTITY_MATRIX: number[];
|
|
485
|
-
|
|
486
|
-
export declare const ImageEditorModal: DefineComponent<ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
|
|
487
|
-
src: string | File | Blob;
|
|
488
|
-
plugins?: string[];
|
|
489
|
-
locale?: "en" | "ar";
|
|
490
|
-
dir?: "ltr" | "rtl";
|
|
491
|
-
theme?: "dark" | "light";
|
|
492
|
-
outputMimeType?: "image/jpeg" | "image/png" | "image/webp";
|
|
493
|
-
outputQuality?: number;
|
|
494
|
-
maxCanvasPixels?: number;
|
|
495
|
-
initialState?: Partial<ImageState>;
|
|
496
|
-
}>, {
|
|
497
|
-
plugins: () => string[];
|
|
498
|
-
locale: string;
|
|
499
|
-
dir: string;
|
|
500
|
-
theme: string;
|
|
501
|
-
outputQuality: number;
|
|
502
|
-
}>>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
|
|
503
|
-
close: () => void;
|
|
504
|
-
process: (result: ProcessResult) => void;
|
|
505
|
-
}, string, PublicProps, Readonly<ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToRuntimeProps<{
|
|
506
|
-
src: string | File | Blob;
|
|
507
|
-
plugins?: string[];
|
|
508
|
-
locale?: "en" | "ar";
|
|
509
|
-
dir?: "ltr" | "rtl";
|
|
510
|
-
theme?: "dark" | "light";
|
|
511
|
-
outputMimeType?: "image/jpeg" | "image/png" | "image/webp";
|
|
512
|
-
outputQuality?: number;
|
|
513
|
-
maxCanvasPixels?: number;
|
|
514
|
-
initialState?: Partial<ImageState>;
|
|
515
|
-
}>, {
|
|
516
|
-
plugins: () => string[];
|
|
517
|
-
locale: string;
|
|
518
|
-
dir: string;
|
|
519
|
-
theme: string;
|
|
520
|
-
outputQuality: number;
|
|
521
|
-
}>>> & Readonly<{
|
|
522
|
-
onClose?: (() => any) | undefined;
|
|
523
|
-
onProcess?: ((result: ProcessResult) => any) | undefined;
|
|
524
|
-
}>, {
|
|
525
|
-
dir: "ltr" | "rtl";
|
|
526
|
-
plugins: string[];
|
|
527
|
-
locale: "en" | "ar";
|
|
528
|
-
theme: "dark" | "light";
|
|
529
|
-
outputQuality: number;
|
|
530
|
-
}, {}, {}, {}, string, ComponentProvideOptions, true, {}, any>;
|
|
531
|
-
|
|
532
|
-
export declare const ImageEditorShell: DefineComponent<ExtractPropTypes<__VLS_TypePropsToRuntimeProps_2<{
|
|
533
|
-
options: EditorOptions;
|
|
534
|
-
}>>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
|
|
535
|
-
close: () => void;
|
|
536
|
-
process: (result: ProcessResult) => void;
|
|
537
|
-
}, string, PublicProps, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps_2<{
|
|
538
|
-
options: EditorOptions;
|
|
539
|
-
}>>> & Readonly<{
|
|
540
|
-
onClose?: (() => any) | undefined;
|
|
541
|
-
onProcess?: ((result: ProcessResult) => any) | undefined;
|
|
542
|
-
}>, {}, {}, {}, {}, string, ComponentProvideOptions, true, {}, any>;
|
|
543
|
-
|
|
544
|
-
export declare class ImageProcessor {
|
|
545
|
-
/**
|
|
546
|
-
* Process image at FULL RESOLUTION.
|
|
547
|
-
* Applies all transformations and exports as Blob.
|
|
548
|
-
*/
|
|
549
|
-
process(source: HTMLImageElement, state: ImageState, options?: ProcessOptions): Promise<ProcessResult>;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
export declare interface ImageState {
|
|
553
|
-
crop: CropState;
|
|
554
|
-
finetune: FinetuneState;
|
|
555
|
-
filter: FilterState;
|
|
556
|
-
stickers: StickerInstance[];
|
|
557
|
-
frame: FrameState;
|
|
558
|
-
resize: ResizeState;
|
|
559
|
-
backgroundColor: [number, number, number, number];
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
export declare function install(app: App): void;
|
|
563
|
-
|
|
564
|
-
/**
|
|
565
|
-
* Load images from various sources into an HTMLImageElement.
|
|
566
|
-
* Handles URL strings, File objects, Blobs, and EXIF orientation.
|
|
567
|
-
*/
|
|
568
|
-
export declare function loadImage(src: string | File | Blob): Promise<HTMLImageElement>;
|
|
569
|
-
|
|
570
|
-
export declare const LOCALE_KEY: InjectionKey<(key: string) => string>;
|
|
571
|
-
|
|
572
|
-
export declare type LocaleStrings = typeof en;
|
|
573
|
-
|
|
574
|
-
/**
|
|
575
|
-
* Create a full ImageState from a partial state, filling in defaults.
|
|
576
|
-
*/
|
|
577
|
-
export declare function mergeWithDefaults(partial: Partial<ImageState>): ImageState;
|
|
578
|
-
|
|
579
|
-
/** Multiply two 5x4 matrices (treating them as 5x5 with implicit last row [0,0,0,0,1]) */
|
|
580
|
-
export declare function multiplyMatrices(a: number[], b: number[]): number[];
|
|
581
|
-
|
|
582
|
-
export declare interface PluginRenderContext {
|
|
583
|
-
/** Canvas 2D rendering context */
|
|
584
|
-
ctx: CanvasRenderingContext2D;
|
|
585
|
-
/** Current editor state */
|
|
586
|
-
state: ImageState;
|
|
587
|
-
/** Image display rect in canvas pixels */
|
|
588
|
-
imageRect: {
|
|
589
|
-
x: number;
|
|
590
|
-
y: number;
|
|
591
|
-
width: number;
|
|
592
|
-
height: number;
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
declare interface ProcessOptions {
|
|
597
|
-
mimeType?: string;
|
|
598
|
-
quality?: number;
|
|
599
|
-
maxCanvasPixels?: number;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
export declare interface ProcessResult {
|
|
603
|
-
/** The processed image as a Blob */
|
|
604
|
-
blob: Blob;
|
|
605
|
-
/** The final image state (serializable for persistence) */
|
|
606
|
-
imageState: ImageState;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
export declare function ResizePlugin(): EditorPlugin;
|
|
610
|
-
|
|
611
|
-
export declare interface ResizeState {
|
|
612
|
-
width?: number;
|
|
613
|
-
height?: number;
|
|
614
|
-
fit: 'contain' | 'cover' | 'force';
|
|
615
|
-
upscale: boolean;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
/** Saturation adjustment matrix. v: -0.25 to 0.25 */
|
|
619
|
-
export declare function saturationMatrix(v: number): number[];
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* Serialize editor state to a JSON string.
|
|
623
|
-
* Safe to store in localStorage, a database, or send over network.
|
|
624
|
-
*/
|
|
625
|
-
export declare function serializeState(state: ImageState): string;
|
|
626
|
-
|
|
627
|
-
declare interface StickerCategory {
|
|
628
|
-
id: string;
|
|
629
|
-
labelKey: string;
|
|
630
|
-
stickers: string[];
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
export declare interface StickerInstance {
|
|
634
|
-
id: string;
|
|
635
|
-
/** URL, data URI, or emoji character */
|
|
636
|
-
src: string;
|
|
637
|
-
/** Normalized 0-1 position */
|
|
638
|
-
x: number;
|
|
639
|
-
y: number;
|
|
640
|
-
/** Normalized 0-1 size */
|
|
641
|
-
width: number;
|
|
642
|
-
height: number;
|
|
643
|
-
/** Rotation in radians */
|
|
644
|
-
rotation: number;
|
|
645
|
-
flipX: boolean;
|
|
646
|
-
flipY: boolean;
|
|
647
|
-
opacity: number;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
export declare function StickerPlugin(): EditorPlugin;
|
|
651
|
-
|
|
652
|
-
/** Temperature adjustment matrix. v: -0.25 to 0.25 (positive = warmer, negative = cooler) */
|
|
653
|
-
export declare function temperatureMatrix(v: number): number[];
|
|
654
|
-
|
|
655
|
-
export declare function useEditor(): EditorEngine;
|
|
656
|
-
|
|
657
|
-
declare interface ViewportTransform {
|
|
658
|
-
zoom: number;
|
|
659
|
-
panX: number;
|
|
660
|
-
panY: number;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
export { }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.vie-editor{--vie-bg: #F7F7F8;--vie-bg-secondary: #FFFFFF;--vie-surface: #F5FBFF;--vie-fg: #0D1B2A;--vie-fg-dim: rgba(13, 27, 42, .6);--vie-fg-muted: rgba(65, 90, 119, .5);--vie-border: #DBDFE9;--vie-accent: #1B4965;--vie-accent-hover: #153A52;--vie-danger: #F3164E;--vie-overlay: rgba(0, 0, 0, .4);--vie-font: "IBM Plex Sans Arabic", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--vie-font-size: 14px;--vie-font-size-sm: 12px;--vie-radius: 4px;--vie-radius-lg: 8px;--vie-gap: 8px;--vie-padding: 12px;--vie-toolbar-height: 48px;--vie-tab-bar-height: 56px;--vie-control-panel-height: auto;--vie-control-panel-max-height: 180px;--vie-transition: .15s ease;font-family:var(--vie-font);font-size:var(--vie-font-size);color:var(--vie-fg);line-height:1.4;box-sizing:border-box}.vie-editor.vie-theme-dark{--vie-bg: #1B263B;--vie-bg-secondary: #0D1B2A;--vie-surface: rgba(255, 255, 255, .08);--vie-fg: #FFFFFF;--vie-fg-dim: rgba(255, 255, 255, .6);--vie-fg-muted: rgba(255, 255, 255, .35);--vie-border: rgba(255, 255, 255, .12);--vie-overlay: rgba(0, 0, 0, .7)}.vie-editor *,.vie-editor *:before,.vie-editor *:after{box-sizing:border-box;margin:0;padding:0}.vie-modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:var(--vie-overlay);padding:16px}.vie-modal-container{width:100%;height:100%;max-width:1100px;max-height:800px;border-radius:var(--vie-radius-lg);overflow:hidden;box-shadow:0 25px 50px -12px #00000040}.vie-modal-enter-active,.vie-modal-leave-active{transition:opacity .2s ease,transform .2s ease}.vie-modal-enter-from,.vie-modal-leave-to{opacity:0}.vie-modal-enter-from .vie-modal-container,.vie-modal-leave-to .vie-modal-container{transform:scale(.95)}.vie-shell{display:flex;flex-direction:column;width:100%;height:100%;background:var(--vie-bg);position:relative;overflow:hidden}.vie-toolbar{display:flex;align-items:center;justify-content:space-between;height:var(--vie-toolbar-height);padding:0 var(--vie-padding);background:var(--vie-bg-secondary);border-bottom:1px solid var(--vie-border);flex-shrink:0}.vie-toolbar-group{display:flex;align-items:center;gap:4px}.vie-zoom-label{font-size:12px;color:var(--vie-fg-dim);min-width:40px;text-align:center;font-variant-numeric:tabular-nums;background:none;border:none;cursor:pointer;padding:4px 6px;border-radius:var(--vie-radius, 6px);font-family:inherit;transition:color .15s ease,background .15s ease}.vie-zoom-label:hover{color:var(--vie-fg);background:var(--vie-surface, rgba(255, 255, 255, .1))}.vie-canvas-wrapper{flex:1;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center;background:var(--vie-bg)}.vie-canvas-wrapper--grab{cursor:grab}.vie-canvas-wrapper--grabbing{cursor:grabbing}.vie-canvas{display:block;will-change:transform}.vie-canvas-overlay{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none}.vie-canvas-overlay--interactive{pointer-events:auto}.vie-canvas-loading{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;color:var(--vie-fg-dim);font-size:14px}.vie-spinner{width:24px;height:24px;border:2px solid var(--vie-border);border-top-color:var(--vie-accent);border-radius:50%;animation:vie-spin .6s linear infinite;margin-inline-end:8px}@keyframes vie-spin{to{transform:rotate(360deg)}}.vie-control-panel{position:relative;z-index:5;min-height:60px;max-height:var(--vie-control-panel-max-height);overflow:visible;padding:var(--vie-padding);background:var(--vie-bg-secondary);border-top:1px solid var(--vie-border);flex-shrink:0}.vie-control-panel::-webkit-scrollbar{width:4px}.vie-control-panel::-webkit-scrollbar-thumb{background:var(--vie-fg-muted);border-radius:2px}.vie-tab-bar{display:flex;align-items:center;justify-content:center;height:var(--vie-tab-bar-height);gap:2px;padding:0 var(--vie-padding);background:var(--vie-bg-secondary);border-top:1px solid var(--vie-border);flex-shrink:0}.vie-tab{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:6px 14px;border-radius:var(--vie-radius);cursor:pointer;color:var(--vie-fg-dim);background:transparent;border:none;font-family:var(--vie-font);font-size:var(--vie-font-size-sm);transition:color var(--vie-transition),background var(--vie-transition);min-width:56px}.vie-tab:hover{color:var(--vie-fg);background:var(--vie-surface)}.vie-tab--active{color:var(--vie-accent);background:var(--vie-surface)}.vie-tab__icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center}.vie-tab__icon svg{width:100%;height:100%}.vie-tab__label{white-space:nowrap}.vie-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--vie-radius);border:none;background:transparent;color:var(--vie-fg-dim);cursor:pointer;transition:color var(--vie-transition),background var(--vie-transition);padding:0;font-family:var(--vie-font)}.vie-icon-btn:hover{color:var(--vie-fg);background:var(--vie-surface)}.vie-icon-btn:disabled{opacity:.3;cursor:not-allowed}.vie-icon-btn:disabled:hover{background:transparent;color:var(--vie-fg-dim)}.vie-icon-btn svg{width:18px;height:18px}.vie-icon-btn--accent{background:var(--vie-accent);color:#fff;padding:0 14px;width:auto;font-size:var(--vie-font-size);font-weight:500;gap:6px}.vie-icon-btn--accent:hover{background:var(--vie-accent-hover);color:#fff}.vie-slider{display:flex;align-items:center;gap:12px;padding:4px 0}.vie-slider__label{font-size:var(--vie-font-size);color:var(--vie-fg-dim);min-width:80px;white-space:nowrap}.vie-slider__track-wrapper{flex:1;position:relative;height:28px;display:flex;align-items:center;cursor:pointer}.vie-slider__track{width:100%;height:4px;background:var(--vie-surface);border-radius:2px;position:relative;overflow:visible}.vie-slider__fill{position:absolute;height:100%;background:var(--vie-accent);border-radius:2px;pointer-events:none;transition:left 60ms ease-out,width 60ms ease-out}.vie-slider__base-mark{position:absolute;top:-3px;width:2px;height:10px;background:var(--vie-fg-muted);border-radius:1px;transform:translate(-50%);pointer-events:none;z-index:1}.vie-slider__tick{position:absolute;top:-1px;width:1px;height:6px;background:var(--vie-fg-muted);transform:translate(-50%);pointer-events:none;opacity:.4}.vie-slider__thumb{position:absolute;top:50%;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 4px #0000004d,0 0 0 1px #0000000f;transform:translate(-50%,-50%);cursor:grab;pointer-events:none;transition:left 60ms ease-out,transform .15s ease,box-shadow .15s ease;display:flex;align-items:center;justify-content:center;z-index:2}.vie-slider__thumb-dot{width:6px;height:6px;border-radius:50%;background:var(--vie-accent);transition:transform .15s ease}.vie-slider__track-wrapper:hover .vie-slider__thumb,.vie-slider__track-wrapper:focus-visible .vie-slider__thumb{transform:translate(-50%,-50%) scale(1.15);box-shadow:0 2px 8px #0000004d,0 0 0 1px #0000000f}.vie-slider--dragging .vie-slider__thumb{cursor:grabbing;transform:translate(-50%,-50%) scale(1.2);box-shadow:0 2px 8px #00000059,0 0 0 2px var(--vie-accent)}.vie-slider__tooltip{position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%);padding:2px 6px;background:var(--vie-bg);color:var(--vie-fg);font-size:10px;font-variant-numeric:tabular-nums;border-radius:4px;white-space:nowrap;pointer-events:none;box-shadow:0 2px 6px #0000001a;border:1px solid var(--vie-border)}.vie-slider__value{font-size:var(--vie-font-size-sm);color:var(--vie-fg-muted);min-width:40px;text-align:end;font-variant-numeric:tabular-nums}.vie-dropdown{position:relative;display:inline-flex}.vie-dropdown__trigger{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:var(--vie-radius);border:1px solid var(--vie-border);background:var(--vie-surface);color:var(--vie-fg);font-family:var(--vie-font);font-size:var(--vie-font-size);cursor:pointer;transition:border-color var(--vie-transition)}.vie-dropdown__trigger:hover{border-color:var(--vie-fg-muted)}.vie-dropdown__chevron{width:12px;height:12px;opacity:.5}.vie-dropdown__menu{position:absolute;bottom:calc(100% + 4px);inset-inline-start:0;min-width:160px;background:var(--vie-bg-secondary);border:1px solid var(--vie-border);border-radius:var(--vie-radius);box-shadow:0 8px 24px #0000001f;z-index:10;padding:4px;max-height:240px;overflow-y:auto}.vie-dropdown__item{display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:4px;cursor:pointer;font-size:var(--vie-font-size);color:var(--vie-fg);background:transparent;border:none;width:100%;text-align:start;font-family:var(--vie-font);transition:background var(--vie-transition)}.vie-dropdown__item:hover{background:var(--vie-surface)}.vie-dropdown__item--active{color:var(--vie-accent)}.vie-crop-overlay{position:absolute;top:0;right:0;bottom:0;left:0;touch-action:none}.vie-crop-overlay--dragging{cursor:grabbing}.vie-crop-overlay--dragging *{cursor:grabbing!important}.vie-crop-mask{fill:#0000008c}.vie-crop-border{stroke:#fffc;stroke-width:1;fill:none}.vie-crop-grid line{stroke:#ffffff40;stroke-width:.5}.vie-crop-handle{fill:#fff;stroke:none;cursor:pointer}.vie-crop-handle--n,.vie-crop-handle--s{cursor:ns-resize}.vie-crop-handle--e,.vie-crop-handle--w{cursor:ew-resize}.vie-crop-handle--nw,.vie-crop-handle--se{cursor:nwse-resize}.vie-crop-handle--ne,.vie-crop-handle--sw{cursor:nesw-resize}.vie-crop-move{cursor:grab;fill:transparent}.vie-crop-confirm-pill{display:inline-flex;align-items:center;gap:4px;height:28px;padding:0 10px;border-radius:14px;border:none;background:#000000a6;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);color:#fff;font-family:var(--vie-font);font-size:11px;font-weight:500;cursor:pointer;pointer-events:auto;box-shadow:0 2px 8px #0000004d,0 0 0 1px #ffffff1a inset;animation:vie-crop-pill-in .2s ease-out;white-space:nowrap;transition:background .15s ease,transform .15s ease}.vie-crop-confirm-pill:hover{background:#000c;transform:scale(1.04)}@keyframes vie-crop-pill-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.vie-crop-controls{display:flex;flex-wrap:wrap;align-items:center;gap:12px}.vie-crop-controls__section{display:flex;align-items:center;gap:6px}.vie-crop-controls__divider{width:1px;height:24px;background:var(--vie-border);margin:0 4px}.vie-crop-controls__rotation{flex:1;min-width:200px}.vie-shell--horizontal{flex-direction:row}.vie-shell--horizontal .vie-tab-bar{flex-direction:column;width:var(--vie-tab-bar-height);height:auto;border-top:none;border-inline-end:1px solid var(--vie-border);padding:var(--vie-padding) 0}.vie-shell--horizontal .vie-main{display:flex;flex-direction:column;flex:1;min-width:0}[dir=rtl] .vie-editor,.vie-editor[dir=rtl]{direction:rtl}[dir=rtl] .vie-crop-handle--nw,[dir=rtl] .vie-crop-handle--se{cursor:nesw-resize}[dir=rtl] .vie-crop-handle--ne,[dir=rtl] .vie-crop-handle--sw{cursor:nwse-resize}[dir=rtl] .vie-crop-handle--w,[dir=rtl] .vie-crop-handle--e{cursor:ew-resize}.vie-finetune-controls{display:flex;flex-direction:column;gap:2px;max-height:200px;overflow-y:auto;padding-inline-end:4px;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.vie-filter-controls{display:flex;flex-direction:column;gap:12px}.vie-filter-strip{display:flex;gap:10px;overflow-x:auto;padding:4px 2px 6px;scrollbar-width:thin;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.vie-filter-strip::-webkit-scrollbar{height:4px}.vie-filter-strip::-webkit-scrollbar-thumb{background:var(--vie-fg-muted);border-radius:2px}.vie-filter-thumb{display:flex;flex-direction:column;align-items:center;gap:6px;background:transparent;border:2px solid transparent;border-radius:var(--vie-radius-lg, 8px);cursor:pointer;padding:5px;min-width:84px;transition:border-color var(--vie-transition),background var(--vie-transition),transform .15s ease;color:var(--vie-fg-dim);font-family:var(--vie-font)}.vie-filter-thumb:hover{border-color:var(--vie-fg-muted);background:#00000008;transform:translateY(-1px)}.vie-filter-thumb--active{border-color:var(--vie-accent);color:var(--vie-accent);background:#1b49650f}.vie-filter-thumb__canvas{width:68px;height:68px;border-radius:4px;background:var(--vie-surface);box-shadow:0 1px 3px #00000014;transition:box-shadow .15s ease}.vie-filter-thumb:hover .vie-filter-thumb__canvas{box-shadow:0 2px 8px #0000001f}.vie-filter-thumb--active .vie-filter-thumb__canvas{box-shadow:0 0 0 1px var(--vie-accent),0 2px 8px #1b496533}.vie-filter-thumb__label{font-size:var(--vie-font-size-sm);white-space:nowrap;font-weight:500}.vie-theme-dark .vie-filter-thumb:hover{background:#ffffff08}.vie-theme-dark .vie-filter-thumb--active{background:#1b496526}.vie-sticker-controls{display:flex;flex-direction:column;gap:8px}.vie-sticker-tabs{display:flex;gap:4px}.vie-sticker-tab{padding:4px 12px;border-radius:var(--vie-radius);border:1px solid var(--vie-border);background:transparent;color:var(--vie-fg-dim);cursor:pointer;font-family:var(--vie-font);font-size:var(--vie-font-size);transition:all var(--vie-transition)}.vie-sticker-tab:hover{color:var(--vie-fg);background:var(--vie-surface)}.vie-sticker-tab--active{background:var(--vie-surface);color:var(--vie-accent);border-color:var(--vie-accent)}.vie-sticker-tab--delete{color:var(--vie-danger);border-color:var(--vie-danger);margin-inline-start:auto}.vie-sticker-tab--delete:hover{background:#f3164e26}.vie-sticker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(40px,1fr));gap:4px;max-height:120px;overflow-y:auto;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.vie-sticker-item{display:flex;align-items:center;justify-content:center;width:40px;height:40px;font-size:24px;border-radius:var(--vie-radius);border:1px solid transparent;background:transparent;cursor:pointer;transition:all var(--vie-transition)}.vie-sticker-item:hover{background:var(--vie-surface);border-color:var(--vie-border);transform:scale(1.15)}.vie-frame-controls{display:flex;flex-direction:column;gap:12px}.vie-frame-strip{display:flex;gap:6px;overflow-x:auto;padding-bottom:4px;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.vie-frame-style{display:flex;flex-direction:column;align-items:center;gap:4px;padding:6px 10px;border-radius:var(--vie-radius);border:2px solid transparent;background:transparent;color:var(--vie-fg-dim);cursor:pointer;font-family:var(--vie-font);font-size:var(--vie-font-size-sm);transition:all var(--vie-transition);white-space:nowrap}.vie-frame-style:hover{border-color:var(--vie-fg-muted);color:var(--vie-fg)}.vie-frame-style--active{border-color:var(--vie-accent);color:var(--vie-accent)}.vie-frame-colors{display:flex;align-items:center;gap:12px}.vie-frame-colors__label{font-size:var(--vie-font-size);color:var(--vie-fg-dim);white-space:nowrap}.vie-frame-colors__swatches{display:flex;gap:6px}.vie-frame-color-swatch{width:24px;height:24px;border-radius:50%;border:2px solid transparent;cursor:pointer;transition:border-color var(--vie-transition),transform var(--vie-transition);box-shadow:0 0 0 1px var(--vie-border)}.vie-frame-color-swatch:hover{transform:scale(1.15)}.vie-frame-color-swatch--active{border-color:var(--vie-accent)}.vie-fill-controls{display:flex;flex-direction:column;gap:12px}.vie-fill-strip{display:flex;gap:8px;overflow-x:auto;padding:4px 0;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.vie-fill-swatch{flex-shrink:0;width:40px;height:40px;border-radius:var(--vie-radius);border:3px solid transparent;cursor:pointer;transition:border-color var(--vie-transition),transform var(--vie-transition);box-shadow:0 0 0 1px var(--vie-border)}.vie-fill-swatch:hover{transform:scale(1.1)}.vie-fill-swatch--active{border-color:var(--vie-accent)}.vie-fill-swatch--transparent{background-image:linear-gradient(45deg,#808080 25%,transparent 25%),linear-gradient(-45deg,#808080 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#808080 75%),linear-gradient(-45deg,transparent 75%,#808080 75%);background-size:10px 10px;background-position:0 0,0 5px,5px -5px,-5px 0}.vie-sticker-overlay{position:absolute;top:0;right:0;bottom:0;left:0;outline:none}.vie-sticker-hitarea{cursor:pointer;stroke-width:2}.vie-sticker-hitarea:hover{stroke:#1b496566;fill:#1b49650d}.vie-sticker-selection{fill:none;stroke:var(--vie-accent);stroke-width:1.5;stroke-dasharray:4;pointer-events:none}.vie-sticker-handle{fill:#fff;stroke:var(--vie-accent);stroke-width:1.5;cursor:nwse-resize;rx:2}.vie-sticker-handle[data-handle=ne],.vie-sticker-handle[data-handle=sw]{cursor:nesw-resize}.vie-sticker-rotate-line{stroke:var(--vie-accent);stroke-width:1;pointer-events:none}.vie-sticker-rotate-handle{fill:#fff;stroke:var(--vie-accent);stroke-width:1.5;cursor:grab}.vie-sticker-rotate-handle:active{cursor:grabbing}.vie-resize-controls{display:flex;flex-direction:column;gap:12px}.vie-resize-dimensions{display:flex;align-items:flex-end;gap:8px}.vie-resize-input{display:flex;flex-direction:column;gap:4px}.vie-resize-input__label{font-size:var(--vie-font-size-sm);color:var(--vie-fg-dim)}.vie-resize-input__field-wrapper{position:relative;display:flex;align-items:center}.vie-resize-input__field{width:100px;padding:6px 28px 6px 8px;border-radius:var(--vie-radius);border:1px solid var(--vie-border);background:var(--vie-surface);color:var(--vie-fg);font-family:var(--vie-font);font-size:var(--vie-font-size);font-variant-numeric:tabular-nums;outline:none;transition:border-color var(--vie-transition);-moz-appearance:textfield}.vie-resize-input__field::-webkit-inner-spin-button,.vie-resize-input__field::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.vie-resize-input__field:focus{border-color:var(--vie-accent)}.vie-resize-input__unit{position:absolute;inset-inline-end:8px;color:var(--vie-fg-muted);font-size:var(--vie-font-size-sm);pointer-events:none}.vie-resize-lock{display:flex;align-items:center;justify-content:center;width:32px;height:32px;margin-bottom:2px;border-radius:var(--vie-radius);border:1px solid var(--vie-border);background:transparent;color:var(--vie-fg-dim);cursor:pointer;transition:all var(--vie-transition)}.vie-resize-lock:hover{color:var(--vie-fg);background:var(--vie-surface)}.vie-resize-lock--active{color:var(--vie-accent);border-color:var(--vie-accent)}.vie-resize-lock:active{transform:scale(.9)}.vie-panel-enter-active,.vie-panel-leave-active{transition:opacity .12s ease}.vie-panel-enter-from,.vie-panel-leave-to{opacity:0}.vie-dropdown-enter-active,.vie-dropdown-leave-active{transition:opacity .1s ease,transform .1s ease}.vie-dropdown-enter-from,.vie-dropdown-leave-to{opacity:0;transform:translateY(4px)}
|