@gradio/gallery 0.16.2 → 0.17.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/CHANGELOG.md +33 -0
- package/Gallery.stories.svelte +52 -20
- package/Index.svelte +178 -30
- package/dist/Index.svelte +178 -30
- package/dist/Index.svelte.d.ts +21 -2
- package/dist/shared/Gallery.svelte +43 -10
- package/dist/shared/Gallery.svelte.d.ts +3 -1
- package/dist/shared/utils.d.ts +2 -1
- package/dist/shared/utils.js +16 -0
- package/dist/types.d.ts +2 -0
- package/package.json +9 -9
- package/shared/Gallery.svelte +43 -10
- package/shared/utils.ts +21 -1
- package/types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @gradio/gallery
|
|
2
2
|
|
|
3
|
+
## 0.17.1
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- [#12865](https://github.com/gradio-app/gradio/pull/12865) [`db7ab39`](https://github.com/gradio-app/gradio/commit/db7ab39c7e6ea3d1a2f0ce1991b5dbc0483b1e27) - Fix Gallery fullscreen button not working in preview mode. Thanks @veeceey!
|
|
8
|
+
- [#12866](https://github.com/gradio-app/gradio/pull/12866) [`9810396`](https://github.com/gradio-app/gradio/commit/981039647a4212c649a33dc4b56a4714980519eb) - Fix Gallery preview=True parameter not working on initial load. Thanks @veeceey!
|
|
9
|
+
|
|
10
|
+
### Dependency updates
|
|
11
|
+
|
|
12
|
+
- @gradio/utils@0.11.3
|
|
13
|
+
- @gradio/atoms@0.22.0
|
|
14
|
+
- @gradio/statustracker@0.12.4
|
|
15
|
+
- @gradio/upload@0.17.6
|
|
16
|
+
- @gradio/video@0.20.3
|
|
17
|
+
- @gradio/file@0.14.3
|
|
18
|
+
- @gradio/image@0.25.3
|
|
19
|
+
|
|
20
|
+
## 0.17.0
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
- [#12804](https://github.com/gradio-app/gradio/pull/12804) [`fc366b4`](https://github.com/gradio-app/gradio/commit/fc366b485325c900b0c2b85ba05b7d23a81a0dee) - Allow webcam uploads and clipboard paste for gallery. Thanks @freddyaboulton!
|
|
25
|
+
|
|
26
|
+
### Dependency updates
|
|
27
|
+
|
|
28
|
+
- @gradio/atoms@0.21.0
|
|
29
|
+
- @gradio/client@2.0.4
|
|
30
|
+
- @gradio/statustracker@0.12.3
|
|
31
|
+
- @gradio/file@0.14.2
|
|
32
|
+
- @gradio/image@0.25.2
|
|
33
|
+
- @gradio/upload@0.17.5
|
|
34
|
+
- @gradio/video@0.20.2
|
|
35
|
+
|
|
3
36
|
## 0.16.2
|
|
4
37
|
|
|
5
38
|
### Fixes
|
package/Gallery.stories.svelte
CHANGED
|
@@ -154,13 +154,16 @@
|
|
|
154
154
|
args={{
|
|
155
155
|
label: "My Cheetah Gallery",
|
|
156
156
|
show_label: true,
|
|
157
|
-
buttons: ["fullscreen", "download", "share"]
|
|
157
|
+
buttons: ["fullscreen", "download", "share"],
|
|
158
|
+
sources: ["upload"]
|
|
158
159
|
}}
|
|
159
160
|
play={async ({ canvasElement }) => {
|
|
160
161
|
const canvas = within(canvasElement);
|
|
161
|
-
const image = canvas.
|
|
162
|
+
const image = await canvas.findByLabelText("Thumbnail 1 of 8");
|
|
162
163
|
await userEvent.click(image);
|
|
163
|
-
const expand_btn = canvas.
|
|
164
|
+
const expand_btn = await canvas.findByRole("button", {
|
|
165
|
+
name: "Fullscreen"
|
|
166
|
+
});
|
|
164
167
|
await userEvent.click(expand_btn);
|
|
165
168
|
}}
|
|
166
169
|
>
|
|
@@ -171,7 +174,12 @@
|
|
|
171
174
|
|
|
172
175
|
<Story
|
|
173
176
|
name="Gallery without label"
|
|
174
|
-
args={{
|
|
177
|
+
args={{
|
|
178
|
+
label: "My Cheetah Gallery",
|
|
179
|
+
show_label: false,
|
|
180
|
+
buttons: [],
|
|
181
|
+
sources: ["upload"]
|
|
182
|
+
}}
|
|
175
183
|
>
|
|
176
184
|
{#snippet template(args)}
|
|
177
185
|
<Gallery {...wrapProps({ value: galleryValue, ...args })} />
|
|
@@ -185,7 +193,8 @@
|
|
|
185
193
|
show_label: true,
|
|
186
194
|
rows: 3,
|
|
187
195
|
columns: 3,
|
|
188
|
-
buttons: []
|
|
196
|
+
buttons: [],
|
|
197
|
+
sources: ["upload"]
|
|
189
198
|
}}
|
|
190
199
|
>
|
|
191
200
|
{#snippet template(args)}
|
|
@@ -199,7 +208,8 @@
|
|
|
199
208
|
label: "My Cheetah Gallery",
|
|
200
209
|
show_label: true,
|
|
201
210
|
columns: 4,
|
|
202
|
-
buttons: []
|
|
211
|
+
buttons: [],
|
|
212
|
+
sources: ["upload"]
|
|
203
213
|
}}
|
|
204
214
|
>
|
|
205
215
|
{#snippet template(args)}
|
|
@@ -209,7 +219,12 @@
|
|
|
209
219
|
|
|
210
220
|
<Story
|
|
211
221
|
name="Gallery with height=600"
|
|
212
|
-
args={{
|
|
222
|
+
args={{
|
|
223
|
+
label: "My Cheetah Gallery",
|
|
224
|
+
height: 600,
|
|
225
|
+
buttons: [],
|
|
226
|
+
sources: ["upload"]
|
|
227
|
+
}}
|
|
213
228
|
>
|
|
214
229
|
{#snippet template(args)}
|
|
215
230
|
<Gallery {...wrapProps({ value: galleryValue, ...args })} />
|
|
@@ -222,7 +237,8 @@
|
|
|
222
237
|
label: "My Cheetah Gallery",
|
|
223
238
|
show_label: true,
|
|
224
239
|
allow_preview: false,
|
|
225
|
-
buttons: []
|
|
240
|
+
buttons: [],
|
|
241
|
+
sources: ["upload"]
|
|
226
242
|
}}
|
|
227
243
|
>
|
|
228
244
|
{#snippet template(args)}
|
|
@@ -236,7 +252,8 @@
|
|
|
236
252
|
label: "My Cheetah Gallery",
|
|
237
253
|
show_label: true,
|
|
238
254
|
preview: true,
|
|
239
|
-
buttons: []
|
|
255
|
+
buttons: [],
|
|
256
|
+
sources: ["upload"]
|
|
240
257
|
}}
|
|
241
258
|
>
|
|
242
259
|
{#snippet template(args)}
|
|
@@ -250,7 +267,8 @@
|
|
|
250
267
|
label: "My Cheetah Gallery",
|
|
251
268
|
show_label: true,
|
|
252
269
|
object_fit: "scale-down",
|
|
253
|
-
buttons: []
|
|
270
|
+
buttons: [],
|
|
271
|
+
sources: ["upload"]
|
|
254
272
|
}}
|
|
255
273
|
>
|
|
256
274
|
{#snippet template(args)}
|
|
@@ -264,7 +282,8 @@
|
|
|
264
282
|
label: "My Cheetah Gallery",
|
|
265
283
|
show_label: true,
|
|
266
284
|
object_fit: "contain",
|
|
267
|
-
buttons: []
|
|
285
|
+
buttons: [],
|
|
286
|
+
sources: ["upload"]
|
|
268
287
|
}}
|
|
269
288
|
>
|
|
270
289
|
{#snippet template(args)}
|
|
@@ -278,7 +297,8 @@
|
|
|
278
297
|
label: "My Cheetah Gallery",
|
|
279
298
|
show_label: true,
|
|
280
299
|
object_fit: "cover",
|
|
281
|
-
buttons: []
|
|
300
|
+
buttons: [],
|
|
301
|
+
sources: ["upload"]
|
|
282
302
|
}}
|
|
283
303
|
>
|
|
284
304
|
{#snippet template(args)}
|
|
@@ -292,7 +312,8 @@
|
|
|
292
312
|
label: "My Cheetah Gallery",
|
|
293
313
|
show_label: true,
|
|
294
314
|
object_fit: "none",
|
|
295
|
-
buttons: []
|
|
315
|
+
buttons: [],
|
|
316
|
+
sources: ["upload"]
|
|
296
317
|
}}
|
|
297
318
|
>
|
|
298
319
|
{#snippet template(args)}
|
|
@@ -306,7 +327,8 @@
|
|
|
306
327
|
label: "My Cheetah Gallery",
|
|
307
328
|
show_label: true,
|
|
308
329
|
object_fit: "fill",
|
|
309
|
-
buttons: []
|
|
330
|
+
buttons: [],
|
|
331
|
+
sources: ["upload"]
|
|
310
332
|
}}
|
|
311
333
|
>
|
|
312
334
|
{#snippet template(args)}
|
|
@@ -316,7 +338,12 @@
|
|
|
316
338
|
|
|
317
339
|
<Story
|
|
318
340
|
name="Gallery with share button"
|
|
319
|
-
args={{
|
|
341
|
+
args={{
|
|
342
|
+
label: "My Cheetah Gallery",
|
|
343
|
+
show_label: true,
|
|
344
|
+
buttons: ["share"],
|
|
345
|
+
sources: ["upload"]
|
|
346
|
+
}}
|
|
320
347
|
>
|
|
321
348
|
{#snippet template(args)}
|
|
322
349
|
<Gallery {...wrapProps({ value: galleryValue, ...args })} />
|
|
@@ -331,7 +358,8 @@
|
|
|
331
358
|
rows: 2,
|
|
332
359
|
columns: 2,
|
|
333
360
|
height: 400,
|
|
334
|
-
buttons: []
|
|
361
|
+
buttons: [],
|
|
362
|
+
sources: ["upload"]
|
|
335
363
|
}}
|
|
336
364
|
>
|
|
337
365
|
{#snippet template(args)}
|
|
@@ -347,7 +375,8 @@
|
|
|
347
375
|
rows: 2,
|
|
348
376
|
columns: 2,
|
|
349
377
|
height: 200,
|
|
350
|
-
buttons: []
|
|
378
|
+
buttons: [],
|
|
379
|
+
sources: ["upload"]
|
|
351
380
|
}}
|
|
352
381
|
>
|
|
353
382
|
{#snippet template(args)}
|
|
@@ -361,7 +390,8 @@
|
|
|
361
390
|
label: "My Cheetah Gallery",
|
|
362
391
|
rows: 2,
|
|
363
392
|
height: 400,
|
|
364
|
-
buttons: ["download"]
|
|
393
|
+
buttons: ["download"],
|
|
394
|
+
sources: ["upload"]
|
|
365
395
|
}}
|
|
366
396
|
>
|
|
367
397
|
{#snippet template(args)}
|
|
@@ -375,7 +405,8 @@
|
|
|
375
405
|
label: "My Cheetah Gallery",
|
|
376
406
|
columns: 10,
|
|
377
407
|
buttons: ["download"],
|
|
378
|
-
fit_columns: false
|
|
408
|
+
fit_columns: false,
|
|
409
|
+
sources: ["upload"]
|
|
379
410
|
}}
|
|
380
411
|
>
|
|
381
412
|
{#snippet template(args)}
|
|
@@ -389,7 +420,8 @@
|
|
|
389
420
|
label: "My Cheetah Gallery",
|
|
390
421
|
columns: 10,
|
|
391
422
|
buttons: ["download"],
|
|
392
|
-
fit_columns: true
|
|
423
|
+
fit_columns: true,
|
|
424
|
+
sources: ["upload"]
|
|
393
425
|
}}
|
|
394
426
|
>
|
|
395
427
|
{#snippet template(args)}
|
package/Index.svelte
CHANGED
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
<script lang="ts">
|
|
7
7
|
import { tick } from "svelte";
|
|
8
8
|
import type { FileData } from "@gradio/client";
|
|
9
|
-
import { Block, UploadText } from "@gradio/atoms";
|
|
9
|
+
import { Block, UploadText, SelectSource } from "@gradio/atoms";
|
|
10
10
|
import Gallery from "./shared/Gallery.svelte";
|
|
11
11
|
import { StatusTracker } from "@gradio/statustracker";
|
|
12
12
|
import { Gradio } from "@gradio/utils";
|
|
13
13
|
import { BaseFileUpload } from "@gradio/file";
|
|
14
|
+
import { Webcam } from "@gradio/image";
|
|
14
15
|
import type { GalleryProps, GalleryEvents, GalleryData } from "./types";
|
|
16
|
+
import { handle_save } from "./shared/utils";
|
|
15
17
|
|
|
16
18
|
let upload_promise = $state<Promise<(FileData | null)[]>>();
|
|
17
19
|
|
|
@@ -29,15 +31,12 @@
|
|
|
29
31
|
|
|
30
32
|
const props = $props();
|
|
31
33
|
const gradio = new GalleryGradio<GalleryEvents, GalleryProps>(props, {
|
|
32
|
-
selected_index: null
|
|
34
|
+
selected_index: null,
|
|
35
|
+
file_types: ["image", "video"]
|
|
33
36
|
});
|
|
34
37
|
|
|
35
38
|
let fullscreen = $state(false);
|
|
36
39
|
|
|
37
|
-
let no_value = $derived(
|
|
38
|
-
gradio.props.value === null ? true : gradio.props.value.length === 0
|
|
39
|
-
);
|
|
40
|
-
|
|
41
40
|
function handle_delete(
|
|
42
41
|
event: CustomEvent<{ file: FileData; index: number }>
|
|
43
42
|
): void {
|
|
@@ -71,6 +70,82 @@
|
|
|
71
70
|
: { image: x, caption: null }
|
|
72
71
|
);
|
|
73
72
|
}
|
|
73
|
+
|
|
74
|
+
let upload_input: BaseFileUpload;
|
|
75
|
+
|
|
76
|
+
let active_source = $state<
|
|
77
|
+
"upload" | "webcam" | "webcam-video" | "clipboard" | null
|
|
78
|
+
>(gradio.props.sources ? gradio.props.sources[0] : "upload");
|
|
79
|
+
|
|
80
|
+
let no_value = $derived(
|
|
81
|
+
gradio.props.value === null ? true : gradio.props.value.length === 0
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
let sources = $derived.by(() => {
|
|
85
|
+
if (
|
|
86
|
+
gradio.props.file_types?.includes("video") &&
|
|
87
|
+
gradio.props.sources.includes("webcam")
|
|
88
|
+
) {
|
|
89
|
+
return gradio.props.sources.concat(["webcam-video"]);
|
|
90
|
+
} else {
|
|
91
|
+
return gradio.props.sources;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
async function paste_clipboard(): Promise<void> {
|
|
96
|
+
navigator.clipboard.read().then(async (items) => {
|
|
97
|
+
let file: File | null = null;
|
|
98
|
+
for (let i = 0; i < items.length; i++) {
|
|
99
|
+
const type = items[i].types.find((t) =>
|
|
100
|
+
(gradio.props.file_types || ["image"]).some((ft) =>
|
|
101
|
+
t.startsWith(ft + "/")
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
if (type) {
|
|
105
|
+
const blob = await items[i].getType(type);
|
|
106
|
+
file = new File([blob], `clipboard.${type.replace("image/", "")}`);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (file) {
|
|
111
|
+
const f = await handle_save(
|
|
112
|
+
file,
|
|
113
|
+
(f) => gradio.shared.client.upload(f, gradio.shared.root),
|
|
114
|
+
"clipboard_upload"
|
|
115
|
+
);
|
|
116
|
+
const processed_files = await process_upload_files(f);
|
|
117
|
+
gradio.props.value?.push(...processed_files);
|
|
118
|
+
gradio.dispatch("change", gradio.props.value);
|
|
119
|
+
active_source = null;
|
|
120
|
+
} else {
|
|
121
|
+
gradio.dispatch("warning", "No image or video found in clipboard");
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function handle_select_source(
|
|
127
|
+
source: "upload" | "webcam" | "clipboard"
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
switch (source) {
|
|
130
|
+
case "clipboard":
|
|
131
|
+
await paste_clipboard();
|
|
132
|
+
break;
|
|
133
|
+
default:
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function onsource_change(
|
|
139
|
+
source: "upload" | "webcam" | "webcam-video" | "clipboard"
|
|
140
|
+
): Promise<void> {
|
|
141
|
+
await tick();
|
|
142
|
+
if (source === "clipboard") {
|
|
143
|
+
await paste_clipboard();
|
|
144
|
+
} else {
|
|
145
|
+
active_source = source;
|
|
146
|
+
no_value = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
74
149
|
</script>
|
|
75
150
|
|
|
76
151
|
<Block
|
|
@@ -96,31 +171,96 @@
|
|
|
96
171
|
gradio.dispatch("clear_status", gradio.shared.loading_status)}
|
|
97
172
|
/>
|
|
98
173
|
{#if gradio.shared.interactive && no_value}
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
max_file_size={gradio.shared.max_file_size}
|
|
105
|
-
file_count={"multiple"}
|
|
106
|
-
file_types={gradio.props.file_types}
|
|
107
|
-
i18n={gradio.i18n}
|
|
108
|
-
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
109
|
-
stream_handler={(...args) => gradio.shared.client.stream(...args)}
|
|
110
|
-
onupload={async (event_data) => {
|
|
111
|
-
const files = Array.isArray(event_data) ? event_data : [event_data];
|
|
112
|
-
gradio.props.value = await process_upload_files(files);
|
|
113
|
-
gradio.dispatch("upload", gradio.props.value);
|
|
114
|
-
gradio.dispatch("change", gradio.props.value);
|
|
115
|
-
}}
|
|
116
|
-
onerror={(event_data) => {
|
|
117
|
-
gradio.shared.loading_status = gradio.shared.loading_status || {};
|
|
118
|
-
gradio.shared.loading_status.status = "error";
|
|
119
|
-
gradio.dispatch("error", event_data);
|
|
120
|
-
}}
|
|
174
|
+
<div
|
|
175
|
+
class={!gradio.props.value ||
|
|
176
|
+
(active_source && active_source.includes("webcam"))
|
|
177
|
+
? "hidden-upload-input"
|
|
178
|
+
: ""}
|
|
121
179
|
>
|
|
122
|
-
<
|
|
123
|
-
|
|
180
|
+
<BaseFileUpload
|
|
181
|
+
bind:upload_promise
|
|
182
|
+
bind:this={upload_input}
|
|
183
|
+
value={null}
|
|
184
|
+
root={gradio.shared.root}
|
|
185
|
+
label={gradio.shared.label}
|
|
186
|
+
max_file_size={gradio.shared.max_file_size}
|
|
187
|
+
file_count={"multiple"}
|
|
188
|
+
file_types={gradio.props.file_types}
|
|
189
|
+
i18n={gradio.i18n}
|
|
190
|
+
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
191
|
+
stream_handler={(...args) => gradio.shared.client.stream(...args)}
|
|
192
|
+
onupload={async (e) => {
|
|
193
|
+
const files = Array.isArray(e) ? e : [e];
|
|
194
|
+
gradio.props.value = await process_upload_files(files);
|
|
195
|
+
active_source = null;
|
|
196
|
+
gradio.dispatch("upload", gradio.props.value);
|
|
197
|
+
gradio.dispatch("change", gradio.props.value);
|
|
198
|
+
}}
|
|
199
|
+
onerror={({ detail }) => {
|
|
200
|
+
gradio.shared.loading_status = gradio.shared.loading_status || {};
|
|
201
|
+
gradio.shared.loading_status.status = "error";
|
|
202
|
+
gradio.dispatch("error", detail);
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
<UploadText i18n={gradio.i18n} type="gallery" />
|
|
206
|
+
</BaseFileUpload>
|
|
207
|
+
</div>
|
|
208
|
+
{#if active_source === "webcam"}
|
|
209
|
+
<Webcam
|
|
210
|
+
root={gradio.shared.root}
|
|
211
|
+
value={null}
|
|
212
|
+
on:capture={async (e) => {
|
|
213
|
+
const f = await handle_save(
|
|
214
|
+
e.detail,
|
|
215
|
+
(f) => gradio.shared.client.upload(f, gradio.shared.root),
|
|
216
|
+
"webcam_upload"
|
|
217
|
+
);
|
|
218
|
+
const processed_files = await process_upload_files(f);
|
|
219
|
+
gradio.props.value?.push(...processed_files);
|
|
220
|
+
active_source = null;
|
|
221
|
+
gradio.dispatch("change", gradio.props.value);
|
|
222
|
+
}}
|
|
223
|
+
on:error
|
|
224
|
+
on:drag
|
|
225
|
+
on:close_stream
|
|
226
|
+
mirror_webcam={true}
|
|
227
|
+
streaming={false}
|
|
228
|
+
mode="image"
|
|
229
|
+
include_audio={false}
|
|
230
|
+
i18n={gradio.i18n}
|
|
231
|
+
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
232
|
+
/>
|
|
233
|
+
{:else if active_source === "webcam-video"}
|
|
234
|
+
<Webcam
|
|
235
|
+
root={gradio.shared.root}
|
|
236
|
+
value={null}
|
|
237
|
+
on:capture={async (e) => {
|
|
238
|
+
const f = { ...e.detail };
|
|
239
|
+
f.mime_type = "video/webm";
|
|
240
|
+
const processed_files = await process_upload_files([f]);
|
|
241
|
+
gradio.props.value?.push(...processed_files);
|
|
242
|
+
active_source = null;
|
|
243
|
+
gradio.dispatch("change", gradio.props.value);
|
|
244
|
+
}}
|
|
245
|
+
on:error
|
|
246
|
+
on:drag
|
|
247
|
+
on:close_stream
|
|
248
|
+
mirror_webcam={true}
|
|
249
|
+
streaming={false}
|
|
250
|
+
mode="video"
|
|
251
|
+
include_audio={false}
|
|
252
|
+
i18n={gradio.i18n}
|
|
253
|
+
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
254
|
+
/>
|
|
255
|
+
{/if}
|
|
256
|
+
{#if sources.length > 1 || sources.includes("clipboard")}
|
|
257
|
+
<SelectSource
|
|
258
|
+
{sources}
|
|
259
|
+
bind:active_source
|
|
260
|
+
handle_clear={() => gradio.dispatch("clear")}
|
|
261
|
+
handle_select={handle_select_source}
|
|
262
|
+
/>
|
|
263
|
+
{/if}
|
|
124
264
|
{:else}
|
|
125
265
|
<Gallery
|
|
126
266
|
onchange={() => gradio.dispatch("change")}
|
|
@@ -145,6 +285,8 @@
|
|
|
145
285
|
gradio.dispatch("upload", new_value);
|
|
146
286
|
gradio.dispatch("change", gradio.props.value);
|
|
147
287
|
}}
|
|
288
|
+
{sources}
|
|
289
|
+
{onsource_change}
|
|
148
290
|
label={gradio.shared.label}
|
|
149
291
|
show_label={gradio.shared.show_label}
|
|
150
292
|
columns={gradio.props.columns}
|
|
@@ -181,3 +323,9 @@
|
|
|
181
323
|
/>
|
|
182
324
|
{/if}
|
|
183
325
|
</Block>
|
|
326
|
+
|
|
327
|
+
<style>
|
|
328
|
+
.hidden-upload-input {
|
|
329
|
+
display: none;
|
|
330
|
+
}
|
|
331
|
+
</style>
|
package/dist/Index.svelte
CHANGED
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
<script lang="ts">
|
|
7
7
|
import { tick } from "svelte";
|
|
8
8
|
import type { FileData } from "@gradio/client";
|
|
9
|
-
import { Block, UploadText } from "@gradio/atoms";
|
|
9
|
+
import { Block, UploadText, SelectSource } from "@gradio/atoms";
|
|
10
10
|
import Gallery from "./shared/Gallery.svelte";
|
|
11
11
|
import { StatusTracker } from "@gradio/statustracker";
|
|
12
12
|
import { Gradio } from "@gradio/utils";
|
|
13
13
|
import { BaseFileUpload } from "@gradio/file";
|
|
14
|
+
import { Webcam } from "@gradio/image";
|
|
14
15
|
import type { GalleryProps, GalleryEvents, GalleryData } from "./types";
|
|
16
|
+
import { handle_save } from "./shared/utils";
|
|
15
17
|
|
|
16
18
|
let upload_promise = $state<Promise<(FileData | null)[]>>();
|
|
17
19
|
|
|
@@ -29,15 +31,12 @@
|
|
|
29
31
|
|
|
30
32
|
const props = $props();
|
|
31
33
|
const gradio = new GalleryGradio<GalleryEvents, GalleryProps>(props, {
|
|
32
|
-
selected_index: null
|
|
34
|
+
selected_index: null,
|
|
35
|
+
file_types: ["image", "video"]
|
|
33
36
|
});
|
|
34
37
|
|
|
35
38
|
let fullscreen = $state(false);
|
|
36
39
|
|
|
37
|
-
let no_value = $derived(
|
|
38
|
-
gradio.props.value === null ? true : gradio.props.value.length === 0
|
|
39
|
-
);
|
|
40
|
-
|
|
41
40
|
function handle_delete(
|
|
42
41
|
event: CustomEvent<{ file: FileData; index: number }>
|
|
43
42
|
): void {
|
|
@@ -71,6 +70,82 @@
|
|
|
71
70
|
: { image: x, caption: null }
|
|
72
71
|
);
|
|
73
72
|
}
|
|
73
|
+
|
|
74
|
+
let upload_input: BaseFileUpload;
|
|
75
|
+
|
|
76
|
+
let active_source = $state<
|
|
77
|
+
"upload" | "webcam" | "webcam-video" | "clipboard" | null
|
|
78
|
+
>(gradio.props.sources ? gradio.props.sources[0] : "upload");
|
|
79
|
+
|
|
80
|
+
let no_value = $derived(
|
|
81
|
+
gradio.props.value === null ? true : gradio.props.value.length === 0
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
let sources = $derived.by(() => {
|
|
85
|
+
if (
|
|
86
|
+
gradio.props.file_types?.includes("video") &&
|
|
87
|
+
gradio.props.sources.includes("webcam")
|
|
88
|
+
) {
|
|
89
|
+
return gradio.props.sources.concat(["webcam-video"]);
|
|
90
|
+
} else {
|
|
91
|
+
return gradio.props.sources;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
async function paste_clipboard(): Promise<void> {
|
|
96
|
+
navigator.clipboard.read().then(async (items) => {
|
|
97
|
+
let file: File | null = null;
|
|
98
|
+
for (let i = 0; i < items.length; i++) {
|
|
99
|
+
const type = items[i].types.find((t) =>
|
|
100
|
+
(gradio.props.file_types || ["image"]).some((ft) =>
|
|
101
|
+
t.startsWith(ft + "/")
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
if (type) {
|
|
105
|
+
const blob = await items[i].getType(type);
|
|
106
|
+
file = new File([blob], `clipboard.${type.replace("image/", "")}`);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (file) {
|
|
111
|
+
const f = await handle_save(
|
|
112
|
+
file,
|
|
113
|
+
(f) => gradio.shared.client.upload(f, gradio.shared.root),
|
|
114
|
+
"clipboard_upload"
|
|
115
|
+
);
|
|
116
|
+
const processed_files = await process_upload_files(f);
|
|
117
|
+
gradio.props.value?.push(...processed_files);
|
|
118
|
+
gradio.dispatch("change", gradio.props.value);
|
|
119
|
+
active_source = null;
|
|
120
|
+
} else {
|
|
121
|
+
gradio.dispatch("warning", "No image or video found in clipboard");
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function handle_select_source(
|
|
127
|
+
source: "upload" | "webcam" | "clipboard"
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
switch (source) {
|
|
130
|
+
case "clipboard":
|
|
131
|
+
await paste_clipboard();
|
|
132
|
+
break;
|
|
133
|
+
default:
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function onsource_change(
|
|
139
|
+
source: "upload" | "webcam" | "webcam-video" | "clipboard"
|
|
140
|
+
): Promise<void> {
|
|
141
|
+
await tick();
|
|
142
|
+
if (source === "clipboard") {
|
|
143
|
+
await paste_clipboard();
|
|
144
|
+
} else {
|
|
145
|
+
active_source = source;
|
|
146
|
+
no_value = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
74
149
|
</script>
|
|
75
150
|
|
|
76
151
|
<Block
|
|
@@ -96,31 +171,96 @@
|
|
|
96
171
|
gradio.dispatch("clear_status", gradio.shared.loading_status)}
|
|
97
172
|
/>
|
|
98
173
|
{#if gradio.shared.interactive && no_value}
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
max_file_size={gradio.shared.max_file_size}
|
|
105
|
-
file_count={"multiple"}
|
|
106
|
-
file_types={gradio.props.file_types}
|
|
107
|
-
i18n={gradio.i18n}
|
|
108
|
-
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
109
|
-
stream_handler={(...args) => gradio.shared.client.stream(...args)}
|
|
110
|
-
onupload={async (event_data) => {
|
|
111
|
-
const files = Array.isArray(event_data) ? event_data : [event_data];
|
|
112
|
-
gradio.props.value = await process_upload_files(files);
|
|
113
|
-
gradio.dispatch("upload", gradio.props.value);
|
|
114
|
-
gradio.dispatch("change", gradio.props.value);
|
|
115
|
-
}}
|
|
116
|
-
onerror={(event_data) => {
|
|
117
|
-
gradio.shared.loading_status = gradio.shared.loading_status || {};
|
|
118
|
-
gradio.shared.loading_status.status = "error";
|
|
119
|
-
gradio.dispatch("error", event_data);
|
|
120
|
-
}}
|
|
174
|
+
<div
|
|
175
|
+
class={!gradio.props.value ||
|
|
176
|
+
(active_source && active_source.includes("webcam"))
|
|
177
|
+
? "hidden-upload-input"
|
|
178
|
+
: ""}
|
|
121
179
|
>
|
|
122
|
-
<
|
|
123
|
-
|
|
180
|
+
<BaseFileUpload
|
|
181
|
+
bind:upload_promise
|
|
182
|
+
bind:this={upload_input}
|
|
183
|
+
value={null}
|
|
184
|
+
root={gradio.shared.root}
|
|
185
|
+
label={gradio.shared.label}
|
|
186
|
+
max_file_size={gradio.shared.max_file_size}
|
|
187
|
+
file_count={"multiple"}
|
|
188
|
+
file_types={gradio.props.file_types}
|
|
189
|
+
i18n={gradio.i18n}
|
|
190
|
+
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
191
|
+
stream_handler={(...args) => gradio.shared.client.stream(...args)}
|
|
192
|
+
onupload={async (e) => {
|
|
193
|
+
const files = Array.isArray(e) ? e : [e];
|
|
194
|
+
gradio.props.value = await process_upload_files(files);
|
|
195
|
+
active_source = null;
|
|
196
|
+
gradio.dispatch("upload", gradio.props.value);
|
|
197
|
+
gradio.dispatch("change", gradio.props.value);
|
|
198
|
+
}}
|
|
199
|
+
onerror={({ detail }) => {
|
|
200
|
+
gradio.shared.loading_status = gradio.shared.loading_status || {};
|
|
201
|
+
gradio.shared.loading_status.status = "error";
|
|
202
|
+
gradio.dispatch("error", detail);
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
<UploadText i18n={gradio.i18n} type="gallery" />
|
|
206
|
+
</BaseFileUpload>
|
|
207
|
+
</div>
|
|
208
|
+
{#if active_source === "webcam"}
|
|
209
|
+
<Webcam
|
|
210
|
+
root={gradio.shared.root}
|
|
211
|
+
value={null}
|
|
212
|
+
on:capture={async (e) => {
|
|
213
|
+
const f = await handle_save(
|
|
214
|
+
e.detail,
|
|
215
|
+
(f) => gradio.shared.client.upload(f, gradio.shared.root),
|
|
216
|
+
"webcam_upload"
|
|
217
|
+
);
|
|
218
|
+
const processed_files = await process_upload_files(f);
|
|
219
|
+
gradio.props.value?.push(...processed_files);
|
|
220
|
+
active_source = null;
|
|
221
|
+
gradio.dispatch("change", gradio.props.value);
|
|
222
|
+
}}
|
|
223
|
+
on:error
|
|
224
|
+
on:drag
|
|
225
|
+
on:close_stream
|
|
226
|
+
mirror_webcam={true}
|
|
227
|
+
streaming={false}
|
|
228
|
+
mode="image"
|
|
229
|
+
include_audio={false}
|
|
230
|
+
i18n={gradio.i18n}
|
|
231
|
+
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
232
|
+
/>
|
|
233
|
+
{:else if active_source === "webcam-video"}
|
|
234
|
+
<Webcam
|
|
235
|
+
root={gradio.shared.root}
|
|
236
|
+
value={null}
|
|
237
|
+
on:capture={async (e) => {
|
|
238
|
+
const f = { ...e.detail };
|
|
239
|
+
f.mime_type = "video/webm";
|
|
240
|
+
const processed_files = await process_upload_files([f]);
|
|
241
|
+
gradio.props.value?.push(...processed_files);
|
|
242
|
+
active_source = null;
|
|
243
|
+
gradio.dispatch("change", gradio.props.value);
|
|
244
|
+
}}
|
|
245
|
+
on:error
|
|
246
|
+
on:drag
|
|
247
|
+
on:close_stream
|
|
248
|
+
mirror_webcam={true}
|
|
249
|
+
streaming={false}
|
|
250
|
+
mode="video"
|
|
251
|
+
include_audio={false}
|
|
252
|
+
i18n={gradio.i18n}
|
|
253
|
+
upload={(...args) => gradio.shared.client.upload(...args)}
|
|
254
|
+
/>
|
|
255
|
+
{/if}
|
|
256
|
+
{#if sources.length > 1 || sources.includes("clipboard")}
|
|
257
|
+
<SelectSource
|
|
258
|
+
{sources}
|
|
259
|
+
bind:active_source
|
|
260
|
+
handle_clear={() => gradio.dispatch("clear")}
|
|
261
|
+
handle_select={handle_select_source}
|
|
262
|
+
/>
|
|
263
|
+
{/if}
|
|
124
264
|
{:else}
|
|
125
265
|
<Gallery
|
|
126
266
|
onchange={() => gradio.dispatch("change")}
|
|
@@ -145,6 +285,8 @@
|
|
|
145
285
|
gradio.dispatch("upload", new_value);
|
|
146
286
|
gradio.dispatch("change", gradio.props.value);
|
|
147
287
|
}}
|
|
288
|
+
{sources}
|
|
289
|
+
{onsource_change}
|
|
148
290
|
label={gradio.shared.label}
|
|
149
291
|
show_label={gradio.shared.show_label}
|
|
150
292
|
columns={gradio.props.columns}
|
|
@@ -181,3 +323,9 @@
|
|
|
181
323
|
/>
|
|
182
324
|
{/if}
|
|
183
325
|
</Block>
|
|
326
|
+
|
|
327
|
+
<style>
|
|
328
|
+
.hidden-upload-input {
|
|
329
|
+
display: none;
|
|
330
|
+
}
|
|
331
|
+
</style>
|
package/dist/Index.svelte.d.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
export { default as BaseGallery } from "./shared/Gallery.svelte";
|
|
2
2
|
export { default as BaseExample } from "./Example.svelte";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
4
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
5
|
+
$$bindings?: Bindings;
|
|
6
|
+
} & Exports;
|
|
7
|
+
(internal: unknown, props: Props & {
|
|
8
|
+
$$events?: Events;
|
|
9
|
+
$$slots?: Slots;
|
|
10
|
+
}): Exports & {
|
|
11
|
+
$set?: any;
|
|
12
|
+
$on?: any;
|
|
13
|
+
};
|
|
14
|
+
z_$$bindings?: Bindings;
|
|
15
|
+
}
|
|
16
|
+
declare const Index: $$__sveltets_2_IsomorphicComponent<$$ComponentProps, {
|
|
17
|
+
error: CustomEvent<string>;
|
|
18
|
+
drag: CustomEvent<any>;
|
|
19
|
+
close_stream: CustomEvent<undefined>;
|
|
20
|
+
} & {
|
|
21
|
+
[evt: string]: CustomEvent<any>;
|
|
22
|
+
}, {}, {}, "">;
|
|
23
|
+
type Index = InstanceType<typeof Index>;
|
|
5
24
|
export default Index;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
IconButtonWrapper,
|
|
8
8
|
FullscreenButton
|
|
9
9
|
} from "@gradio/atoms";
|
|
10
|
-
import type
|
|
10
|
+
import { type CustomButton as CustomButtonType } from "@gradio/utils";
|
|
11
11
|
import { ModifyUpload, Upload as UploadComponent } from "@gradio/upload";
|
|
12
12
|
import { Image } from "@gradio/image/shared";
|
|
13
13
|
import { Video } from "@gradio/video/shared";
|
|
@@ -21,13 +21,15 @@
|
|
|
21
21
|
Image as ImageIcon,
|
|
22
22
|
Clear,
|
|
23
23
|
Play,
|
|
24
|
-
Upload as UploadIcon
|
|
24
|
+
Upload as UploadIcon,
|
|
25
|
+
Webcam,
|
|
26
|
+
Video as VideoIcon,
|
|
27
|
+
ImagePaste
|
|
25
28
|
} from "@gradio/icons";
|
|
26
29
|
import { FileData } from "@gradio/client";
|
|
27
30
|
import type { Client } from "@gradio/client";
|
|
28
31
|
import { format_gallery_for_sharing } from "./utils";
|
|
29
32
|
import type { I18nFormatter } from "@gradio/utils";
|
|
30
|
-
import { on } from "svelte/events";
|
|
31
33
|
|
|
32
34
|
type GalleryData = GalleryImage | GalleryVideo;
|
|
33
35
|
|
|
@@ -51,6 +53,7 @@
|
|
|
51
53
|
fullscreen = false,
|
|
52
54
|
root = "",
|
|
53
55
|
file_types = ["image", "video"],
|
|
56
|
+
sources,
|
|
54
57
|
max_file_size = null,
|
|
55
58
|
upload,
|
|
56
59
|
stream_handler,
|
|
@@ -66,7 +69,8 @@
|
|
|
66
69
|
onpreview_close = () => {},
|
|
67
70
|
onfullscreen = (data) => {},
|
|
68
71
|
ondelete = () => {},
|
|
69
|
-
onupload = () => {}
|
|
72
|
+
onupload = () => {},
|
|
73
|
+
onsource_change = () => {}
|
|
70
74
|
}: {
|
|
71
75
|
show_label: boolean;
|
|
72
76
|
label: string;
|
|
@@ -87,6 +91,7 @@
|
|
|
87
91
|
fullscreen: boolean;
|
|
88
92
|
root: string;
|
|
89
93
|
file_types: string[] | null;
|
|
94
|
+
sources: ("upload" | "webcam" | "clipboard" | "webcam-video")[];
|
|
90
95
|
max_file_size: number | null;
|
|
91
96
|
upload: Client["upload"] | undefined;
|
|
92
97
|
stream_handler: Client["stream"] | undefined;
|
|
@@ -103,6 +108,7 @@
|
|
|
103
108
|
onfullscreen: (data: any) => void;
|
|
104
109
|
ondelete: (data: any) => void;
|
|
105
110
|
onupload: (data: FileData | FileData[]) => void;
|
|
111
|
+
onsource_change: (source: string) => void;
|
|
106
112
|
} = $props();
|
|
107
113
|
|
|
108
114
|
let upload_promise: Promise<any> | null = null;
|
|
@@ -151,9 +157,9 @@
|
|
|
151
157
|
);
|
|
152
158
|
|
|
153
159
|
let prev_value: GalleryData[] | null = $state(value);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
if (selected_index == null && preview && value?.length) {
|
|
161
|
+
selected_index = 0;
|
|
162
|
+
}
|
|
157
163
|
let old_selected_index: number | null = $state(selected_index);
|
|
158
164
|
|
|
159
165
|
$effect(() => {
|
|
@@ -189,8 +195,6 @@
|
|
|
189
195
|
() => ((selected_index ?? 0) + 1) % (resolved_value?.length ?? 0)
|
|
190
196
|
);
|
|
191
197
|
|
|
192
|
-
$inspect("selected_index", selected_index);
|
|
193
|
-
|
|
194
198
|
function handle_preview_click(event: MouseEvent): void {
|
|
195
199
|
const element = event.target as HTMLElement;
|
|
196
200
|
const x = event.offsetX;
|
|
@@ -412,7 +416,8 @@
|
|
|
412
416
|
{#if show_fullscreen_button}
|
|
413
417
|
<FullscreenButton
|
|
414
418
|
{fullscreen}
|
|
415
|
-
|
|
419
|
+
onclick={(is_fullscreen) =>
|
|
420
|
+
onfullscreen({ detail: is_fullscreen })}
|
|
416
421
|
/>
|
|
417
422
|
{/if}
|
|
418
423
|
|
|
@@ -541,6 +546,7 @@
|
|
|
541
546
|
{i18n}
|
|
542
547
|
onclear={() => {
|
|
543
548
|
value = [];
|
|
549
|
+
onsource_change("upload");
|
|
544
550
|
onclear();
|
|
545
551
|
}}
|
|
546
552
|
>
|
|
@@ -564,6 +570,33 @@
|
|
|
564
570
|
/>
|
|
565
571
|
</IconButton>
|
|
566
572
|
{/if}
|
|
573
|
+
{#if sources.includes("webcam")}
|
|
574
|
+
<IconButton
|
|
575
|
+
Icon={Webcam}
|
|
576
|
+
label={i18n("common.webcam")}
|
|
577
|
+
onclick={() => {
|
|
578
|
+
onsource_change("webcam");
|
|
579
|
+
}}
|
|
580
|
+
/>
|
|
581
|
+
{/if}
|
|
582
|
+
{#if sources.includes("webcam-video")}
|
|
583
|
+
<IconButton
|
|
584
|
+
Icon={VideoIcon}
|
|
585
|
+
label={i18n("common.video")}
|
|
586
|
+
onclick={() => {
|
|
587
|
+
onsource_change("webcam-video");
|
|
588
|
+
}}
|
|
589
|
+
/>
|
|
590
|
+
{/if}
|
|
591
|
+
{#if sources.includes("clipboard")}
|
|
592
|
+
<IconButton
|
|
593
|
+
label={i18n("upload_text.paste_clipboard")}
|
|
594
|
+
onclick={() => {
|
|
595
|
+
onsource_change("clipboard");
|
|
596
|
+
}}
|
|
597
|
+
Icon={ImagePaste}
|
|
598
|
+
/>
|
|
599
|
+
{/if}
|
|
567
600
|
</ModifyUpload>
|
|
568
601
|
{/if}
|
|
569
602
|
<div
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CustomButton as CustomButtonType } from "@gradio/utils";
|
|
2
2
|
import type { GalleryImage, GalleryVideo } from "../types";
|
|
3
3
|
import { FileData } from "@gradio/client";
|
|
4
4
|
import type { Client } from "@gradio/client";
|
|
@@ -24,6 +24,7 @@ type $$ComponentProps = {
|
|
|
24
24
|
fullscreen: boolean;
|
|
25
25
|
root: string;
|
|
26
26
|
file_types: string[] | null;
|
|
27
|
+
sources: ("upload" | "webcam" | "clipboard" | "webcam-video")[];
|
|
27
28
|
max_file_size: number | null;
|
|
28
29
|
upload: Client["upload"] | undefined;
|
|
29
30
|
stream_handler: Client["stream"] | undefined;
|
|
@@ -40,6 +41,7 @@ type $$ComponentProps = {
|
|
|
40
41
|
onfullscreen: (data: any) => void;
|
|
41
42
|
ondelete: (data: any) => void;
|
|
42
43
|
onupload: (data: FileData | FileData[]) => void;
|
|
44
|
+
onsource_change: (source: string) => void;
|
|
43
45
|
};
|
|
44
46
|
declare const Gallery: import("svelte").Component<$$ComponentProps, {}, "value" | "selected_index">;
|
|
45
47
|
type Gallery = ReturnType<typeof Gallery>;
|
package/dist/shared/utils.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FileData, Client } from "@gradio/client";
|
|
2
2
|
export declare function format_gallery_for_sharing(value: [FileData, string | null][] | null): Promise<string>;
|
|
3
|
+
export declare function handle_save(img_blob: Blob, upload: Client["upload"], filename?: string): Promise<FileData[]>;
|
package/dist/shared/utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { uploadToHuggingFace } from "@gradio/utils";
|
|
2
|
+
import { FileData, Client } from "@gradio/client";
|
|
2
3
|
export async function format_gallery_for_sharing(value) {
|
|
3
4
|
if (!value)
|
|
4
5
|
return "";
|
|
@@ -11,3 +12,18 @@ export async function format_gallery_for_sharing(value) {
|
|
|
11
12
|
.map((url) => `<img src="${url}" style="height: 400px" />`)
|
|
12
13
|
.join("")}</div>`;
|
|
13
14
|
}
|
|
15
|
+
export async function handle_save(img_blob, upload, filename = "uploaded_file") {
|
|
16
|
+
const ext = img_blob.type.split("/")[1] || "png";
|
|
17
|
+
const f_ = new File([img_blob], `${filename}.${ext}`);
|
|
18
|
+
const files = [
|
|
19
|
+
new FileData({
|
|
20
|
+
path: f_.name,
|
|
21
|
+
orig_name: f_.name,
|
|
22
|
+
blob: f_,
|
|
23
|
+
size: f_.size,
|
|
24
|
+
mime_type: f_.type,
|
|
25
|
+
is_stream: false
|
|
26
|
+
})
|
|
27
|
+
];
|
|
28
|
+
return (await upload(...files));
|
|
29
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface GalleryProps {
|
|
|
22
22
|
buttons: (string | CustomButton)[];
|
|
23
23
|
type: "numpy" | "pil" | "filepath";
|
|
24
24
|
fit_columns: boolean;
|
|
25
|
+
sources: ("upload" | "webcam-video" | "webcam" | "clipboard")[];
|
|
25
26
|
}
|
|
26
27
|
export interface GalleryEvents {
|
|
27
28
|
change: GalleryData[] | null;
|
|
@@ -39,4 +40,5 @@ export interface GalleryEvents {
|
|
|
39
40
|
custom_button_click: {
|
|
40
41
|
id: number;
|
|
41
42
|
};
|
|
43
|
+
warning: string;
|
|
42
44
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/gallery",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
"private": false,
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"dequal": "^2.0.3",
|
|
11
|
-
"@gradio/atoms": "^0.
|
|
12
|
-
"@gradio/client": "^2.0.
|
|
11
|
+
"@gradio/atoms": "^0.22.0",
|
|
12
|
+
"@gradio/client": "^2.0.4",
|
|
13
|
+
"@gradio/file": "^0.14.3",
|
|
14
|
+
"@gradio/image": "^0.25.3",
|
|
13
15
|
"@gradio/icons": "^0.15.1",
|
|
14
|
-
"@gradio/
|
|
15
|
-
"@gradio/
|
|
16
|
-
"@gradio/
|
|
17
|
-
"@gradio/
|
|
18
|
-
"@gradio/utils": "^0.11.2",
|
|
19
|
-
"@gradio/video": "^0.20.1"
|
|
16
|
+
"@gradio/statustracker": "^0.12.4",
|
|
17
|
+
"@gradio/upload": "^0.17.6",
|
|
18
|
+
"@gradio/utils": "^0.11.3",
|
|
19
|
+
"@gradio/video": "^0.20.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@gradio/preview": "^0.15.2"
|
package/shared/Gallery.svelte
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
IconButtonWrapper,
|
|
8
8
|
FullscreenButton
|
|
9
9
|
} from "@gradio/atoms";
|
|
10
|
-
import type
|
|
10
|
+
import { type CustomButton as CustomButtonType } from "@gradio/utils";
|
|
11
11
|
import { ModifyUpload, Upload as UploadComponent } from "@gradio/upload";
|
|
12
12
|
import { Image } from "@gradio/image/shared";
|
|
13
13
|
import { Video } from "@gradio/video/shared";
|
|
@@ -21,13 +21,15 @@
|
|
|
21
21
|
Image as ImageIcon,
|
|
22
22
|
Clear,
|
|
23
23
|
Play,
|
|
24
|
-
Upload as UploadIcon
|
|
24
|
+
Upload as UploadIcon,
|
|
25
|
+
Webcam,
|
|
26
|
+
Video as VideoIcon,
|
|
27
|
+
ImagePaste
|
|
25
28
|
} from "@gradio/icons";
|
|
26
29
|
import { FileData } from "@gradio/client";
|
|
27
30
|
import type { Client } from "@gradio/client";
|
|
28
31
|
import { format_gallery_for_sharing } from "./utils";
|
|
29
32
|
import type { I18nFormatter } from "@gradio/utils";
|
|
30
|
-
import { on } from "svelte/events";
|
|
31
33
|
|
|
32
34
|
type GalleryData = GalleryImage | GalleryVideo;
|
|
33
35
|
|
|
@@ -51,6 +53,7 @@
|
|
|
51
53
|
fullscreen = false,
|
|
52
54
|
root = "",
|
|
53
55
|
file_types = ["image", "video"],
|
|
56
|
+
sources,
|
|
54
57
|
max_file_size = null,
|
|
55
58
|
upload,
|
|
56
59
|
stream_handler,
|
|
@@ -66,7 +69,8 @@
|
|
|
66
69
|
onpreview_close = () => {},
|
|
67
70
|
onfullscreen = (data) => {},
|
|
68
71
|
ondelete = () => {},
|
|
69
|
-
onupload = () => {}
|
|
72
|
+
onupload = () => {},
|
|
73
|
+
onsource_change = () => {}
|
|
70
74
|
}: {
|
|
71
75
|
show_label: boolean;
|
|
72
76
|
label: string;
|
|
@@ -87,6 +91,7 @@
|
|
|
87
91
|
fullscreen: boolean;
|
|
88
92
|
root: string;
|
|
89
93
|
file_types: string[] | null;
|
|
94
|
+
sources: ("upload" | "webcam" | "clipboard" | "webcam-video")[];
|
|
90
95
|
max_file_size: number | null;
|
|
91
96
|
upload: Client["upload"] | undefined;
|
|
92
97
|
stream_handler: Client["stream"] | undefined;
|
|
@@ -103,6 +108,7 @@
|
|
|
103
108
|
onfullscreen: (data: any) => void;
|
|
104
109
|
ondelete: (data: any) => void;
|
|
105
110
|
onupload: (data: FileData | FileData[]) => void;
|
|
111
|
+
onsource_change: (source: string) => void;
|
|
106
112
|
} = $props();
|
|
107
113
|
|
|
108
114
|
let upload_promise: Promise<any> | null = null;
|
|
@@ -151,9 +157,9 @@
|
|
|
151
157
|
);
|
|
152
158
|
|
|
153
159
|
let prev_value: GalleryData[] | null = $state(value);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
if (selected_index == null && preview && value?.length) {
|
|
161
|
+
selected_index = 0;
|
|
162
|
+
}
|
|
157
163
|
let old_selected_index: number | null = $state(selected_index);
|
|
158
164
|
|
|
159
165
|
$effect(() => {
|
|
@@ -189,8 +195,6 @@
|
|
|
189
195
|
() => ((selected_index ?? 0) + 1) % (resolved_value?.length ?? 0)
|
|
190
196
|
);
|
|
191
197
|
|
|
192
|
-
$inspect("selected_index", selected_index);
|
|
193
|
-
|
|
194
198
|
function handle_preview_click(event: MouseEvent): void {
|
|
195
199
|
const element = event.target as HTMLElement;
|
|
196
200
|
const x = event.offsetX;
|
|
@@ -412,7 +416,8 @@
|
|
|
412
416
|
{#if show_fullscreen_button}
|
|
413
417
|
<FullscreenButton
|
|
414
418
|
{fullscreen}
|
|
415
|
-
|
|
419
|
+
onclick={(is_fullscreen) =>
|
|
420
|
+
onfullscreen({ detail: is_fullscreen })}
|
|
416
421
|
/>
|
|
417
422
|
{/if}
|
|
418
423
|
|
|
@@ -541,6 +546,7 @@
|
|
|
541
546
|
{i18n}
|
|
542
547
|
onclear={() => {
|
|
543
548
|
value = [];
|
|
549
|
+
onsource_change("upload");
|
|
544
550
|
onclear();
|
|
545
551
|
}}
|
|
546
552
|
>
|
|
@@ -564,6 +570,33 @@
|
|
|
564
570
|
/>
|
|
565
571
|
</IconButton>
|
|
566
572
|
{/if}
|
|
573
|
+
{#if sources.includes("webcam")}
|
|
574
|
+
<IconButton
|
|
575
|
+
Icon={Webcam}
|
|
576
|
+
label={i18n("common.webcam")}
|
|
577
|
+
onclick={() => {
|
|
578
|
+
onsource_change("webcam");
|
|
579
|
+
}}
|
|
580
|
+
/>
|
|
581
|
+
{/if}
|
|
582
|
+
{#if sources.includes("webcam-video")}
|
|
583
|
+
<IconButton
|
|
584
|
+
Icon={VideoIcon}
|
|
585
|
+
label={i18n("common.video")}
|
|
586
|
+
onclick={() => {
|
|
587
|
+
onsource_change("webcam-video");
|
|
588
|
+
}}
|
|
589
|
+
/>
|
|
590
|
+
{/if}
|
|
591
|
+
{#if sources.includes("clipboard")}
|
|
592
|
+
<IconButton
|
|
593
|
+
label={i18n("upload_text.paste_clipboard")}
|
|
594
|
+
onclick={() => {
|
|
595
|
+
onsource_change("clipboard");
|
|
596
|
+
}}
|
|
597
|
+
Icon={ImagePaste}
|
|
598
|
+
/>
|
|
599
|
+
{/if}
|
|
567
600
|
</ModifyUpload>
|
|
568
601
|
{/if}
|
|
569
602
|
<div
|
package/shared/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { uploadToHuggingFace } from "@gradio/utils";
|
|
2
|
-
import
|
|
2
|
+
import { FileData, Client } from "@gradio/client";
|
|
3
3
|
|
|
4
4
|
export async function format_gallery_for_sharing(
|
|
5
5
|
value: [FileData, string | null][] | null
|
|
@@ -16,3 +16,23 @@ export async function format_gallery_for_sharing(
|
|
|
16
16
|
.map((url) => `<img src="${url}" style="height: 400px" />`)
|
|
17
17
|
.join("")}</div>`;
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
export async function handle_save(
|
|
21
|
+
img_blob: Blob,
|
|
22
|
+
upload: Client["upload"],
|
|
23
|
+
filename: string = "uploaded_file"
|
|
24
|
+
): Promise<FileData[]> {
|
|
25
|
+
const ext = img_blob.type.split("/")[1] || "png";
|
|
26
|
+
const f_ = new File([img_blob], `${filename}.${ext}`);
|
|
27
|
+
const files = [
|
|
28
|
+
new FileData({
|
|
29
|
+
path: f_.name,
|
|
30
|
+
orig_name: f_.name,
|
|
31
|
+
blob: f_,
|
|
32
|
+
size: f_.size,
|
|
33
|
+
mime_type: f_.type,
|
|
34
|
+
is_stream: false
|
|
35
|
+
})
|
|
36
|
+
];
|
|
37
|
+
return (await upload(...files)) as FileData[];
|
|
38
|
+
}
|
package/types.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface GalleryProps {
|
|
|
26
26
|
buttons: (string | CustomButton)[];
|
|
27
27
|
type: "numpy" | "pil" | "filepath";
|
|
28
28
|
fit_columns: boolean;
|
|
29
|
+
sources: ("upload" | "webcam-video" | "webcam" | "clipboard")[];
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export interface GalleryEvents {
|
|
@@ -39,4 +40,5 @@ export interface GalleryEvents {
|
|
|
39
40
|
share: any;
|
|
40
41
|
error: any;
|
|
41
42
|
custom_button_click: { id: number };
|
|
43
|
+
warning: string;
|
|
42
44
|
}
|