@gradio/annotatedimage 0.11.5 → 0.11.7

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.
@@ -0,0 +1,595 @@
1
+ import { test, describe, afterEach, expect } from "vitest";
2
+ import { cleanup, render, fireEvent } from "@self/tootils/render";
3
+ import { run_shared_prop_tests } from "@self/tootils/shared-prop-tests";
4
+
5
+ import AnnotatedImage from "./Index.svelte";
6
+
7
+ const fake_image = {
8
+ path: "test.png",
9
+ url: "https://example.com/test.png",
10
+ orig_name: "test.png",
11
+ size: 1024,
12
+ mime_type: "image/png",
13
+ is_stream: false
14
+ };
15
+
16
+ const fake_mask_1 = {
17
+ path: "mask1.png",
18
+ url: "https://example.com/mask1.png",
19
+ orig_name: "mask1.png",
20
+ size: 512,
21
+ mime_type: "image/png",
22
+ is_stream: false
23
+ };
24
+
25
+ const fake_mask_2 = {
26
+ path: "mask2.png",
27
+ url: "https://example.com/mask2.png",
28
+ orig_name: "mask2.png",
29
+ size: 512,
30
+ mime_type: "image/png",
31
+ is_stream: false
32
+ };
33
+
34
+ const fake_value = {
35
+ image: fake_image,
36
+ annotations: [
37
+ { image: fake_mask_1, label: "cat" },
38
+ { image: fake_mask_2, label: "dog" }
39
+ ]
40
+ };
41
+
42
+ const single_annotation_value = {
43
+ image: fake_image,
44
+ annotations: [{ image: fake_mask_1, label: "cat" }]
45
+ };
46
+
47
+ const default_props = {
48
+ value: null as any,
49
+ label: "Annotated Image",
50
+ show_label: true,
51
+ show_legend: true,
52
+ color_map: {} as Record<string, string>,
53
+ buttons: ["fullscreen"] as (
54
+ | string
55
+ | { value: string; id: number; icon: null }
56
+ )[]
57
+ };
58
+
59
+ // BlockLabel in AnnotatedImage doesn't use data-testid='block-info',
60
+ // so the shared show_label: false test fails. Disable has_label and
61
+ // write custom label tests below.
62
+ run_shared_prop_tests({
63
+ component: AnnotatedImage,
64
+ name: "AnnotatedImage",
65
+ base_props: {
66
+ ...default_props
67
+ },
68
+ has_label: false,
69
+ has_validation_error: false
70
+ });
71
+
72
+ describe("Label", () => {
73
+ afterEach(() => cleanup());
74
+
75
+ test("label text is rendered", async () => {
76
+ const { getByText } = await render(AnnotatedImage, {
77
+ ...default_props,
78
+ label: "My Image",
79
+ show_label: true
80
+ });
81
+
82
+ expect(getByText("My Image")).toBeTruthy();
83
+ expect(getByText("My Image")).toBeVisible();
84
+ });
85
+
86
+ test("show_label=false hides the label visually", async () => {
87
+ const { getByText } = await render(AnnotatedImage, {
88
+ ...default_props,
89
+ label: "My Image",
90
+ show_label: false
91
+ });
92
+
93
+ // BlockLabel hides via CSS when show_label is false
94
+ expect(getByText("My Image")).not.toBeVisible();
95
+ });
96
+ });
97
+
98
+ describe("AnnotatedImage", () => {
99
+ afterEach(() => cleanup());
100
+
101
+ test("renders empty state when value is null", async () => {
102
+ const { queryByAltText } = await render(AnnotatedImage, {
103
+ ...default_props,
104
+ value: null
105
+ });
106
+
107
+ expect(queryByAltText("the base file that is annotated")).toBeNull();
108
+ });
109
+
110
+ test("renders base image when value is set", async () => {
111
+ const { getByAltText } = await render(AnnotatedImage, {
112
+ ...default_props,
113
+ value: fake_value
114
+ });
115
+
116
+ const img = getByAltText("the base file that is annotated");
117
+ expect(img).toBeTruthy();
118
+ expect(img.getAttribute("src")).toBe("https://example.com/test.png");
119
+ });
120
+
121
+ test("renders annotation masks when value has annotations", async () => {
122
+ const { container } = await render(AnnotatedImage, {
123
+ ...default_props,
124
+ value: fake_value
125
+ });
126
+
127
+ const masks = container.querySelectorAll("img.mask");
128
+ expect(masks.length).toBe(2);
129
+ expect(masks[0].getAttribute("src")).toBe("https://example.com/mask1.png");
130
+ expect(masks[1].getAttribute("src")).toBe("https://example.com/mask2.png");
131
+ });
132
+ });
133
+
134
+ describe("Props: show_legend", () => {
135
+ afterEach(() => cleanup());
136
+
137
+ test("show_legend=true renders legend buttons for each annotation", async () => {
138
+ const { getByRole } = await render(AnnotatedImage, {
139
+ ...default_props,
140
+ value: fake_value,
141
+ show_legend: true
142
+ });
143
+
144
+ expect(getByRole("button", { name: "cat" })).toBeTruthy();
145
+ expect(getByRole("button", { name: "dog" })).toBeTruthy();
146
+ expect(getByRole("button", { name: "cat" })).toBeVisible();
147
+ expect(getByRole("button", { name: "dog" })).toBeVisible();
148
+ });
149
+
150
+ test("show_legend=false hides legend", async () => {
151
+ const { queryByRole } = await render(AnnotatedImage, {
152
+ ...default_props,
153
+ value: fake_value,
154
+ show_legend: false
155
+ });
156
+
157
+ expect(queryByRole("button", { name: "cat" })).toBeNull();
158
+ expect(queryByRole("button", { name: "dog" })).toBeNull();
159
+ });
160
+
161
+ test("legend is not rendered when value is null", async () => {
162
+ const { queryByRole } = await render(AnnotatedImage, {
163
+ ...default_props,
164
+ value: null,
165
+ show_legend: true
166
+ });
167
+
168
+ expect(queryByRole("button", { name: "cat" })).toBeNull();
169
+ });
170
+ });
171
+
172
+ describe("Props: color_map", () => {
173
+ afterEach(() => cleanup());
174
+
175
+ test("color_map applies custom background colors to legend items", async () => {
176
+ const { getByRole } = await render(AnnotatedImage, {
177
+ ...default_props,
178
+ value: fake_value,
179
+ color_map: { cat: "#ff0000", dog: "#00ff00" }
180
+ });
181
+
182
+ const catBtn = getByRole("button", { name: "cat" });
183
+ const dogBtn = getByRole("button", { name: "dog" });
184
+
185
+ // color_map colors get '88' (0x88/0xff ≈ 0.533) appended for semi-transparency
186
+ // The browser normalizes to rgba
187
+ expect(catBtn.style.backgroundColor).toContain("rgba(255, 0, 0");
188
+ expect(dogBtn.style.backgroundColor).toContain("rgba(0, 255, 0");
189
+ });
190
+
191
+ test("without color_map, masks have hue-rotate filter style", async () => {
192
+ const { container } = await render(AnnotatedImage, {
193
+ ...default_props,
194
+ value: fake_value,
195
+ color_map: {}
196
+ });
197
+
198
+ const masks = container.querySelectorAll("img.mask");
199
+ // First mask: hue-rotate(0deg), second: hue-rotate(180deg)
200
+ expect(masks[0].getAttribute("style")).toContain("hue-rotate(0deg)");
201
+ expect(masks[1].getAttribute("style")).toContain("hue-rotate(180deg)");
202
+ });
203
+
204
+ test("with color_map, masks have no hue-rotate filter", async () => {
205
+ const { container } = await render(AnnotatedImage, {
206
+ ...default_props,
207
+ value: fake_value,
208
+ color_map: { cat: "#ff0000", dog: "#00ff00" }
209
+ });
210
+
211
+ const masks = container.querySelectorAll("img.mask");
212
+ for (const mask of masks) {
213
+ const style = mask.getAttribute("style");
214
+ // Should be null or not contain hue-rotate
215
+ if (style) {
216
+ expect(style).not.toContain("hue-rotate");
217
+ }
218
+ }
219
+ });
220
+ });
221
+
222
+ describe("Props: buttons", () => {
223
+ afterEach(() => cleanup());
224
+
225
+ test("fullscreen button renders when 'fullscreen' is in buttons", async () => {
226
+ const { getByLabelText } = await render(AnnotatedImage, {
227
+ ...default_props,
228
+ value: fake_value,
229
+ buttons: ["fullscreen"]
230
+ });
231
+
232
+ expect(getByLabelText("Fullscreen")).toBeTruthy();
233
+ expect(getByLabelText("Fullscreen")).toBeVisible();
234
+ });
235
+
236
+ test("empty buttons array shows no fullscreen button", async () => {
237
+ const { queryByLabelText } = await render(AnnotatedImage, {
238
+ ...default_props,
239
+ value: fake_value,
240
+ buttons: []
241
+ });
242
+
243
+ expect(queryByLabelText("Fullscreen")).toBeNull();
244
+ });
245
+
246
+ test("custom button renders and dispatches custom_button_click", async () => {
247
+ const { listen, getByLabelText } = await render(AnnotatedImage, {
248
+ ...default_props,
249
+ value: fake_value,
250
+ buttons: [{ value: "Analyze", id: 5, icon: null }]
251
+ });
252
+
253
+ const custom = listen("custom_button_click");
254
+ const btn = getByLabelText("Analyze");
255
+
256
+ await fireEvent.click(btn);
257
+
258
+ expect(custom).toHaveBeenCalledTimes(1);
259
+ expect(custom).toHaveBeenCalledWith({ id: 5 });
260
+ });
261
+
262
+ test("buttons not rendered when value is null (empty state)", async () => {
263
+ const { queryByLabelText } = await render(AnnotatedImage, {
264
+ ...default_props,
265
+ value: null,
266
+ buttons: ["fullscreen"]
267
+ });
268
+
269
+ expect(queryByLabelText("Fullscreen")).toBeNull();
270
+ });
271
+ });
272
+
273
+ describe("Events: change", () => {
274
+ afterEach(() => cleanup());
275
+
276
+ test("setting value triggers change event", async () => {
277
+ const { listen, set_data } = await render(AnnotatedImage, {
278
+ ...default_props,
279
+ value: null
280
+ });
281
+
282
+ const change = listen("change");
283
+
284
+ await set_data({ value: fake_value });
285
+
286
+ expect(change).toHaveBeenCalledTimes(1);
287
+ });
288
+
289
+ test("change event is not triggered on mount with null value", async () => {
290
+ const { listen } = await render(AnnotatedImage, {
291
+ ...default_props,
292
+ value: null
293
+ });
294
+
295
+ const change = listen("change", { retrospective: true });
296
+
297
+ expect(change).not.toHaveBeenCalled();
298
+ });
299
+
300
+ test("change event does not fire once on mount when initial value is set", async () => {
301
+ // The component's $effect fires change when old_value != value on mount
302
+ const { listen } = await render(AnnotatedImage, {
303
+ ...default_props,
304
+ value: fake_value
305
+ });
306
+
307
+ const change = listen("change", { retrospective: true });
308
+
309
+ expect(change).not.toHaveBeenCalled();
310
+ });
311
+
312
+ test("changing value multiple times triggers change each time", async () => {
313
+ const { listen, set_data } = await render(AnnotatedImage, {
314
+ ...default_props,
315
+ value: null
316
+ });
317
+
318
+ const change = listen("change");
319
+
320
+ await set_data({ value: fake_value });
321
+ expect(change).toHaveBeenCalledTimes(1);
322
+ await set_data({ value: single_annotation_value });
323
+
324
+ expect(change).toHaveBeenCalledTimes(2);
325
+ });
326
+
327
+ test("setting value to null after a value triggers change", async () => {
328
+ const { listen, set_data } = await render(AnnotatedImage, {
329
+ ...default_props,
330
+ value: fake_value
331
+ });
332
+
333
+ const change = listen("change");
334
+
335
+ await set_data({ value: null });
336
+
337
+ expect(change).toHaveBeenCalledTimes(1);
338
+ });
339
+ });
340
+
341
+ describe("Events: select", () => {
342
+ afterEach(() => cleanup());
343
+
344
+ test("clicking legend item dispatches select with value and index", async () => {
345
+ const { listen, getByRole } = await render(AnnotatedImage, {
346
+ ...default_props,
347
+ value: fake_value
348
+ });
349
+
350
+ const select = listen("select");
351
+
352
+ await fireEvent.click(getByRole("button", { name: "cat" }));
353
+
354
+ expect(select).toHaveBeenCalledTimes(1);
355
+ expect(select).toHaveBeenCalledWith({ value: "cat", index: 0 });
356
+ });
357
+
358
+ test("clicking second legend item dispatches correct index", async () => {
359
+ const { listen, getByRole } = await render(AnnotatedImage, {
360
+ ...default_props,
361
+ value: fake_value
362
+ });
363
+
364
+ const select = listen("select");
365
+
366
+ await fireEvent.click(getByRole("button", { name: "dog" }));
367
+
368
+ expect(select).toHaveBeenCalledTimes(1);
369
+ expect(select).toHaveBeenCalledWith({ value: "dog", index: 1 });
370
+ });
371
+
372
+ test("clicking multiple legend items dispatches select for each", async () => {
373
+ const { listen, getByRole } = await render(AnnotatedImage, {
374
+ ...default_props,
375
+ value: fake_value
376
+ });
377
+
378
+ const select = listen("select");
379
+
380
+ await fireEvent.click(getByRole("button", { name: "cat" }));
381
+ await fireEvent.click(getByRole("button", { name: "dog" }));
382
+
383
+ expect(select).toHaveBeenCalledTimes(2);
384
+ expect(select).toHaveBeenNthCalledWith(1, { value: "cat", index: 0 });
385
+ expect(select).toHaveBeenNthCalledWith(2, { value: "dog", index: 1 });
386
+ });
387
+ });
388
+
389
+ describe("Legend hover interactions", () => {
390
+ afterEach(() => cleanup());
391
+
392
+ test("hovering over legend item sets active class on corresponding mask", async () => {
393
+ const { container, getByRole } = await render(AnnotatedImage, {
394
+ ...default_props,
395
+ value: fake_value
396
+ });
397
+
398
+ await fireEvent.mouseOver(getByRole("button", { name: "cat" }));
399
+
400
+ const masks = container.querySelectorAll("img.mask");
401
+ expect(masks[0].classList.contains("active")).toBe(true);
402
+ });
403
+
404
+ test("hovering over legend item sets inactive class on other masks", async () => {
405
+ const { container, getByRole } = await render(AnnotatedImage, {
406
+ ...default_props,
407
+ value: fake_value
408
+ });
409
+
410
+ await fireEvent.mouseOver(getByRole("button", { name: "cat" }));
411
+
412
+ const masks = container.querySelectorAll("img.mask");
413
+ expect(masks[0].classList.contains("active")).toBe(true);
414
+ expect(masks[1].classList.contains("inactive")).toBe(true);
415
+ });
416
+
417
+ test("mouseout resets all masks to default state", async () => {
418
+ const { container, getByRole } = await render(AnnotatedImage, {
419
+ ...default_props,
420
+ value: fake_value
421
+ });
422
+
423
+ await fireEvent.mouseOver(getByRole("button", { name: "cat" }));
424
+ await fireEvent.mouseOut(getByRole("button", { name: "cat" }));
425
+
426
+ const masks = container.querySelectorAll("img.mask");
427
+ expect(masks[0].classList.contains("active")).toBe(false);
428
+ expect(masks[0].classList.contains("inactive")).toBe(false);
429
+ expect(masks[1].classList.contains("active")).toBe(false);
430
+ expect(masks[1].classList.contains("inactive")).toBe(false);
431
+ });
432
+
433
+ test("focus on legend item activates mask (keyboard accessibility)", async () => {
434
+ const { container, getByRole } = await render(AnnotatedImage, {
435
+ ...default_props,
436
+ value: fake_value
437
+ });
438
+
439
+ await fireEvent.focus(getByRole("button", { name: "dog" }));
440
+
441
+ const masks = container.querySelectorAll("img.mask");
442
+ expect(masks[1].classList.contains("active")).toBe(true);
443
+ expect(masks[0].classList.contains("inactive")).toBe(true);
444
+ });
445
+
446
+ test("blur on legend item resets masks (keyboard accessibility)", async () => {
447
+ const { container, getByRole } = await render(AnnotatedImage, {
448
+ ...default_props,
449
+ value: fake_value
450
+ });
451
+
452
+ await fireEvent.focus(getByRole("button", { name: "dog" }));
453
+ await fireEvent.blur(getByRole("button", { name: "dog" }));
454
+
455
+ const masks = container.querySelectorAll("img.mask");
456
+ expect(masks[0].classList.contains("active")).toBe(false);
457
+ expect(masks[0].classList.contains("inactive")).toBe(false);
458
+ expect(masks[1].classList.contains("active")).toBe(false);
459
+ expect(masks[1].classList.contains("inactive")).toBe(false);
460
+ });
461
+ });
462
+
463
+ describe("get_data / set_data", () => {
464
+ afterEach(() => cleanup());
465
+
466
+ test("get_data returns null when no value", async () => {
467
+ const { get_data } = await render(AnnotatedImage, {
468
+ ...default_props,
469
+ value: null
470
+ });
471
+
472
+ const data = await get_data();
473
+ expect(data.value).toBeNull();
474
+ });
475
+
476
+ test("set_data updates the displayed image", async () => {
477
+ const { set_data, getByAltText } = await render(AnnotatedImage, {
478
+ ...default_props,
479
+ value: null
480
+ });
481
+
482
+ await set_data({ value: fake_value });
483
+
484
+ const img = getByAltText("the base file that is annotated");
485
+ expect(img).toBeTruthy();
486
+ expect(img.getAttribute("src")).toBe("https://example.com/test.png");
487
+ });
488
+
489
+ test("get_data reflects set_data value (round-trip)", async () => {
490
+ const { get_data, set_data } = await render(AnnotatedImage, {
491
+ ...default_props,
492
+ value: null
493
+ });
494
+
495
+ await set_data({ value: fake_value });
496
+
497
+ const data = await get_data();
498
+ expect(data.value).toEqual(fake_value);
499
+ });
500
+
501
+ test("set_data to null shows empty state", async () => {
502
+ const { set_data, queryByAltText } = await render(AnnotatedImage, {
503
+ ...default_props,
504
+ value: fake_value
505
+ });
506
+
507
+ await set_data({ value: null });
508
+
509
+ expect(queryByAltText("the base file that is annotated")).toBeNull();
510
+ });
511
+
512
+ test("set_data updates legend items", async () => {
513
+ const { set_data, getByRole, queryByRole } = await render(AnnotatedImage, {
514
+ ...default_props,
515
+ value: null
516
+ });
517
+
518
+ await set_data({ value: fake_value });
519
+
520
+ expect(getByRole("button", { name: "cat" })).toBeTruthy();
521
+ expect(getByRole("button", { name: "dog" })).toBeTruthy();
522
+
523
+ await set_data({ value: single_annotation_value });
524
+
525
+ expect(getByRole("button", { name: "cat" })).toBeTruthy();
526
+ expect(queryByRole("button", { name: "dog" })).toBeNull();
527
+ });
528
+ });
529
+
530
+ describe("Edge cases", () => {
531
+ afterEach(() => cleanup());
532
+
533
+ test("single annotation renders without errors in hue calculation", async () => {
534
+ const { container } = await render(AnnotatedImage, {
535
+ ...default_props,
536
+ value: single_annotation_value,
537
+ color_map: {}
538
+ });
539
+
540
+ const masks = container.querySelectorAll("img.mask");
541
+ expect(masks.length).toBe(1);
542
+ // With 1 annotation: hue-rotate(0 * 360 / 1) = hue-rotate(0deg)
543
+ expect(masks[0].getAttribute("style")).toContain("hue-rotate(0deg)");
544
+ });
545
+
546
+ test("value with empty annotations array renders base image but no masks or legend", async () => {
547
+ const { container, getByAltText, queryByRole } = await render(
548
+ AnnotatedImage,
549
+ {
550
+ ...default_props,
551
+ value: {
552
+ image: fake_image,
553
+ annotations: []
554
+ }
555
+ }
556
+ );
557
+
558
+ expect(getByAltText("the base file that is annotated")).toBeTruthy();
559
+
560
+ const masks = container.querySelectorAll("img.mask");
561
+ expect(masks.length).toBe(0);
562
+
563
+ // No legend buttons since there are no annotations
564
+ expect(queryByRole("button", { name: "cat" })).toBeNull();
565
+ });
566
+
567
+ test("color_map with only some labels falls back to hue-rotate for unmapped labels", async () => {
568
+ const { container } = await render(AnnotatedImage, {
569
+ ...default_props,
570
+ value: fake_value,
571
+ color_map: { cat: "#ff0000" } // only cat is mapped, dog is not
572
+ });
573
+
574
+ const masks = container.querySelectorAll("img.mask");
575
+ // cat mask: has color_map entry, no hue-rotate
576
+ const catStyle = masks[0].getAttribute("style");
577
+ if (catStyle) {
578
+ expect(catStyle).not.toContain("hue-rotate");
579
+ }
580
+ // dog mask: no color_map entry, should have hue-rotate
581
+ expect(masks[1].getAttribute("style")).toContain("hue-rotate");
582
+ });
583
+ });
584
+
585
+ test.todo(
586
+ "VISUAL: mask opacity transitions on hover — masks should fade to 0.3 opacity on container hover, active mask at 1.0, inactive at 0 — needs Playwright visual regression screenshot comparison"
587
+ );
588
+
589
+ test.todo(
590
+ "VISUAL: height/width props control component dimensions — needs Playwright visual regression screenshot comparison"
591
+ );
592
+
593
+ test.todo(
594
+ "VISUAL: default hue-rotate coloring distributes colors evenly across the color wheel — needs Playwright visual regression screenshot comparison"
595
+ );
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @gradio/annotatedimage
2
2
 
