@gradio/image 0.3.0-beta.7 → 0.3.0-beta.9
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 +26 -1
- package/Image.stories.svelte +17 -5
- package/Image.test.ts +15 -10
- package/Index.svelte +34 -29
- package/README.md +47 -5
- package/package.json +9 -7
- package/shared/{ModifySketch.svelte → ClearImage.svelte} +1 -16
- package/shared/ImagePreview.svelte +8 -12
- package/shared/ImageUploader.svelte +224 -0
- package/shared/Webcam.svelte +172 -29
- package/shared/Cropper.svelte +0 -33
- package/shared/ImageEditor.svelte +0 -456
- package/shared/Sketch.svelte +0 -624
- package/shared/SketchSettings.svelte +0 -77
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
# @gradio/image
|
2
2
|
|
3
|
+
## 0.3.0-beta.9
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
- [#6143](https://github.com/gradio-app/gradio/pull/6143) [`e4f7b4b40`](https://github.com/gradio-app/gradio/commit/e4f7b4b409323b01aa01b39e15ce6139e29aa073) - fix circular dependency with client + upload. Thanks [@pngwn](https://github.com/pngwn)!
|
8
|
+
- [#6136](https://github.com/gradio-app/gradio/pull/6136) [`667802a6c`](https://github.com/gradio-app/gradio/commit/667802a6cdbfb2ce454a3be5a78e0990b194548a) - JS Component Documentation. Thanks [@freddyaboulton](https://github.com/freddyaboulton)!
|
9
|
+
- [#6094](https://github.com/gradio-app/gradio/pull/6094) [`c476bd5a5`](https://github.com/gradio-app/gradio/commit/c476bd5a5b70836163b9c69bf4bfe068b17fbe13) - Image v4. Thanks [@pngwn](https://github.com/pngwn)!
|
10
|
+
- [#6149](https://github.com/gradio-app/gradio/pull/6149) [`90318b1dd`](https://github.com/gradio-app/gradio/commit/90318b1dd118ae08a695a50e7c556226234ab6dc) - swap `mode` on the frontned to `interactive` to match the backend. Thanks [@pngwn](https://github.com/pngwn)!
|
11
|
+
- [#6135](https://github.com/gradio-app/gradio/pull/6135) [`bce37ac74`](https://github.com/gradio-app/gradio/commit/bce37ac744496537e71546d2bb889bf248dcf5d3) - Fix selectable prop in the backend. Thanks [@freddyaboulton](https://github.com/freddyaboulton)!
|
12
|
+
|
13
|
+
### Fixes
|
14
|
+
|
15
|
+
- [#6146](https://github.com/gradio-app/gradio/pull/6146) [`40a171ea6`](https://github.com/gradio-app/gradio/commit/40a171ea60c74afa9519d6cb159def16ce68e1ca) - Fix image double change bug. Thanks [@pngwn](https://github.com/pngwn)!
|
16
|
+
|
17
|
+
## 0.3.0-beta.8
|
18
|
+
|
19
|
+
### Features
|
20
|
+
|
21
|
+
- [#6016](https://github.com/gradio-app/gradio/pull/6016) [`83e947676`](https://github.com/gradio-app/gradio/commit/83e947676d327ca2ab6ae2a2d710c78961c771a0) - Format js in v4 branch. Thanks [@freddyaboulton](https://github.com/freddyaboulton)!
|
22
|
+
- [#6044](https://github.com/gradio-app/gradio/pull/6044) [`9053c95a1`](https://github.com/gradio-app/gradio/commit/9053c95a10de12aef572018ee37c71106d2da675) - Simplify File Component. Thanks [@freddyaboulton](https://github.com/freddyaboulton)!
|
23
|
+
|
24
|
+
### Fixes
|
25
|
+
|
26
|
+
- [#6046](https://github.com/gradio-app/gradio/pull/6046) [`dbb7de5e0`](https://github.com/gradio-app/gradio/commit/dbb7de5e02c53fee05889d696d764d212cb96c74) - fix tests. Thanks [@pngwn](https://github.com/pngwn)!
|
27
|
+
|
3
28
|
## 0.3.0-beta.7
|
4
29
|
|
5
30
|
### Patch Changes
|
@@ -117,4 +142,4 @@ Thanks [@pngwn](https://github.com/pngwn)!
|
|
117
142
|
|
118
143
|
### Features
|
119
144
|
|
120
|
-
- [#4979](https://github.com/gradio-app/gradio/pull/4979) [`44ac8ad0`](https://github.com/gradio-app/gradio/commit/44ac8ad08d82ea12c503dde5c78f999eb0452de2) - Allow setting sketch color default. Thanks [@aliabid94](https://github.com/aliabid94)!
|
145
|
+
- [#4979](https://github.com/gradio-app/gradio/pull/4979) [`44ac8ad0`](https://github.com/gradio-app/gradio/commit/44ac8ad08d82ea12c503dde5c78f999eb0452de2) - Allow setting sketch color default. Thanks [@aliabid94](https://github.com/aliabid94)!
|
package/Image.stories.svelte
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
<script>
|
1
|
+
<script lang="ts">
|
2
2
|
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
|
3
3
|
import StaticImage from "./Index.svelte";
|
4
4
|
</script>
|
@@ -8,10 +8,14 @@
|
|
8
8
|
component={Image}
|
9
9
|
argTypes={{
|
10
10
|
value: {
|
11
|
-
control: "
|
11
|
+
control: "object",
|
12
12
|
description: "The image URL or file to display",
|
13
13
|
name: "value",
|
14
|
-
value:
|
14
|
+
value: {
|
15
|
+
name: "https://i.ibb.co/6BgKdSj/groot.jpg",
|
16
|
+
is_file: true,
|
17
|
+
data: null
|
18
|
+
}
|
15
19
|
},
|
16
20
|
show_download_button: {
|
17
21
|
options: [true, false],
|
@@ -34,7 +38,11 @@
|
|
34
38
|
<Story
|
35
39
|
name="Static Image with label and download button"
|
36
40
|
args={{
|
37
|
-
value:
|
41
|
+
value: {
|
42
|
+
name: "https://i.ibb.co/6BgKdSj/groot.jpg",
|
43
|
+
is_file: true,
|
44
|
+
data: null
|
45
|
+
},
|
38
46
|
show_label: true,
|
39
47
|
show_download_button: true
|
40
48
|
}}
|
@@ -43,7 +51,11 @@
|
|
43
51
|
<Story
|
44
52
|
name="Static Image with no label or download button"
|
45
53
|
args={{
|
46
|
-
value:
|
54
|
+
value: {
|
55
|
+
name: "https://i.ibb.co/6BgKdSj/groot.jpg",
|
56
|
+
is_file: true,
|
57
|
+
data: null
|
58
|
+
},
|
47
59
|
show_label: false,
|
48
60
|
show_download_button: false
|
49
61
|
}}
|
package/Image.test.ts
CHANGED
@@ -11,7 +11,7 @@ import { spy } from "tinyspy";
|
|
11
11
|
import { cleanup, render } from "@gradio/tootils";
|
12
12
|
import { setupi18n } from "../app/src/i18n";
|
13
13
|
|
14
|
-
import Image from "./
|
14
|
+
import Image from "./Index.svelte";
|
15
15
|
import type { LoadingStatus } from "@gradio/statustracker";
|
16
16
|
|
17
17
|
const loading_status = {
|
@@ -37,25 +37,30 @@ describe("Image", () => {
|
|
37
37
|
const { component, listen } = await render(Image, {
|
38
38
|
show_label: true,
|
39
39
|
loading_status,
|
40
|
-
value:
|
41
|
-
"https://raw.githubusercontent.com/gradio-app/gradio/main/test/test_files/bus.png",
|
40
|
+
value: {
|
41
|
+
url: "https://raw.githubusercontent.com/gradio-app/gradio/main/test/test_files/bus.png",
|
42
|
+
orig_name: "bus.png",
|
43
|
+
path: "https://raw.githubusercontent.com/gradio-app/gradio/main/test/test_files/bus.png"
|
44
|
+
},
|
42
45
|
streaming: false,
|
43
46
|
pending: false,
|
44
|
-
source: "upload",
|
45
47
|
label: "Test Label",
|
46
48
|
width: 224,
|
47
49
|
height: 224,
|
48
50
|
mirror_webcam: false,
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
// brush_color: "#000000",
|
52
|
+
// brush_radius: 5,
|
53
|
+
// mask_opacity: 0.5,
|
54
|
+
interactive: true
|
53
55
|
});
|
54
56
|
|
55
57
|
const mock = listen("change");
|
56
58
|
|
57
|
-
component.value =
|
58
|
-
"https://github.com/gradio-app/gradio/blob/main/test/test_files/cheetah1.jpg"
|
59
|
+
component.value = {
|
60
|
+
url: "https://github.com/gradio-app/gradio/blob/main/test/test_files/cheetah1.jpg",
|
61
|
+
orig_name: "bus.png",
|
62
|
+
path: "https://github.com/gradio-app/gradio/blob/main/test/test_files/cheetah1.jpg"
|
63
|
+
};
|
59
64
|
assert.equal(mock.callCount, 1);
|
60
65
|
});
|
61
66
|
});
|
package/Index.svelte
CHANGED
@@ -2,17 +2,20 @@
|
|
2
2
|
|
3
3
|
<script context="module" lang="ts">
|
4
4
|
export { default as Webcam } from "./shared/Webcam.svelte";
|
5
|
+
export { default as BaseImageUploader } from "./shared/ImageUploader.svelte";
|
6
|
+
export { default as BaseStaticImage } from "./shared/ImagePreview.svelte";
|
7
|
+
export { default as BaseExample } from "./Example.svelte";
|
5
8
|
</script>
|
6
9
|
|
7
10
|
<script lang="ts">
|
8
11
|
import type { Gradio, SelectData } from "@gradio/utils";
|
9
12
|
import StaticImage from "./shared/ImagePreview.svelte";
|
10
|
-
import
|
11
|
-
|
12
|
-
import { Block, UploadText } from "@gradio/atoms";
|
13
|
+
import ImageUploader from "./shared/ImageUploader.svelte";
|
13
14
|
|
15
|
+
import { Block, Empty, UploadText } from "@gradio/atoms";
|
16
|
+
import { Image } from "@gradio/icons";
|
14
17
|
import { StatusTracker } from "@gradio/statustracker";
|
15
|
-
import type { FileData } from "
|
18
|
+
import type { FileData } from "@gradio/client";
|
16
19
|
import type { LoadingStatus } from "@gradio/statustracker";
|
17
20
|
|
18
21
|
export let elem_id = "";
|
@@ -27,23 +30,21 @@
|
|
27
30
|
export let height: number | undefined;
|
28
31
|
export let width: number | undefined;
|
29
32
|
|
30
|
-
export let
|
33
|
+
export let _selectable = false;
|
31
34
|
export let container = true;
|
32
35
|
export let scale: number | null = null;
|
33
36
|
export let min_width: number | undefined = undefined;
|
34
37
|
export let loading_status: LoadingStatus;
|
35
38
|
export let show_share_button = false;
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
export let sources: ("clipboard" | "webcam" | "upload")[] = [
|
40
|
+
"upload",
|
41
|
+
"clipboard",
|
42
|
+
"webcam"
|
43
|
+
];
|
44
|
+
export let interactive: boolean;
|
40
45
|
export let streaming: boolean;
|
41
46
|
export let pending: boolean;
|
42
47
|
export let mirror_webcam: boolean;
|
43
|
-
export let shape: [number, number];
|
44
|
-
export let brush_radius: number;
|
45
|
-
export let brush_color: string;
|
46
|
-
export let mask_opacity: number;
|
47
48
|
|
48
49
|
export let gradio: Gradio<{
|
49
50
|
change: never;
|
@@ -57,15 +58,15 @@
|
|
57
58
|
share: ShareData;
|
58
59
|
}>;
|
59
60
|
|
60
|
-
$: value
|
61
|
+
$: value?.url && gradio.dispatch("change");
|
61
62
|
let dragging: boolean;
|
62
63
|
|
63
64
|
$: value = !value ? null : value;
|
64
65
|
|
65
|
-
|
66
|
+
let active_tool: null | "webcam" = null;
|
66
67
|
</script>
|
67
68
|
|
68
|
-
{#if
|
69
|
+
{#if !interactive}
|
69
70
|
<Block
|
70
71
|
{visible}
|
71
72
|
variant={"solid"}
|
@@ -81,9 +82,11 @@
|
|
81
82
|
{min_width}
|
82
83
|
>
|
83
84
|
<StatusTracker
|
85
|
+
translucent={true}
|
84
86
|
autoscroll={gradio.autoscroll}
|
85
87
|
i18n={gradio.i18n}
|
86
88
|
{...loading_status}
|
89
|
+
show_progress="hidden"
|
87
90
|
/>
|
88
91
|
<StaticImage
|
89
92
|
on:select={({ detail }) => gradio.dispatch("select", detail)}
|
@@ -94,7 +97,7 @@
|
|
94
97
|
{label}
|
95
98
|
{show_label}
|
96
99
|
{show_download_button}
|
97
|
-
{
|
100
|
+
selectable={_selectable}
|
98
101
|
{show_share_button}
|
99
102
|
i18n={gradio.i18n}
|
100
103
|
/>
|
@@ -102,12 +105,12 @@
|
|
102
105
|
{:else}
|
103
106
|
<Block
|
104
107
|
{visible}
|
105
|
-
variant={value === null
|
108
|
+
variant={value === null ? "dashed" : "solid"}
|
106
109
|
border_mode={dragging ? "focus" : "base"}
|
107
110
|
padding={false}
|
108
111
|
{elem_id}
|
109
112
|
{elem_classes}
|
110
|
-
height={height ||
|
113
|
+
height={height || undefined}
|
111
114
|
{width}
|
112
115
|
allow_overflow={false}
|
113
116
|
{container}
|
@@ -120,16 +123,12 @@
|
|
120
123
|
{...loading_status}
|
121
124
|
/>
|
122
125
|
|
123
|
-
<
|
124
|
-
|
125
|
-
{brush_color}
|
126
|
-
{shape}
|
126
|
+
<ImageUploader
|
127
|
+
bind:active_tool
|
127
128
|
bind:value
|
128
|
-
{
|
129
|
-
{tool}
|
130
|
-
{selectable}
|
131
|
-
{mask_opacity}
|
129
|
+
selectable={_selectable}
|
132
130
|
{root}
|
131
|
+
{sources}
|
133
132
|
on:edit={() => gradio.dispatch("edit")}
|
134
133
|
on:clear={() => gradio.dispatch("clear")}
|
135
134
|
on:stream={() => gradio.dispatch("stream")}
|
@@ -142,6 +141,8 @@
|
|
142
141
|
loading_status.status = "error";
|
143
142
|
gradio.dispatch("error", detail);
|
144
143
|
}}
|
144
|
+
on:click={() => gradio.dispatch("error", "bad thing happened")}
|
145
|
+
on:error
|
145
146
|
{label}
|
146
147
|
{show_label}
|
147
148
|
{pending}
|
@@ -149,7 +150,11 @@
|
|
149
150
|
{mirror_webcam}
|
150
151
|
i18n={gradio.i18n}
|
151
152
|
>
|
152
|
-
|
153
|
-
|
153
|
+
{#if sources.includes("upload")}
|
154
|
+
<UploadText i18n={gradio.i18n} type="image" mode="short" />
|
155
|
+
{:else}
|
156
|
+
<Empty unpadded_box={true} size="large"><Image /></Empty>
|
157
|
+
{/if}
|
158
|
+
</ImageUploader>
|
154
159
|
</Block>
|
155
160
|
{/if}
|
package/README.md
CHANGED
@@ -1,11 +1,53 @@
|
|
1
|
-
# `@gradio/
|
1
|
+
# `@gradio/image`
|
2
2
|
|
3
3
|
```html
|
4
4
|
<script>
|
5
|
-
import {
|
5
|
+
import { BaseImageUploader, BaseStaticImage, Webcam, BaseExample } from "@gradio/image";
|
6
6
|
</script>
|
7
|
+
```
|
8
|
+
|
9
|
+
BaseImageUploader
|
10
|
+
```javascript
|
11
|
+
export let sources: ("clipboard" | "webcam" | "upload")[] = [
|
12
|
+
"upload",
|
13
|
+
"clipboard",
|
14
|
+
"webcam"
|
15
|
+
];
|
16
|
+
export let streaming = false;
|
17
|
+
export let pending = false;
|
18
|
+
export let mirror_webcam: boolean;
|
19
|
+
export let selectable = false;
|
20
|
+
export let root: string;
|
21
|
+
export let i18n: I18nFormatter;
|
22
|
+
```
|
7
23
|
|
8
|
-
|
9
|
-
|
10
|
-
|
24
|
+
BaseStaticImage
|
25
|
+
```javascript
|
26
|
+
export let value: null | FileData;
|
27
|
+
export let label: string | undefined = undefined;
|
28
|
+
export let show_label: boolean;
|
29
|
+
export let show_download_button = true;
|
30
|
+
export let selectable = false;
|
31
|
+
export let show_share_button = false;
|
32
|
+
export let root: string;
|
33
|
+
export let i18n: I18nFormatter;
|
11
34
|
```
|
35
|
+
|
36
|
+
Webcam
|
37
|
+
```javascript
|
38
|
+
export let streaming = false;
|
39
|
+
export let pending = false;
|
40
|
+
|
41
|
+
export let mode: "image" | "video" = "image";
|
42
|
+
export let mirror_webcam: boolean;
|
43
|
+
export let include_audio: boolean;
|
44
|
+
export let i18n: I18nFormatter;
|
45
|
+
```
|
46
|
+
|
47
|
+
BaseExample
|
48
|
+
```javascript
|
49
|
+
export let value: string;
|
50
|
+
export let samples_dir: string;
|
51
|
+
export let type: "gallery" | "table";
|
52
|
+
export let selected = false;
|
53
|
+
```
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@gradio/image",
|
3
|
-
"version": "0.3.0-beta.
|
3
|
+
"version": "0.3.0-beta.9",
|
4
4
|
"description": "Gradio UI packages",
|
5
5
|
"type": "module",
|
6
6
|
"author": "",
|
@@ -10,14 +10,16 @@
|
|
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.2.0-beta.
|
14
|
-
"@gradio/
|
15
|
-
"@gradio/
|
16
|
-
"@gradio/
|
17
|
-
"@gradio/
|
18
|
-
"@gradio/
|
13
|
+
"@gradio/atoms": "^0.2.0-beta.6",
|
14
|
+
"@gradio/client": "^0.7.0-beta.1",
|
15
|
+
"@gradio/icons": "^0.2.0-beta.3",
|
16
|
+
"@gradio/statustracker": "^0.3.0-beta.8",
|
17
|
+
"@gradio/upload": "^0.3.0-beta.6",
|
18
|
+
"@gradio/utils": "^0.2.0-beta.6",
|
19
|
+
"@gradio/wasm": "^0.2.0-beta.2"
|
19
20
|
},
|
20
21
|
"main_changeset": true,
|
22
|
+
"main": "./Index.svelte",
|
21
23
|
"exports": {
|
22
24
|
".": "./Index.svelte",
|
23
25
|
"./example": "./Example.svelte",
|
@@ -1,27 +1,12 @@
|
|
1
1
|
<script lang="ts">
|
2
2
|
import { createEventDispatcher } from "svelte";
|
3
3
|
import { IconButton } from "@gradio/atoms";
|
4
|
-
import {
|
4
|
+
import { Clear } from "@gradio/icons";
|
5
5
|
|
6
6
|
const dispatch = createEventDispatcher();
|
7
|
-
|
8
|
-
export let show_eraser = false;
|
9
7
|
</script>
|
10
8
|
|
11
9
|
<div>
|
12
|
-
<IconButton Icon={Undo} label="Undo" on:click={() => dispatch("undo")} />
|
13
|
-
|
14
|
-
{#if show_eraser}
|
15
|
-
<IconButton
|
16
|
-
Icon={Erase}
|
17
|
-
label="Clear"
|
18
|
-
on:click={(event) => {
|
19
|
-
dispatch("clear_mask");
|
20
|
-
event.stopPropagation();
|
21
|
-
}}
|
22
|
-
/>
|
23
|
-
{/if}
|
24
|
-
|
25
10
|
<IconButton
|
26
11
|
Icon={Clear}
|
27
12
|
label="Remove Image"
|
@@ -7,11 +7,10 @@
|
|
7
7
|
import { get_coordinates_of_clicked_image } from "./utils";
|
8
8
|
|
9
9
|
import { Image } from "@gradio/icons";
|
10
|
-
import { type FileData, normalise_file } from "@gradio/
|
10
|
+
import { type FileData, normalise_file } from "@gradio/client";
|
11
11
|
import type { I18nFormatter } from "@gradio/utils";
|
12
12
|
|
13
13
|
export let value: null | FileData;
|
14
|
-
let value_: null | FileData;
|
15
14
|
export let label: string | undefined = undefined;
|
16
15
|
export let show_label: boolean;
|
17
16
|
export let show_download_button = true;
|
@@ -25,12 +24,7 @@
|
|
25
24
|
select: SelectData;
|
26
25
|
}>();
|
27
26
|
|
28
|
-
$: value
|
29
|
-
|
30
|
-
$: if (value !== value_) {
|
31
|
-
value_ = value;
|
32
|
-
normalise_file(value_, root, null);
|
33
|
-
}
|
27
|
+
$: value = normalise_file(value, root, null);
|
34
28
|
|
35
29
|
const handle_click = (evt: MouseEvent): void => {
|
36
30
|
let coordinates = get_coordinates_of_clicked_image(evt);
|
@@ -41,13 +35,13 @@
|
|
41
35
|
</script>
|
42
36
|
|
43
37
|
<BlockLabel {show_label} Icon={Image} label={label || i18n("image.image")} />
|
44
|
-
{#if
|
38
|
+
{#if value === null || !value.url}
|
45
39
|
<Empty unpadded_box={true} size="large"><Image /></Empty>
|
46
40
|
{:else}
|
47
41
|
<div class="icon-buttons">
|
48
42
|
{#if show_download_button}
|
49
43
|
<a
|
50
|
-
href={
|
44
|
+
href={value.url}
|
51
45
|
target={window.__is_colab__ ? "_blank" : null}
|
52
46
|
download={"image"}
|
53
47
|
>
|
@@ -69,15 +63,17 @@
|
|
69
63
|
{/if}
|
70
64
|
</div>
|
71
65
|
<button on:click={handle_click}>
|
72
|
-
<img src={
|
66
|
+
<img src={value.url} alt="" class:selectable loading="lazy" />
|
73
67
|
</button>
|
74
68
|
{/if}
|
75
69
|
|
76
70
|
<style>
|
77
|
-
img
|
71
|
+
img,
|
72
|
+
button {
|
78
73
|
width: var(--size-full);
|
79
74
|
height: var(--size-full);
|
80
75
|
object-fit: contain;
|
76
|
+
display: block;
|
81
77
|
}
|
82
78
|
|
83
79
|
.selectable {
|
@@ -0,0 +1,224 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import { createEventDispatcher, tick } from "svelte";
|
3
|
+
import { BlockLabel } from "@gradio/atoms";
|
4
|
+
import { Image } from "@gradio/icons";
|
5
|
+
import type { SelectData, I18nFormatter } from "@gradio/utils";
|
6
|
+
import { get_coordinates_of_clicked_image } from "./utils";
|
7
|
+
import {
|
8
|
+
Webcam as WebcamIcon,
|
9
|
+
ImagePaste,
|
10
|
+
Upload as UploadIcon
|
11
|
+
} from "@gradio/icons";
|
12
|
+
import Webcam from "./Webcam.svelte";
|
13
|
+
import { Toolbar, IconButton } from "@gradio/atoms";
|
14
|
+
|
15
|
+
import { Upload } from "@gradio/upload";
|
16
|
+
import { type FileData, normalise_file } from "@gradio/client";
|
17
|
+
import ClearImage from "./ClearImage.svelte";
|
18
|
+
|
19
|
+
export let value: null | FileData;
|
20
|
+
export let label: string | undefined = undefined;
|
21
|
+
export let show_label: boolean;
|
22
|
+
|
23
|
+
export let sources: ("clipboard" | "webcam" | "upload")[] = [
|
24
|
+
"upload",
|
25
|
+
"clipboard",
|
26
|
+
"webcam"
|
27
|
+
];
|
28
|
+
export let streaming = false;
|
29
|
+
export let pending = false;
|
30
|
+
export let mirror_webcam: boolean;
|
31
|
+
export let selectable = false;
|
32
|
+
export let root: string;
|
33
|
+
export let i18n: I18nFormatter;
|
34
|
+
|
35
|
+
let upload: Upload;
|
36
|
+
export let active_tool: "webcam" | null = null;
|
37
|
+
|
38
|
+
function handle_upload({ detail }: CustomEvent<FileData>): void {
|
39
|
+
value = normalise_file(detail, root, null);
|
40
|
+
}
|
41
|
+
|
42
|
+
async function handle_save(img_blob: Blob | any): Promise<void> {
|
43
|
+
pending = true;
|
44
|
+
const f = await upload.load_files([new File([img_blob], `webcam.png`)]);
|
45
|
+
|
46
|
+
value = f?.[0] || null;
|
47
|
+
if (!streaming) active_tool = null;
|
48
|
+
|
49
|
+
await tick();
|
50
|
+
|
51
|
+
dispatch(streaming ? "stream" : "change");
|
52
|
+
pending = false;
|
53
|
+
}
|
54
|
+
|
55
|
+
$: value && !value.url && (value = normalise_file(value, root, null));
|
56
|
+
|
57
|
+
const dispatch = createEventDispatcher<{
|
58
|
+
change?: never;
|
59
|
+
stream?: never;
|
60
|
+
clear?: never;
|
61
|
+
drag: boolean;
|
62
|
+
upload?: never;
|
63
|
+
select: SelectData;
|
64
|
+
}>();
|
65
|
+
|
66
|
+
let dragging = false;
|
67
|
+
|
68
|
+
$: dispatch("drag", dragging);
|
69
|
+
|
70
|
+
function handle_click(evt: MouseEvent): void {
|
71
|
+
let coordinates = get_coordinates_of_clicked_image(evt);
|
72
|
+
if (coordinates) {
|
73
|
+
dispatch("select", { index: coordinates, value: null });
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
const sources_meta = {
|
78
|
+
upload: {
|
79
|
+
icon: UploadIcon,
|
80
|
+
label: i18n("Upload"),
|
81
|
+
order: 0
|
82
|
+
},
|
83
|
+
webcam: {
|
84
|
+
icon: WebcamIcon,
|
85
|
+
label: i18n("Webcam"),
|
86
|
+
order: 1
|
87
|
+
},
|
88
|
+
clipboard: {
|
89
|
+
icon: ImagePaste,
|
90
|
+
label: i18n("Paste"),
|
91
|
+
order: 2
|
92
|
+
}
|
93
|
+
};
|
94
|
+
|
95
|
+
$: sources_list = sources.sort(
|
96
|
+
(a, b) => sources_meta[a].order - sources_meta[b].order
|
97
|
+
);
|
98
|
+
|
99
|
+
$: {
|
100
|
+
if (sources.length === 1 && sources[0] === "webcam") {
|
101
|
+
active_tool = "webcam";
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
async function handle_toolbar(
|
106
|
+
source: (typeof sources)[number]
|
107
|
+
): Promise<void> {
|
108
|
+
switch (source) {
|
109
|
+
case "clipboard":
|
110
|
+
navigator.clipboard.read().then(async (items) => {
|
111
|
+
for (let i = 0; i < items.length; i++) {
|
112
|
+
const type = items[i].types.find((t) => t.startsWith("image/"));
|
113
|
+
if (type) {
|
114
|
+
items[i].getType(type).then(async (blob) => {
|
115
|
+
const f = await upload.load_files([
|
116
|
+
new File([blob], `clipboard.${type.replace("image/", "")}`)
|
117
|
+
]);
|
118
|
+
f;
|
119
|
+
value = f?.[0] || null;
|
120
|
+
});
|
121
|
+
break;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
});
|
125
|
+
break;
|
126
|
+
case "webcam":
|
127
|
+
active_tool = "webcam";
|
128
|
+
break;
|
129
|
+
case "upload":
|
130
|
+
upload.open_file_upload();
|
131
|
+
break;
|
132
|
+
default:
|
133
|
+
break;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
</script>
|
137
|
+
|
138
|
+
<BlockLabel {show_label} Icon={Image} label={label || "Image"} />
|
139
|
+
|
140
|
+
<div data-testid="image" class="image-container">
|
141
|
+
{#if value?.url}
|
142
|
+
<ClearImage on:remove_image={() => (value = null)} />
|
143
|
+
{/if}
|
144
|
+
<div class="upload-container">
|
145
|
+
<Upload
|
146
|
+
hidden={value !== null || active_tool === "webcam"}
|
147
|
+
bind:this={upload}
|
148
|
+
bind:dragging
|
149
|
+
filetype="image/*"
|
150
|
+
on:load={handle_upload}
|
151
|
+
on:error
|
152
|
+
{root}
|
153
|
+
disable_click={!sources.includes("upload")}
|
154
|
+
>
|
155
|
+
{#if value === null && !active_tool}
|
156
|
+
<slot />
|
157
|
+
{/if}
|
158
|
+
</Upload>
|
159
|
+
{#if active_tool === "webcam"}
|
160
|
+
<Webcam
|
161
|
+
on:capture={(e) => handle_save(e.detail)}
|
162
|
+
on:stream={(e) => handle_save(e.detail)}
|
163
|
+
on:error
|
164
|
+
on:drag
|
165
|
+
on:upload={(e) => handle_save(e.detail)}
|
166
|
+
{mirror_webcam}
|
167
|
+
{streaming}
|
168
|
+
mode="image"
|
169
|
+
include_audio={false}
|
170
|
+
{i18n}
|
171
|
+
/>
|
172
|
+
{:else if value !== null && !streaming}
|
173
|
+
<!-- svelte-ignore a11y-click-events-have-key-events-->
|
174
|
+
<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
|
175
|
+
<img
|
176
|
+
src={value.url}
|
177
|
+
alt={value.alt_text}
|
178
|
+
on:click={handle_click}
|
179
|
+
class:selectable
|
180
|
+
/>
|
181
|
+
{/if}
|
182
|
+
</div>
|
183
|
+
{#if sources.length > 1 || sources.includes("clipboard")}
|
184
|
+
<Toolbar show_border={!value?.url}>
|
185
|
+
{#each sources_list as source}
|
186
|
+
<IconButton
|
187
|
+
on:click={() => handle_toolbar(source)}
|
188
|
+
Icon={sources_meta[source].icon}
|
189
|
+
size="large"
|
190
|
+
padded={false}
|
191
|
+
/>
|
192
|
+
{/each}
|
193
|
+
</Toolbar>
|
194
|
+
{/if}
|
195
|
+
</div>
|
196
|
+
|
197
|
+
<style>
|
198
|
+
/* .image-container {
|
199
|
+
height: auto;
|
200
|
+
} */
|
201
|
+
img {
|
202
|
+
width: var(--size-full);
|
203
|
+
height: var(--size-full);
|
204
|
+
}
|
205
|
+
|
206
|
+
.upload-container {
|
207
|
+
height: 100%;
|
208
|
+
flex-shrink: 1;
|
209
|
+
max-height: 100%;
|
210
|
+
}
|
211
|
+
|
212
|
+
.image-container {
|
213
|
+
display: flex;
|
214
|
+
height: 100%;
|
215
|
+
flex-direction: column;
|
216
|
+
justify-content: center;
|
217
|
+
align-items: center;
|
218
|
+
max-height: 100%;
|
219
|
+
}
|
220
|
+
|
221
|
+
.selectable {
|
222
|
+
cursor: crosshair;
|
223
|
+
}
|
224
|
+
</style>
|