@gradio/image 0.18.0 → 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 +32 -0
- package/dist/shared/ImagePreview.svelte +10 -35
- package/dist/shared/ImagePreview.svelte.d.ts +2 -1
- package/dist/shared/ImageUploader.svelte +17 -25
- package/dist/shared/Webcam.svelte +8 -2
- package/package.json +8 -8
- package/shared/ImagePreview.svelte +11 -37
- package/shared/ImageUploader.svelte +17 -27
- package/shared/Webcam.svelte +8 -2
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,37 @@
|
|
1
1
|
# @gradio/image
|
2
2
|
|
3
|
+
## 0.20.0
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
- [#10192](https://github.com/gradio-app/gradio/pull/10192) [`4fc7fb7`](https://github.com/gradio-app/gradio/commit/4fc7fb777c42af537e4af612423fa44029657d41) - Ensure components can be remounted with their previous data. Thanks @pngwn!
|
8
|
+
|
9
|
+
### Fixes
|
10
|
+
|
11
|
+
- [#10269](https://github.com/gradio-app/gradio/pull/10269) [`890eaa3`](https://github.com/gradio-app/gradio/commit/890eaa3a9e53dab5bcb16c5d017ae0470109b8fb) - Allow displaying SVG images securely in `gr.Image` and `gr.Gallery` components. Thanks @abidlabs!
|
12
|
+
|
13
|
+
### Dependency updates
|
14
|
+
|
15
|
+
- @gradio/atoms@0.13.0
|
16
|
+
- @gradio/utils@0.10.0
|
17
|
+
- @gradio/upload@0.14.4
|
18
|
+
- @gradio/client@1.9.0
|
19
|
+
- @gradio/icons@0.9.0
|
20
|
+
- @gradio/statustracker@0.10.0
|
21
|
+
- @gradio/wasm@0.16.0
|
22
|
+
|
23
|
+
## 0.19.0
|
24
|
+
|
25
|
+
### Features
|
26
|
+
|
27
|
+
- [#10098](https://github.com/gradio-app/gradio/pull/10098) [`9a6ce6f`](https://github.com/gradio-app/gradio/commit/9a6ce6f6b089d94c06da0b8620f28967f39f8383) - Refactor full screen logic to be reusable. Thanks @hannahblair!
|
28
|
+
|
29
|
+
### Dependency updates
|
30
|
+
|
31
|
+
- @gradio/statustracker@0.9.7
|
32
|
+
- @gradio/upload@0.14.3
|
33
|
+
- @gradio/atoms@0.12.0
|
34
|
+
|
3
35
|
## 0.18.0
|
4
36
|
|
5
37
|
### Features
|
@@ -5,15 +5,13 @@ import {
|
|
5
5
|
Empty,
|
6
6
|
IconButton,
|
7
7
|
ShareButton,
|
8
|
-
IconButtonWrapper
|
8
|
+
IconButtonWrapper,
|
9
|
+
FullscreenButton
|
9
10
|
} from "@gradio/atoms";
|
10
|
-
import { Download } from "@gradio/icons";
|
11
|
+
import { Download, Image as ImageIcon } from "@gradio/icons";
|
11
12
|
import { get_coordinates_of_clicked_image } from "./utils";
|
12
|
-
import Image from "
|
13
|
+
import { Image } from "@gradio/image/shared";
|
13
14
|
import { DownloadLink } from "@gradio/wasm/svelte";
|
14
|
-
import { Maximize, Minimize } from "@gradio/icons";
|
15
|
-
import { Image as ImageIcon } from "@gradio/icons";
|
16
|
-
import {} from "@gradio/client";
|
17
15
|
export let value;
|
18
16
|
export let label = void 0;
|
19
17
|
export let show_label;
|
@@ -22,6 +20,7 @@ export let selectable = false;
|
|
22
20
|
export let show_share_button = false;
|
23
21
|
export let i18n;
|
24
22
|
export let show_fullscreen_button = true;
|
23
|
+
export let display_icon_button_wrapper_top_corner = false;
|
25
24
|
const dispatch = createEventDispatcher();
|
26
25
|
const handle_click = (evt) => {
|
27
26
|
let coordinates = get_coordinates_of_clicked_image(evt);
|
@@ -29,21 +28,7 @@ const handle_click = (evt) => {
|
|
29
28
|
dispatch("select", { index: coordinates, value: null });
|
30
29
|
}
|
31
30
|
};
|
32
|
-
let is_full_screen = false;
|
33
31
|
let image_container;
|
34
|
-
onMount(() => {
|
35
|
-
document.addEventListener("fullscreenchange", () => {
|
36
|
-
is_full_screen = !!document.fullscreenElement;
|
37
|
-
});
|
38
|
-
});
|
39
|
-
const toggle_full_screen = async () => {
|
40
|
-
if (!is_full_screen) {
|
41
|
-
await image_container.requestFullscreen();
|
42
|
-
} else {
|
43
|
-
await document.exitFullscreen();
|
44
|
-
is_full_screen = !is_full_screen;
|
45
|
-
}
|
46
|
-
};
|
47
32
|
</script>
|
48
33
|
|
49
34
|
<BlockLabel
|
@@ -55,21 +40,11 @@ const toggle_full_screen = async () => {
|
|
55
40
|
<Empty unpadded_box={true} size="large"><ImageIcon /></Empty>
|
56
41
|
{:else}
|
57
42
|
<div class="image-container" bind:this={image_container}>
|
58
|
-
<IconButtonWrapper
|
59
|
-
{
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
on:click={toggle_full_screen}
|
64
|
-
/>
|
65
|
-
{/if}
|
66
|
-
|
67
|
-
{#if is_full_screen && show_fullscreen_button}
|
68
|
-
<IconButton
|
69
|
-
Icon={Minimize}
|
70
|
-
label={is_full_screen ? "Exit full screen" : "View in full screen"}
|
71
|
-
on:click={toggle_full_screen}
|
72
|
-
/>
|
43
|
+
<IconButtonWrapper
|
44
|
+
display_top_corner={display_icon_button_wrapper_top_corner}
|
45
|
+
>
|
46
|
+
{#if show_fullscreen_button}
|
47
|
+
<FullscreenButton container={image_container} />
|
73
48
|
{/if}
|
74
49
|
|
75
50
|
{#if show_download_button}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
2
2
|
import type { SelectData } from "@gradio/utils";
|
3
|
-
import { type FileData } from "@gradio/client";
|
4
3
|
import type { I18nFormatter } from "@gradio/utils";
|
4
|
+
import type { FileData } from "@gradio/client";
|
5
5
|
declare const __propDef: {
|
6
6
|
props: {
|
7
7
|
value: null | FileData;
|
@@ -12,6 +12,7 @@ declare const __propDef: {
|
|
12
12
|
show_share_button?: boolean | undefined;
|
13
13
|
i18n: I18nFormatter;
|
14
14
|
show_fullscreen_button?: boolean | undefined;
|
15
|
+
display_icon_button_wrapper_top_corner?: boolean | undefined;
|
15
16
|
};
|
16
17
|
events: {
|
17
18
|
share: CustomEvent<import("@gradio/utils").ShareData>;
|
@@ -1,6 +1,7 @@
|
|
1
1
|
<script>import { createEventDispatcher, tick } from "svelte";
|
2
2
|
import { BlockLabel, IconButtonWrapper, IconButton } from "@gradio/atoms";
|
3
|
-
import { Clear, Image as ImageIcon
|
3
|
+
import { Clear, Image as ImageIcon } from "@gradio/icons";
|
4
|
+
import { FullscreenButton } from "@gradio/atoms";
|
4
5
|
import {
|
5
6
|
} from "@gradio/utils";
|
6
7
|
import { get_coordinates_of_clicked_image } from "./utils";
|
@@ -30,9 +31,20 @@ let upload_input;
|
|
30
31
|
export let uploading = false;
|
31
32
|
export let active_source = null;
|
32
33
|
export let webcam_constraints = void 0;
|
33
|
-
function handle_upload({
|
34
|
+
async function handle_upload({
|
35
|
+
detail
|
36
|
+
}) {
|
34
37
|
if (!streaming) {
|
35
|
-
|
38
|
+
if (detail.path?.toLowerCase().endsWith(".svg") && detail.url) {
|
39
|
+
const response = await fetch(detail.url);
|
40
|
+
const svgContent = await response.text();
|
41
|
+
value = {
|
42
|
+
...detail,
|
43
|
+
url: `data:image/svg+xml,${encodeURIComponent(svgContent)}`
|
44
|
+
};
|
45
|
+
} else {
|
46
|
+
value = detail;
|
47
|
+
}
|
36
48
|
dispatch("upload");
|
37
49
|
}
|
38
50
|
}
|
@@ -88,16 +100,7 @@ async function handle_select_source(source) {
|
|
88
100
|
break;
|
89
101
|
}
|
90
102
|
}
|
91
|
-
let is_full_screen = false;
|
92
103
|
let image_container;
|
93
|
-
const toggle_full_screen = async () => {
|
94
|
-
if (!is_full_screen) {
|
95
|
-
await image_container.requestFullscreen();
|
96
|
-
} else {
|
97
|
-
await document.exitFullscreen();
|
98
|
-
is_full_screen = !is_full_screen;
|
99
|
-
}
|
100
|
-
};
|
101
104
|
</script>
|
102
105
|
|
103
106
|
<BlockLabel {show_label} Icon={ImageIcon} label={label || "Image"} />
|
@@ -105,19 +108,8 @@ const toggle_full_screen = async () => {
|
|
105
108
|
<div data-testid="image" class="image-container" bind:this={image_container}>
|
106
109
|
<IconButtonWrapper>
|
107
110
|
{#if value?.url && !active_streaming}
|
108
|
-
{#if
|
109
|
-
<
|
110
|
-
Icon={Maximize}
|
111
|
-
label={is_full_screen ? "Exit full screen" : "View in full screen"}
|
112
|
-
on:click={toggle_full_screen}
|
113
|
-
/>
|
114
|
-
{/if}
|
115
|
-
{#if is_full_screen && show_fullscreen_button}
|
116
|
-
<IconButton
|
117
|
-
Icon={Minimize}
|
118
|
-
label={is_full_screen ? "Exit full screen" : "View in full screen"}
|
119
|
-
on:click={toggle_full_screen}
|
120
|
-
/>
|
111
|
+
{#if show_fullscreen_button}
|
112
|
+
<FullscreenButton container={image_container} />
|
121
113
|
{/if}
|
122
114
|
<IconButton
|
123
115
|
Icon={Clear}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<script>import { createEventDispatcher, onMount } from "svelte";
|
1
|
+
<script>import { createEventDispatcher, onDestroy, onMount } from "svelte";
|
2
2
|
import {
|
3
3
|
Camera,
|
4
4
|
Circle,
|
@@ -152,7 +152,7 @@ function take_recording() {
|
|
152
152
|
}
|
153
153
|
};
|
154
154
|
ReaderObj.readAsDataURL(video_blob);
|
155
|
-
} else {
|
155
|
+
} else if (typeof MediaRecorder !== "undefined") {
|
156
156
|
dispatch("start_recording");
|
157
157
|
recorded_blobs = [];
|
158
158
|
let validMimeTypes = ["video/webm", "video/mp4"];
|
@@ -216,6 +216,12 @@ function handle_click_outside(event) {
|
|
216
216
|
event.stopPropagation();
|
217
217
|
options_open = false;
|
218
218
|
}
|
219
|
+
onDestroy(() => {
|
220
|
+
if (typeof window === "undefined")
|
221
|
+
return;
|
222
|
+
record_video_or_photo();
|
223
|
+
stream?.getTracks().forEach((track) => track.stop());
|
224
|
+
});
|
219
225
|
</script>
|
220
226
|
|
221
227
|
<div class="wrap">
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@gradio/image",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.20.0",
|
4
4
|
"description": "Gradio UI packages",
|
5
5
|
"type": "module",
|
6
6
|
"author": "",
|
@@ -10,13 +10,13 @@
|
|
10
10
|
"cropperjs": "^1.5.12",
|
11
11
|
"lazy-brush": "^1.0.1",
|
12
12
|
"resize-observer-polyfill": "^1.5.1",
|
13
|
-
"@gradio/atoms": "^0.
|
14
|
-
"@gradio/
|
15
|
-
"@gradio/
|
16
|
-
"@gradio/
|
17
|
-
"@gradio/
|
18
|
-
"@gradio/
|
19
|
-
"@gradio/
|
13
|
+
"@gradio/atoms": "^0.13.0",
|
14
|
+
"@gradio/client": "^1.9.0",
|
15
|
+
"@gradio/statustracker": "^0.10.0",
|
16
|
+
"@gradio/icons": "^0.9.0",
|
17
|
+
"@gradio/utils": "^0.10.0",
|
18
|
+
"@gradio/wasm": "^0.16.0",
|
19
|
+
"@gradio/upload": "^0.14.4"
|
20
20
|
},
|
21
21
|
"devDependencies": {
|
22
22
|
"@gradio/preview": "^0.13.0"
|
@@ -7,17 +7,16 @@
|
|
7
7
|
Empty,
|
8
8
|
IconButton,
|
9
9
|
ShareButton,
|
10
|
-
IconButtonWrapper
|
10
|
+
IconButtonWrapper,
|
11
|
+
FullscreenButton
|
11
12
|
} from "@gradio/atoms";
|
12
|
-
import { Download } from "@gradio/icons";
|
13
|
+
import { Download, Image as ImageIcon } from "@gradio/icons";
|
13
14
|
import { get_coordinates_of_clicked_image } from "./utils";
|
14
|
-
import Image from "
|
15
|
+
import { Image } from "@gradio/image/shared";
|
15
16
|
import { DownloadLink } from "@gradio/wasm/svelte";
|
16
|
-
import { Maximize, Minimize } from "@gradio/icons";
|
17
17
|
|
18
|
-
import { Image as ImageIcon } from "@gradio/icons";
|
19
|
-
import { type FileData } from "@gradio/client";
|
20
18
|
import type { I18nFormatter } from "@gradio/utils";
|
19
|
+
import type { FileData } from "@gradio/client";
|
21
20
|
|
22
21
|
export let value: null | FileData;
|
23
22
|
export let label: string | undefined = undefined;
|
@@ -27,6 +26,7 @@
|
|
27
26
|
export let show_share_button = false;
|
28
27
|
export let i18n: I18nFormatter;
|
29
28
|
export let show_fullscreen_button = true;
|
29
|
+
export let display_icon_button_wrapper_top_corner = false;
|
30
30
|
|
31
31
|
const dispatch = createEventDispatcher<{
|
32
32
|
change: string;
|
@@ -40,23 +40,7 @@
|
|
40
40
|
}
|
41
41
|
};
|
42
42
|
|
43
|
-
let is_full_screen = false;
|
44
43
|
let image_container: HTMLElement;
|
45
|
-
|
46
|
-
onMount(() => {
|
47
|
-
document.addEventListener("fullscreenchange", () => {
|
48
|
-
is_full_screen = !!document.fullscreenElement;
|
49
|
-
});
|
50
|
-
});
|
51
|
-
|
52
|
-
const toggle_full_screen = async (): Promise<void> => {
|
53
|
-
if (!is_full_screen) {
|
54
|
-
await image_container.requestFullscreen();
|
55
|
-
} else {
|
56
|
-
await document.exitFullscreen();
|
57
|
-
is_full_screen = !is_full_screen;
|
58
|
-
}
|
59
|
-
};
|
60
44
|
</script>
|
61
45
|
|
62
46
|
<BlockLabel
|
@@ -68,21 +52,11 @@
|
|
68
52
|
<Empty unpadded_box={true} size="large"><ImageIcon /></Empty>
|
69
53
|
{:else}
|
70
54
|
<div class="image-container" bind:this={image_container}>
|
71
|
-
<IconButtonWrapper
|
72
|
-
{
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
on:click={toggle_full_screen}
|
77
|
-
/>
|
78
|
-
{/if}
|
79
|
-
|
80
|
-
{#if is_full_screen && show_fullscreen_button}
|
81
|
-
<IconButton
|
82
|
-
Icon={Minimize}
|
83
|
-
label={is_full_screen ? "Exit full screen" : "View in full screen"}
|
84
|
-
on:click={toggle_full_screen}
|
85
|
-
/>
|
55
|
+
<IconButtonWrapper
|
56
|
+
display_top_corner={display_icon_button_wrapper_top_corner}
|
57
|
+
>
|
58
|
+
{#if show_fullscreen_button}
|
59
|
+
<FullscreenButton container={image_container} />
|
86
60
|
{/if}
|
87
61
|
|
88
62
|
{#if show_download_button}
|
@@ -1,7 +1,8 @@
|
|
1
1
|
<script lang="ts">
|
2
2
|
import { createEventDispatcher, tick } from "svelte";
|
3
3
|
import { BlockLabel, IconButtonWrapper, IconButton } from "@gradio/atoms";
|
4
|
-
import { Clear, Image as ImageIcon
|
4
|
+
import { Clear, Image as ImageIcon } from "@gradio/icons";
|
5
|
+
import { FullscreenButton } from "@gradio/atoms";
|
5
6
|
import {
|
6
7
|
type SelectData,
|
7
8
|
type I18nFormatter,
|
@@ -44,10 +45,20 @@
|
|
44
45
|
|
45
46
|
export let webcam_constraints: { [key: string]: any } | undefined = undefined;
|
46
47
|
|
47
|
-
function handle_upload({
|
48
|
-
|
48
|
+
async function handle_upload({
|
49
|
+
detail
|
50
|
+
}: CustomEvent<FileData>): Promise<void> {
|
49
51
|
if (!streaming) {
|
50
|
-
|
52
|
+
if (detail.path?.toLowerCase().endsWith(".svg") && detail.url) {
|
53
|
+
const response = await fetch(detail.url);
|
54
|
+
const svgContent = await response.text();
|
55
|
+
value = {
|
56
|
+
...detail,
|
57
|
+
url: `data:image/svg+xml,${encodeURIComponent(svgContent)}`
|
58
|
+
};
|
59
|
+
} else {
|
60
|
+
value = detail;
|
61
|
+
}
|
51
62
|
dispatch("upload");
|
52
63
|
}
|
53
64
|
}
|
@@ -122,17 +133,7 @@
|
|
122
133
|
}
|
123
134
|
}
|
124
135
|
|
125
|
-
let is_full_screen = false;
|
126
136
|
let image_container: HTMLElement;
|
127
|
-
|
128
|
-
const toggle_full_screen = async (): Promise<void> => {
|
129
|
-
if (!is_full_screen) {
|
130
|
-
await image_container.requestFullscreen();
|
131
|
-
} else {
|
132
|
-
await document.exitFullscreen();
|
133
|
-
is_full_screen = !is_full_screen;
|
134
|
-
}
|
135
|
-
};
|
136
137
|
</script>
|
137
138
|
|
138
139
|
<BlockLabel {show_label} Icon={ImageIcon} label={label || "Image"} />
|
@@ -140,19 +141,8 @@
|
|
140
141
|
<div data-testid="image" class="image-container" bind:this={image_container}>
|
141
142
|
<IconButtonWrapper>
|
142
143
|
{#if value?.url && !active_streaming}
|
143
|
-
{#if
|
144
|
-
<
|
145
|
-
Icon={Maximize}
|
146
|
-
label={is_full_screen ? "Exit full screen" : "View in full screen"}
|
147
|
-
on:click={toggle_full_screen}
|
148
|
-
/>
|
149
|
-
{/if}
|
150
|
-
{#if is_full_screen && show_fullscreen_button}
|
151
|
-
<IconButton
|
152
|
-
Icon={Minimize}
|
153
|
-
label={is_full_screen ? "Exit full screen" : "View in full screen"}
|
154
|
-
on:click={toggle_full_screen}
|
155
|
-
/>
|
144
|
+
{#if show_fullscreen_button}
|
145
|
+
<FullscreenButton container={image_container} />
|
156
146
|
{/if}
|
157
147
|
<IconButton
|
158
148
|
Icon={Clear}
|
package/shared/Webcam.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
<script lang="ts">
|
2
|
-
import { createEventDispatcher, onMount } from "svelte";
|
2
|
+
import { createEventDispatcher, onDestroy, onMount } from "svelte";
|
3
3
|
import {
|
4
4
|
Camera,
|
5
5
|
Circle,
|
@@ -198,7 +198,7 @@
|
|
198
198
|
}
|
199
199
|
};
|
200
200
|
ReaderObj.readAsDataURL(video_blob);
|
201
|
-
} else {
|
201
|
+
} else if (typeof MediaRecorder !== "undefined") {
|
202
202
|
dispatch("start_recording");
|
203
203
|
recorded_blobs = [];
|
204
204
|
let validMimeTypes = ["video/webm", "video/mp4"];
|
@@ -273,6 +273,12 @@
|
|
273
273
|
event.stopPropagation();
|
274
274
|
options_open = false;
|
275
275
|
}
|
276
|
+
|
277
|
+
onDestroy(() => {
|
278
|
+
if (typeof window === "undefined") return;
|
279
|
+
record_video_or_photo();
|
280
|
+
stream?.getTracks().forEach((track) => track.stop());
|
281
|
+
});
|
276
282
|
</script>
|
277
283
|
|
278
284
|
<div class="wrap">
|