3
+ ## 0.11.7
4
+
5
+ ### Features
6
+
7
+ - [#13185](https://github.com/gradio-app/gradio/pull/13185) [`ffc00ff`](https://github.com/gradio-app/gradio/commit/ffc00ff4cf23641e90f0963cec6ed52f85ed511c) - Annotated image unit tests. Thanks @freddyaboulton!
8
+
9
+ ### Dependency updates
10
+
11
+ - @gradio/atoms@0.23.1
12
+ - @gradio/statustracker@0.14.0
13
+ - @gradio/client@2.2.0
14
+
15
+ ## 0.11.6
16
+
17
+ ### Dependency updates
18
+
19
+ - @gradio/utils@0.12.2
20
+ - @gradio/atoms@0.23.0
21
+ - @gradio/statustracker@0.13.1
22
+ - @gradio/upload@0.17.8
23
+
3
24
  ## 0.11.5
4
25
 
5
26
  ### Dependency updates
package/Index.svelte CHANGED
@@ -15,7 +15,6 @@
15
15
  const props = $props();
16
16
  const gradio = new Gradio<AnnotatedImageEvents, AnnotatedImageProps>(props);
17
17
 
18
- let old_value = $state(gradio.props.value);
19
18
  let active: string | null = $state(null);
20
19
  let image_container: HTMLElement;
21
20
  let fullscreen = $state(false);
@@ -23,12 +22,7 @@
23
22
  gradio.shared.label || gradio.i18n("annotated_image.annotated_image")
24
23
  );
