@gradio/image 0.15.0 → 0.15.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 +16 -0
- package/Image.test.ts +1 -1
- package/dist/Example.svelte +46 -0
- package/dist/Example.svelte.d.ts +19 -0
- package/dist/Index.svelte +185 -0
- package/dist/Index.svelte.d.ts +158 -0
- package/dist/shared/ClearImage.svelte +28 -0
- package/dist/shared/ClearImage.svelte.d.ts +18 -0
- package/dist/shared/Image.svelte +26 -0
- package/dist/shared/Image.svelte.d.ts +247 -0
- package/dist/shared/ImagePreview.svelte +155 -0
- package/dist/shared/ImagePreview.svelte.d.ts +32 -0
- package/dist/shared/ImageUploader.svelte +181 -0
- package/dist/shared/ImageUploader.svelte.d.ts +42 -0
- package/dist/shared/Webcam.svelte +371 -0
- package/dist/shared/Webcam.svelte.d.ts +33 -0
- package/dist/shared/WebcamPermissions.svelte +42 -0
- package/dist/shared/WebcamPermissions.svelte.d.ts +18 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/stream_utils.d.ts +5 -0
- package/dist/shared/stream_utils.js +31 -0
- package/dist/shared/utils.d.ts +1 -0
- package/dist/shared/utils.js +28 -0
- package/package.json +33 -14
@@ -0,0 +1,371 @@
|
|
1
|
+
<script>import { createEventDispatcher, onMount } from "svelte";
|
2
|
+
import { Camera, Circle, Square, DropdownArrow } from "@gradio/icons";
|
3
|
+
import { prepare_files } from "@gradio/client";
|
4
|
+
import WebcamPermissions from "./WebcamPermissions.svelte";
|
5
|
+
import { fade } from "svelte/transition";
|
6
|
+
import {
|
7
|
+
get_devices,
|
8
|
+
get_video_stream,
|
9
|
+
set_available_devices
|
10
|
+
} from "./stream_utils";
|
11
|
+
let video_source;
|
12
|
+
let available_video_devices = [];
|
13
|
+
let selected_device = null;
|
14
|
+
let canvas;
|
15
|
+
export let streaming = false;
|
16
|
+
export let pending = false;
|
17
|
+
export let root = "";
|
18
|
+
export let mode = "image";
|
19
|
+
export let mirror_webcam;
|
20
|
+
export let include_audio;
|
21
|
+
export let i18n;
|
22
|
+
export let upload;
|
23
|
+
const dispatch = createEventDispatcher();
|
24
|
+
onMount(() => canvas = document.createElement("canvas"));
|
25
|
+
const handle_device_change = async (event) => {
|
26
|
+
const target = event.target;
|
27
|
+
const device_id = target.value;
|
28
|
+
await get_video_stream(include_audio, video_source, device_id).then(
|
29
|
+
async (local_stream) => {
|
30
|
+
stream = local_stream;
|
31
|
+
selected_device = available_video_devices.find(
|
32
|
+
(device) => device.deviceId === device_id
|
33
|
+
) || null;
|
34
|
+
options_open = false;
|
35
|
+
}
|
36
|
+
);
|
37
|
+
};
|
38
|
+
async function access_webcam() {
|
39
|
+
try {
|
40
|
+
get_video_stream(include_audio, video_source).then(async (local_stream) => {
|
41
|
+
webcam_accessed = true;
|
42
|
+
available_video_devices = await get_devices();
|
43
|
+
stream = local_stream;
|
44
|
+
}).then(() => set_available_devices(available_video_devices)).then((devices) => {
|
45
|
+
available_video_devices = devices;
|
46
|
+
const used_devices = stream.getTracks().map((track) => track.getSettings()?.deviceId)[0];
|
47
|
+
selected_device = used_devices ? devices.find((device) => device.deviceId === used_devices) || available_video_devices[0] : available_video_devices[0];
|
48
|
+
});
|
49
|
+
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
50
|
+
dispatch("error", i18n("image.no_webcam_support"));
|
51
|
+
}
|
52
|
+
} catch (err) {
|
53
|
+
if (err instanceof DOMException && err.name == "NotAllowedError") {
|
54
|
+
dispatch("error", i18n("image.allow_webcam_access"));
|
55
|
+
} else {
|
56
|
+
throw err;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
function take_picture() {
|
61
|
+
var context = canvas.getContext("2d");
|
62
|
+
if ((!streaming || streaming && recording) && video_source.videoWidth && video_source.videoHeight) {
|
63
|
+
canvas.width = video_source.videoWidth;
|
64
|
+
canvas.height = video_source.videoHeight;
|
65
|
+
context.drawImage(
|
66
|
+
video_source,
|
67
|
+
0,
|
68
|
+
0,
|
69
|
+
video_source.videoWidth,
|
70
|
+
video_source.videoHeight
|
71
|
+
);
|
72
|
+
if (mirror_webcam) {
|
73
|
+
context.scale(-1, 1);
|
74
|
+
context.drawImage(video_source, -video_source.videoWidth, 0);
|
75
|
+
}
|
76
|
+
canvas.toBlob(
|
77
|
+
(blob) => {
|
78
|
+
dispatch(streaming ? "stream" : "capture", blob);
|
79
|
+
},
|
80
|
+
"image/png",
|
81
|
+
0.8
|
82
|
+
);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
let recording = false;
|
86
|
+
let recorded_blobs = [];
|
87
|
+
let stream;
|
88
|
+
let mimeType;
|
89
|
+
let media_recorder;
|
90
|
+
function take_recording() {
|
91
|
+
if (recording) {
|
92
|
+
media_recorder.stop();
|
93
|
+
let video_blob = new Blob(recorded_blobs, { type: mimeType });
|
94
|
+
let ReaderObj = new FileReader();
|
95
|
+
ReaderObj.onload = async function(e) {
|
96
|
+
if (e.target) {
|
97
|
+
let _video_blob = new File(
|
98
|
+
[video_blob],
|
99
|
+
"sample." + mimeType.substring(6)
|
100
|
+
);
|
101
|
+
const val = await prepare_files([_video_blob]);
|
102
|
+
let value = ((await upload(val, root))?.filter(Boolean))[0];
|
103
|
+
dispatch("capture", value);
|
104
|
+
dispatch("stop_recording");
|
105
|
+
}
|
106
|
+
};
|
107
|
+
ReaderObj.readAsDataURL(video_blob);
|
108
|
+
} else {
|
109
|
+
dispatch("start_recording");
|
110
|
+
recorded_blobs = [];
|
111
|
+
let validMimeTypes = ["video/webm", "video/mp4"];
|
112
|
+
for (let validMimeType of validMimeTypes) {
|
113
|
+
if (MediaRecorder.isTypeSupported(validMimeType)) {
|
114
|
+
mimeType = validMimeType;
|
115
|
+
break;
|
116
|
+
}
|
117
|
+
}
|
118
|
+
if (mimeType === null) {
|
119
|
+
console.error("No supported MediaRecorder mimeType");
|
120
|
+
return;
|
121
|
+
}
|
122
|
+
media_recorder = new MediaRecorder(stream, {
|
123
|
+
mimeType
|
124
|
+
});
|
125
|
+
media_recorder.addEventListener("dataavailable", function(e) {
|
126
|
+
recorded_blobs.push(e.data);
|
127
|
+
});
|
128
|
+
media_recorder.start(200);
|
129
|
+
}
|
130
|
+
recording = !recording;
|
131
|
+
}
|
132
|
+
let webcam_accessed = false;
|
133
|
+
function record_video_or_photo() {
|
134
|
+
if (mode === "image" && streaming) {
|
135
|
+
recording = !recording;
|
136
|
+
}
|
137
|
+
if (mode === "image") {
|
138
|
+
take_picture();
|
139
|
+
} else {
|
140
|
+
take_recording();
|
141
|
+
}
|
142
|
+
if (!recording && stream) {
|
143
|
+
stream.getTracks().forEach((track) => track.stop());
|
144
|
+
video_source.srcObject = null;
|
145
|
+
webcam_accessed = false;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
if (streaming && mode === "image") {
|
149
|
+
window.setInterval(() => {
|
150
|
+
if (video_source && !pending) {
|
151
|
+
take_picture();
|
152
|
+
}
|
153
|
+
}, 500);
|
154
|
+
}
|
155
|
+
let options_open = false;
|
156
|
+
export function click_outside(node, cb) {
|
157
|
+
const handle_click = (event) => {
|
158
|
+
if (node && !node.contains(event.target) && !event.defaultPrevented) {
|
159
|
+
cb(event);
|
160
|
+
}
|
161
|
+
};
|
162
|
+
document.addEventListener("click", handle_click, true);
|
163
|
+
return {
|
164
|
+
destroy() {
|
165
|
+
document.removeEventListener("click", handle_click, true);
|
166
|
+
}
|
167
|
+
};
|
168
|
+
}
|
169
|
+
function handle_click_outside(event) {
|
170
|
+
event.preventDefault();
|
171
|
+
event.stopPropagation();
|
172
|
+
options_open = false;
|
173
|
+
}
|
174
|
+
</script>
|
175
|
+
|
176
|
+
<div class="wrap">
|
177
|
+
<!-- svelte-ignore a11y-media-has-caption -->
|
178
|
+
<!-- need to suppress for video streaming https://github.com/sveltejs/svelte/issues/5967 -->
|
179
|
+
<video
|
180
|
+
bind:this={video_source}
|
181
|
+
class:flip={mirror_webcam}
|
182
|
+
class:hide={!webcam_accessed}
|
183
|
+
/>
|
184
|
+
{#if !webcam_accessed}
|
185
|
+
<div
|
186
|
+
in:fade={{ delay: 100, duration: 200 }}
|
187
|
+
title="grant webcam access"
|
188
|
+
style="height: 100%"
|
189
|
+
>
|
190
|
+
<WebcamPermissions on:click={async () => access_webcam()} />
|
191
|
+
</div>
|
192
|
+
{:else}
|
193
|
+
<div class="button-wrap">
|
194
|
+
<button
|
195
|
+
on:click={record_video_or_photo}
|
196
|
+
aria-label={mode === "image" ? "capture photo" : "start recording"}
|
197
|
+
>
|
198
|
+
{#if mode === "video" || streaming}
|
199
|
+
{#if recording}
|
200
|
+
<div class="icon red" title="stop recording">
|
201
|
+
<Square />
|
202
|
+
</div>
|
203
|
+
{:else}
|
204
|
+
<div class="icon red" title="start recording">
|
205
|
+
<Circle />
|
206
|
+
</div>
|
207
|
+
{/if}
|
208
|
+
{:else}
|
209
|
+
<div class="icon" title="capture photo">
|
210
|
+
<Camera />
|
211
|
+
</div>
|
212
|
+
{/if}
|
213
|
+
</button>
|
214
|
+
{#if !recording}
|
215
|
+
<button
|
216
|
+
class="icon"
|
217
|
+
on:click={() => (options_open = true)}
|
218
|
+
aria-label="select input source"
|
219
|
+
>
|
220
|
+
<DropdownArrow />
|
221
|
+
</button>
|
222
|
+
{/if}
|
223
|
+
</div>
|
224
|
+
{#if options_open && selected_device}
|
225
|
+
<select
|
226
|
+
class="select-wrap"
|
227
|
+
aria-label="select source"
|
228
|
+
use:click_outside={handle_click_outside}
|
229
|
+
on:change={handle_device_change}
|
230
|
+
>
|
231
|
+
<button
|
232
|
+
class="inset-icon"
|
233
|
+
on:click|stopPropagation={() => (options_open = false)}
|
234
|
+
>
|
235
|
+
<DropdownArrow />
|
236
|
+
</button>
|
237
|
+
{#if available_video_devices.length === 0}
|
238
|
+
<option value="">{i18n("common.no_devices")}</option>
|
239
|
+
{:else}
|
240
|
+
{#each available_video_devices as device}
|
241
|
+
<option
|
242
|
+
value={device.deviceId}
|
243
|
+
selected={selected_device.deviceId === device.deviceId}
|
244
|
+
>
|
245
|
+
{device.label}
|
246
|
+
</option>
|
247
|
+
{/each}
|
248
|
+
{/if}
|
249
|
+
</select>
|
250
|
+
{/if}
|
251
|
+
{/if}
|
252
|
+
</div>
|
253
|
+
|
254
|
+
<style>
|
255
|
+
.wrap {
|
256
|
+
position: relative;
|
257
|
+
width: var(--size-full);
|
258
|
+
height: var(--size-full);
|
259
|
+
}
|
260
|
+
|
261
|
+
.hide {
|
262
|
+
display: none;
|
263
|
+
}
|
264
|
+
|
265
|
+
video {
|
266
|
+
width: var(--size-full);
|
267
|
+
height: var(--size-full);
|
268
|
+
object-fit: cover;
|
269
|
+
}
|
270
|
+
|
271
|
+
.button-wrap {
|
272
|
+
position: absolute;
|
273
|
+
background-color: var(--block-background-fill);
|
274
|
+
border: 1px solid var(--border-color-primary);
|
275
|
+
border-radius: var(--radius-xl);
|
276
|
+
padding: var(--size-1-5);
|
277
|
+
display: flex;
|
278
|
+
bottom: var(--size-2);
|
279
|
+
left: 50%;
|
280
|
+
transform: translate(-50%, 0);
|
281
|
+
box-shadow: var(--shadow-drop-lg);
|
282
|
+
border-radius: var(--radius-xl);
|
283
|
+
line-height: var(--size-3);
|
284
|
+
color: var(--button-secondary-text-color);
|
285
|
+
}
|
286
|
+
|
287
|
+
@media (--screen-md) {
|
288
|
+
button {
|
289
|
+
bottom: var(--size-4);
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
@media (--screen-xl) {
|
294
|
+
button {
|
295
|
+
bottom: var(--size-8);
|
296
|
+
}
|
297
|
+
}
|
298
|
+
|
299
|
+
.icon {
|
300
|
+
opacity: 0.8;
|
301
|
+
width: 18px;
|
302
|
+
height: 18px;
|
303
|
+
display: flex;
|
304
|
+
justify-content: space-between;
|
305
|
+
align-items: center;
|
306
|
+
}
|
307
|
+
|
308
|
+
.red {
|
309
|
+
fill: red;
|
310
|
+
stroke: red;
|
311
|
+
}
|
312
|
+
|
313
|
+
.flip {
|
314
|
+
transform: scaleX(-1);
|
315
|
+
}
|
316
|
+
|
317
|
+
.select-wrap {
|
318
|
+
-webkit-appearance: none;
|
319
|
+
-moz-appearance: none;
|
320
|
+
appearance: none;
|
321
|
+
color: var(--button-secondary-text-color);
|
322
|
+
background-color: transparent;
|
323
|
+
width: 95%;
|
324
|
+
font-size: var(--text-md);
|
325
|
+
position: absolute;
|
326
|
+
bottom: var(--size-2);
|
327
|
+
background-color: var(--block-background-fill);
|
328
|
+
box-shadow: var(--shadow-drop-lg);
|
329
|
+
border-radius: var(--radius-xl);
|
330
|
+
z-index: var(--layer-top);
|
331
|
+
border: 1px solid var(--border-color-primary);
|
332
|
+
text-align: left;
|
333
|
+
line-height: var(--size-4);
|
334
|
+
white-space: nowrap;
|
335
|
+
text-overflow: ellipsis;
|
336
|
+
left: 50%;
|
337
|
+
transform: translate(-50%, 0);
|
338
|
+
max-width: var(--size-52);
|
339
|
+
}
|
340
|
+
|
341
|
+
.select-wrap > option {
|
342
|
+
padding: 0.25rem 0.5rem;
|
343
|
+
border-bottom: 1px solid var(--border-color-accent);
|
344
|
+
padding-right: var(--size-8);
|
345
|
+
text-overflow: ellipsis;
|
346
|
+
overflow: hidden;
|
347
|
+
}
|
348
|
+
|
349
|
+
.select-wrap > option:hover {
|
350
|
+
background-color: var(--color-accent);
|
351
|
+
}
|
352
|
+
|
353
|
+
.select-wrap > option:last-child {
|
354
|
+
border: none;
|
355
|
+
}
|
356
|
+
|
357
|
+
.inset-icon {
|
358
|
+
position: absolute;
|
359
|
+
top: 5px;
|
360
|
+
right: -6.5px;
|
361
|
+
width: var(--size-10);
|
362
|
+
height: var(--size-5);
|
363
|
+
opacity: 0.8;
|
364
|
+
}
|
365
|
+
|
366
|
+
@media (--screen-md) {
|
367
|
+
.wrap {
|
368
|
+
font-size: var(--text-lg);
|
369
|
+
}
|
370
|
+
}
|
371
|
+
</style>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import type { I18nFormatter } from "@gradio/utils";
|
3
|
+
import { type FileData, type Client } from "@gradio/client";
|
4
|
+
declare const __propDef: {
|
5
|
+
props: {
|
6
|
+
streaming?: boolean | undefined;
|
7
|
+
pending?: boolean | undefined;
|
8
|
+
root?: string | undefined;
|
9
|
+
mode?: ("image" | "video") | undefined;
|
10
|
+
mirror_webcam: boolean;
|
11
|
+
include_audio: boolean;
|
12
|
+
i18n: I18nFormatter;
|
13
|
+
upload: Client["upload"];
|
14
|
+
click_outside?: ((node: Node, cb: any) => any) | undefined;
|
15
|
+
};
|
16
|
+
events: {
|
17
|
+
stream: CustomEvent<undefined>;
|
18
|
+
capture: CustomEvent<Blob | FileData | null>;
|
19
|
+
error: CustomEvent<string>;
|
20
|
+
start_recording: CustomEvent<undefined>;
|
21
|
+
stop_recording: CustomEvent<undefined>;
|
22
|
+
} & {
|
23
|
+
[evt: string]: CustomEvent<any>;
|
24
|
+
};
|
25
|
+
slots: {};
|
26
|
+
};
|
27
|
+
export type WebcamProps = typeof __propDef.props;
|
28
|
+
export type WebcamEvents = typeof __propDef.events;
|
29
|
+
export type WebcamSlots = typeof __propDef.slots;
|
30
|
+
export default class Webcam extends SvelteComponent<WebcamProps, WebcamEvents, WebcamSlots> {
|
31
|
+
get click_outside(): (node: Node, cb: any) => any;
|
32
|
+
}
|
33
|
+
export {};
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<script>import { Webcam } from "@gradio/icons";
|
2
|
+
import { createEventDispatcher } from "svelte";
|
3
|
+
const dispatch = createEventDispatcher();
|
4
|
+
</script>
|
5
|
+
|
6
|
+
<button style:height="100%" on:click={() => dispatch("click")}>
|
7
|
+
<div class="wrap">
|
8
|
+
<span class="icon-wrap">
|
9
|
+
<Webcam />
|
10
|
+
</span>
|
11
|
+
{"Click to Access Webcam"}
|
12
|
+
</div>
|
13
|
+
</button>
|
14
|
+
|
15
|
+
<style>
|
16
|
+
button {
|
17
|
+
cursor: pointer;
|
18
|
+
width: var(--size-full);
|
19
|
+
}
|
20
|
+
|
21
|
+
.wrap {
|
22
|
+
display: flex;
|
23
|
+
flex-direction: column;
|
24
|
+
justify-content: center;
|
25
|
+
align-items: center;
|
26
|
+
min-height: var(--size-60);
|
27
|
+
color: var(--block-label-text-color);
|
28
|
+
height: 100%;
|
29
|
+
padding-top: var(--size-3);
|
30
|
+
}
|
31
|
+
|
32
|
+
.icon-wrap {
|
33
|
+
width: 30px;
|
34
|
+
margin-bottom: var(--spacing-lg);
|
35
|
+
}
|
36
|
+
|
37
|
+
@media (--screen-md) {
|
38
|
+
.wrap {
|
39
|
+
font-size: var(--text-lg);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
</style>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
declare const __propDef: {
|
3
|
+
props: {
|
4
|
+
[x: string]: never;
|
5
|
+
};
|
6
|
+
events: {
|
7
|
+
click: CustomEvent<undefined>;
|
8
|
+
} & {
|
9
|
+
[evt: string]: CustomEvent<any>;
|
10
|
+
};
|
11
|
+
slots: {};
|
12
|
+
};
|
13
|
+
export type WebcamPermissionsProps = typeof __propDef.props;
|
14
|
+
export type WebcamPermissionsEvents = typeof __propDef.events;
|
15
|
+
export type WebcamPermissionsSlots = typeof __propDef.slots;
|
16
|
+
export default class WebcamPermissions extends SvelteComponent<WebcamPermissionsProps, WebcamPermissionsEvents, WebcamPermissionsSlots> {
|
17
|
+
}
|
18
|
+
export {};
|
@@ -0,0 +1,5 @@
|
|
1
|
+
export declare function get_devices(): Promise<MediaDeviceInfo[]>;
|
2
|
+
export declare function handle_error(error: string): void;
|
3
|
+
export declare function set_local_stream(local_stream: MediaStream | null, video_source: HTMLVideoElement): void;
|
4
|
+
export declare function get_video_stream(include_audio: boolean, video_source: HTMLVideoElement, device_id?: string): Promise<MediaStream>;
|
5
|
+
export declare function set_available_devices(devices: MediaDeviceInfo[]): MediaDeviceInfo[];
|
@@ -0,0 +1,31 @@
|
|
1
|
+
export function get_devices() {
|
2
|
+
return navigator.mediaDevices.enumerateDevices();
|
3
|
+
}
|
4
|
+
export function handle_error(error) {
|
5
|
+
throw new Error(error);
|
6
|
+
}
|
7
|
+
export function set_local_stream(local_stream, video_source) {
|
8
|
+
video_source.srcObject = local_stream;
|
9
|
+
video_source.muted = true;
|
10
|
+
video_source.play();
|
11
|
+
}
|
12
|
+
export async function get_video_stream(include_audio, video_source, device_id) {
|
13
|
+
const size = {
|
14
|
+
width: { ideal: 1920 },
|
15
|
+
height: { ideal: 1440 }
|
16
|
+
};
|
17
|
+
const constraints = {
|
18
|
+
video: device_id ? { deviceId: { exact: device_id }, ...size } : size,
|
19
|
+
audio: include_audio
|
20
|
+
};
|
21
|
+
return navigator.mediaDevices
|
22
|
+
.getUserMedia(constraints)
|
23
|
+
.then((local_stream) => {
|
24
|
+
set_local_stream(local_stream, video_source);
|
25
|
+
return local_stream;
|
26
|
+
});
|
27
|
+
}
|
28
|
+
export function set_available_devices(devices) {
|
29
|
+
const cameras = devices.filter((device) => device.kind === "videoinput");
|
30
|
+
return cameras;
|
31
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const get_coordinates_of_clicked_image: (evt: MouseEvent) => [number, number] | null;
|
@@ -0,0 +1,28 @@
|
|
1
|
+
export const get_coordinates_of_clicked_image = (evt) => {
|
2
|
+
let image;
|
3
|
+
if (evt.currentTarget instanceof Element) {
|
4
|
+
image = evt.currentTarget.querySelector("img");
|
5
|
+
}
|
6
|
+
else {
|
7
|
+
return [NaN, NaN];
|
8
|
+
}
|
9
|
+
const imageRect = image.getBoundingClientRect();
|
10
|
+
const xScale = image.naturalWidth / imageRect.width;
|
11
|
+
const yScale = image.naturalHeight / imageRect.height;
|
12
|
+
if (xScale > yScale) {
|
13
|
+
const displayed_height = image.naturalHeight / xScale;
|
14
|
+
const y_offset = (imageRect.height - displayed_height) / 2;
|
15
|
+
var x = Math.round((evt.clientX - imageRect.left) * xScale);
|
16
|
+
var y = Math.round((evt.clientY - imageRect.top - y_offset) * xScale);
|
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
|
+
}
|
24
|
+
if (x < 0 || x >= image.naturalWidth || y < 0 || y >= image.naturalHeight) {
|
25
|
+
return null;
|
26
|
+
}
|
27
|
+
return [x, y];
|
28
|
+
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@gradio/image",
|
3
|
-
"version": "0.15.
|
3
|
+
"version": "0.15.1",
|
4
4
|
"description": "Gradio UI packages",
|
5
5
|
"type": "module",
|
6
6
|
"author": "",
|
@@ -10,25 +10,44 @@
|
|
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.8.
|
14
|
-
"@gradio/
|
15
|
-
"@gradio/
|
16
|
-
"@gradio/
|
17
|
-
"@gradio/utils": "^0.6.
|
18
|
-
"@gradio/
|
19
|
-
"@gradio/
|
13
|
+
"@gradio/atoms": "^0.8.1",
|
14
|
+
"@gradio/client": "^1.5.2",
|
15
|
+
"@gradio/icons": "^0.7.2",
|
16
|
+
"@gradio/statustracker": "^0.7.6",
|
17
|
+
"@gradio/utils": "^0.6.1",
|
18
|
+
"@gradio/upload": "^0.12.4",
|
19
|
+
"@gradio/wasm": "^0.13.1"
|
20
20
|
},
|
21
21
|
"devDependencies": {
|
22
|
-
"@gradio/preview": "^0.11.
|
22
|
+
"@gradio/preview": "^0.11.1"
|
23
23
|
},
|
24
24
|
"main_changeset": true,
|
25
25
|
"main": "./Index.svelte",
|
26
26
|
"exports": {
|
27
|
-
".": "./
|
28
|
-
"
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
"./package.json": "./package.json",
|
28
|
+
".": {
|
29
|
+
"gradio": "./Index.svelte",
|
30
|
+
"svelte": "./dist/Index.svelte",
|
31
|
+
"types": "./dist/Index.svelte.d.ts"
|
32
|
+
},
|
33
|
+
"./example": {
|
34
|
+
"gradio": "./Example.svelte",
|
35
|
+
"svelte": "./dist/Example.svelte",
|
36
|
+
"types": "./dist/Example.svelte.d.ts"
|
37
|
+
},
|
38
|
+
"./base": {
|
39
|
+
"gradio": "./shared/ImagePreview.svelte",
|
40
|
+
"svelte": "./dist/shared/ImagePreview.svelte",
|
41
|
+
"types": "./dist/shared/ImagePreview.svelte.d.ts"
|
42
|
+
},
|
43
|
+
"./shared": {
|
44
|
+
"gradio": "./shared/index.ts",
|
45
|
+
"svelte": "./dist/shared/index.js",
|
46
|
+
"types": "./dist/shared/index.d.ts"
|
47
|
+
}
|
48
|
+
},
|
49
|
+
"peerDependencies": {
|
50
|
+
"svelte": "^4.0.0"
|
32
51
|
},
|
33
52
|
"repository": {
|
34
53
|
"type": "git",
|