@gradio/core 0.19.3 → 0.21.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/src/init.ts CHANGED
@@ -73,6 +73,10 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
73
73
  let keys_per_render_id: Record<number, (string | number)[]> = {};
74
74
  let _rootNode: ComponentMeta;
75
75
 
76
+ // Store current layout and root for dynamic visibility recalculation
77
+ let current_layout: LayoutNode;
78
+ let current_root: string;
79
+
76
80
  function set_event_specific_args(dependencies: Dependency[]): void {
77
81
  dependencies.forEach((dep) => {
78
82
  dep.targets.forEach((target) => {
@@ -107,6 +111,15 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
107
111
  flush();
108
112
  app = _app;
109
113
 
114
+ if (instance_map) {
115
+ // re-render in reload mode
116
+ components.forEach((c) => {
117
+ if (c.props.value == null && c.id in instance_map) {
118
+ c.props.value = instance_map[c.id].props.value;
119
+ }
120
+ });
121
+ }
122
+
110
123
  _components = components;
111
124
  inputs = new Set();
112
125
  outputs = new Set();
@@ -116,6 +129,10 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
116
129
 
117
130
  instance_map = {};
118
131
 
132
+ // Store current layout and root for dynamic visibility recalculation
133
+ current_layout = layout;
134
+ current_root = root;
135
+
119
136
  _rootNode = {
120
137
  id: layout.id,
121
138
  type: "column",
@@ -147,7 +164,7 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
147
164
 
148
165
  target_map.set(_target_map);
149
166
 
150
- constructor_map = preload_all_components(components, root);
167
+ constructor_map = preload_visible_components(components, layout, root);
151
168
 
152
169
  instance_map = components.reduce(
153
170
  (acc, c) => {
@@ -179,6 +196,10 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
179
196
  root: string;
180
197
  dependencies: Dependency[];
181
198
  }): void {
199
+ // Update current layout and root for dynamic visibility recalculation
200
+ current_layout = layout;
201
+ current_root = root;
202
+
182
203
  components.forEach((c) => {
183
204
  for (const prop in c.props) {
184
205
  if (c.props[prop] === null) {
@@ -195,7 +216,11 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
195
216
  replacement_components.push(c);
196
217
  }
197
218
  });
198
- let _constructor_map = preload_all_components(new_components, root);
219
+ let _constructor_map = preload_visible_components(
220
+ new_components,
221
+ layout,
222
+ root
223
+ );
199
224
  _constructor_map.forEach((v, k) => {
200
225
  constructor_map.set(k, v);
201
226
  });
@@ -293,12 +318,22 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
293
318
  ): Promise<ComponentMeta> {
294
319
  const instance = instance_map[node.id];
295
320
  if (!instance.component) {
296
- instance.component = (await constructor_map.get(
297
- instance.component_class_id || instance.type
298
- ))!?.default;
321
+ const constructor_key = instance.component_class_id || instance.type;
322
+ let component_constructor = constructor_map.get(constructor_key);
323
+
324
+ // Only load component if it was preloaded (i.e., it's visible)
325
+ if (component_constructor) {
326
+ instance.component = (await component_constructor)?.default;
327
+ }
328
+ // If component wasn't preloaded, leave it unloaded for now
329
+ // It will be loaded later when/if it becomes visible
299
330
  }
300
331
  instance.parent = parent;
301
332
 
333
+ // if (instance.type === "timer") {
334
+ // console.log("timer", instance, constructor_map);
335
+ // }
336
+
302
337
  if (instance.type === "dataset") {
303
338
  instance.props.component_map = get_component(
304
339
  instance.type,
@@ -357,7 +392,8 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
357
392
  instance.props.initial_tabs = child_tab_items?.map((child) => ({
358
393
  label: child.props.label,
359
394
  id: child.props.id,
360
- visible: child.props.visible,
395
+ visible:
396
+ typeof child.props.visible === "boolean" ? child.props.visible : true,
361
397
  interactive: child.props.interactive,
362
398
  order: child.props.order
363
399
  }));
@@ -376,7 +412,86 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
376
412
  let update_scheduled = false;
377
413
  let update_scheduled_store = writable(false);
378
414
 
415
+ /**
416
+ * Load newly visible components after visibility changes
417
+ * @param newly_visible_ids Set of component IDs that are now visible
418
+ */
419
+ async function load_newly_visible_components(
420
+ newly_visible_ids: Set<number>
421
+ ): Promise<void> {
422
+ if (newly_visible_ids.size === 0) return;
423
+
424
+ const components_to_load = _components.filter((c) =>
425
+ newly_visible_ids.has(c.id)
426
+ );
427
+
428
+ for (const component of components_to_load) {
429
+ const constructor_key = component.component_class_id || component.type;
430
+
431
+ // Only load if not already loaded
432
+ if (!constructor_map.has(constructor_key)) {
433
+ const { component: loadable_component, example_components } =
434
+ get_component(
435
+ component.type,
436
+ component.component_class_id,
437
+ current_root,
438
+ _components
439
+ );
440
+
441
+ constructor_map.set(constructor_key, loadable_component);
442
+
443
+ if (example_components) {
444
+ for (const [name, example_component] of example_components) {
445
+ constructor_map.set(name, example_component);
446
+ }
447
+ }
448
+
449
+ // Load the component if it doesn't exist yet
450
+ if (!component.component) {
451
+ component.component = (await loadable_component)?.default;
452
+ }
453
+ } else {
454
+ component.component =
455
+ (await constructor_map.get(constructor_key))?.default ??
456
+ component.component;
457
+ }
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Check if any visibility-affecting properties have changed
463
+ * @param updates Array of update transactions
464
+ * @returns True if visibility might have changed
465
+ */
466
+ function has_visibility_changes(updates: UpdateTransaction[][]): boolean {
467
+ return updates.some((update_batch) =>
468
+ update_batch.some((update) => {
469
+ const instance = instance_map[update.id];
470
+ if (!instance) return false;
471
+
472
+ // Check for visibility property changes
473
+ if (update.prop === "visible") return true;
474
+
475
+ // Check for selected tab changes in tabs components
476
+ if (update.prop === "selected" && instance.type === "tabs") return true;
477
+
478
+ return false;
479
+ })
480
+ );
481
+ }
482
+
379
483
  function flush(): void {
484
+ const had_visibility_changes = has_visibility_changes(pending_updates);
485
+ let previous_visible_ids: Set<number> | undefined;
486
+
487
+ // Capture current visibility state before applying updates
488
+ if (had_visibility_changes && current_layout) {
489
+ previous_visible_ids = determine_visible_components(
490
+ current_layout,
491
+ _components
492
+ );
493
+ }
494
+
380
495
  layout_store.update((layout) => {
381
496
  for (let i = 0; i < pending_updates.length; i++) {
382
497
  for (let j = 0; j < pending_updates[i].length; j++) {
@@ -398,6 +513,33 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
398
513
  }
399
514
  return layout;
400
515
  });
516
+
517
+ // After applying updates, check if we need to load new components
518
+ if (had_visibility_changes && current_layout && previous_visible_ids) {
519
+ raf(async () => {
520
+ const new_visible_ids = determine_visible_components(
521
+ current_layout,
522
+ _components
523
+ );
524
+ const newly_visible_ids = new Set<number>();
525
+
526
+ // Find components that are now visible but weren't before
527
+ for (const id of new_visible_ids) {
528
+ if (!previous_visible_ids!.has(id)) {
529
+ newly_visible_ids.add(id);
530
+ }
531
+ }
532
+
533
+ // Load the newly visible components
534
+ await load_newly_visible_components(newly_visible_ids);
535
+
536
+ // Trigger a layout update to render the newly loaded components
537
+ if (newly_visible_ids.size > 0) {
538
+ layout_store.update((layout) => layout);
539
+ }
540
+ });
541
+ }
542
+
401
543
  pending_updates = [];
402
544
  update_scheduled = false;
403
545
  update_scheduled_store.set(false);
@@ -704,7 +846,219 @@ export function get_component(
704
846
  }
705
847
 
706
848
  /**
707
- * Preload all components
849
+ * Check if a tab item should be visible based on selection state
850
+ * @param component The tab item component
851
+ * @param component_visible Whether the component is visible
852
+ * @param parent_tabs_context Tab context from parent
853
+ * @returns Whether the tab item should be visible
854
+ */
855
+ function is_tab_item_visible(
856
+ component: ComponentMeta,
857
+ component_visible: boolean,
858
+ parent_tabs_context?: { selected_tab_id?: string | number }
859
+ ): boolean {
860
+ const is_selected_tab =
861
+ parent_tabs_context?.selected_tab_id === component.id ||
862
+ parent_tabs_context?.selected_tab_id === component.props.id;
863
+ return component_visible && is_selected_tab;
864
+ }
865
+
866
+ /**
867
+ * Determine the selected tab ID for a tabs component
868
+ * @param component The tabs component
869
+ * @param layout The layout node
870
+ * @param components All components
871
+ * @returns The selected tab ID
872
+ */
873
+ function get_selected_tab_id(
874
+ component: ComponentMeta,
875
+ layout: LayoutNode,
876
+ components: ComponentMeta[]
877
+ ): string | number | undefined {
878
+ // Check if selected prop is a string or number
879
+ const selected = component.props.selected;
880
+ if (typeof selected === "string" || typeof selected === "number") {
881
+ return selected;
882
+ }
883
+
884
+ // If no tab is explicitly selected, find the first visible and interactive tab
885
+ if (layout.children) {
886
+ for (const child of layout.children) {
887
+ const child_component = components.find((c) => c.id === child.id);
888
+ if (
889
+ child_component?.type === "tabitem" &&
890
+ child_component.props.visible !== false &&
891
+ child_component.props.interactive !== false
892
+ ) {
893
+ return (
894
+ child_component.id || (child_component.props.id as string | number)
895
+ );
896
+ }
897
+ }
898
+ }
899
+
900
+ return undefined;
901
+ }
902
+
903
+ /**
904
+ * Process children components for visibility
905
+ * @param layout The layout node
906
+ * @param components All components
907
+ * @param parent_tabs_context Tab context
908
+ * @returns Set of visible child component IDs
909
+ */
910
+ function process_children_visibility(
911
+ layout: LayoutNode,
912
+ components: ComponentMeta[],
913
+ parent_tabs_context?: { selected_tab_id?: string | number }
914
+ ): Set<number> {
915
+ const visible_components: Set<number> = new Set();
916
+
917
+ if (layout.children) {
918
+ for (const child of layout.children) {
919
+ const child_visible = determine_visible_components(
920
+ child,
921
+ components,
922
+ true,
923
+ parent_tabs_context
924
+ );
925
+ child_visible.forEach((id) => visible_components.add(id));
926
+ }
927
+ }
928
+
929
+ return visible_components;
930
+ }
931
+
932
+ /**
933
+ * Determine which components should be visible based on layout structure and visibility rules
934
+ * @param layout The layout tree
935
+ * @param components All component metadata
936
+ * @param parent_visible Whether the parent component is visible
937
+ * @param parent_tabs_context Information about parent tabs if any
938
+ * @returns Set of component IDs that should be visible
939
+ */
940
+ function determine_visible_components(
941
+ layout: LayoutNode,
942
+ components: ComponentMeta[],
943
+ parent_visible = true,
944
+ parent_tabs_context?: { selected_tab_id?: string | number }
945
+ ): Set<number> {
946
+ const visible_components: Set<number> = new Set();
947
+ const component = components.find((c) => c.id === layout.id);
948
+
949
+ if (!component) {
950
+ return visible_components;
951
+ }
952
+
953
+ // Check if the component itself is visible
954
+ const component_visible =
955
+ parent_visible &&
956
+ (typeof component.props.visible === "boolean"
957
+ ? component.props.visible
958
+ : true);
959
+
960
+ // Handle tab_item special case
961
+ if (component.type === "tabitem") {
962
+ if (
963
+ is_tab_item_visible(component, component_visible, parent_tabs_context)
964
+ ) {
965
+ visible_components.add(component.id);
966
+
967
+ // Process children if this tab item is visible
968
+ const child_visible = process_children_visibility(
969
+ layout,
970
+ components,
971
+ parent_tabs_context
972
+ );
973
+ child_visible.forEach((id) => visible_components.add(id));
974
+ }
975
+ // If tab item is not visible, none of its children should be loaded
976
+ return visible_components;
977
+ }
978
+
979
+ // Handle tabs component
980
+ if (component.type === "tabs") {
981
+ if (component_visible) {
982
+ visible_components.add(component.id);
983
+
984
+ // Determine which tab should be selected
985
+ const selected_tab_id = get_selected_tab_id(
986
+ component,
987
+ layout,
988
+ components
989
+ );
990
+
991
+ // Process children with tabs context
992
+ const child_visible = process_children_visibility(layout, components, {
993
+ selected_tab_id
994
+ });
995
+ child_visible.forEach((id) => visible_components.add(id));
996
+ }
997
+ return visible_components;
998
+ }
999
+
1000
+ // For regular components
1001
+ if (component_visible) {
1002
+ visible_components.add(component.id);
1003
+
1004
+ // Process children if this component is visible
1005
+ const child_visible = process_children_visibility(
1006
+ layout,
1007
+ components,
1008
+ parent_tabs_context
1009
+ );
1010
+ child_visible.forEach((id) => visible_components.add(id));
1011
+ }
1012
+ // If component is not visible, don't process children
1013
+
1014
+ return visible_components;
1015
+ }
1016
+
1017
+ /**
1018
+ * Preload only visible components
1019
+ * @param components A list of component metadata
1020
+ * @param layout The layout tree to determine visibility
1021
+ * @param root The root url of the app
1022
+ * @returns A map of component ids to their constructors
1023
+ */
1024
+ export function preload_visible_components(
1025
+ components: ComponentMeta[],
1026
+ layout: LayoutNode,
1027
+ root: string
1028
+ ): Map<ComponentMeta["type"], LoadingComponent> {
1029
+ let constructor_map: Map<ComponentMeta["type"], LoadingComponent> = new Map();
1030
+
1031
+ // Determine which components should be visible
1032
+ const visible_component_ids = determine_visible_components(
1033
+ layout,
1034
+ components
1035
+ );
1036
+
1037
+ // Only preload visible components
1038
+ components.forEach((c) => {
1039
+ if (visible_component_ids.has(c.id)) {
1040
+ const { component, example_components } = get_component(
1041
+ c.type,
1042
+ c.component_class_id,
1043
+ root,
1044
+ components
1045
+ );
1046
+
1047
+ constructor_map.set(c.component_class_id || c.type, component);
1048
+
1049
+ if (example_components) {
1050
+ for (const [name, example_component] of example_components) {
1051
+ constructor_map.set(name, example_component);
1052
+ }
1053
+ }
1054
+ }
1055
+ });
1056
+
1057
+ return constructor_map;
1058
+ }
1059
+
1060
+ /**
1061
+ * Preload all components (legacy function, kept for backwards compatibility)
708
1062
  * @param components A list of component metadata
709
1063
  * @param root The root url of the app
710
1064
  * @returns A map of component ids to their constructors
package/src/lang/en.json CHANGED
@@ -89,7 +89,9 @@
89
89
  "sort_ascending": "Sort ascending",
90
90
  "sort_descending": "Sort descending",
91
91
  "drop_to_upload": "Drop CSV or TSV files here to import data into dataframe",
92
- "clear_sort": "Clear sort"
92
+ "clear_sort": "Clear sort",
93
+ "filter": "Filter",
94
+ "clear_filter": "Clear filters"
93
95
  },
94
96
  "dropdown": {
95
97
  "dropdown": "Dropdown"
@@ -0,0 +1,23 @@
1
+ //@ts-nocheck
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ const langs = fs.readdirSync(path.join(__dirname, "..", "lang"));
10
+
11
+ let lang_names = {};
12
+ for (const lang of langs) {
13
+ if (lang.endsWith(".json")) {
14
+ const lang_text = fs.readFileSync(
15
+ path.join(__dirname, "..", "lang", lang),
16
+ "utf8"
17
+ );
18
+ const lang_data = JSON.parse(lang_text.trim());
19
+ lang_names[lang.split(".")[0]] = lang_data._name;
20
+ }
21
+ }
22
+
23
+ console.log(lang_names);
@@ -85,7 +85,9 @@
85
85
  "sort_ascending": "Ordenar em ordem crescente",
86
86
  "sort_descending": "Ordenar em ordem decrescente",
87
87
  "drop_to_upload": "Solte arquivos CSV ou TSV aqui para importar dados para o dataframe",
88
- "clear_sort": "Limpar classificação"
88
+ "clear_sort": "Limpar classificação",
89
+ "filter": "Filtrar",
90
+ "clear_filter": "Limpar filtros"
89
91
  },
90
92
  "dropdown": {
91
93
  "dropdown": "Menu suspenso"
package/src/lang/pt.json CHANGED
@@ -74,7 +74,9 @@
74
74
  "sort_ascending": "Ordenar por ordem crescente",
75
75
  "sort_descending": "Ordenar por ordem decrescente",
76
76
  "drop_to_upload": "Solte ficheiros CSV ou TSV aqui para importar dados para o dataframe",
77
- "clear_sort": "Limpar ordenação"
77
+ "clear_sort": "Limpar ordenação",
78
+ "filter": "Filtrar",
79
+ "clear_filter": "Limpar filtros"
78
80
  },
79
81
  "dropdown": {
80
82
  "dropdown": "Lista suspensa"