@gradio/image 0.26.3 → 0.28.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 +46 -0
- package/Example.svelte +9 -3
- package/Image.stories.svelte +0 -68
- package/Image.test.ts +65 -0
- package/ImageExample.stories.svelte +0 -11
- package/Index.svelte +27 -50
- package/dist/Example.svelte +9 -3
- package/dist/Example.svelte.d.ts +4 -18
- package/dist/Index.svelte +27 -50
- package/dist/shared/Image.svelte +30 -10
- package/dist/shared/Image.svelte.d.ts +9 -23
- package/dist/shared/ImagePreview.svelte +39 -23
- package/dist/shared/ImagePreview.svelte.d.ts +10 -26
- package/dist/shared/ImageUploader.svelte +104 -63
- package/dist/shared/ImageUploader.svelte.d.ts +19 -42
- package/dist/shared/Webcam.svelte +63 -47
- package/dist/shared/Webcam.svelte.d.ts +11 -27
- package/dist/shared/WebcamPermissions.svelte +2 -5
- package/dist/shared/WebcamPermissions.svelte.d.ts +5 -19
- package/dist/shared/types.d.ts +1 -1
- package/dist/shared/utils.js +7 -14
- package/package.json +7 -7
- package/shared/Image.svelte +30 -10
- package/shared/ImagePreview.svelte +39 -23
- package/shared/ImageUploader.svelte +104 -63
- package/shared/Webcam.svelte +63 -47
- package/shared/WebcamPermissions.svelte +2 -5
- package/shared/types.ts +1 -1
- package/shared/utils.ts +11 -13
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { onDestroy, onMount } from "svelte";
|
|
3
3
|
import {
|
|
4
4
|
Camera,
|
|
5
5
|
Circle,
|
|
@@ -20,33 +20,51 @@
|
|
|
20
20
|
import type { Base64File } from "./types";
|
|
21
21
|
|
|
22
22
|
let video_source: HTMLVideoElement;
|
|
23
|
-
let available_video_devices
|
|
24
|
-
let selected_device
|
|
25
|
-
|
|
26
|
-
export let stream_state: "open" | "waiting" | "closed" = "closed";
|
|
23
|
+
let available_video_devices = $state<MediaDeviceInfo[]>([]);
|
|
24
|
+
let selected_device = $state<MediaDeviceInfo | null>(null);
|
|
27
25
|
|
|
28
26
|
let canvas: HTMLCanvasElement;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
27
|
+
let {
|
|
28
|
+
stream_state = "closed",
|
|
29
|
+
streaming = false,
|
|
30
|
+
pending = false,
|
|
31
|
+
root = "",
|
|
32
|
+
stream_every = 1,
|
|
33
|
+
mode = "image",
|
|
34
|
+
mirror_webcam,
|
|
35
|
+
include_audio,
|
|
36
|
+
webcam_constraints = null,
|
|
37
|
+
i18n,
|
|
38
|
+
upload,
|
|
39
|
+
value = null,
|
|
40
|
+
time_limit = null,
|
|
41
|
+
onstream,
|
|
42
|
+
oncapture,
|
|
43
|
+
onerror,
|
|
44
|
+
onstart_recording,
|
|
45
|
+
onstop_recording,
|
|
46
|
+
onclose_stream
|
|
47
|
+
}: {
|
|
48
|
+
stream_state?: "open" | "waiting" | "closed";
|
|
49
|
+
streaming?: boolean;
|
|
50
|
+
pending?: boolean;
|
|
51
|
+
root?: string;
|
|
52
|
+
stream_every?: number;
|
|
53
|
+
mode?: "image" | "video";
|
|
54
|
+
mirror_webcam: boolean;
|
|
55
|
+
include_audio: boolean;
|
|
56
|
+
webcam_constraints?: { [key: string]: any } | null;
|
|
57
|
+
i18n: I18nFormatter;
|
|
58
|
+
upload: Client["upload"];
|
|
59
|
+
value?: FileData | null | Base64File;
|
|
60
|
+
time_limit?: number | null;
|
|
61
|
+
onstream?: (value: Blob | string) => void;
|
|
62
|
+
oncapture?: (value: FileData | Blob | null) => void;
|
|
63
|
+
onerror?: (error: string) => void;
|
|
64
|
+
onstart_recording?: () => void;
|
|
65
|
+
onstop_recording?: () => void;
|
|
66
|
+
onclose_stream?: () => void;
|
|
67
|
+
} = $props();
|
|
50
68
|
|
|
51
69
|
onMount(() => {
|
|
52
70
|
canvas = document.createElement("canvas");
|
|
@@ -101,11 +119,11 @@
|
|
|
101
119
|
});
|
|
102
120
|
|
|
103
121
|
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
|
104
|
-
|
|
122
|
+
onerror?.(i18n("image.no_webcam_support"));
|
|
105
123
|
}
|
|
106
124
|
} catch (err) {
|
|
107
125
|
if (err instanceof DOMException && err.name == "NotAllowedError") {
|
|
108
|
-
|
|
126
|
+
onerror?.(i18n("image.allow_webcam_access"));
|
|
109
127
|
} else {
|
|
110
128
|
throw err;
|
|
111
129
|
}
|
|
@@ -139,13 +157,17 @@
|
|
|
139
157
|
}
|
|
140
158
|
if (streaming) {
|
|
141
159
|
const image_data = canvas.toDataURL("image/jpeg");
|
|
142
|
-
|
|
160
|
+
onstream?.(image_data);
|
|
143
161
|
return;
|
|
144
162
|
}
|
|
145
163
|
|
|
146
164
|
canvas.toBlob(
|
|
147
165
|
(blob) => {
|
|
148
|
-
|
|
166
|
+
if (streaming) {
|
|
167
|
+
onstream?.(blob as Blob);
|
|
168
|
+
} else {
|
|
169
|
+
oncapture?.(blob);
|
|
170
|
+
}
|
|
149
171
|
},
|
|
150
172
|
`image/${streaming ? "jpeg" : "png"}`,
|
|
151
173
|
0.8
|
|
@@ -153,7 +175,7 @@
|
|
|
153
175
|
}
|
|
154
176
|
}
|
|
155
177
|
|
|
156
|
-
let recording = false;
|
|
178
|
+
let recording = $state(false);
|
|
157
179
|
let recorded_blobs: BlobPart[] = [];
|
|
158
180
|
let stream: MediaStream;
|
|
159
181
|
let mimeType: string;
|
|
@@ -174,13 +196,13 @@
|
|
|
174
196
|
let val_ = (
|
|
175
197
|
(await upload(val, root))?.filter(Boolean) as FileData[]
|
|
176
198
|
)[0];
|
|
177
|
-
|
|
178
|
-
|
|
199
|
+
oncapture?.(val_);
|
|
200
|
+
onstop_recording?.();
|
|
179
201
|
}
|
|
180
202
|
};
|
|
181
203
|
ReaderObj.readAsDataURL(video_blob);
|
|
182
204
|
} else if (typeof MediaRecorder !== "undefined") {
|
|
183
|
-
|
|
205
|
+
onstart_recording?.();
|
|
184
206
|
recorded_blobs = [];
|
|
185
207
|
let validMimeTypes = ["video/webm", "video/mp4"];
|
|
186
208
|
for (let validMimeType of validMimeTypes) {
|
|
@@ -204,7 +226,7 @@
|
|
|
204
226
|
recording = !recording;
|
|
205
227
|
}
|
|
206
228
|
|
|
207
|
-
let webcam_accessed = false;
|
|
229
|
+
let webcam_accessed = $state(false);
|
|
208
230
|
|
|
209
231
|
function record_video_or_photo({
|
|
210
232
|
destroy
|
|
@@ -222,11 +244,11 @@
|
|
|
222
244
|
}
|
|
223
245
|
|
|
224
246
|
if (!recording && stream) {
|
|
225
|
-
|
|
247
|
+
onclose_stream?.();
|
|
226
248
|
}
|
|
227
249
|
}
|
|
228
250
|
|
|
229
|
-
let options_open = false;
|
|
251
|
+
let options_open = $state(false);
|
|
230
252
|
|
|
231
253
|
export function click_outside(node: Node, cb: any): any {
|
|
232
254
|
const handle_click = (event: MouseEvent): void => {
|
|
@@ -281,12 +303,12 @@
|
|
|
281
303
|
title="grant webcam access"
|
|
282
304
|
style="height: 100%"
|
|
283
305
|
>
|
|
284
|
-
<WebcamPermissions
|
|
306
|
+
<WebcamPermissions onclick={async () => access_webcam()} />
|
|
285
307
|
</div>
|
|
286
308
|
{:else}
|
|
287
309
|
<div class="button-wrap">
|
|
288
310
|
<button
|
|
289
|
-
|
|
311
|
+
onclick={() => record_video_or_photo()}
|
|
290
312
|
aria-label={mode === "image" ? "capture photo" : "start recording"}
|
|
291
313
|
>
|
|
292
314
|
{#if mode === "video" || streaming}
|
|
@@ -321,7 +343,7 @@
|
|
|
321
343
|
{#if !recording}
|
|
322
344
|
<button
|
|
323
345
|
class="icon"
|
|
324
|
-
|
|
346
|
+
onclick={() => (options_open = true)}
|
|
325
347
|
aria-label="select input source"
|
|
326
348
|
>
|
|
327
349
|
<DropdownArrow />
|
|
@@ -333,14 +355,8 @@
|
|
|
333
355
|
class="select-wrap"
|
|
334
356
|
aria-label="select source"
|
|
335
357
|
use:click_outside={handle_click_outside}
|
|
336
|
-
|
|
358
|
+
onchange={handle_device_change}
|
|
337
359
|
>
|
|
338
|
-
<!-- <button
|
|
339
|
-
class="inset-icon"
|
|
340
|
-
on:click|stopPropagation={() => (options_open = false)}
|
|
341
|
-
>
|
|
342
|
-
<DropdownArrow />
|
|
343
|
-
</button> -->
|
|
344
360
|
{#if available_video_devices.length === 0}
|
|
345
361
|
<option value="">{i18n("common.no_devices")}</option>
|
|
346
362
|
{:else}
|
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
import type { I18nFormatter } from "@gradio/utils";
|
|
2
2
|
import { type FileData, type Client } from "@gradio/client";
|
|
3
3
|
import type { Base64File } from "./types";
|
|
4
|
-
|
|
5
|
-
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
6
|
-
$$bindings?: Bindings;
|
|
7
|
-
} & Exports;
|
|
8
|
-
(internal: unknown, props: Props & {
|
|
9
|
-
$$events?: Events;
|
|
10
|
-
$$slots?: Slots;
|
|
11
|
-
}): Exports & {
|
|
12
|
-
$set?: any;
|
|
13
|
-
$on?: any;
|
|
14
|
-
};
|
|
15
|
-
z_$$bindings?: Bindings;
|
|
16
|
-
}
|
|
17
|
-
declare const Webcam: $$__sveltets_2_IsomorphicComponent<{
|
|
4
|
+
type $$ComponentProps = {
|
|
18
5
|
stream_state?: "open" | "waiting" | "closed";
|
|
19
6
|
streaming?: boolean;
|
|
20
7
|
pending?: boolean;
|
|
@@ -30,18 +17,15 @@ declare const Webcam: $$__sveltets_2_IsomorphicComponent<{
|
|
|
30
17
|
upload: Client["upload"];
|
|
31
18
|
value?: FileData | null | Base64File;
|
|
32
19
|
time_limit?: number | null;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} & {
|
|
42
|
-
[evt: string]: CustomEvent<any>;
|
|
43
|
-
}, {}, {
|
|
20
|
+
onstream?: (value: Blob | string) => void;
|
|
21
|
+
oncapture?: (value: FileData | Blob | null) => void;
|
|
22
|
+
onerror?: (error: string) => void;
|
|
23
|
+
onstart_recording?: () => void;
|
|
24
|
+
onstop_recording?: () => void;
|
|
25
|
+
onclose_stream?: () => void;
|
|
26
|
+
};
|
|
27
|
+
declare const Webcam: import("svelte").Component<$$ComponentProps, {
|
|
44
28
|
click_outside: (node: Node, cb: any) => any;
|
|
45
|
-
},
|
|
46
|
-
type Webcam =
|
|
29
|
+
}, "">;
|
|
30
|
+
type Webcam = ReturnType<typeof Webcam>;
|
|
47
31
|
export default Webcam;
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Webcam } from "@gradio/icons";
|
|
3
|
-
import { createEventDispatcher } from "svelte";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
click: undefined;
|
|
7
|
-
}>();
|
|
4
|
+
let { onclick }: { onclick?: () => void } = $props();
|
|
8
5
|
</script>
|
|
9
6
|
|
|
10
|
-
<button style:height="100%"
|
|
7
|
+
<button style:height="100%" {onclick}>
|
|
11
8
|
<div class="wrap">
|
|
12
9
|
<span class="icon-wrap">
|
|
13
10
|
<Webcam />
|
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
$$events?: Events;
|
|
7
|
-
$$slots?: Slots;
|
|
8
|
-
}): Exports & {
|
|
9
|
-
$set?: any;
|
|
10
|
-
$on?: any;
|
|
11
|
-
};
|
|
12
|
-
z_$$bindings?: Bindings;
|
|
13
|
-
}
|
|
14
|
-
declare const WebcamPermissions: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
-
click: CustomEvent<undefined>;
|
|
16
|
-
} & {
|
|
17
|
-
[evt: string]: CustomEvent<any>;
|
|
18
|
-
}, {}, {}, string>;
|
|
19
|
-
type WebcamPermissions = InstanceType<typeof WebcamPermissions>;
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
onclick?: () => void;
|
|
3
|
+
};
|
|
4
|
+
declare const WebcamPermissions: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
5
|
+
type WebcamPermissions = ReturnType<typeof WebcamPermissions>;
|
|
20
6
|
export default WebcamPermissions;
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LoadingStatus } from "@gradio/statustracker";
|
|
1
|
+
import type { ILoadingStatus as LoadingStatus } from "@gradio/statustracker";
|
|
2
2
|
import type { FileData } from "@gradio/client";
|
|
3
3
|
import type { CustomButton } from "@gradio/utils";
|
|
4
4
|
export interface Base64File {
|
package/dist/shared/utils.js
CHANGED
|
@@ -7,20 +7,13 @@ export const get_coordinates_of_clicked_image = (evt) => {
|
|
|
7
7
|
return [NaN, NaN];
|
|
8
8
|
}
|
|
9
9
|
const imageRect = image.getBoundingClientRect();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
const displayed_width = image.naturalWidth / yScale;
|
|
20
|
-
const x_offset = (imageRect.width - displayed_width) / 2;
|
|
21
|
-
var x = Math.round((evt.clientX - imageRect.left - x_offset) * yScale);
|
|
22
|
-
var y = Math.round((evt.clientY - imageRect.top) * yScale);
|
|
23
|
-
}
|
|
10
|
+
// The image is rendered with `object-fit: scale-down`: centered, never
|
|
11
|
+
// scaled above its natural size, so empty space can appear on both axes.
|
|
12
|
+
const scale = Math.min(imageRect.width / image.naturalWidth, imageRect.height / image.naturalHeight, 1);
|
|
13
|
+
const x_offset = (imageRect.width - image.naturalWidth * scale) / 2;
|
|
14
|
+
const y_offset = (imageRect.height - image.naturalHeight * scale) / 2;
|
|
15
|
+
const x = Math.round((evt.clientX - imageRect.left - x_offset) / scale);
|
|
16
|
+
const y = Math.round((evt.clientY - imageRect.top - y_offset) / scale);
|
|
24
17
|
if (x < 0 || x >= image.naturalWidth || y < 0 || y >= image.naturalHeight) {
|
|
25
18
|
return null;
|
|
26
19
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/image",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
"cropperjs": "^2.0.1",
|
|
11
11
|
"lazy-brush": "^2.0.2",
|
|
12
12
|
"resize-observer-polyfill": "^1.5.1",
|
|
13
|
-
"@gradio/atoms": "^0.
|
|
14
|
-
"@gradio/
|
|
15
|
-
"@gradio/
|
|
16
|
-
"@gradio/
|
|
17
|
-
"@gradio/
|
|
18
|
-
"@gradio/
|
|
13
|
+
"@gradio/atoms": "^0.26.0",
|
|
14
|
+
"@gradio/statustracker": "^0.15.1",
|
|
15
|
+
"@gradio/client": "^2.3.1",
|
|
16
|
+
"@gradio/icons": "^0.16.0",
|
|
17
|
+
"@gradio/upload": "^0.18.1",
|
|
18
|
+
"@gradio/utils": "^0.13.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@gradio/preview": "^0.16.2"
|
package/shared/Image.svelte
CHANGED
|
@@ -1,24 +1,44 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { HTMLImgAttributes } from "svelte/elements";
|
|
3
|
+
|
|
2
4
|
let {
|
|
3
|
-
src,
|
|
4
|
-
restProps,
|
|
5
|
+
src = "",
|
|
6
|
+
restProps = {},
|
|
5
7
|
data_testid,
|
|
6
|
-
class_names
|
|
8
|
+
class_names = [],
|
|
9
|
+
onload,
|
|
10
|
+
...imgProps
|
|
7
11
|
}: {
|
|
8
|
-
src
|
|
9
|
-
restProps
|
|
10
|
-
data_testid
|
|
11
|
-
class_names
|
|
12
|
+
src?: string;
|
|
13
|
+
restProps?: Record<string, any>;
|
|
14
|
+
data_testid?: string;
|
|
15
|
+
class_names?: string[];
|
|
16
|
+
onload?: HTMLImgAttributes["onload"];
|
|
17
|
+
[key: string]: any;
|
|
12
18
|
} = $props();
|
|
19
|
+
|
|
20
|
+
const without_class = ({
|
|
21
|
+
class: _class,
|
|
22
|
+
...props
|
|
23
|
+
}: Record<string, any>): Record<string, any> => props;
|
|
24
|
+
|
|
25
|
+
let rest_img_props = $derived(without_class(restProps));
|
|
26
|
+
let direct_img_props = $derived(without_class(imgProps));
|
|
27
|
+
let classes = $derived(
|
|
28
|
+
[class_names.join(" "), restProps.class, imgProps.class]
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.join(" ")
|
|
31
|
+
);
|
|
13
32
|
</script>
|
|
14
33
|
|
|
15
34
|
<!-- svelte-ignore a11y-missing-attribute -->
|
|
16
35
|
<img
|
|
17
36
|
{src}
|
|
18
|
-
class={
|
|
37
|
+
class={classes}
|
|
19
38
|
data-testid={data_testid}
|
|
20
|
-
{...
|
|
21
|
-
|
|
39
|
+
{...rest_img_props}
|
|
40
|
+
{...direct_img_props}
|
|
41
|
+
{onload}
|
|
22
42
|
/>
|
|
23
43
|
|
|
24
44
|
<style>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { createEventDispatcher, onMount } from "svelte";
|
|
3
2
|
import type { SelectData } from "@gradio/utils";
|
|
4
3
|
import { uploadToHuggingFace } from "@gradio/utils";
|
|
5
4
|
import {
|
|
@@ -19,27 +18,44 @@
|
|
|
19
18
|
import type { I18nFormatter } from "@gradio/utils";
|
|
20
19
|
import type { FileData } from "@gradio/client";
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
21
|
+
let {
|
|
22
|
+
value,
|
|
23
|
+
label = undefined,
|
|
24
|
+
show_label,
|
|
25
|
+
buttons = [],
|
|
26
|
+
on_custom_button_click = null,
|
|
27
|
+
selectable = false,
|
|
28
|
+
i18n,
|
|
29
|
+
display_icon_button_wrapper_top_corner = false,
|
|
30
|
+
fullscreen = $bindable(false),
|
|
31
|
+
show_button_background = true,
|
|
32
|
+
onselect,
|
|
33
|
+
onfullscreen,
|
|
34
|
+
onshare,
|
|
35
|
+
onerror,
|
|
36
|
+
onload
|
|
37
|
+
}: {
|
|
38
|
+
value: null | FileData;
|
|
39
|
+
label?: string;
|
|
40
|
+
show_label: boolean;
|
|
41
|
+
buttons?: (string | CustomButtonType)[];
|
|
42
|
+
on_custom_button_click?: ((id: number) => void) | null;
|
|
43
|
+
selectable?: boolean;
|
|
44
|
+
i18n: I18nFormatter;
|
|
45
|
+
display_icon_button_wrapper_top_corner?: boolean;
|
|
46
|
+
fullscreen?: boolean;
|
|
47
|
+
show_button_background?: boolean;
|
|
48
|
+
onselect?: (value: SelectData) => void;
|
|
49
|
+
onfullscreen?: (fullscreen: boolean) => void;
|
|
50
|
+
onshare?: (detail: unknown) => void;
|
|
51
|
+
onerror?: (error: string) => void;
|
|
52
|
+
onload?: () => void;
|
|
53
|
+
} = $props();
|
|
38
54
|
|
|
39
55
|
const handle_click = (evt: MouseEvent): void => {
|
|
40
56
|
let coordinates = get_coordinates_of_clicked_image(evt);
|
|
41
57
|
if (coordinates) {
|
|
42
|
-
|
|
58
|
+
onselect?.({ index: coordinates, value: null });
|
|
43
59
|
}
|
|
44
60
|
};
|
|
45
61
|
|
|
@@ -66,7 +82,7 @@
|
|
|
66
82
|
{fullscreen}
|
|
67
83
|
onclick={(is_fullscreen) => {
|
|
68
84
|
fullscreen = is_fullscreen;
|
|
69
|
-
|
|
85
|
+
onfullscreen?.(is_fullscreen);
|
|
70
86
|
}}
|
|
71
87
|
/>
|
|
72
88
|
{/if}
|
|
@@ -78,8 +94,8 @@
|
|
|
78
94
|
{#if buttons.some((btn) => typeof btn === "string" && btn === "share")}
|
|
79
95
|
<ShareButton
|
|
80
96
|
{i18n}
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
onshare={(detail) => onshare?.(detail)}
|
|
98
|
+
onerror={(detail) => onerror?.(detail)}
|
|
83
99
|
formatter={async (value) => {
|
|
84
100
|
if (!value) return "";
|
|
85
101
|
let url = await uploadToHuggingFace(value, "url");
|
|
@@ -89,12 +105,12 @@
|
|
|
89
105
|
/>
|
|
90
106
|
{/if}
|
|
91
107
|
</IconButtonWrapper>
|
|
92
|
-
<button
|
|
108
|
+
<button onclick={handle_click}>
|
|
93
109
|
<div class:selectable class="image-frame">
|
|
94
110
|
<Image
|
|
95
111
|
src={value.url}
|
|
96
112
|
restProps={{ loading: "lazy", alt: "" }}
|
|
97
|
-
|
|
113
|
+
{onload}
|
|
98
114
|
/>
|
|
99
115
|
</div>
|
|
100
116
|
</button>
|