25
24
 
26
- $effect(() => {
27
- if (old_value != gradio.props.value) {
28
- old_value = gradio.props.value;
29
- gradio.dispatch("change");
30
- }
31
- });
25
+ gradio.watch_for_change();
32
26
 
33
27
  function handle_mouseover(_label: string): void {
34
28
  active = _label;
package/dist/Index.svelte CHANGED
@@ -15,7 +15,6 @@
15
15
  const props = $props();
16
16
  const gradio = new Gradio<AnnotatedImageEvents, AnnotatedImageProps>(props);
17
17
 
18
- let old_value = $state(gradio.props.value);
19
18
  let active: string | null = $state(null);
20
19
  let image_container: HTMLElement;
21
20
  let fullscreen = $state(false);
@@ -23,12 +22,7 @@
23
22
  gradio.shared.label || gradio.i18n("annotated_image.annotated_image")
24
23
  );
25
24
 
26
- $effect(() => {
27
- if (old_value != gradio.props.value) {
28
- old_value = gradio.props.value;
29
- gradio.dispatch("change");
30
- }
31
- });
25
+ gradio.watch_for_change();
32
26
 
33
27
  function handle_mouseover(_label: string): void {
34
28
  active = _label;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/annotatedimage",
3
- "version": "0.11.5",
3
+ "version": "0.11.7",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -16,18 +16,18 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "devDependencies": {
19
- "@gradio/preview": "^0.16.1"
19
+ "@gradio/preview": "^0.16.2"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "svelte": "^5.48.0"
23
23
  },
24
24
  "dependencies": {
25
- "@gradio/atoms": "^0.22.2",
25
+ "@gradio/atoms": "^0.23.1",
26
+ "@gradio/statustracker": "^0.14.0",
26
27
  "@gradio/icons": "^0.15.1",
27
- "@gradio/upload": "^0.17.7",
28
- "@gradio/statustracker": "^0.13.0",
29
- "@gradio/utils": "^0.12.1",
30
- "@gradio/client": "^2.1.0"
28
+ "@gradio/upload": "^0.17.8",
29
+ "@gradio/utils": "^0.12.2",
30
+ "@gradio/client": "^2.2.0"
31
31
  },
32
32
  "repository": {
33
33
  "type": "git",