@gradio/core 0.27.2 → 0.28.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/package.json CHANGED
@@ -1,69 +1,69 @@
1
1
  {
2
2
  "name": "@gradio/core",
3
- "version": "0.27.2",
3
+ "version": "0.28.0",
4
4
  "type": "module",
5
5
  "devDependencies": {
6
- "@gradio/accordion": "^0.5.22",
7
- "@gradio/annotatedimage": "^0.9.29",
8
- "@gradio/audio": "^0.17.26",
9
- "@gradio/box": "^0.2.23",
6
+ "@gradio/accordion": "^0.5.23",
7
+ "@gradio/annotatedimage": "^0.9.30",
8
+ "@gradio/atoms": "^0.17.0",
9
+ "@gradio/audio": "^0.18.0",
10
10
  "@gradio/browserstate": "^0.3.2",
11
- "@gradio/atoms": "^0.16.5",
12
- "@gradio/button": "^0.5.11",
13
- "@gradio/checkbox": "^0.4.28",
14
- "@gradio/chatbot": "^0.26.23",
15
- "@gradio/checkboxgroup": "^0.6.27",
16
- "@gradio/code": "^0.14.15",
17
- "@gradio/client": "^1.17.1",
18
- "@gradio/colorpicker": "^0.4.27",
19
- "@gradio/column": "^0.2.1",
20
- "@gradio/dataframe": "^0.19.1",
21
- "@gradio/dataset": "^0.4.32",
22
- "@gradio/dropdown": "^0.10.2",
23
- "@gradio/fallback": "^0.4.27",
24
- "@gradio/datetime": "^0.3.20",
11
+ "@gradio/box": "^0.2.24",
12
+ "@gradio/button": "^0.5.12",
13
+ "@gradio/chatbot": "^0.26.24",
14
+ "@gradio/checkbox": "^0.4.29",
15
+ "@gradio/checkboxgroup": "^0.6.28",
16
+ "@gradio/client": "^1.18.0",
17
+ "@gradio/code": "^0.14.16",
18
+ "@gradio/colorpicker": "^0.4.28",
19
+ "@gradio/dataframe": "^0.19.2",
20
+ "@gradio/dataset": "^0.4.33",
21
+ "@gradio/datetime": "^0.3.21",
25
22
  "@gradio/downloadbutton": "^0.4.11",
26
- "@gradio/fileexplorer": "^0.5.39",
27
- "@gradio/file": "^0.12.28",
28
- "@gradio/form": "^0.2.23",
23
+ "@gradio/dropdown": "^0.10.3",
24
+ "@gradio/fallback": "^0.4.28",
25
+ "@gradio/column": "^0.2.1",
26
+ "@gradio/file": "^0.12.29",
27
+ "@gradio/fileexplorer": "^0.5.40",
28
+ "@gradio/form": "^0.2.24",
29
+ "@gradio/highlightedtext": "^0.9.11",
29
30
  "@gradio/group": "^0.2.0",
30
- "@gradio/html": "^0.7.0",
31
- "@gradio/icons": "^0.13.1",
32
- "@gradio/highlightedtext": "^0.9.10",
33
- "@gradio/gallery": "^0.15.31",
34
- "@gradio/imageeditor": "^0.16.5",
35
- "@gradio/image": "^0.22.17",
36
- "@gradio/imageslider": "^0.2.13",
37
- "@gradio/json": "^0.5.29",
38
- "@gradio/label": "^0.5.19",
39
- "@gradio/model3d": "^0.14.25",
40
- "@gradio/markdown": "^0.13.20",
41
- "@gradio/number": "^0.6.4",
42
- "@gradio/multimodaltextbox": "^0.10.17",
43
- "@gradio/paramviewer": "^0.7.15",
44
- "@gradio/nativeplot": "^0.7.4",
45
- "@gradio/plot": "^0.9.22",
31
+ "@gradio/html": "^0.7.1",
32
+ "@gradio/gallery": "^0.15.32",
33
+ "@gradio/icons": "^0.14.0",
34
+ "@gradio/imageeditor": "^0.16.6",
35
+ "@gradio/imageslider": "^0.2.14",
36
+ "@gradio/image": "^0.22.18",
37
+ "@gradio/json": "^0.5.30",
38
+ "@gradio/label": "^0.5.20",
39
+ "@gradio/multimodaltextbox": "^0.10.18",
40
+ "@gradio/nativeplot": "^0.7.5",
41
+ "@gradio/model3d": "^0.14.26",
42
+ "@gradio/markdown": "^0.13.21",
43
+ "@gradio/number": "^0.7.0",
44
+ "@gradio/paramviewer": "^0.7.16",
46
45
  "@gradio/row": "^0.2.1",
47
- "@gradio/sidebar": "^0.1.20",
48
- "@gradio/radio": "^0.7.10",
49
- "@gradio/simpledropdown": "^0.3.27",
50
- "@gradio/simpletextbox": "^0.3.28",
46
+ "@gradio/simpleimage": "^0.8.40",
47
+ "@gradio/sidebar": "^0.1.21",
48
+ "@gradio/plot": "^0.9.23",
49
+ "@gradio/simpledropdown": "^0.3.28",
50
+ "@gradio/simpletextbox": "^0.3.29",
51
+ "@gradio/sketchbox": "^0.6.16",
52
+ "@gradio/slider": "^0.6.17",
51
53
  "@gradio/state": "^0.1.2",
52
- "@gradio/sketchbox": "^0.6.15",
53
- "@gradio/simpleimage": "^0.8.39",
54
- "@gradio/statustracker": "^0.10.18",
55
- "@gradio/tabitem": "^0.5.0",
56
- "@gradio/slider": "^0.6.16",
57
- "@gradio/textbox": "^0.10.20",
58
- "@gradio/tabs": "^0.4.5",
54
+ "@gradio/statustracker": "^0.11.0",
55
+ "@gradio/tabitem": "^0.6.0",
56
+ "@gradio/radio": "^0.7.11",
57
+ "@gradio/tabs": "^0.5.0",
58
+ "@gradio/textbox": "^0.11.0",
59
+ "@gradio/timer": "^0.4.5",
59
60
  "@gradio/theme": "^0.4.0",
60
61
  "@gradio/uploadbutton": "^0.9.11",
61
- "@gradio/upload": "^0.16.16",
62
- "@gradio/timer": "^0.4.5",
63
62
  "@gradio/utils": "^0.10.2",
64
- "@gradio/vibeeditor": "^0.2.2",
65
- "@gradio/wasm": "^0.18.1",
66
- "@gradio/video": "^0.15.0"
63
+ "@gradio/upload": "^0.16.17",
64
+ "@gradio/vibeeditor": "^0.2.3",
65
+ "@gradio/video": "^0.15.1",
66
+ "@gradio/wasm": "^0.18.1"
67
67
  },
68
68
  "msw": {
69
69
  "workerDirectory": "public"
@@ -80,6 +80,11 @@
80
80
  "svelte": "./dist/src/Login.svelte",
81
81
  "types": "./dist/src/Login.svelte.d.ts"
82
82
  },
83
+ "./navbar_store": {
84
+ "gradio": "./src/navbar_store.ts",
85
+ "import": "./src/navbar_store.ts",
86
+ "types": "./src/navbar_store.ts"
87
+ },
83
88
  "./package.json": "./package.json",
84
89
  ".": {
85
90
  "gradio": "./index.ts",
package/src/Blocks.svelte CHANGED
@@ -255,6 +255,19 @@
255
255
  });
