@gradio/core 1.0.1 → 1.1.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/CHANGELOG.md +54 -0
- package/dist/src/Blocks.svelte +117 -83
- package/dist/src/api_docs/Settings.svelte +21 -22
- package/dist/src/api_docs/Settings.svelte.d.ts +6 -21
- package/dist/src/api_docs/SettingsBanner.svelte +2 -4
- package/dist/src/api_docs/SettingsBanner.svelte.d.ts +5 -20
- package/dist/src/dependency.js +9 -1
- package/dist/src/init.svelte.d.ts +6 -2
- package/dist/src/init.svelte.js +156 -22
- package/dist/src/types.d.ts +1 -0
- package/package.json +56 -56
- package/src/Blocks.svelte +117 -83
- package/src/api_docs/Settings.svelte +21 -22
- package/src/api_docs/SettingsBanner.svelte +2 -4
- package/src/dependency.ts +16 -2
- package/src/init.svelte.ts +202 -30
- package/src/init.test.ts +1 -1
- package/src/types.ts +1 -0
package/src/init.svelte.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "./init_utils";
|
|
6
6
|
import { translate_if_needed } from "./i18n";
|
|
7
7
|
import { tick } from "svelte";
|
|
8
|
+
import { dequal } from "dequal";
|
|
8
9
|
|
|
9
10
|
import type {
|
|
10
11
|
ComponentMeta,
|
|
@@ -15,7 +16,7 @@ import type {
|
|
|
15
16
|
AppConfig,
|
|
16
17
|
ServerFunctions
|
|
17
18
|
} from "./types";
|
|
18
|
-
import type
|
|
19
|
+
import { type SharedProps } from "@gradio/utils";
|
|
19
20
|
import { allowed_shared_props } from "@gradio/utils";
|
|
20
21
|
import { Client } from "@gradio/client";
|
|
21
22
|
|
|
@@ -33,6 +34,7 @@ type Tab = {
|
|
|
33
34
|
elem_id: string | undefined;
|
|
34
35
|
scale: number | null;
|
|
35
36
|
order?: number;
|
|
37
|
+
component_id: number;
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
const type_map = {
|
|
@@ -54,6 +56,7 @@ export class AppTree {
|
|
|
54
56
|
|
|
55
57
|
/** the root node of the processed layout tree */
|
|
56
58
|
root = $state<ProcessedComponentMeta>();
|
|
59
|
+
root_untracked: ProcessedComponentMeta;
|
|
57
60
|
|
|
58
61
|
/** a set of all component IDs that are inputs to dependencies */
|
|
59
62
|
#input_ids: Set<number> = new Set();
|
|
@@ -65,6 +68,7 @@ export class AppTree {
|
|
|
65
68
|
|
|
66
69
|
#get_callbacks = new Map<number, get_data_type>();
|
|
67
70
|
#set_callbacks = new Map<number, set_data_type>();
|
|
71
|
+
#event_dispatcher: (id: number, event: string, data: unknown) => void;
|
|
68
72
|
component_ids: number[];
|
|
69
73
|
initial_tabs: Record<number, Tab[]> = {};
|
|
70
74
|
|
|
@@ -72,6 +76,7 @@ export class AppTree {
|
|
|
72
76
|
ready: Promise<void>;
|
|
73
77
|
ready_resolve!: () => void;
|
|
74
78
|
resolved: boolean = false;
|
|
79
|
+
#hidden_on_startup: Set<number> = new Set();
|
|
75
80
|
|
|
76
81
|
constructor(
|
|
77
82
|
components: ComponentMeta[],
|
|
@@ -79,7 +84,8 @@ export class AppTree {
|
|
|
79
84
|
dependencies: Dependency[],
|
|
80
85
|
config: Omit<AppConfig, "api_url">,
|
|
81
86
|
app: client_return,
|
|
82
|
-
reactive_formatter: (str: string) => string
|
|
87
|
+
reactive_formatter: (str: string) => string,
|
|
88
|
+
event_dispatcher: (id: number, event: string, data: unknown) => void
|
|
83
89
|
) {
|
|
84
90
|
this.ready = new Promise<void>((resolve) => {
|
|
85
91
|
this.ready_resolve = resolve;
|
|
@@ -125,6 +131,8 @@ export class AppTree {
|
|
|
125
131
|
this.initial_tabs = {};
|
|
126
132
|
gather_initial_tabs(this.root!, this.initial_tabs);
|
|
127
133
|
this.postprocess(this.root!);
|
|
134
|
+
this.#event_dispatcher = event_dispatcher;
|
|
135
|
+
this.root_untracked = this.root;
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
reload(
|
|
@@ -217,7 +225,13 @@ export class AppTree {
|
|
|
217
225
|
(node) => handle_empty_forms(node, this.components_to_register),
|
|
218
226
|
(node) => translate_props(node),
|
|
219
227
|
(node) => apply_initial_tabs(node, this.initial_tabs),
|
|
220
|
-
(node) => this.find_attached_events(node, this.#dependency_payload)
|
|
228
|
+
(node) => this.find_attached_events(node, this.#dependency_payload),
|
|
229
|
+
(node) =>
|
|
230
|
+
untrack_children_of_closed_accordions_or_inactive_tabs(
|
|
231
|
+
node,
|
|
232
|
+
this.components_to_register,
|
|
233
|
+
this.#hidden_on_startup
|
|
234
|
+
)
|
|
221
235
|
]);
|
|
222
236
|
}
|
|
223
237
|
|
|
@@ -340,9 +354,9 @@ export class AppTree {
|
|
|
340
354
|
: null,
|
|
341
355
|
key: component.key,
|
|
342
356
|
rendered_in: component.rendered_in,
|
|
343
|
-
documentation: component.documentation
|
|
357
|
+
documentation: component.documentation,
|
|
358
|
+
original_visibility: processed_props.shared_props.visible
|
|
344
359
|
};
|
|
345
|
-
|
|
346
360
|
return node;
|
|
347
361
|
}
|
|
348
362
|
|
|
@@ -369,6 +383,19 @@ export class AppTree {
|
|
|
369
383
|
n.children = subtree.children;
|
|
370
384
|
}
|
|
371
385
|
|
|
386
|
+
async update_visibility(
|
|
387
|
+
node: ProcessedComponentMeta,
|
|
388
|
+
new_state: any
|
|
389
|
+
): Promise<void> {
|
|
390
|
+
node.children.forEach((child) => {
|
|
391
|
+
const _set_data = this.#set_callbacks.get(child.id);
|
|
392
|
+
if (_set_data) {
|
|
393
|
+
_set_data(new_state);
|
|
394
|
+
}
|
|
395
|
+
this.update_visibility(child, new_state);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
372
399
|
/*
|
|
373
400
|
* Updates the state of a component by its ID
|
|
374
401
|
* @param id the ID of the component to update
|
|
@@ -391,20 +418,42 @@ export class AppTree {
|
|
|
391
418
|
this.root = this.traverse(this.root!, [
|
|
392
419
|
//@ts-ignore
|
|
393
420
|
(n) => set_visibility_for_updated_node(n, id, new_state.visible),
|
|
421
|
+
//@ts-ignore
|
|
422
|
+
(n) => update_parent_visibility(n, id, new_state.visible),
|
|
394
423
|
(n) => handle_visibility(n, this.#config.api_url)
|
|
395
424
|
]);
|
|
425
|
+
await tick();
|
|
396
426
|
already_updated_visibility = true;
|
|
397
427
|
}
|
|
398
428
|
const _set_data = this.#set_callbacks.get(id);
|
|
399
|
-
if (!_set_data)
|
|
400
|
-
|
|
429
|
+
if (!_set_data) {
|
|
430
|
+
const old_value = node?.props.props.value;
|
|
431
|
+
// @ts-ignore
|
|
432
|
+
const new_props = create_props_shared_props(new_state);
|
|
433
|
+
node!.props.shared_props = {
|
|
434
|
+
...node?.props.shared_props,
|
|
435
|
+
...new_props.shared_props
|
|
436
|
+
};
|
|
437
|
+
node!.props.props = { ...node?.props.props, ...new_props.props };
|
|
438
|
+
if ("value" in new_state && !dequal(old_value, new_state.value)) {
|
|
439
|
+
this.#event_dispatcher(id, "change", null);
|
|
440
|
+
}
|
|
441
|
+
} else if (_set_data) {
|
|
442
|
+
_set_data(new_state);
|
|
443
|
+
}
|
|
401
444
|
if (!check_visibility || already_updated_visibility) return;
|
|
402
445
|
// need to let the UI settle before traversing again
|
|
403
446
|
// otherwise there could be
|
|
404
447
|
await tick();
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
448
|
+
// Update the visibility in a way that does not
|
|
449
|
+
// re-render the root/tree. Doing that would nuke
|
|
450
|
+
// any values currently in the UI.
|
|
451
|
+
// @ts-ignore
|
|
452
|
+
await this.update_visibility(node, new_state);
|
|
453
|
+
const parent_node = find_parent(this.root!, id);
|
|
454
|
+
if (parent_node)
|
|
455
|
+
// @ts-ignore
|
|
456
|
+
update_parent_visibility(parent_node, id, new_state.visible);
|
|
408
457
|
}
|
|
409
458
|
|
|
410
459
|
/**
|
|
@@ -414,14 +463,39 @@ export class AppTree {
|
|
|
414
463
|
*/
|
|
415
464
|
async get_state(id: number): Promise<Record<string, unknown> | null> {
|
|
416
465
|
const _get_data = this.#get_callbacks.get(id);
|
|
417
|
-
const component = this
|
|
466
|
+
const component = find_node_by_id(this.root!, id);
|
|
418
467
|
if (!_get_data && !component) return null;
|
|
419
468
|
if (_get_data) return await _get_data();
|
|
420
469
|
|
|
421
|
-
if (component)
|
|
470
|
+
if (component)
|
|
471
|
+
return Promise.resolve({ value: component.props.props.value });
|
|
422
472
|
|
|
423
473
|
return null;
|
|
424
474
|
}
|
|
475
|
+
|
|
476
|
+
async render_previously_invisible_children(id: number) {
|
|
477
|
+
this.root = this.traverse(this.root!, [
|
|
478
|
+
(node) => {
|
|
479
|
+
if (node.id === id) {
|
|
480
|
+
make_visible_if_not_rendered(node, this.#hidden_on_startup);
|
|
481
|
+
}
|
|
482
|
+
return node;
|
|
483
|
+
},
|
|
484
|
+
(node) => handle_visibility(node, this.#config.api_url)
|
|
485
|
+
]);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function make_visible_if_not_rendered(
|
|
490
|
+
node: ProcessedComponentMeta,
|
|
491
|
+
hidden_on_startup: Set<number>
|
|
492
|
+
): void {
|
|
493
|
+
node.props.shared_props.visible = hidden_on_startup.has(node.id)
|
|
494
|
+
? true
|
|
495
|
+
: node.props.shared_props.visible;
|
|
496
|
+
node.children.forEach((child) => {
|
|
497
|
+
make_visible_if_not_rendered(child, hidden_on_startup);
|
|
498
|
+
});
|
|
425
499
|
}
|
|
426
500
|
|
|
427
501
|
/**
|
|
@@ -451,6 +525,27 @@ export function process_server_fn(
|
|
|
451
525
|
}, {} as ServerFunctions);
|
|
452
526
|
}
|
|
453
527
|
|
|
528
|
+
function create_props_shared_props(props: ComponentMeta["props"]): {
|
|
529
|
+
shared_props: SharedProps;
|
|
530
|
+
props: Record<string, unknown>;
|
|
531
|
+
} {
|
|
532
|
+
const _shared_props: Partial<SharedProps> = {};
|
|
533
|
+
const _props: Record<string, unknown> = {};
|
|
534
|
+
for (const key in props) {
|
|
535
|
+
// For Tabs (or any component that already has an id prop)
|
|
536
|
+
// Set the id to the props so that it doesn't get overwritten
|
|
537
|
+
if (key === "id" || key === "autoscroll") {
|
|
538
|
+
_props[key] = props[key];
|
|
539
|
+
} else if (allowed_shared_props.includes(key as keyof SharedProps)) {
|
|
540
|
+
const _key = key as keyof SharedProps;
|
|
541
|
+
_shared_props[_key] = props[key];
|
|
542
|
+
} else {
|
|
543
|
+
_props[key] = props[key];
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return { shared_props: _shared_props as SharedProps, props: _props };
|
|
547
|
+
}
|
|
548
|
+
|
|
454
549
|
/**
|
|
455
550
|
* Gathers the props for a component
|
|
456
551
|
* @param id the ID of the component
|
|
@@ -470,23 +565,9 @@ function gather_props(
|
|
|
470
565
|
shared_props: SharedProps;
|
|
471
566
|
props: Record<string, unknown>;
|
|
472
567
|
} {
|
|
473
|
-
const _shared_props:
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
// For Tabs (or any component that already has an id prop)
|
|
477
|
-
// Set the id to the props so that it doesn't get overwritten
|
|
478
|
-
if (key === "id" || key === "autoscroll") {
|
|
479
|
-
_props[key] = props[key];
|
|
480
|
-
} else if (allowed_shared_props.includes(key as keyof SharedProps)) {
|
|
481
|
-
const _key = key as keyof SharedProps;
|
|
482
|
-
_shared_props[_key] = props[key];
|
|
483
|
-
if (_key === "server_fns") {
|
|
484
|
-
_shared_props.server = process_server_fn(id, props.server_fns, client);
|
|
485
|
-
}
|
|
486
|
-
} else {
|
|
487
|
-
_props[key] = props[key];
|
|
488
|
-
}
|
|
489
|
-
}
|
|
568
|
+
const { shared_props: _shared_props, props: _props } =
|
|
569
|
+
create_props_shared_props(props);
|
|
570
|
+
_shared_props.server = process_server_fn(id, props.server_fns, client);
|
|
490
571
|
|
|
491
572
|
for (const key in additional) {
|
|
492
573
|
if (allowed_shared_props.includes(key as keyof SharedProps)) {
|
|
@@ -574,6 +655,49 @@ function untrack_children_of_invisible_parents(
|
|
|
574
655
|
return node;
|
|
575
656
|
}
|
|
576
657
|
|
|
658
|
+
function mark_component_invisible_if_visible(
|
|
659
|
+
node: ProcessedComponentMeta,
|
|
660
|
+
hidden_on_startup: Set<number>
|
|
661
|
+
): ProcessedComponentMeta {
|
|
662
|
+
if (node.props.shared_props.visible === true) {
|
|
663
|
+
hidden_on_startup.add(node.id);
|
|
664
|
+
node.props.shared_props.visible = false;
|
|
665
|
+
}
|
|
666
|
+
node.children.forEach((child) => {
|
|
667
|
+
mark_component_invisible_if_visible(child, hidden_on_startup);
|
|
668
|
+
});
|
|
669
|
+
return node;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function untrack_children_of_closed_accordions_or_inactive_tabs(
|
|
673
|
+
node: ProcessedComponentMeta,
|
|
674
|
+
components_to_register: Set<number>,
|
|
675
|
+
hidden_on_startup: Set<number>
|
|
676
|
+
): ProcessedComponentMeta {
|
|
677
|
+
// Check if the node is an accordion or tabs
|
|
678
|
+
if (node.type === "accordion" && node.props.props.open === false) {
|
|
679
|
+
_untrack(node, components_to_register);
|
|
680
|
+
if (node.children) {
|
|
681
|
+
node.children.forEach((child) => {
|
|
682
|
+
mark_component_invisible_if_visible(child, hidden_on_startup);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (node.type === "tabs") {
|
|
687
|
+
node.children.forEach((child) => {
|
|
688
|
+
if (
|
|
689
|
+
child.type === "tabitem" &&
|
|
690
|
+
child.props.props.id !==
|
|
691
|
+
(node.props.props.selected || node.props.props.initial_tabs[0].id)
|
|
692
|
+
) {
|
|
693
|
+
_untrack(child, components_to_register);
|
|
694
|
+
mark_component_invisible_if_visible(child, hidden_on_startup);
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
return node;
|
|
699
|
+
}
|
|
700
|
+
|
|
577
701
|
function handle_empty_forms(
|
|
578
702
|
node: ProcessedComponentMeta,
|
|
579
703
|
components_to_register: Set<number>
|
|
@@ -594,6 +718,33 @@ function handle_empty_forms(
|
|
|
594
718
|
return node;
|
|
595
719
|
}
|
|
596
720
|
|
|
721
|
+
function update_parent_visibility(
|
|
722
|
+
node: ProcessedComponentMeta,
|
|
723
|
+
child_made_visible: number,
|
|
724
|
+
visibility_state: boolean | "hidden"
|
|
725
|
+
): ProcessedComponentMeta {
|
|
726
|
+
// This function was added to address a tricky situation:
|
|
727
|
+
// Form components are wrapped in a Form component automatically.
|
|
728
|
+
// If all the children of the Form are invisible, the Form itself is marked invisible.
|
|
729
|
+
// in AppTree.postprocess -> handle_empty_forms
|
|
730
|
+
// This is to avoid rendering empty forms in the UI. They look ugly.
|
|
731
|
+
// So what happens when a child inside the Form is made visible again?
|
|
732
|
+
// The Form needs to become visible again too.
|
|
733
|
+
// If the child is made invisible, the form should be too if all other children are invisible.
|
|
734
|
+
// However, we are not doing this now since what we want to do is fetch the latest visibility of all
|
|
735
|
+
// the children from the UI. However, get_data only returns the props, not the shared props.
|
|
736
|
+
if (
|
|
737
|
+
node.type === "form" &&
|
|
738
|
+
node.children.length &&
|
|
739
|
+
node.children.some((child) => child.id === child_made_visible)
|
|
740
|
+
) {
|
|
741
|
+
if (visibility_state === true) node.props.shared_props.visible = true;
|
|
742
|
+
else if (!visibility_state && node.children.length === 1)
|
|
743
|
+
node.props.shared_props.visible = "hidden";
|
|
744
|
+
}
|
|
745
|
+
return node;
|
|
746
|
+
}
|
|
747
|
+
|
|
597
748
|
function translate_props(node: ProcessedComponentMeta): ProcessedComponentMeta {
|
|
598
749
|
const supported_props = [
|
|
599
750
|
"description",
|
|
@@ -626,6 +777,8 @@ function apply_initial_tabs(
|
|
|
626
777
|
if (node.type === "tabs" && node.id in initial_tabs) {
|
|
627
778
|
const tabs = initial_tabs[node.id].sort((a, b) => a.order! - b.order!);
|
|
628
779
|
node.props.props.initial_tabs = tabs;
|
|
780
|
+
} else if (node.type === "tabitem") {
|
|
781
|
+
node.props.props.component_id = node.id;
|
|
629
782
|
}
|
|
630
783
|
return node;
|
|
631
784
|
}
|
|
@@ -649,7 +802,8 @@ function _gather_initial_tabs(
|
|
|
649
802
|
elem_id: node.props.shared_props.elem_id,
|
|
650
803
|
visible: node.props.shared_props.visible as boolean,
|
|
651
804
|
interactive: node.props.shared_props.interactive,
|
|
652
|
-
scale: node.props.shared_props.scale || null
|
|
805
|
+
scale: node.props.shared_props.scale || null,
|
|
806
|
+
component_id: node.id
|
|
653
807
|
});
|
|
654
808
|
node.props.props.order = order;
|
|
655
809
|
}
|
|
@@ -704,3 +858,21 @@ function find_node_by_id(
|
|
|
704
858
|
|
|
705
859
|
return null;
|
|
706
860
|
}
|
|
861
|
+
|
|
862
|
+
function find_parent(
|
|
863
|
+
tree: ProcessedComponentMeta,
|
|
864
|
+
id: number
|
|
865
|
+
): ProcessedComponentMeta | null {
|
|
866
|
+
if (tree.children) {
|
|
867
|
+
for (const child of tree.children) {
|
|
868
|
+
if (child.id === id) {
|
|
869
|
+
return tree;
|
|
870
|
+
}
|
|
871
|
+
const result = find_parent(child, id);
|
|
872
|
+
if (result) {
|
|
873
|
+
return result;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return null;
|
|
878
|
+
}
|
package/src/init.test.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface ProcessedComponentMeta {
|
|
|
32
32
|
component_class_id: string; // ?;
|
|
33
33
|
key: string | number | null; // ?;
|
|
34
34
|
rendered_in?: number; // ?;
|
|
35
|
+
original_visibility: boolean | "hidden";
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/** Dictates whether a dependency is continous and/or a generator */
|