@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 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 "./Image.svelte";
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
- {#if !is_full_screen && show_fullscreen_button}
60
- <IconButton
61
- Icon={Maximize}
62
- label={is_full_screen ? "Exit full screen" : "View in full screen"}
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, Maximize, Minimize } from "@gradio/icons";
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({ detail }) {
34
+ async function handle_upload({
35
+ detail
36
+ }) {
34
37
  if (!streaming) {
35
- value = detail;
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 !is_full_screen && show_fullscreen_button}
109
- <IconButton
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.18.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.11.2",
14
- "@gradio/icons": "^0.8.1",
15
- "@gradio/client": "^1.8.0",
16
- "@gradio/statustracker": "^0.9.6",
17
- "@gradio/upload": "^0.14.2",
18
- "@gradio/utils": "^0.9.0",
19
- "@gradio/wasm": "^0.15.0"
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 "./Image.svelte";
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
- {#if !is_full_screen && show_fullscreen_button}
73
- <IconButton
74
- Icon={Maximize}
75
- label={is_full_screen ? "Exit full screen" : "View in full screen"}
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, Maximize, Minimize } from "@gradio/icons";
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({ detail }: CustomEvent<FileData>): void {
48
- // only trigger streaming event if streaming
48
+ async function handle_upload({
49
+ detail
50
+ }: CustomEvent<FileData>): Promise<void> {
49
51
  if (!streaming) {
50
- value = detail;
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 !is_full_screen && show_fullscreen_button}
144
- <IconButton
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}
@@ -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">