@gradio/core 0.20.0 → 0.22.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) => {
@@ -125,6 +129,10 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
125
129
 
126
130
  instance_map = {};
127
131
 
132
+ // Store current layout and root for dynamic visibility recalculation
133
+ current_layout = layout;
134
+ current_root = root;
135
+
128
136
  _rootNode = {
129
137
  id: layout.id,
130
138
  type: "column",
@@ -156,7 +164,7 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
156
164
 
157
165
  target_map.set(_target_map);
158
166
 
159
- constructor_map = preload_all_components(components, root);
167
+ constructor_map = preload_visible_components(components, layout, root);
160
168
 
161
169
  instance_map = components.reduce(
162
170
  (acc, c) => {
@@ -188,6 +196,10 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
188
196
  root: string;
189
197
  dependencies: Dependency[];
190
198
  }): void {
199
+ // Update current layout and root for dynamic visibility recalculation
200
+ current_layout = layout;
201
+ current_root = root;
202
+
191
203
  components.forEach((c) => {
192
204
  for (const prop in c.props) {
193
205
  if (c.props[prop] === null) {
@@ -204,7 +216,11 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
204
216
  replacement_components.push(c);
205
217
  }
206
218
  });
207
- let _constructor_map = preload_all_components(new_components, root);
219
+ let _constructor_map = preload_visible_components(
220
+ new_components,
221
+ layout,
222
+ root
223
+ );
208
224
  _constructor_map.forEach((v, k) => {
209
225
  constructor_map.set(k, v);
210
226
  });
@@ -302,12 +318,22 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
302
318
  ): Promise<ComponentMeta> {
303
319
  const instance = instance_map[node.id];
304
320
  if (!instance.component) {
305
- instance.component = (await constructor_map.get(
306
- instance.component_class_id || instance.type
307
- ))!?.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
308
330
  }
309
331
  instance.parent = parent;
310
332
 
333
+ // if (instance.type === "timer") {
334
+ // console.log("timer", instance, constructor_map);
335
+ // }
336
+
311
337
  if (instance.type === "dataset") {
312
338
  instance.props.component_map = get_component(
313
339
  instance.type,
@@ -366,7 +392,8 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
366
392
  instance.props.initial_tabs = child_tab_items?.map((child) => ({
367
393
  label: child.props.label,
368
394
  id: child.props.id,
369
- visible: child.props.visible,
395
+ visible:
396
+ typeof child.props.visible === "boolean" ? child.props.visible : true,
370
397
  interactive: child.props.interactive,
371
398
  order: child.props.order
372
399
  }));
@@ -385,7 +412,86 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
385
412
  let update_scheduled = false;
386
413
  let update_scheduled_store = writable(false);
387
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
+
388
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
+
389
495
  layout_store.update((layout) => {
390
496
  for (let i = 0; i < pending_updates.length; i++) {
391
497
  for (let j = 0; j < pending_updates[i].length; j++) {
@@ -407,6 +513,33 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
407
513
  }
408
514
  return layout;
409
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
+
410
543
  pending_updates = [];
411
544
  update_scheduled = false;
412
545
  update_scheduled_store.set(false);
@@ -713,7 +846,219 @@ export function get_component(
713
846
  }
714
847
 
715
848
  /**
716
- * 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)
717
1062
  * @param components A list of component metadata
718
1063
  * @param root The root url of the app
719
1064
  * @returns A map of component ids to their constructors
@@ -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);