@gradio/imageeditor 0.18.12 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/Example.svelte +9 -3
- package/ImageEditor.stories.svelte +0 -74
- package/Index.svelte +34 -35
- package/InteractiveImageEditor.svelte +99 -57
- package/dist/Example.svelte +9 -3
- package/dist/Example.svelte.d.ts +4 -18
- package/dist/Index.svelte +34 -35
- package/dist/Index.svelte.d.ts +2 -19
- package/dist/InteractiveImageEditor.svelte +99 -57
- package/dist/InteractiveImageEditor.svelte.d.ts +20 -37
- package/dist/shared/Anchor.svelte +13 -11
- package/dist/shared/Anchor.svelte.d.ts +4 -19
- package/dist/shared/Controls.svelte +63 -39
- package/dist/shared/Controls.svelte.d.ts +13 -28
- package/dist/shared/IconButton.svelte +39 -18
- package/dist/shared/IconButton.svelte.d.ts +7 -21
- package/dist/shared/ImageEditor.svelte +246 -167
- package/dist/shared/ImageEditor.svelte.d.ts +55 -2
- package/dist/shared/Layers.svelte +43 -29
- package/dist/shared/Layers.svelte.d.ts +10 -25
- package/dist/shared/Resize.svelte +22 -16
- package/dist/shared/Resize.svelte.d.ts +6 -22
- package/dist/shared/SecondaryToolbar.svelte +34 -18
- package/dist/shared/SecondaryToolbar.svelte.d.ts +10 -25
- package/dist/shared/Toolbar.svelte +60 -76
- package/dist/shared/Toolbar.svelte.d.ts +10 -25
- package/dist/shared/brush/BrushOptions.svelte +49 -29
- package/dist/shared/brush/BrushOptions.svelte.d.ts +6 -21
- package/dist/shared/brush/BrushSize.svelte +12 -9
- package/dist/shared/brush/BrushSize.svelte.d.ts +5 -20
- package/dist/shared/brush/ColorField.svelte +21 -16
- package/dist/shared/brush/ColorField.svelte.d.ts +6 -22
- package/dist/shared/brush/ColorPicker.svelte +22 -16
- package/dist/shared/brush/ColorPicker.svelte.d.ts +4 -18
- package/dist/shared/brush/ColorSwatch.svelte +41 -21
- package/dist/shared/brush/ColorSwatch.svelte.d.ts +10 -24
- package/dist/shared/brush/brush.js +7 -8
- package/dist/shared/core/editor.d.ts +5 -0
- package/dist/shared/core/editor.js +43 -2
- package/dist/shared/resize/resize.d.ts +2 -0
- package/dist/shared/resize/resize.js +27 -18
- package/dist/types.d.ts +1 -1
- package/package.json +8 -8
- package/shared/Anchor.svelte +13 -11
- package/shared/Controls.svelte +63 -39
- package/shared/IconButton.svelte +39 -18
- package/shared/ImageEditor.svelte +246 -167
- package/shared/Layers.svelte +43 -29
- package/shared/Resize.svelte +22 -16
- package/shared/SecondaryToolbar.svelte +34 -18
- package/shared/Toolbar.svelte +60 -76
- package/shared/brush/BrushOptions.svelte +49 -29
- package/shared/brush/BrushSize.svelte +12 -9
- package/shared/brush/ColorField.svelte +21 -16
- package/shared/brush/ColorPicker.svelte +22 -16
- package/shared/brush/ColorSwatch.svelte +41 -21
- package/shared/brush/brush.ts +8 -10
- package/shared/core/editor.ts +49 -2
- package/shared/resize/resize.ts +28 -20
- package/types.ts +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
8
|
<script lang="ts">
|
|
9
|
-
import { onMount,
|
|
9
|
+
import { onMount, tick, type Snippet } from "svelte";
|
|
10
10
|
import { get } from "svelte/store";
|
|
11
11
|
import Toolbar, { type Tool as ToolbarTool } from "./Toolbar.svelte";
|
|
12
12
|
import { CropTool } from "./crop/crop";
|
|
@@ -30,49 +30,92 @@
|
|
|
30
30
|
import IconButton from "./IconButton.svelte";
|
|
31
31
|
|
|
32
32
|
const { drag, open_file_upload } = create_drag();
|
|
33
|
-
const dispatch = createEventDispatcher<{
|
|
34
|
-
clear?: never;
|
|
35
|
-
save: void;
|
|
36
|
-
change: void;
|
|
37
|
-
upload: void;
|
|
38
|
-
input: void;
|
|
39
|
-
download_error: string;
|
|
40
|
-
}>();
|
|
41
33
|
|
|
42
34
|
export const antialias = true;
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
36
|
+
let {
|
|
37
|
+
changeable = false,
|
|
38
|
+
sources = ["upload", "webcam", "clipboard"],
|
|
39
|
+
transforms = ["crop", "resize"],
|
|
40
|
+
canvas_size,
|
|
41
|
+
is_dragging = $bindable(false),
|
|
42
|
+
background_image = $bindable(false),
|
|
43
|
+
brush_options,
|
|
44
|
+
eraser_options,
|
|
45
|
+
fixed_canvas = false,
|
|
46
|
+
root,
|
|
47
|
+
i18n,
|
|
48
|
+
upload,
|
|
49
|
+
composite,
|
|
50
|
+
layers,
|
|
51
|
+
background,
|
|
52
|
+
border_region = 0,
|
|
53
|
+
layer_options,
|
|
54
|
+
current_tool = $bindable(),
|
|
55
|
+
webcam_options,
|
|
56
|
+
show_download_button = false,
|
|
57
|
+
theme_mode,
|
|
58
|
+
full_history = $bindable(null),
|
|
59
|
+
has_drawn = $bindable(false),
|
|
60
|
+
can_undo = $bindable(false),
|
|
61
|
+
onclear,
|
|
62
|
+
onsave,
|
|
63
|
+
onchange,
|
|
64
|
+
onupload,
|
|
65
|
+
oninput,
|
|
66
|
+
ondownload_error,
|
|
67
|
+
children
|
|
68
|
+
}: {
|
|
69
|
+
changeable?: boolean;
|
|
70
|
+
sources?: Source[];
|
|
71
|
+
transforms?: Transform[];
|
|
72
|
+
canvas_size: [number, number];
|
|
73
|
+
is_dragging?: boolean;
|
|
74
|
+
background_image?: boolean;
|
|
75
|
+
brush_options: Brush | false;
|
|
76
|
+
eraser_options: Eraser | false;
|
|
77
|
+
fixed_canvas?: boolean;
|
|
78
|
+
root: string;
|
|
79
|
+
i18n: I18nFormatter;
|
|
80
|
+
upload: Client["upload"];
|
|
81
|
+
composite: FileData | null;
|
|
82
|
+
layers: FileData[];
|
|
83
|
+
background: FileData | null;
|
|
84
|
+
border_region?: number;
|
|
85
|
+
layer_options: LayerOptions;
|
|
86
|
+
current_tool: ToolbarTool;
|
|
87
|
+
webcam_options: WebcamOptions;
|
|
88
|
+
show_download_button?: boolean;
|
|
89
|
+
theme_mode: "dark" | "light";
|
|
90
|
+
full_history?: CommandNode | null;
|
|
91
|
+
has_drawn?: boolean;
|
|
92
|
+
can_undo?: boolean;
|
|
93
|
+
onclear?: () => void;
|
|
94
|
+
onsave?: () => void;
|
|
95
|
+
onchange?: () => void;
|
|
96
|
+
onupload?: () => void;
|
|
97
|
+
oninput?: () => void;
|
|
98
|
+
ondownload_error?: (error: string) => void;
|
|
99
|
+
children?: Snippet;
|
|
100
|
+
} = $props();
|
|
66
101
|
|
|
67
102
|
let pixi_target: HTMLDivElement;
|
|
68
103
|
let pixi_target_crop: HTMLDivElement;
|
|
69
104
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
105
|
+
let layer_options_key: string | undefined | null = null;
|
|
106
|
+
$effect(() => {
|
|
107
|
+
if (!check_if_should_init()) return;
|
|
108
|
+
|
|
109
|
+
const next_layer_options_key = JSON.stringify(layer_options);
|
|
110
|
+
if (next_layer_options_key === layer_options_key) return;
|
|
111
|
+
|
|
112
|
+
layer_options_key = next_layer_options_key;
|
|
113
|
+
editor.set_layer_options(layer_options);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
$effect(() => {
|
|
117
|
+
refresh_tools();
|
|
118
|
+
});
|
|
76
119
|
|
|
77
120
|
function refresh_tools(): void {
|
|
78
121
|
if (!editor || !ready) return;
|
|
@@ -80,13 +123,14 @@
|
|
|
80
123
|
editor.set_subtool(current_subtool);
|
|
81
124
|
}
|
|
82
125
|
|
|
83
|
-
|
|
126
|
+
$effect(() => {
|
|
127
|
+
if (!(editor && ready && editor.layers)) return;
|
|
84
128
|
const current_layers = get(editor.layers);
|
|
85
129
|
|
|
86
130
|
if (current_layers.layers.length > 0 && !current_layers.active_layer) {
|
|
87
131
|
refresh_tools_for_layer_changes(current_layers);
|
|
88
132
|
}
|
|
89
|
-
}
|
|
133
|
+
});
|
|
90
134
|
|
|
91
135
|
/**
|
|
92
136
|
* Refreshes tools when layer state changes
|
|
@@ -115,8 +159,6 @@
|
|
|
115
159
|
return layer_options && editor && ready;
|
|
116
160
|
}
|
|
117
161
|
|
|
118
|
-
export let has_drawn = false;
|
|
119
|
-
|
|
120
162
|
/**
|
|
121
163
|
* Gets the image blobs from the editor
|
|
122
164
|
* @returns {Promise<ImageBlobs>} Object containing background, layers, and composite image blobs
|
|
@@ -176,8 +218,8 @@
|
|
|
176
218
|
crop.set_subtool("crop");
|
|
177
219
|
background_image = true;
|
|
178
220
|
zoom.set_zoom("fit");
|
|
179
|
-
|
|
180
|
-
|
|
221
|
+
onupload?.();
|
|
222
|
+
oninput?.();
|
|
181
223
|
} catch (error) {
|
|
182
224
|
console.error("Error adding image from URL:", error);
|
|
183
225
|
}
|
|
@@ -200,8 +242,8 @@
|
|
|
200
242
|
try {
|
|
201
243
|
await pending_bg;
|
|
202
244
|
await editor.add_layers_from_url(source.map((item) => item.url));
|
|
203
|
-
|
|
204
|
-
|
|
245
|
+
onchange?.();
|
|
246
|
+
oninput?.();
|
|
205
247
|
} catch (error) {
|
|
206
248
|
console.error("Error adding layer from URL:", error);
|
|
207
249
|
}
|
|
@@ -210,10 +252,10 @@
|
|
|
210
252
|
|
|
211
253
|
let brush: BrushTool;
|
|
212
254
|
let zoom: ZoomTool;
|
|
213
|
-
let zoom_level = 1;
|
|
214
|
-
let ready = false;
|
|
215
|
-
let mounted = false;
|
|
216
|
-
let min_zoom = true;
|
|
255
|
+
let zoom_level = $state(1);
|
|
256
|
+
let ready = $state(false);
|
|
257
|
+
let mounted = $state(false);
|
|
258
|
+
let min_zoom = $state(true);
|
|
217
259
|
|
|
218
260
|
let last_dimensions = { width: 0, height: 0 };
|
|
219
261
|
|
|
@@ -221,9 +263,11 @@
|
|
|
221
263
|
* Handles visibility changes and resets zoom if dimensions have changed
|
|
222
264
|
*/
|
|
223
265
|
async function handle_visibility_change(): Promise<void> {
|
|
224
|
-
if (!editor || !ready || !zoom) return;
|
|
266
|
+
if (!editor || !ready || !zoom || !pixi_target) return;
|
|
225
267
|
await tick();
|
|
226
268
|
|
|
269
|
+
if (!pixi_target) return;
|
|
270
|
+
|
|
227
271
|
const is_visible = pixi_target.offsetParent !== null;
|
|
228
272
|
|
|
229
273
|
if (is_visible) {
|
|
@@ -316,8 +360,7 @@
|
|
|
316
360
|
|
|
317
361
|
let crop: ImageEditor;
|
|
318
362
|
let crop_zoom: ZoomTool;
|
|
319
|
-
|
|
320
|
-
let can_redo = false;
|
|
363
|
+
let can_redo = $state(false);
|
|
321
364
|
async function init_image_editor(): Promise<void> {
|
|
322
365
|
brush = new BrushTool();
|
|
323
366
|
zoom = new ZoomTool();
|
|
@@ -382,8 +425,8 @@
|
|
|
382
425
|
});
|
|
383
426
|
|
|
384
427
|
editor.on("change", () => {
|
|
385
|
-
|
|
386
|
-
|
|
428
|
+
onchange?.();
|
|
429
|
+
oninput?.();
|
|
387
430
|
full_history = editor.command_manager.history;
|
|
388
431
|
});
|
|
389
432
|
|
|
@@ -403,23 +446,27 @@
|
|
|
403
446
|
refresh_tools();
|
|
404
447
|
}
|
|
405
448
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
449
|
+
$effect(() => {
|
|
450
|
+
if (
|
|
451
|
+
background == null &&
|
|
452
|
+
layers.length == 0 &&
|
|
453
|
+
composite == null &&
|
|
454
|
+
editor &&
|
|
455
|
+
ready
|
|
456
|
+
) {
|
|
457
|
+
editor.reset_canvas();
|
|
458
|
+
zoom.set_zoom("fit");
|
|
459
|
+
handle_tool_change({ tool: "image" });
|
|
460
|
+
background_image = false;
|
|
461
|
+
has_drawn = false;
|
|
462
|
+
}
|
|
463
|
+
});
|
|
419
464
|
|
|
420
|
-
|
|
421
|
-
current_subtool === "crop"
|
|
422
|
-
|
|
465
|
+
$effect(() => {
|
|
466
|
+
if (current_tool === "image" && current_subtool === "crop") {
|
|
467
|
+
crop_zoom.set_zoom("fit");
|
|
468
|
+
}
|
|
469
|
+
});
|
|
423
470
|
|
|
424
471
|
/**
|
|
425
472
|
* Handles file uploads
|
|
@@ -439,10 +486,15 @@
|
|
|
439
486
|
background_image = true;
|
|
440
487
|
zoom.set_zoom("fit");
|
|
441
488
|
handle_tool_change({ tool: "draw" });
|
|
442
|
-
|
|
489
|
+
onupload?.();
|
|
443
490
|
}
|
|
444
491
|
|
|
445
|
-
|
|
492
|
+
$effect(() => {
|
|
493
|
+
if (editor) {
|
|
494
|
+
background_image =
|
|
495
|
+
can_undo && editor.command_manager.contains("AddImage");
|
|
496
|
+
}
|
|
497
|
+
});
|
|
446
498
|
|
|
447
499
|
/**
|
|
448
500
|
* Handles tool change events
|
|
@@ -500,13 +552,13 @@
|
|
|
500
552
|
}
|
|
501
553
|
}
|
|
502
554
|
|
|
503
|
-
let eraser_size_visible = false;
|
|
504
|
-
let selected_color
|
|
505
|
-
let selected_size
|
|
506
|
-
let selected_opacity = 1;
|
|
507
|
-
let selected_eraser_size
|
|
555
|
+
let eraser_size_visible = $state(false);
|
|
556
|
+
let selected_color = $state<ColorInput | string>();
|
|
557
|
+
let selected_size = $state<number>();
|
|
558
|
+
let selected_opacity = $state(1);
|
|
559
|
+
let selected_eraser_size = $state<number>();
|
|
508
560
|
|
|
509
|
-
|
|
561
|
+
$effect(() => {
|
|
510
562
|
if (brush_options) {
|
|
511
563
|
update_brush_options();
|
|
512
564
|
}
|
|
@@ -514,13 +566,15 @@
|
|
|
514
566
|
if (eraser_options) {
|
|
515
567
|
update_eraser_options();
|
|
516
568
|
}
|
|
517
|
-
}
|
|
569
|
+
});
|
|
518
570
|
|
|
519
571
|
function update_brush_options(): void {
|
|
572
|
+
if (!brush_options) return;
|
|
573
|
+
const options = brush_options;
|
|
520
574
|
const default_color =
|
|
521
|
-
|
|
522
|
-
?
|
|
523
|
-
:
|
|
575
|
+
options.default_color === "auto"
|
|
576
|
+
? options.colors[0]
|
|
577
|
+
: options.default_color;
|
|
524
578
|
|
|
525
579
|
// color is already a tuple [color, opacity]
|
|
526
580
|
if (Array.isArray(default_color)) {
|
|
@@ -539,59 +593,86 @@
|
|
|
539
593
|
}
|
|
540
594
|
|
|
541
595
|
selected_size =
|
|
542
|
-
typeof
|
|
543
|
-
? brush_options.default_size
|
|
544
|
-
: 25;
|
|
596
|
+
typeof options.default_size === "number" ? options.default_size : 25;
|
|
545
597
|
}
|
|
546
598
|
|
|
547
599
|
function update_eraser_options(): void {
|
|
600
|
+
if (!eraser_options) return;
|
|
601
|
+
const options = eraser_options;
|
|
548
602
|
selected_eraser_size =
|
|
549
|
-
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
let brush_size_visible = false;
|
|
553
|
-
|
|
554
|
-
let brush_color_visible = false;
|
|
603
|
+
options.default_size === "auto" ? 25 : options.default_size;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
let brush_size_visible = $state(false);
|
|
607
|
+
|
|
608
|
+
let brush_color_visible = $state(false);
|
|
609
|
+
|
|
610
|
+
$effect(() => {
|
|
611
|
+
if (!brush) return;
|
|
612
|
+
brush.set_brush_color(
|
|
613
|
+
(() => {
|
|
614
|
+
let color_value: ColorInput = "black";
|
|
615
|
+
if (selected_color === "auto") {
|
|
616
|
+
if (!brush_options) return "black";
|
|
617
|
+
const default_color =
|
|
618
|
+
brush_options.colors.find((color) =>
|
|
619
|
+
Array.isArray(color)
|
|
620
|
+
? color[0] === brush_options.default_color
|
|
621
|
+
: color === brush_options.default_color
|
|
622
|
+
) || brush_options.colors[0];
|
|
623
|
+
|
|
624
|
+
color_value = Array.isArray(default_color)
|
|
625
|
+
? default_color[0]
|
|
626
|
+
: default_color;
|
|
627
|
+
} else {
|
|
628
|
+
color_value = selected_color ?? "black";
|
|
629
|
+
}
|
|
630
|
+
return color_value;
|
|
631
|
+
})()
|
|
632
|
+
);
|
|
633
|
+
});
|
|
555
634
|
|
|
556
|
-
|
|
557
|
-
(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
brush_options.colors.find((color) =>
|
|
562
|
-
Array.isArray(color)
|
|
563
|
-
? color[0] === brush_options.default_color
|
|
564
|
-
: color === brush_options.default_color
|
|
565
|
-
) || brush_options.colors[0];
|
|
635
|
+
$effect(() => {
|
|
636
|
+
brush?.set_brush_size(
|
|
637
|
+
typeof selected_size === "number" ? selected_size : 25
|
|
638
|
+
);
|
|
639
|
+
});
|
|
566
640
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
return color_value;
|
|
574
|
-
})()
|
|
575
|
-
);
|
|
641
|
+
$effect(() => {
|
|
642
|
+
brush?.set_eraser_size(
|
|
643
|
+
typeof selected_eraser_size === "number" ? selected_eraser_size : 25
|
|
644
|
+
);
|
|
645
|
+
});
|
|
576
646
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
647
|
+
let disable_click = $state(true);
|
|
648
|
+
$effect(() => {
|
|
649
|
+
disable_click =
|
|
650
|
+
current_tool !== "image" ||
|
|
651
|
+
(current_tool === "image" && background_image) ||
|
|
652
|
+
(current_tool === "image" && current_subtool === "webcam") ||
|
|
653
|
+
!sources.includes("upload");
|
|
654
|
+
});
|
|
580
655
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
)
|
|
656
|
+
let current_subtool = $state<Subtool | null>(null);
|
|
657
|
+
let preview = $state(false);
|
|
658
|
+
$effect(() => {
|
|
659
|
+
brush?.preview_brush(preview);
|
|
660
|
+
});
|
|
661
|
+
$effect(() => {
|
|
662
|
+
brush?.set_brush_opacity(selected_opacity);
|
|
663
|
+
});
|
|
584
664
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
(
|
|
588
|
-
(
|
|
589
|
-
|
|
665
|
+
function remove_image(): void {
|
|
666
|
+
onclear?.();
|
|
667
|
+
editor.reset_canvas();
|
|
668
|
+
handle_tool_change({ tool: "image" });
|
|
669
|
+
background_image = false;
|
|
670
|
+
has_drawn = false;
|
|
671
|
+
}
|
|
590
672
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
$: brush?.set_brush_opacity(selected_opacity);
|
|
673
|
+
export function handle_remove(): void {
|
|
674
|
+
remove_image();
|
|
675
|
+
}
|
|
595
676
|
|
|
596
677
|
function handle_zoom_change(zoom_level: number | "fit"): void {
|
|
597
678
|
zoom.set_zoom(zoom_level);
|
|
@@ -618,19 +699,23 @@
|
|
|
618
699
|
}
|
|
619
700
|
}
|
|
620
701
|
|
|
621
|
-
function handle_capture(
|
|
622
|
-
if (
|
|
623
|
-
handle_files(
|
|
702
|
+
function handle_capture(value: FileData | Blob | null): void {
|
|
703
|
+
if (value !== null) {
|
|
704
|
+
handle_files(value as Blob);
|
|
624
705
|
}
|
|
625
706
|
handle_subtool_change({ tool: current_tool, subtool: null });
|
|
626
707
|
}
|
|
627
708
|
|
|
628
709
|
function handle_save(): void {
|
|
629
|
-
|
|
710
|
+
onsave?.();
|
|
630
711
|
}
|
|
631
712
|
|
|
632
|
-
|
|
633
|
-
|
|
713
|
+
$effect(() => {
|
|
714
|
+
add_image_from_url(composite || background);
|
|
715
|
+
});
|
|
716
|
+
$effect(() => {
|
|
717
|
+
add_layers_from_url(layers);
|
|
718
|
+
});
|
|
634
719
|
|
|
635
720
|
async function handle_crop_confirm(): Promise<void> {
|
|
636
721
|
const { image } = await crop.get_crop_bounds();
|
|
@@ -641,8 +726,8 @@
|
|
|
641
726
|
resize: false
|
|
642
727
|
});
|
|
643
728
|
handle_subtool_change({ tool: "image", subtool: null });
|
|
644
|
-
|
|
645
|
-
|
|
729
|
+
onchange?.();
|
|
730
|
+
oninput?.();
|
|
646
731
|
}
|
|
647
732
|
|
|
648
733
|
async function handle_download(): Promise<void> {
|
|
@@ -650,7 +735,7 @@
|
|
|
650
735
|
|
|
651
736
|
const blob = blobs.composite;
|
|
652
737
|
if (!blob) {
|
|
653
|
-
|
|
738
|
+
ondownload_error?.("Unable to generate image to download.");
|
|
654
739
|
return;
|
|
655
740
|
}
|
|
656
741
|
const url = URL.createObjectURL(blob);
|
|
@@ -671,12 +756,13 @@
|
|
|
671
756
|
|
|
672
757
|
let old_background = background;
|
|
673
758
|
|
|
674
|
-
|
|
759
|
+
$effect(() => {
|
|
760
|
+
if (old_background === background) return;
|
|
675
761
|
handle_tool_change({ tool: "draw" });
|
|
676
762
|
handle_subtool_change({ tool: "draw", subtool: null });
|
|
677
763
|
current_tool = "draw";
|
|
678
764
|
old_background = background;
|
|
679
|
-
}
|
|
765
|
+
});
|
|
680
766
|
</script>
|
|
681
767
|
|
|
682
768
|
<div
|
|
@@ -687,7 +773,8 @@
|
|
|
687
773
|
on_drag_change: (dragging) => (is_dragging = dragging),
|
|
688
774
|
on_files: handle_files,
|
|
689
775
|
accepted_types: "image/*",
|
|
690
|
-
disable_click: disable_click
|
|
776
|
+
disable_click: disable_click,
|
|
777
|
+
ignore_click_selector: ".toolbar-wrap"
|
|
691
778
|
}}
|
|
692
779
|
aria-label={"Click to upload or drop files"}
|
|
693
780
|
aria-dropeffect="copy"
|
|
@@ -696,30 +783,24 @@
|
|
|
696
783
|
{#if current_subtool !== "crop"}
|
|
697
784
|
<Controls
|
|
698
785
|
{changeable}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
786
|
+
onset_zoom={(zoom) => handle_zoom_change(zoom)}
|
|
787
|
+
onzoom_in={() => zoom_in_out("in")}
|
|
788
|
+
onzoom_out={() => zoom_in_out("out")}
|
|
702
789
|
{min_zoom}
|
|
703
790
|
current_zoom={zoom_level}
|
|
704
|
-
|
|
705
|
-
dispatch("clear");
|
|
706
|
-
editor.reset_canvas();
|
|
707
|
-
handle_tool_change({ tool: "image" });
|
|
708
|
-
background_image = false;
|
|
709
|
-
has_drawn = false;
|
|
710
|
-
}}
|
|
791
|
+
onremove_image={remove_image}
|
|
711
792
|
tool={current_tool}
|
|
712
793
|
can_save={true}
|
|
713
|
-
|
|
714
|
-
|
|
794
|
+
onsave={handle_save}
|
|
795
|
+
onpan={() => {
|
|
715
796
|
handle_tool_change({ tool: "pan" });
|
|
716
797
|
}}
|
|
717
798
|
enable_download={show_download_button}
|
|
718
|
-
|
|
799
|
+
ondownload={() => handle_download()}
|
|
719
800
|
{can_undo}
|
|
720
801
|
{can_redo}
|
|
721
|
-
|
|
722
|
-
|
|
802
|
+
onundo={handle_undo}
|
|
803
|
+
onredo={handle_redo}
|
|
723
804
|
/>
|
|
724
805
|
{/if}
|
|
725
806
|
|
|
@@ -728,8 +809,8 @@
|
|
|
728
809
|
{sources}
|
|
729
810
|
{transforms}
|
|
730
811
|
background={background_image}
|
|
731
|
-
|
|
732
|
-
|
|
812
|
+
ontool_change={(detail) => handle_tool_change(detail)}
|
|
813
|
+
onsubtool_change={(detail) => handle_subtool_change(detail)}
|
|
733
814
|
show_brush_size={brush_size_visible}
|
|
734
815
|
show_brush_color={brush_color_visible}
|
|
735
816
|
show_eraser_size={eraser_size_visible}
|
|
@@ -751,9 +832,7 @@
|
|
|
751
832
|
<Webcam
|
|
752
833
|
{upload}
|
|
753
834
|
{root}
|
|
754
|
-
|
|
755
|
-
on:error
|
|
756
|
-
on:drag
|
|
835
|
+
oncapture={handle_capture}
|
|
757
836
|
streaming={false}
|
|
758
837
|
mode="image"
|
|
759
838
|
include_audio={false}
|
|
@@ -769,23 +848,23 @@
|
|
|
769
848
|
<SecondaryToolbar
|
|
770
849
|
enable_additional_layers={layer_options.allow_additional_layers}
|
|
771
850
|
layers={editor.layers}
|
|
772
|
-
|
|
851
|
+
onnew_layer={() => {
|
|
773
852
|
editor.add_layer();
|
|
774
853
|
}}
|
|
775
|
-
|
|
776
|
-
editor.set_layer(
|
|
854
|
+
onchange_layer={(id) => {
|
|
855
|
+
editor.set_layer(id);
|
|
777
856
|
if (current_tool === "draw") {
|
|
778
857
|
handle_tool_change({ tool: "draw" });
|
|
779
858
|
}
|
|
780
859
|
}}
|
|
781
|
-
|
|
782
|
-
editor.move_layer(
|
|
860
|
+
onmove_layer={({ id, direction }) => {
|
|
861
|
+
editor.move_layer(id, direction);
|
|
783
862
|
}}
|
|
784
|
-
|
|
785
|
-
editor.delete_layer(
|
|
863
|
+
ondelete_layer={(id) => {
|
|
864
|
+
editor.delete_layer(id);
|
|
786
865
|
}}
|
|
787
|
-
|
|
788
|
-
editor.toggle_layer_visibility(
|
|
866
|
+
ontoggle_layer_visibility={(id) => {
|
|
867
|
+
editor.toggle_layer_visibility(id);
|
|
789
868
|
}}
|
|
790
869
|
/>
|
|
791
870
|
{/if}
|
|
@@ -816,7 +895,7 @@
|
|
|
816
895
|
/>
|
|
817
896
|
</div>
|
|
818
897
|
{/if}
|
|
819
|
-
|
|
898
|
+
{#if children}{@render children()}{/if}
|
|
820
899
|
</div>
|
|
821
900
|
|
|
822
901
|
<style>
|