@gradio/imageeditor 0.20.0 → 0.20.2
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 +37 -0
- package/ImageEditor.test.ts +129 -0
- package/ImageEditorTestWrapper.svelte +50 -0
- package/Index.svelte +0 -2
- package/InteractiveImageEditor.svelte +64 -15
- package/dist/ImageEditorTestWrapper.svelte +50 -0
- package/dist/ImageEditorTestWrapper.svelte.d.ts +10 -0
- package/dist/Index.svelte +0 -2
- package/dist/InteractiveImageEditor.svelte +64 -15
- package/dist/shared/Controls.svelte +12 -9
- package/dist/shared/Controls.svelte.d.ts +2 -0
- package/dist/shared/ImageEditor.svelte +25 -9
- package/dist/shared/ImageEditor.svelte.d.ts +2 -1
- package/dist/shared/Layers.svelte +18 -5
- package/dist/shared/Layers.svelte.d.ts +2 -0
- package/dist/shared/Resize.svelte +1 -1
- package/dist/shared/SecondaryToolbar.svelte +4 -0
- package/dist/shared/SecondaryToolbar.svelte.d.ts +2 -0
- package/dist/shared/Toolbar.svelte +15 -12
- package/dist/shared/Toolbar.svelte.d.ts +2 -0
- package/dist/shared/brush/brush-textures.js +8 -24
- package/package.json +8 -8
- package/shared/Controls.svelte +12 -9
- package/shared/ImageEditor.svelte +25 -9
- package/shared/Layers.svelte +18 -5
- package/shared/Resize.svelte +1 -1
- package/shared/SecondaryToolbar.svelte +4 -0
- package/shared/Toolbar.svelte +15 -12
- package/shared/brush/brush-textures.test.ts +77 -0
- package/shared/brush/brush-textures.ts +8 -27
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts"
|
|
1
|
+
<script module lang="ts">
|
|
2
2
|
import type { Tool, Subtool } from "./Toolbar.svelte";
|
|
3
3
|
|
|
4
4
|
export const EDITOR_KEY = Symbol("editor");
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
full_history = $bindable(null),
|
|
59
59
|
has_drawn = $bindable(false),
|
|
60
60
|
can_undo = $bindable(false),
|
|
61
|
+
has_user_edits = $bindable(false),
|
|
61
62
|
onclear,
|
|
62
63
|
onsave,
|
|
63
64
|
onchange,
|
|
@@ -90,6 +91,7 @@
|
|
|
90
91
|
full_history?: CommandNode | null;
|
|
91
92
|
has_drawn?: boolean;
|
|
92
93
|
can_undo?: boolean;
|
|
94
|
+
has_user_edits?: boolean;
|
|
93
95
|
onclear?: () => void;
|
|
94
96
|
onsave?: () => void;
|
|
95
97
|
onchange?: () => void;
|
|
@@ -182,6 +184,7 @@
|
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
let pending_bg: Promise<void>;
|
|
187
|
+
let loading_value = 0;
|
|
185
188
|
/**
|
|
186
189
|
* Adds an image to the editor from a URL
|
|
187
190
|
* @param {string | FileData} source - The URL of the image or a FileData object
|
|
@@ -199,6 +202,8 @@
|
|
|
199
202
|
| any
|
|
200
203
|
): Promise<void> {
|
|
201
204
|
if (!editor || !source || !check_if_should_init()) return;
|
|
205
|
+
has_user_edits = false;
|
|
206
|
+
loading_value += 1;
|
|
202
207
|
let url: string;
|
|
203
208
|
|
|
204
209
|
if (typeof source === "string") {
|
|
@@ -207,6 +212,7 @@
|
|
|
207
212
|
url = source.url;
|
|
208
213
|
} else {
|
|
209
214
|
console.warn("Invalid source provided to add_image_from_url:", source);
|
|
215
|
+
loading_value -= 1;
|
|
210
216
|
return;
|
|
211
217
|
}
|
|
212
218
|
|
|
@@ -222,6 +228,8 @@
|
|
|
222
228
|
oninput?.();
|
|
223
229
|
} catch (error) {
|
|
224
230
|
console.error("Error adding image from URL:", error);
|
|
231
|
+
} finally {
|
|
232
|
+
loading_value -= 1;
|
|
225
233
|
}
|
|
226
234
|
}
|
|
227
235
|
|
|
@@ -234,19 +242,23 @@
|
|
|
234
242
|
source: FileData[] | any
|
|
235
243
|
): Promise<void> {
|
|
236
244
|
if (!editor || !source.length || !check_if_should_init()) return;
|
|
245
|
+
has_user_edits = false;
|
|
246
|
+
loading_value += 1;
|
|
237
247
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
248
|
+
try {
|
|
249
|
+
if (
|
|
250
|
+
Array.isArray(source) &&
|
|
251
|
+
source.every((item) => item?.meta?._type === "gradio.FileData")
|
|
252
|
+
) {
|
|
243
253
|
await pending_bg;
|
|
244
254
|
await editor.add_layers_from_url(source.map((item) => item.url));
|
|
245
255
|
onchange?.();
|
|
246
256
|
oninput?.();
|
|
247
|
-
} catch (error) {
|
|
248
|
-
console.error("Error adding layer from URL:", error);
|
|
249
257
|
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error("Error adding layer from URL:", error);
|
|
260
|
+
} finally {
|
|
261
|
+
loading_value -= 1;
|
|
250
262
|
}
|
|
251
263
|
}
|
|
252
264
|
|
|
@@ -425,6 +437,7 @@
|
|
|
425
437
|
});
|
|
426
438
|
|
|
427
439
|
editor.on("change", () => {
|
|
440
|
+
if (loading_value === 0) has_user_edits = true;
|
|
428
441
|
onchange?.();
|
|
429
442
|
oninput?.();
|
|
430
443
|
full_history = editor.command_manager.history;
|
|
@@ -782,6 +795,7 @@
|
|
|
782
795
|
{#if ready}
|
|
783
796
|
{#if current_subtool !== "crop"}
|
|
784
797
|
<Controls
|
|
798
|
+
{i18n}
|
|
785
799
|
{changeable}
|
|
786
800
|
onset_zoom={(zoom) => handle_zoom_change(zoom)}
|
|
787
801
|
onzoom_in={() => zoom_in_out("in")}
|
|
@@ -806,6 +820,7 @@
|
|
|
806
820
|
|
|
807
821
|
{#if current_subtool !== "crop"}
|
|
808
822
|
<Toolbar
|
|
823
|
+
{i18n}
|
|
809
824
|
{sources}
|
|
810
825
|
{transforms}
|
|
811
826
|
background={background_image}
|
|
@@ -846,6 +861,7 @@
|
|
|
846
861
|
|
|
847
862
|
{#if current_subtool !== "crop" && !layer_options.disabled}
|
|
848
863
|
<SecondaryToolbar
|
|
864
|
+
{i18n}
|
|
849
865
|
enable_additional_layers={layer_options.allow_additional_layers}
|
|
850
866
|
layers={editor.layers}
|
|
851
867
|
onnew_layer={() => {
|
|
@@ -884,7 +900,7 @@
|
|
|
884
900
|
<div class="crop-confirm-button">
|
|
885
901
|
<IconButton
|
|
886
902
|
Icon={Check}
|
|
887
|
-
label="
|
|
903
|
+
label={i18n("image_editor.confirm_crop")}
|
|
888
904
|
show_label={true}
|
|
889
905
|
size="large"
|
|
890
906
|
padded={true}
|
|
@@ -34,6 +34,7 @@ type $$ComponentProps = {
|
|
|
34
34
|
full_history?: CommandNode | null;
|
|
35
35
|
has_drawn?: boolean;
|
|
36
36
|
can_undo?: boolean;
|
|
37
|
+
has_user_edits?: boolean;
|
|
37
38
|
onclear?: () => void;
|
|
38
39
|
onsave?: () => void;
|
|
39
40
|
onchange?: () => void;
|
|
@@ -54,6 +55,6 @@ declare const ImageEditor: import("svelte").Component<$$ComponentProps, {
|
|
|
54
55
|
} | any) => Promise<void>;
|
|
55
56
|
add_layers_from_url: (source: FileData[] | any) => Promise<void>;
|
|
56
57
|
handle_remove: () => void;
|
|
57
|
-
}, "current_tool" | "can_undo" | "is_dragging" | "background_image" | "full_history" | "has_drawn">;
|
|
58
|
+
}, "current_tool" | "can_undo" | "is_dragging" | "background_image" | "full_history" | "has_drawn" | "has_user_edits">;
|
|
58
59
|
type ImageEditor = ReturnType<typeof ImageEditor>;
|
|
59
60
|
export default ImageEditor;
|
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
} from "@gradio/icons";
|
|
12
12
|
import { IconButton } from "@gradio/atoms";
|
|
13
13
|
import type { Writable } from "svelte/store";
|
|
14
|
+
import type { I18nFormatter } from "@gradio/utils";
|
|
14
15
|
|
|
15
16
|
let {
|
|
17
|
+
i18n,
|
|
16
18
|
layers,
|
|
17
19
|
enable_additional_layers = true,
|
|
18
20
|
enable_layers = true,
|
|
@@ -23,6 +25,7 @@
|
|
|
23
25
|
ondelete_layer,
|
|
24
26
|
ontoggle_layer_visibility
|
|
25
27
|
}: {
|
|
28
|
+
i18n: I18nFormatter;
|
|
26
29
|
layers: Writable<{
|
|
27
30
|
active_layer: string;
|
|
28
31
|
layers: {
|
|
@@ -58,6 +61,13 @@
|
|
|
58
61
|
function delete_layer(id: string): void {
|
|
59
62
|
ondelete_layer?.(id);
|
|
60
63
|
}
|
|
64
|
+
|
|
65
|
+
function localize_layer_name(name: string): string {
|
|
66
|
+
const generated_layer_name = /^Layer (\d+)$/.exec(name);
|
|
67
|
+
return generated_layer_name
|
|
68
|
+
? `${i18n("image_editor.layer")} ${generated_layer_name[1]}`
|
|
69
|
+
: name;
|
|
70
|
+
}
|
|
61
71
|
</script>
|
|
62
72
|
|
|
63
73
|
{#if enable_layers}
|
|
@@ -68,15 +78,18 @@
|
|
|
68
78
|
>
|
|
69
79
|
<button
|
|
70
80
|
class="layer-title-button"
|
|
71
|
-
aria-label="
|
|
81
|
+
aria-label={i18n("image_editor.show_layers")}
|
|
72
82
|
onclick={(event) => {
|
|
73
83
|
event.stopPropagation();
|
|
74
84
|
show_layers = !show_layers;
|
|
75
85
|
}}
|
|
76
86
|
>
|
|
77
87
|
{show_layers
|
|
78
|
-
? "
|
|
79
|
-
:
|
|
88
|
+
? i18n("image_editor.layers")
|
|
89
|
+
: localize_layer_name(
|
|
90
|
+
$layers.layers.find((l) => l.id === $layers.active_layer)?.name ??
|
|
91
|
+
""
|
|
92
|
+
)}
|
|
80
93
|
<span class="icon"><Layers /></span>
|
|
81
94
|
</button>
|
|
82
95
|
{#if show_layers}
|
|
@@ -108,7 +121,7 @@
|
|
|
108
121
|
onclick={(event) => {
|
|
109
122
|
event.stopPropagation();
|
|
110
123
|
change_layer(id);
|
|
111
|
-
}}>{name}</button
|
|
124
|
+
}}>{localize_layer_name(name)}</button
|
|
112
125
|
>
|
|
113
126
|
{#if $layers.layers.length > 1}
|
|
114
127
|
<div>
|
|
@@ -150,7 +163,7 @@
|
|
|
150
163
|
<li class="add-layer">
|
|
151
164
|
<IconButton
|
|
152
165
|
Icon={Plus}
|
|
153
|
-
label="
|
|
166
|
+
label={i18n("image_editor.add_layer")}
|
|
154
167
|
onclick={(e) => {
|
|
155
168
|
e.stopPropagation();
|
|
156
169
|
new_layer();
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Layers from "./Layers.svelte";
|
|
3
3
|
import type { Writable } from "svelte/store";
|
|
4
|
+
import type { I18nFormatter } from "@gradio/utils";
|
|
4
5
|
|
|
5
6
|
let {
|
|
7
|
+
i18n,
|
|
6
8
|
layers,
|
|
7
9
|
enable_additional_layers = true,
|
|
8
10
|
enable_layers = true,
|
|
@@ -13,6 +15,7 @@
|
|
|
13
15
|
ondelete_layer,
|
|
14
16
|
ontoggle_layer_visibility
|
|
15
17
|
}: {
|
|
18
|
+
i18n: I18nFormatter;
|
|
16
19
|
layers: Writable<{
|
|
17
20
|
active_layer: string;
|
|
18
21
|
layers: {
|
|
@@ -37,6 +40,7 @@
|
|
|
37
40
|
<div class="toolbar-wrap">
|
|
38
41
|
{#if enable_layers}
|
|
39
42
|
<Layers
|
|
43
|
+
{i18n}
|
|
40
44
|
{layers}
|
|
41
45
|
{enable_additional_layers}
|
|
42
46
|
{enable_layers}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts"
|
|
1
|
+
<script module lang="ts">
|
|
2
2
|
export type Tool = "image" | "draw" | "erase" | "pan";
|
|
3
3
|
export type Subtool =
|
|
4
4
|
| "upload"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
<script lang="ts">
|
|
15
15
|
import IconButton from "./IconButton.svelte";
|
|
16
|
+
import type { I18nFormatter } from "@gradio/utils";
|
|
16
17
|
import {
|
|
17
18
|
Image,
|
|
18
19
|
Brush,
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
} from "./brush/types";
|
|
34
35
|
|
|
35
36
|
let {
|
|
37
|
+
i18n,
|
|
36
38
|
tool = "image",
|
|
37
39
|
subtool = null,
|
|
38
40
|
background = false,
|
|
@@ -51,6 +53,7 @@
|
|
|
51
53
|
ontool_change,
|
|
52
54
|
onsubtool_change
|
|
53
55
|
}: {
|
|
56
|
+
i18n: I18nFormatter;
|
|
54
57
|
tool?: Tool;
|
|
55
58
|
subtool?: Subtool;
|
|
56
59
|
background?: boolean;
|
|
@@ -113,7 +116,7 @@
|
|
|
113
116
|
{#if can_edit_image}
|
|
114
117
|
<IconButton
|
|
115
118
|
Icon={Image}
|
|
116
|
-
label="
|
|
119
|
+
label={i18n("image.image")}
|
|
117
120
|
highlight={tool === "image"}
|
|
118
121
|
onclick={(e) => handle_tool_click(e, "image")}
|
|
119
122
|
size="medium"
|
|
@@ -124,7 +127,7 @@
|
|
|
124
127
|
{#if brush_options}
|
|
125
128
|
<IconButton
|
|
126
129
|
Icon={Brush}
|
|
127
|
-
label="
|
|
130
|
+
label={i18n("image_editor.brush")}
|
|
128
131
|
onclick={(e) => handle_tool_click(e, "draw")}
|
|
129
132
|
highlight={tool === "draw"}
|
|
130
133
|
size="medium"
|
|
@@ -135,7 +138,7 @@
|
|
|
135
138
|
{#if eraser_options}
|
|
136
139
|
<IconButton
|
|
137
140
|
Icon={Erase}
|
|
138
|
-
label="
|
|
141
|
+
label={i18n("image_editor.erase")}
|
|
139
142
|
onclick={(e) => handle_tool_click(e, "erase")}
|
|
140
143
|
highlight={tool === "erase"}
|
|
141
144
|
size="medium"
|
|
@@ -155,7 +158,7 @@
|
|
|
155
158
|
{#if can_crop}
|
|
156
159
|
<IconButton
|
|
157
160
|
Icon={Crop}
|
|
158
|
-
label="
|
|
161
|
+
label={i18n("image_editor.crop")}
|
|
159
162
|
onclick={(e) => handle_subtool_click(e, "crop")}
|
|
160
163
|
highlight={subtool === "crop"}
|
|
161
164
|
size="medium"
|
|
@@ -167,7 +170,7 @@
|
|
|
167
170
|
{#if can_resize}
|
|
168
171
|
<IconButton
|
|
169
172
|
Icon={Resize}
|
|
170
|
-
label="
|
|
173
|
+
label={i18n("image_editor.resize")}
|
|
171
174
|
onclick={(e) => handle_subtool_click(e, "size")}
|
|
172
175
|
highlight={subtool === "size"}
|
|
173
176
|
size="medium"
|
|
@@ -180,7 +183,7 @@
|
|
|
180
183
|
{#if can_upload}
|
|
181
184
|
<IconButton
|
|
182
185
|
Icon={Upload}
|
|
183
|
-
label="
|
|
186
|
+
label={i18n("image_editor.upload")}
|
|
184
187
|
onclick={(e) => handle_subtool_click(e, "upload")}
|
|
185
188
|
highlight={subtool === "upload"}
|
|
186
189
|
size="medium"
|
|
@@ -192,7 +195,7 @@
|
|
|
192
195
|
{#if can_paste}
|
|
193
196
|
<IconButton
|
|
194
197
|
Icon={ImagePaste}
|
|
195
|
-
label="
|
|
198
|
+
label={i18n("image_editor.paste")}
|
|
196
199
|
onclick={(e) => handle_subtool_click(e, "paste")}
|
|
197
200
|
highlight={subtool === "paste"}
|
|
198
201
|
size="large"
|
|
@@ -204,7 +207,7 @@
|
|
|
204
207
|
{#if can_webcam}
|
|
205
208
|
<IconButton
|
|
206
209
|
Icon={Webcam}
|
|
207
|
-
label="
|
|
210
|
+
label={i18n("image_editor.webcam")}
|
|
208
211
|
onclick={(e) => handle_subtool_click(e, "webcam")}
|
|
209
212
|
highlight={subtool === "webcam"}
|
|
210
213
|
size="medium"
|
|
@@ -219,7 +222,7 @@
|
|
|
219
222
|
{#if tool === "draw" && brush_options}
|
|
220
223
|
<IconButton
|
|
221
224
|
Icon={ColorPickerSolid}
|
|
222
|
-
label="
|
|
225
|
+
label={i18n("image_editor.color")}
|
|
223
226
|
color={selected_color}
|
|
224
227
|
onclick={(e) => handle_subtool_click(e, "color")}
|
|
225
228
|
size="medium"
|
|
@@ -229,7 +232,7 @@
|
|
|
229
232
|
/>
|
|
230
233
|
<IconButton
|
|
231
234
|
Icon={BrushSize}
|
|
232
|
-
label="
|
|
235
|
+
label={i18n("image_editor.brush_size")}
|
|
233
236
|
onclick={(e) => handle_subtool_click(e, "size")}
|
|
234
237
|
highlight={subtool === "size"}
|
|
235
238
|
size="medium"
|
|
@@ -264,7 +267,7 @@
|
|
|
264
267
|
{#if tool === "erase" && eraser_options}
|
|
265
268
|
<IconButton
|
|
266
269
|
Icon={BrushSize}
|
|
267
|
-
label="
|
|
270
|
+
label={i18n("image_editor.eraser_size")}
|
|
268
271
|
onclick={(e) => handle_subtool_click(e, "size")}
|
|
269
272
|
highlight={subtool === "size"}
|
|
270
273
|
size="medium"
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export type Tool = "image" | "draw" | "erase" | "pan";
|
|
2
2
|
export type Subtool = "upload" | "paste" | "webcam" | "color" | "size" | "crop" | "remove_background" | null;
|
|
3
|
+
import type { I18nFormatter } from "@gradio/utils";
|
|
3
4
|
import type { Source, Transform } from "./types";
|
|
4
5
|
import { type Brush as BrushType, type Eraser as EraserType } from "./brush/types";
|
|
5
6
|
type $$ComponentProps = {
|
|
7
|
+
i18n: I18nFormatter;
|
|
6
8
|
tool?: Tool;
|
|
7
9
|
subtool?: Subtool;
|
|
8
10
|
background?: boolean;
|
|
@@ -41,44 +41,28 @@ export class BrushCommand {
|
|
|
41
41
|
const erase_segments = stroke_data.segments.filter((s) => s.mode === "erase");
|
|
42
42
|
if (draw_segments.length > 0) {
|
|
43
43
|
const graphics = new Graphics();
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const replacement_container = new Container();
|
|
45
|
+
const render_root = new Container();
|
|
46
|
+
replacement_container.blendMode = "none";
|
|
47
|
+
replacement_container.addChild(graphics);
|
|
48
|
+
render_root.addChild(replacement_container);
|
|
47
49
|
for (const segment of draw_segments) {
|
|
48
|
-
if (segment.opacity < alpha) {
|
|
49
|
-
alpha = segment.opacity;
|
|
50
|
-
}
|
|
51
50
|
let colorValue = 0xffffff;
|
|
52
51
|
if (segment.color.startsWith("#")) {
|
|
53
52
|
colorValue = parseInt(segment.color.replace("#", "0x"), 16);
|
|
54
53
|
}
|
|
55
54
|
graphics.setFillStyle({
|
|
56
55
|
color: colorValue,
|
|
57
|
-
alpha:
|
|
56
|
+
alpha: segment.opacity
|
|
58
57
|
});
|
|
59
58
|
this.render_segment_to_graphics(graphics, segment);
|
|
60
59
|
}
|
|
61
|
-
// we need a sprite in order to set the alpha and for that we need a texture
|
|
62
|
-
// i'm not entirely sure why other approaches didn't work
|
|
63
|
-
const alpha_sprite_texture = RenderTexture.create({
|
|
64
|
-
width: target_texture.width,
|
|
65
|
-
height: target_texture.height,
|
|
66
|
-
resolution: window.devicePixelRatio || 1
|
|
67
|
-
});
|
|
68
|
-
const alpha_sprite = new Sprite(alpha_sprite_texture);
|
|
69
|
-
this.context.app.renderer.render({
|
|
70
|
-
container: container,
|
|
71
|
-
target: alpha_sprite_texture
|
|
72
|
-
});
|
|
73
|
-
alpha_sprite.alpha = alpha;
|
|
74
60
|
this.context.app.renderer.render({
|
|
75
|
-
container:
|
|
61
|
+
container: render_root,
|
|
76
62
|
target: target_texture,
|
|
77
63
|
clear: false
|
|
78
64
|
});
|
|
79
|
-
|
|
80
|
-
alpha_sprite.destroy();
|
|
81
|
-
alpha_sprite_texture.destroy();
|
|
65
|
+
render_root.destroy({ children: true });
|
|
82
66
|
}
|
|
83
67
|
if (erase_segments.length > 0) {
|
|
84
68
|
// create a temp texture to work with
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/imageeditor",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.2",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
"pixi-filters": "^6.1.4",
|
|
12
12
|
"pixi.js": "^8.14.0",
|
|
13
13
|
"tinycolor2": "^1.6.0",
|
|
14
|
-
"@gradio/
|
|
15
|
-
"@gradio/
|
|
14
|
+
"@gradio/client": "^2.6.0",
|
|
15
|
+
"@gradio/image": "^0.28.3",
|
|
16
16
|
"@gradio/icons": "^0.16.0",
|
|
17
|
-
"@gradio/
|
|
18
|
-
"@gradio/
|
|
19
|
-
"@gradio/
|
|
20
|
-
"@gradio/
|
|
17
|
+
"@gradio/statustracker": "^0.15.3",
|
|
18
|
+
"@gradio/utils": "^0.14.1",
|
|
19
|
+
"@gradio/atoms": "^0.26.2",
|
|
20
|
+
"@gradio/upload": "^0.18.3"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@gradio/preview": "^0.
|
|
23
|
+
"@gradio/preview": "^0.17.0"
|
|
24
24
|
},
|
|
25
25
|
"main_changeset": true,
|
|
26
26
|
"main": "./Index.svelte",
|
package/shared/Controls.svelte
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { IconButton, IconButtonWrapper } from "@gradio/atoms";
|
|
3
|
+
import type { I18nFormatter } from "@gradio/utils";
|
|
3
4
|
import {
|
|
4
5
|
Check,
|
|
5
6
|
Trash,
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
Redo
|
|
12
13
|
} from "@gradio/icons";
|
|
13
14
|
let {
|
|
15
|
+
i18n,
|
|
14
16
|
can_save = false,
|
|
15
17
|
changeable = false,
|
|
16
18
|
current_zoom = 1,
|
|
@@ -29,6 +31,7 @@
|
|
|
29
31
|
onpan,
|
|
30
32
|
ondownload
|
|
31
33
|
}: {
|
|
34
|
+
i18n: I18nFormatter;
|
|
32
35
|
can_save?: boolean;
|
|
33
36
|
changeable?: boolean;
|
|
34
37
|
current_zoom?: number;
|
|
@@ -73,7 +76,7 @@
|
|
|
73
76
|
{#if enable_download}
|
|
74
77
|
<IconButton
|
|
75
78
|
Icon={Download}
|
|
76
|
-
label="
|
|
79
|
+
label={i18n("common.download")}
|
|
77
80
|
onclick={(event) => {
|
|
78
81
|
ondownload?.();
|
|
79
82
|
event.stopPropagation();
|
|
@@ -83,7 +86,7 @@
|
|
|
83
86
|
|
|
84
87
|
<IconButton
|
|
85
88
|
Icon={Pan}
|
|
86
|
-
label="
|
|
89
|
+
label={i18n("image_editor.pan")}
|
|
87
90
|
onclick={(e) => {
|
|
88
91
|
e.stopPropagation();
|
|
89
92
|
onpan?.();
|
|
@@ -97,7 +100,7 @@
|
|
|
97
100
|
|
|
98
101
|
<IconButton
|
|
99
102
|
Icon={ZoomOut}
|
|
100
|
-
label="
|
|
103
|
+
label={i18n("image_editor.zoom_out")}
|
|
101
104
|
onclick={(event) => {
|
|
102
105
|
onzoom_out?.();
|
|
103
106
|
event.stopPropagation();
|
|
@@ -105,7 +108,7 @@
|
|
|
105
108
|
/>
|
|
106
109
|
<IconButton
|
|
107
110
|
Icon={ZoomIn}
|
|
108
|
-
label="
|
|
111
|
+
label={i18n("image_editor.zoom_in")}
|
|
109
112
|
onclick={(event) => {
|
|
110
113
|
onzoom_in?.();
|
|
111
114
|
event.stopPropagation();
|
|
@@ -129,7 +132,7 @@
|
|
|
129
132
|
handle_zoom_change("fit");
|
|
130
133
|
}}
|
|
131
134
|
>
|
|
132
|
-
|
|
135
|
+
{i18n("image_editor.fit_to_screen")}
|
|
133
136
|
</button>
|
|
134
137
|
</li>
|
|
135
138
|
{#each [0.25, 0.5, 1, 2, 4] as zoom}
|
|
@@ -152,7 +155,7 @@
|
|
|
152
155
|
|
|
153
156
|
<IconButton
|
|
154
157
|
Icon={Undo}
|
|
155
|
-
label="
|
|
158
|
+
label={i18n("common.undo")}
|
|
156
159
|
onclick={(event) => {
|
|
157
160
|
onundo?.();
|
|
158
161
|
event.stopPropagation();
|
|
@@ -162,7 +165,7 @@
|
|
|
162
165
|
|
|
163
166
|
<IconButton
|
|
164
167
|
Icon={Redo}
|
|
165
|
-
label="
|
|
168
|
+
label={i18n("image_editor.redo")}
|
|
166
169
|
onclick={(event) => {
|
|
167
170
|
onredo?.();
|
|
168
171
|
event.stopPropagation();
|
|
@@ -174,7 +177,7 @@
|
|
|
174
177
|
<IconButton
|
|
175
178
|
disabled={!can_save}
|
|
176
179
|
Icon={Check}
|
|
177
|
-
label="
|
|
180
|
+
label={i18n("image_editor.save_changes")}
|
|
178
181
|
onclick={(event) => {
|
|
179
182
|
onsave?.();
|
|
180
183
|
event.stopPropagation();
|
|
@@ -185,7 +188,7 @@
|
|
|
185
188
|
|
|
186
189
|
<IconButton
|
|
187
190
|
Icon={Trash}
|
|
188
|
-
label="
|
|
191
|
+
label={i18n("image_editor.clear_canvas")}
|
|
189
192
|
onclick={(event) => {
|
|
190
193
|
onremove_image?.();
|
|
191
194
|
event.stopPropagation();
|