256
256
  update_value(updates);
257
257
 
258
+ // Handle navbar updates separately since they need to be updated in the store.
259
+ updates.forEach((update) => {
260
+ const component = components.find((comp) => comp.id === update.id);
261
+ if (component && component.type === "navbar") {
262
+ import("./navbar_store").then(({ navbar_config }) => {
263
+ navbar_config.update((current) => ({
264
+ ...current,
265
+ [update.prop]: update.value
266
+ }));
267
+ });
268
+ }
269
+ });
270
+
258
271
  await tick();
259
272
  }
260
273
 
@@ -623,6 +636,53 @@
623
636
 
624
637
  /* eslint-disable complexity */
625
638
  function handle_status_update(message: StatusMessage): void {
639
+ if (message.code === "validation_error") {
640
+ const dep = dependencies.find((dep) => dep.id === message.fn_index);
641
+ if (
642
+ dep === undefined ||
643
+ message.message === undefined ||
644
+ typeof message.message === "string"
645
+ ) {
646
+ return;
647
+ }
648
+
649
+ const validation_error_data: {
650
+ id: number;
651
+ prop: string;
652
+ value: unknown;
653
+ }[] = [];
654
+
655
+ message.message.forEach((message, i) => {
656
+ if (message.is_valid) {
657
+ return;
658
+ }
659
+ validation_error_data.push({
660
+ id: dep.inputs[i],
661
+ prop: "validation_error",
662
+ value: message.message
663
+ });
664
+
665
+ validation_error_data.push({
666
+ id: dep.inputs[i],
667
+ prop: "loading_status",
668
+ value: { validation_error: message.message }
669
+ });
670
+ });
671
+
672
+ if (validation_error_data.length > 0) {
673
+ update_value(validation_error_data);
674
+ loading_status.update({
675
+ status: "complete",
676
+ fn_index: message.fn_index,
677
+ eta: 0,
678
+ queue: false,
679
+ queue_position: null
680
+ });
681
+ set_status($loading_status);
682
+
683
+ return;
684
+ }
685
+ }
626
686
  if (message.broken && !broken_connection) {
627
687
  messages = [
628
688
  new_message(
@@ -729,7 +789,7 @@
729
789
  !broken_connection &&
730
790
  !message.session_not_found
731
791
  ) {
732
- if (status.message) {
792
+ if (status.message && typeof status.message === "string") {
733
793
  const _message = status.message.replace(
734
794
  MESSAGE_QUOTE_RE,
735
795
  (_, b) => b
@@ -814,6 +874,11 @@
814
874
  target.addEventListener("prop_change", (e: Event) => {
815
875
  if (!isCustomEvent(e)) throw new Error("not a custom event");
816
876
  const { id, prop, value } = e.detail;
877
+ if (prop === "value") {
878
+ update_value([
879
+ { id, prop: "loading_status", value: { validation_error: undefined } }
880
+ ]);
881
+ }
817
882
  update_value([{ id, prop, value }]);
818
883
  if (prop === "input_ready" && value === false) {
819
884
  inputs_waiting.push(id);
@@ -990,6 +1055,40 @@
990
1055
  screen_recorder.startRecording();
991
1056
  }
992
1057
  }
1058
+
1059
+ let footer_height = 0;
1060
+
1061
+ let root_container: HTMLElement;
1062
+ $: root_node = $_layout && get_root_node(root_container);
1063
+
1064
+ function get_root_node(container: HTMLElement | null): HTMLElement | null {
1065
+ if (!container) return null;
1066
+ return container.children[container.children.length - 1] as HTMLElement;
1067
+ }
1068
+
1069
+ onMount(() => {
1070
+ if ("parentIFrame" in window) {
1071
+ window.parentIFrame?.autoResize(false);
1072
+ }
1073
+
1074
+ const mut = new MutationObserver((mutations) => {
1075
+ if ("parentIFrame" in window) {
1076
+ const box = root_node?.getBoundingClientRect();
1077
+ if (!box) return;
1078
+ window.parentIFrame?.size(box.bottom + footer_height + 32);
1079
+ }
1080
+ });
1081
+
1082
+ mut.observe(root_container, {
1083
+ childList: true,
1084
+ subtree: true,
1085
+ attributes: true
1086
+ });
1087
+
1088
+ return () => {
1089
+ mut.disconnect();
1090
+ };
1091
+ });
993
1092
  </script>
994
1093
 
995
1094
  <svelte:head>
@@ -1005,6 +1104,7 @@
1005
1104
  <div
1006
1105
  class="contain"
1007
1106
  style:flex-grow={app_mode ? "1" : "auto"}
1107
+ bind:this={root_container}
1008
1108
  style:margin-right={vibe_mode ? `${vibe_editor_width}px` : "0"}
1009
1109
  >
1010
1110
  {#if $_layout && app.config}
@@ -1023,7 +1123,7 @@
1023
1123
  </div>
1024
1124
 
1025
1125
  {#if show_footer}
1026
- <footer>
1126
+ <footer bind:clientHeight={footer_height}>
1027
1127
  {#if show_api}
1028
1128
  <button
1029
1129
  on:click={() => {
package/src/Embed.svelte CHANGED
@@ -1,7 +1,9 @@
1
1
  <script lang="ts">
2
- import { getContext } from "svelte";
2
+ import { getContext, onMount } from "svelte";
3
3
  import space_logo from "./images/spaces.svg";
4
4
  import { _ } from "svelte-i18n";
5
+ import { navbar_config } from "./navbar_store";
6
+
5
7
  export let wrapper: HTMLDivElement;
6
8
  export let version: string;
7
9
  export let initial_height: string;
@@ -16,9 +18,63 @@
16
18
  export let pages: [string, string][] = [];
17
19
  export let current_page = "";
18
20
  export let root: string;
21
+ export let components: any[] = [];
19
22
 
20
23
  const set_page: ((page: string) => void) | undefined =
21
24
  getContext("set_lite_page");
25
+
26
+ let navbar_component = components.find((c) => c.type === "navbar");
27
+ let navbar: {
28
+ visible: boolean;
29
+ main_page_name: string | false;
30
+ value: [string, string][] | null;
31
+ } | null = navbar_component
32
+ ? {
33
+ visible: navbar_component.props.visible,
34
+ main_page_name: navbar_component.props.main_page_name,
35
+ value: navbar_component.props.value
36
+ }
37
+ : null;
38
+
39
+ if (navbar) {
40
+ navbar_config.set(navbar);
41
+ }
42
+
43
+ $: if ($navbar_config) {
44
+ navbar = {
45
+ visible: $navbar_config.visible ?? true,
46
+ main_page_name: $navbar_config.main_page_name ?? "Home",
47
+ value: $navbar_config.value ?? null
48
+ };
49
+ }
50
+
51
+ $: show_navbar =
52
+ pages.length > 1 && (navbar === null || navbar.visible !== false);
53
+
54
+ $: effective_pages = (() => {
55
+ let base_pages =
56
+ navbar &&
57
+ navbar.main_page_name !== false &&
58
+ navbar.main_page_name !== "Home"
59
+ ? pages.map(([route, label], index) =>
60
+ index === 0 && route === "" && label === "Home"
61
+ ? ([route, navbar!.main_page_name] as [string, string])
62
+ : ([route, label] as [string, string])
63
+ )
64
+ : pages;
65
+
66
+ if (navbar?.value && navbar.value.length > 0) {
67
+ const existing_routes = new Set(base_pages.map(([route]) => route));
68
+ const additional_pages = navbar.value
69
+ .map(
70
+ ([page_name, page_path]) => [page_path, page_name] as [string, string]
71
+ )
72
+ .filter(([route]) => !existing_routes.has(route));
73
+ return [...base_pages, ...additional_pages];
74
+ }
75
+
76
+ return base_pages;
77
+ })();
22
78
  </script>
23
79
 
24
80
  <div
@@ -31,10 +87,10 @@
31
87
  style:flex-grow={!display ? "1" : "auto"}
32
88
  data-iframe-height
33
89
  >
34
- {#if pages.length > 1}
90
+ {#if show_navbar}
35
91
  <div class="nav-holder">
36
92
  <nav class="fillable" class:fill_width>
37
- {#each pages as [route, label], i}
93
+ {#each effective_pages as [route, label], i}
38
94
  {#if is_lite}
39
95
  <button
40
96
  class:active={route === current_page}
@@ -46,9 +102,18 @@
46
102
  </button>
47
103
  {:else}
48
104
  <a
49
- href={`${root}/${route}`}
105
+ href={route.startsWith("http://") || route.startsWith("https://")
106
+ ? route
107
+ : `${root}/${route}`}
50
108
  class:active={route === current_page}
51
109
  data-sveltekit-reload
110
+ target={route.startsWith("http://") ||
111
+ route.startsWith("https://")
112
+ ? "_blank"
113
+ : "_self"}
114
+ rel={route.startsWith("http://") || route.startsWith("https://")
115
+ ? "noopener noreferrer"
116
+ : ""}
52
117
  >{label}
53
118
  </a>
54
119
  {/if}
package/src/i18n.ts CHANGED
@@ -71,13 +71,14 @@ export function is_translation_metadata(obj: any): obj is I18nData {
71
71
  return result;
72
72
  }
73
73
 
74
+ export const i18n_marker = "__i18n__";
75
+
74
76
  // handles strings with embedded JSON metadata of shape "__i18n__{"key": "some.key"}"
75
77
  export function translate_if_needed(value: any): string {
76
78
  if (typeof value !== "string") {
77
79
  return value;
78
80
  }
79
81
 
80
- const i18n_marker = "__i18n__";
81
82
  const marker_index = value.indexOf(i18n_marker);
82
83
 
83
84
  if (marker_index === -1) {
@@ -155,7 +156,8 @@ let i18n_initialized = false;
155
156
  let previous_translations: Record<string, Record<string, string>> | undefined;
156
157
 
157
158
  export async function setupi18n(
158
- custom_translations?: Record<string, Record<string, string>>
159
+ custom_translations?: Record<string, Record<string, string>>,
160
+ preferred_locale?: string
159
161
  ): Promise<void> {
160
162
  const should_reinitialize =
161
163
  i18n_initialized && custom_translations !== previous_translations;
@@ -171,7 +173,7 @@ export async function setupi18n(
171
173
  custom_translations: custom_translations ?? {}
172
174
  });
173
175
 
174
- const browser_locale = getLocaleFromNavigator();
176
+ const browser_locale = preferred_locale ?? getLocaleFromNavigator();
175
177
 
176
178
  let initial_locale =
177
179
  browser_locale && available_locales.includes(browser_locale)
package/src/init.ts CHANGED
@@ -12,6 +12,7 @@ import { load_component } from "virtual:component-loader";
12
12
  import type { client_return } from "@gradio/client";
13
13
  import { create_loading_status_store } from "./stores";
14
14
  import { _ } from "svelte-i18n";
15
+ import { i18n_marker } from "./i18n";
15
16
 
16
17
  export interface UpdateTransaction {
17
18
  id: number;
@@ -95,7 +96,7 @@ export function create_components(
95
96
  function set_event_specific_args(dependencies: Dependency[]): void {
96
97
  dependencies.forEach((dep) => {
97
98
  dep.targets.forEach((target) => {
98
- const instance = instance_map[target[0]];
99
+ const instance = instance_map?.[target[0]];
99
100
  if (instance && dep.event_specific_args?.length > 0) {
100
101
  dep.event_specific_args?.forEach((arg: string) => {
101
102
  instance.props[arg] = dep[arg as keyof Dependency];
@@ -257,7 +258,7 @@ export function create_components(
257
258
 
258
259
  target_map.set(_target_map);
259
260
 
260
- let current_element = instance_map[layout.id];
261
+ let current_element = instance_map?.[layout.id];
261
262
  let all_current_children: ComponentMeta[] = [];
262
263
  const add_to_current_children = (component: ComponentMeta): void => {
263
264
  all_current_children.push(component);
@@ -276,7 +277,7 @@ export function create_components(
276
277
  (c) => c.key === component.key
277
278
  );
278
279
  if (component.key != null && replacement_component !== undefined) {
279
- const instance = instance_map[component.id];
280
+ const instance = instance_map?.[component.id];
280
281
  for (const prop in replacement_component.props) {
281
282
  if (
282
283
  !(
@@ -289,7 +290,7 @@ export function create_components(
289
290
  }
290
291
  }
291
292
  } else {
292
- delete instance_map[_id];
293
+ if (instance_map) delete instance_map[_id];
293
294
  if (_component_map.has(_id)) {
294
295
  _component_map.delete(_id);
295
296
  }
@@ -298,17 +299,18 @@ export function create_components(
298
299
  });
299
300
 
300
301
  const components_to_add = new_components.concat(
301
- replacement_components.filter((c) => !instance_map[c.id])
302
+ replacement_components.filter((c) => !instance_map?.[c.id])
302
303
  );
303
304
 
304
305
  components_to_add.forEach((c) => {
305
306
  instance_map[c.id] = c;
307
+
306
308
  _component_map.set(c.id, c);
307
309
  });
308
310
  if (current_element.parent) {
309
311
  current_element.parent.children![
310
312
  current_element.parent.children!.indexOf(current_element)
311
- ] = instance_map[layout.id];
313
+ ] = instance_map?.[layout.id];
312
314
  }
313
315
 
314
316
  walk_layout(
@@ -332,7 +334,7 @@ export function create_components(
332
334
  components: ComponentMeta[],
333
335
  parent?: ComponentMeta
334
336
  ): Promise<ComponentMeta> {
335
- const instance = instance_map[node.id];
337
+ const instance = instance_map?.[node.id];
336
338
  if (!instance.component) {
337
339
  const constructor_key = instance.component_class_id || instance.type;
338
340
  let component_constructor = constructor_map.get(constructor_key);
@@ -379,10 +381,13 @@ export function create_components(
379
381
  );
380
382
  }
381
383
 
382
- if (instance.type === "tabs" && !instance.props.initial_tabs) {
384
+ if (
385
+ (instance.type === "tabs" && !instance.props.initial_tabs) ||
386
+ (instance.type === "walkthrough" && !instance.props.initial_tabs)
387
+ ) {
383
388
  const tab_items_props =
384
389
  node.children?.map((c, i) => {
385
- const instance = instance_map[c.id];
390
+ const instance = instance_map?.[c.id];
386
391
  instance.props.id ??= c.id;
387
392
  return {
388
393
  type: instance.type,
@@ -394,12 +399,15 @@ export function create_components(
394
399
  };
395
400
  }) || [];
396
401
 
402
+ const _type =
403
+ instance.type === "walkthrough" ? "walkthroughstep" : "tabitem";
404
+
397
405
  const child_tab_items = tab_items_props.filter(
398
- (child) => child.type === "tabitem"
406
+ (child) => child.type === _type
399
407
  );
400
408
 
401
409
  instance.props.initial_tabs = child_tab_items?.map((child) => ({
402
- label: child.props.label,
410
+ label: child.props.label.includes(i18n_marker) ? "" : child.props.label,
403
411
  id: child.props.id,
404
412
  visible:
405
413
  typeof child.props.visible === "boolean" ? child.props.visible : true,
@@ -408,9 +416,9 @@ export function create_components(
408
416
  }));
409
417
  }
410
418
 
411
- if (instance.type === "tabs") {
419
+ if (instance.type === "tabs" || instance.type === "walkthrough") {
412
420
  node.children?.forEach((c, i) => {
413
- const child = instance_map[c.id];
421
+ const child = instance_map?.[c.id];
414
422
  child.props.order = i;
415
423
  });
416
424
  }
@@ -476,7 +484,7 @@ export function create_components(
476
484
  function has_visibility_changes(updates: UpdateTransaction[][]): boolean {
477
485
  return updates.some((update_batch) =>
478
486
  update_batch.some((update) => {
479
- const instance = instance_map[update.id];
487
+ const instance = instance_map?.[update.id];
480
488
  if (!instance) return false;
481
489
 
482
490
  // Check for visibility property changes
@@ -510,7 +518,7 @@ export function create_components(
510
518
  for (let j = 0; j < pending_updates[i].length; j++) {
511
519
  const update = pending_updates[i][j];
512
520
  if (!update) continue;
513
- const instance = instance_map[update.id];
521
+ const instance = instance_map?.[update.id];
514
522
  if (!instance) continue;
515
523
  let new_value;
516
524
  const old_value = instance.props[update.prop];