@gradio/core 1.0.0-dev.0 → 1.0.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 +110 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/src/Blocks.svelte +534 -1001
- package/dist/src/Blocks.svelte.d.ts +32 -45
- package/dist/src/Embed.svelte +82 -55
- package/dist/src/Embed.svelte.d.ts +39 -30
- package/dist/src/Login.svelte +33 -29
- package/dist/src/Login.svelte.d.ts +21 -19
- package/dist/src/MountComponents.svelte +19 -25
- package/dist/src/MountComponents.svelte.d.ts +5 -28
- package/dist/src/{init.d.ts → _init.d.ts} +5 -4
- package/dist/src/{init.js → _init.js} +31 -108
- package/dist/src/api_docs/ApiBanner.svelte +12 -8
- package/dist/src/api_docs/ApiBanner.svelte.d.ts +22 -20
- package/dist/src/api_docs/ApiDocs.svelte +356 -247
- package/dist/src/api_docs/ApiDocs.svelte.d.ts +27 -24
- package/dist/src/api_docs/ApiRecorder.svelte +6 -3
- package/dist/src/api_docs/ApiRecorder.svelte.d.ts +19 -17
- package/dist/src/api_docs/CodeSnippet.svelte +122 -48
- package/dist/src/api_docs/CodeSnippet.svelte.d.ts +29 -25
- package/dist/src/api_docs/CopyButton.svelte +69 -13
- package/dist/src/api_docs/CopyButton.svelte.d.ts +18 -16
- package/dist/src/api_docs/CopyMarkdown.svelte +734 -0
- package/dist/src/api_docs/CopyMarkdown.svelte.d.ts +37 -0
- package/dist/src/api_docs/EndpointDetail.svelte +81 -23
- package/dist/src/api_docs/EndpointDetail.svelte.d.ts +23 -18
- package/dist/src/api_docs/IconArrowUpRight.svelte +34 -0
- package/dist/src/api_docs/IconArrowUpRight.svelte.d.ts +20 -0
- package/dist/src/api_docs/IconCaret.svelte +39 -0
- package/dist/src/api_docs/IconCaret.svelte.d.ts +20 -0
- package/dist/src/api_docs/IconHuggingChat.svelte +62 -0
- package/dist/src/api_docs/IconHuggingChat.svelte.d.ts +20 -0
- package/dist/src/api_docs/InputPayload.svelte +17 -11
- package/dist/src/api_docs/InputPayload.svelte.d.ts +25 -23
- package/dist/src/api_docs/InstallSnippet.svelte +9 -6
- package/dist/src/api_docs/InstallSnippet.svelte.d.ts +18 -16
- package/dist/src/api_docs/MCPSnippet.svelte +139 -126
- package/dist/src/api_docs/MCPSnippet.svelte.d.ts +60 -58
- package/dist/src/api_docs/NoApi.svelte +7 -4
- package/dist/src/api_docs/NoApi.svelte.d.ts +20 -18
- package/dist/src/api_docs/ParametersSnippet.svelte +8 -6
- package/dist/src/api_docs/ParametersSnippet.svelte.d.ts +21 -19
- package/dist/src/api_docs/PercentileChart.svelte +125 -0
- package/dist/src/api_docs/PercentileChart.svelte.d.ts +22 -0
- package/dist/src/api_docs/RecordingSnippet.svelte +124 -110
- package/dist/src/api_docs/RecordingSnippet.svelte.d.ts +24 -22
- package/dist/src/api_docs/ResponseSnippet.svelte +7 -5
- package/dist/src/api_docs/ResponseSnippet.svelte.d.ts +21 -19
- package/dist/src/api_docs/Settings.svelte +73 -62
- package/dist/src/api_docs/Settings.svelte.d.ts +25 -23
- package/dist/src/api_docs/SettingsBanner.svelte +11 -8
- package/dist/src/api_docs/SettingsBanner.svelte.d.ts +20 -18
- package/dist/src/api_docs/TryButton.svelte +5 -3
- package/dist/src/api_docs/TryButton.svelte.d.ts +19 -17
- package/dist/src/api_docs/img/IconCheck.svelte +33 -0
- package/dist/src/api_docs/img/IconCheck.svelte.d.ts +26 -0
- package/dist/src/api_docs/img/IconCopy.svelte +40 -0
- package/dist/src/api_docs/img/IconCopy.svelte.d.ts +26 -0
- package/dist/src/api_docs/img/clear.svelte.d.ts +22 -21
- package/dist/src/dependency.d.ts +145 -0
- package/dist/src/dependency.js +668 -0
- package/dist/src/init.svelte.d.ts +78 -0
- package/dist/src/init.svelte.js +469 -0
- package/dist/src/init_utils.d.ts +32 -0
- package/dist/src/init_utils.js +73 -0
- package/dist/src/lang/en.json +10 -1
- package/dist/src/lang/get_lang_names.js +0 -3
- package/dist/src/lang/ru.json +10 -1
- package/dist/src/stores.d.ts +0 -21
- package/dist/src/stories/I18nMultiLanguageTestComponent.svelte +5 -3
- package/dist/src/stories/I18nMultiLanguageTestComponent.svelte.d.ts +16 -14
- package/dist/src/stories/I18nTestSetup.svelte +14 -10
- package/dist/src/stories/I18nTestSetup.svelte.d.ts +18 -16
- package/dist/src/types.d.ts +31 -26
- package/index.ts +1 -1
- package/package.json +62 -63
- package/src/Blocks.svelte +360 -1063
- package/src/MountComponents.svelte +17 -27
- package/src/{init.ts → _init.ts} +49 -126
- package/src/api_docs/ApiDocs.svelte +84 -62
- package/src/api_docs/CodeSnippet.svelte +83 -24
- package/src/api_docs/CopyButton.svelte +61 -7
- package/src/api_docs/CopyMarkdown.svelte +734 -0
- package/src/api_docs/EndpointDetail.svelte +73 -17
- package/src/api_docs/IconArrowUpRight.svelte +34 -0
- package/src/api_docs/IconCaret.svelte +39 -0
- package/src/api_docs/IconHuggingChat.svelte +62 -0
- package/src/api_docs/MCPSnippet.svelte +44 -73
- package/src/api_docs/ParametersSnippet.svelte +1 -1
- package/src/api_docs/PercentileChart.svelte +125 -0
- package/src/api_docs/ResponseSnippet.svelte +1 -1
- package/src/api_docs/Settings.svelte +11 -11
- package/src/api_docs/img/IconCheck.svelte +33 -0
- package/src/api_docs/img/IconCopy.svelte +40 -0
- package/src/dependency.ts +909 -0
- package/src/init.svelte.ts +717 -0
- package/src/init_utils.ts +99 -0
- package/src/lang/en.json +10 -1
- package/src/lang/get_lang_names.js +0 -3
- package/src/lang/ru.json +10 -1
- package/src/stores.ts +22 -22
- package/src/types.ts +55 -43
- package/dist/src/Render.svelte +0 -105
- package/dist/src/Render.svelte.d.ts +0 -31
- package/dist/src/RenderComponent.svelte +0 -72
- package/dist/src/RenderComponent.svelte.d.ts +0 -33
- package/src/Render.svelte +0 -126
- package/src/RenderComponent.svelte +0 -91
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
import {
|
|
2
|
+
determine_interactivity,
|
|
3
|
+
get_component,
|
|
4
|
+
get_inputs_outputs
|
|
5
|
+
} from "./init_utils";
|
|
6
|
+
import { translate_if_needed } from "./i18n";
|
|
7
|
+
import { tick } from "svelte";
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
ComponentMeta,
|
|
11
|
+
ProcessedComponentMeta,
|
|
12
|
+
LayoutNode,
|
|
13
|
+
Dependency,
|
|
14
|
+
LoadingComponent,
|
|
15
|
+
AppConfig,
|
|
16
|
+
ServerFunctions
|
|
17
|
+
} from "./types";
|
|
18
|
+
import type { SharedProps } from "@gradio/utils";
|
|
19
|
+
import { allowed_shared_props } from "@gradio/utils";
|
|
20
|
+
import { Client } from "@gradio/client";
|
|
21
|
+
|
|
22
|
+
type client_return = Awaited<ReturnType<typeof Client.connect>>;
|
|
23
|
+
|
|
24
|
+
type set_data_type = (data: Record<string, unknown>) => void;
|
|
25
|
+
type get_data_type = () => Promise<Record<string, unknown>>;
|
|
26
|
+
type visitor<T> = (node: T) => ProcessedComponentMeta;
|
|
27
|
+
|
|
28
|
+
type Tab = {
|
|
29
|
+
label: string;
|
|
30
|
+
id: string;
|
|
31
|
+
visible: boolean;
|
|
32
|
+
interactive: boolean;
|
|
33
|
+
elem_id: string | undefined;
|
|
34
|
+
scale: number | null;
|
|
35
|
+
order?: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const type_map = {
|
|
39
|
+
walkthrough: "tabs",
|
|
40
|
+
walkthroughstep: "tabitem"
|
|
41
|
+
};
|
|
42
|
+
export class AppTree {
|
|
43
|
+
/** the raw component structure received from the backend */
|
|
44
|
+
#component_payload: ComponentMeta[];
|
|
45
|
+
/** the raw layout node structure received from the backend */
|
|
46
|
+
#layout_payload: LayoutNode;
|
|
47
|
+
/** the raw dependency structure received from the backend */
|
|
48
|
+
#dependency_payload: Dependency[];
|
|
49
|
+
/** Need this to set i18n in re-render */
|
|
50
|
+
reactive_formatter: (str: string) => string = (str: string) => str;
|
|
51
|
+
/** the config for the app */
|
|
52
|
+
#config: AppConfig;
|
|
53
|
+
client: client_return;
|
|
54
|
+
|
|
55
|
+
/** the root node of the processed layout tree */
|
|
56
|
+
root = $state<ProcessedComponentMeta>();
|
|
57
|
+
|
|
58
|
+
/** a set of all component IDs that are inputs to dependencies */
|
|
59
|
+
#input_ids: Set<number> = new Set();
|
|
60
|
+
/** a set of all component IDs that are outputs of dependencies */
|
|
61
|
+
#output_ids: Set<number> = new Set();
|
|
62
|
+
|
|
63
|
+
/** A list of components that are currently loading */
|
|
64
|
+
#pending_components: Array<LoadingComponent> = [];
|
|
65
|
+
|
|
66
|
+
#get_callbacks = new Map<number, get_data_type>();
|
|
67
|
+
#set_callbacks = new Map<number, set_data_type>();
|
|
68
|
+
component_ids: number[];
|
|
69
|
+
initial_tabs: Record<number, Tab[]> = {};
|
|
70
|
+
|
|
71
|
+
components_to_register: Set<number> = new Set();
|
|
72
|
+
ready: Promise<void>;
|
|
73
|
+
ready_resolve!: () => void;
|
|
74
|
+
resolved: boolean = false;
|
|
75
|
+
|
|
76
|
+
constructor(
|
|
77
|
+
components: ComponentMeta[],
|
|
78
|
+
layout: LayoutNode,
|
|
79
|
+
dependencies: Dependency[],
|
|
80
|
+
config: AppConfig,
|
|
81
|
+
app: client_return,
|
|
82
|
+
reactive_formatter: (str: string) => string
|
|
83
|
+
) {
|
|
84
|
+
this.ready = new Promise<void>((resolve) => {
|
|
85
|
+
this.ready_resolve = resolve;
|
|
86
|
+
});
|
|
87
|
+
this.reactive_formatter = reactive_formatter;
|
|
88
|
+
|
|
89
|
+
this.#config = config;
|
|
90
|
+
this.#component_payload = components;
|
|
91
|
+
this.#layout_payload = layout;
|
|
92
|
+
this.#dependency_payload = dependencies;
|
|
93
|
+
this.root = this.create_node(
|
|
94
|
+
{ id: layout.id, children: [] },
|
|
95
|
+
new Map(),
|
|
96
|
+
true
|
|
97
|
+
);
|
|
98
|
+
for (const comp of components) {
|
|
99
|
+
if (comp.props.visible != false) this.components_to_register.add(comp.id);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this.client = app;
|
|
103
|
+
|
|
104
|
+
this.prepare();
|
|
105
|
+
|
|
106
|
+
const component_map = components.reduce((map, comp) => {
|
|
107
|
+
map.set(comp.id, comp);
|
|
108
|
+
return map;
|
|
109
|
+
}, new Map<number, ComponentMeta>());
|
|
110
|
+
|
|
111
|
+
this.root!.children = this.#layout_payload.children.map((node) =>
|
|
112
|
+
this.traverse(node, (node) => {
|
|
113
|
+
const new_node = this.create_node(
|
|
114
|
+
node,
|
|
115
|
+
component_map,
|
|
116
|
+
false,
|
|
117
|
+
this.reactive_formatter
|
|
118
|
+
);
|
|
119
|
+
return new_node;
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
this.component_ids = components.map((c) => c.id);
|
|
123
|
+
this.initial_tabs = {};
|
|
124
|
+
gather_initial_tabs(this.root!, this.initial_tabs);
|
|
125
|
+
this.postprocess(this.root!);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
reload(
|
|
129
|
+
components: ComponentMeta[],
|
|
130
|
+
layout: LayoutNode,
|
|
131
|
+
dependencies: Dependency[],
|
|
132
|
+
config: AppConfig
|
|
133
|
+
) {
|
|
134
|
+
this.#layout_payload = layout;
|
|
135
|
+
this.#component_payload = components;
|
|
136
|
+
this.#config = config;
|
|
137
|
+
this.#dependency_payload = dependencies;
|
|
138
|
+
|
|
139
|
+
this.root = this.create_node(
|
|
140
|
+
{ id: layout.id, children: [] },
|
|
141
|
+
new Map(),
|
|
142
|
+
true
|
|
143
|
+
);
|
|
144
|
+
for (const comp of components) {
|
|
145
|
+
if (comp.props.visible != false) this.components_to_register.add(comp.id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.prepare();
|
|
149
|
+
|
|
150
|
+
const component_map = components.reduce((map, comp) => {
|
|
151
|
+
map.set(comp.id, comp);
|
|
152
|
+
return map;
|
|
153
|
+
}, new Map<number, ComponentMeta>());
|
|
154
|
+
|
|
155
|
+
this.root!.children = this.#layout_payload.children.map((node) =>
|
|
156
|
+
this.traverse(node, (node) => {
|
|
157
|
+
const new_node = this.create_node(
|
|
158
|
+
node,
|
|
159
|
+
component_map,
|
|
160
|
+
false,
|
|
161
|
+
this.reactive_formatter
|
|
162
|
+
);
|
|
163
|
+
return new_node;
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
this.component_ids = components.map((c) => c.id);
|
|
167
|
+
this.initial_tabs = {};
|
|
168
|
+
gather_initial_tabs(this.root!, this.initial_tabs);
|
|
169
|
+
this.postprocess(this.root!);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Registers a component with its ID and data callbacks
|
|
174
|
+
* @param id the ID of the component
|
|
175
|
+
* @param _set_data the set data callback
|
|
176
|
+
* @param _get_data the get data callback
|
|
177
|
+
*/
|
|
178
|
+
register_component(
|
|
179
|
+
id: number,
|
|
180
|
+
_set_data: set_data_type,
|
|
181
|
+
_get_data: get_data_type
|
|
182
|
+
): void {
|
|
183
|
+
this.#set_callbacks.set(id, _set_data);
|
|
184
|
+
this.#get_callbacks.set(id, _get_data);
|
|
185
|
+
this.components_to_register.delete(id);
|
|
186
|
+
if (this.components_to_register.size === 0 && !this.resolved) {
|
|
187
|
+
this.resolved = true;
|
|
188
|
+
this.ready_resolve();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Preprocess the payloads to get the correct state read to build the tree
|
|
194
|
+
*/
|
|
195
|
+
prepare() {
|
|
196
|
+
const [inputs, outputs] = get_inputs_outputs(this.#dependency_payload);
|
|
197
|
+
this.#input_ids = inputs;
|
|
198
|
+
this.#output_ids = outputs;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Processes the layout payload into a tree of components */
|
|
202
|
+
process() {}
|
|
203
|
+
|
|
204
|
+
postprocess(tree: ProcessedComponentMeta) {
|
|
205
|
+
this.root = this.traverse(tree, [
|
|
206
|
+
(node) => handle_visibility(node, this.#config.root),
|
|
207
|
+
(node) =>
|
|
208
|
+
untrack_children_of_invisible_parents(
|
|
209
|
+
node,
|
|
210
|
+
this.#config.root,
|
|
211
|
+
this.components_to_register
|
|
212
|
+
),
|
|
213
|
+
(node) =>
|
|
214
|
+
handle_empty_forms(
|
|
215
|
+
node,
|
|
216
|
+
this.#config.root,
|
|
217
|
+
this.components_to_register
|
|
218
|
+
),
|
|
219
|
+
(node) => translate_props(node, this.#config.root),
|
|
220
|
+
(node) => apply_initial_tabs(node, this.#config.root, this.initial_tabs),
|
|
221
|
+
(node) => this.find_attached_events(node, this.#dependency_payload)
|
|
222
|
+
]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
find_attached_events(
|
|
226
|
+
node: ProcessedComponentMeta,
|
|
227
|
+
dependencies: Dependency[]
|
|
228
|
+
): ProcessedComponentMeta {
|
|
229
|
+
const attached_events = dependencies
|
|
230
|
+
.filter((dep) => dep.targets.find(([id]) => id === node.id))
|
|
231
|
+
.map((dep) => {
|
|
232
|
+
const target = dep.targets.find(([id]) => id === node.id);
|
|
233
|
+
return target ? target[1] : null;
|
|
234
|
+
})
|
|
235
|
+
.filter(Boolean) as string[];
|
|
236
|
+
|
|
237
|
+
node.props.shared_props.attached_events = attached_events;
|
|
238
|
+
|
|
239
|
+
return node;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Traverses the layout tree and applies a callback to each node
|
|
244
|
+
* @param node the current layout node
|
|
245
|
+
* @param visit the callback to apply to each node
|
|
246
|
+
* @returns the return value of the callback, with a `children` property added for any child nodes
|
|
247
|
+
*/
|
|
248
|
+
|
|
249
|
+
traverse<T extends LayoutNode | ProcessedComponentMeta>(
|
|
250
|
+
node: T,
|
|
251
|
+
visit: visitor<T> | visitor<T>[]
|
|
252
|
+
): ProcessedComponentMeta {
|
|
253
|
+
function single_visit<U extends T>(
|
|
254
|
+
node: U,
|
|
255
|
+
visit: visitor<U>,
|
|
256
|
+
traverse_fn: any
|
|
257
|
+
): ProcessedComponentMeta {
|
|
258
|
+
const result = visit(node);
|
|
259
|
+
if ("children" in node && node.children.length > 0) {
|
|
260
|
+
result.children =
|
|
261
|
+
node.children?.map((child) => traverse_fn(child, visit)) || [];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (Array.isArray(visit)) {
|
|
268
|
+
let result: ProcessedComponentMeta = node as ProcessedComponentMeta;
|
|
269
|
+
for (const v of visit) {
|
|
270
|
+
result = single_visit(result as T, v, this.traverse.bind(this));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return result;
|
|
274
|
+
} else {
|
|
275
|
+
return single_visit(node, visit, this.traverse.bind(this));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Creates a processed component node from a layout node
|
|
281
|
+
* @param opts the layout node options
|
|
282
|
+
* @param root whether this is the root node
|
|
283
|
+
* @returns the processed component node
|
|
284
|
+
*/
|
|
285
|
+
create_node(
|
|
286
|
+
opts: LayoutNode,
|
|
287
|
+
component_map: Map<number, ComponentMeta>,
|
|
288
|
+
root = false,
|
|
289
|
+
reactive_formatter?: (str: string) => string
|
|
290
|
+
): ProcessedComponentMeta {
|
|
291
|
+
let component: ComponentMeta | undefined;
|
|
292
|
+
if (!root) {
|
|
293
|
+
component = component_map.get(opts.id);
|
|
294
|
+
} else {
|
|
295
|
+
component = {
|
|
296
|
+
type: "column",
|
|
297
|
+
id: opts.id,
|
|
298
|
+
// @ts-ignore
|
|
299
|
+
props: {
|
|
300
|
+
visible: true,
|
|
301
|
+
root: "",
|
|
302
|
+
theme_mode: "light"
|
|
303
|
+
},
|
|
304
|
+
component_class_id: "column",
|
|
305
|
+
key: null
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!component) {
|
|
310
|
+
throw new Error(`Component with ID ${opts.id} not found`);
|
|
311
|
+
}
|
|
312
|
+
if (reactive_formatter) {
|
|
313
|
+
component.props.i18n = reactive_formatter;
|
|
314
|
+
}
|
|
315
|
+
const processed_props = gather_props(
|
|
316
|
+
opts.id,
|
|
317
|
+
component.props,
|
|
318
|
+
[this.#input_ids, this.#output_ids],
|
|
319
|
+
this.client,
|
|
320
|
+
{ ...this.#config }
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const type =
|
|
324
|
+
type_map[component.type as keyof typeof type_map] || component.type;
|
|
325
|
+
|
|
326
|
+
const node = {
|
|
327
|
+
id: opts.id,
|
|
328
|
+
type: type,
|
|
329
|
+
props: processed_props,
|
|
330
|
+
children: [],
|
|
331
|
+
show_progress_on: null,
|
|
332
|
+
component_class_id: component.component_class_id || component.type,
|
|
333
|
+
component:
|
|
334
|
+
processed_props.shared_props.visible !== false
|
|
335
|
+
? get_component(
|
|
336
|
+
component.type,
|
|
337
|
+
component.component_class_id,
|
|
338
|
+
this.#config.root || ""
|
|
339
|
+
)
|
|
340
|
+
: null,
|
|
341
|
+
key: component.key,
|
|
342
|
+
rendered_in: component.rendered_in,
|
|
343
|
+
documentation: component.documentation
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
return node;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
rerender(components: ComponentMeta[], layout: LayoutNode) {
|
|
350
|
+
const component_map = components.reduce((map, comp) => {
|
|
351
|
+
map.set(comp.id, comp);
|
|
352
|
+
return map;
|
|
353
|
+
}, new Map<number, ComponentMeta>());
|
|
354
|
+
const subtree = this.traverse(layout, (node) => {
|
|
355
|
+
const new_node = this.create_node(
|
|
356
|
+
node,
|
|
357
|
+
component_map,
|
|
358
|
+
false,
|
|
359
|
+
this.reactive_formatter
|
|
360
|
+
);
|
|
361
|
+
return new_node;
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const n = find_node_by_id(this.root!, subtree.id);
|
|
365
|
+
|
|
366
|
+
if (!n) {
|
|
367
|
+
throw new Error("Rerender failed: root node not found in current tree");
|
|
368
|
+
}
|
|
369
|
+
n.children = subtree.children;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/*
|
|
373
|
+
* Updates the state of a component by its ID
|
|
374
|
+
* @param id the ID of the component to update
|
|
375
|
+
* @param new_state the new state to set
|
|
376
|
+
* */
|
|
377
|
+
async update_state(
|
|
378
|
+
id: number,
|
|
379
|
+
new_state: Partial<SharedProps> & Record<string, unknown>,
|
|
380
|
+
check_visibility: boolean = true
|
|
381
|
+
) {
|
|
382
|
+
// Visibility is tricky 😅
|
|
383
|
+
// If the component is not visible, it has not been rendered
|
|
384
|
+
// and so it has no _set_data callback
|
|
385
|
+
// Therefore, we need to traverse the tree and set the visible prop to true
|
|
386
|
+
// and then render it and its children. After that, we can call the _set_data callback
|
|
387
|
+
const node = find_node_by_id(this.root!, id);
|
|
388
|
+
let already_updated_visibility = false;
|
|
389
|
+
if (check_visibility && !node?.component) {
|
|
390
|
+
await tick();
|
|
391
|
+
this.root = this.traverse(this.root!, [
|
|
392
|
+
//@ts-ignore
|
|
393
|
+
(n) => set_visibility_for_updated_node(n, id, new_state.visible),
|
|
394
|
+
(n) => handle_visibility(n, this.#config.root)
|
|
395
|
+
]);
|
|
396
|
+
already_updated_visibility = true;
|
|
397
|
+
}
|
|
398
|
+
const _set_data = this.#set_callbacks.get(id);
|
|
399
|
+
if (!_set_data) return;
|
|
400
|
+
_set_data(new_state);
|
|
401
|
+
if (!check_visibility || already_updated_visibility) return;
|
|
402
|
+
// need to let the UI settle before traversing again
|
|
403
|
+
// otherwise there could be
|
|
404
|
+
await tick();
|
|
405
|
+
this.root = this.traverse(this.root!, (n) =>
|
|
406
|
+
handle_visibility(n, this.#config.root)
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Gets the current state of a component by its ID
|
|
412
|
+
* @param id the ID of the component to get the state of
|
|
413
|
+
* @returns the current state of the component, or null if not found
|
|
414
|
+
*/
|
|
415
|
+
async get_state(id: number): Promise<Record<string, unknown> | null> {
|
|
416
|
+
const _get_data = this.#get_callbacks.get(id);
|
|
417
|
+
const component = this.#component_payload.find((c) => c.id === id);
|
|
418
|
+
if (!_get_data && !component) return null;
|
|
419
|
+
if (_get_data) return await _get_data();
|
|
420
|
+
|
|
421
|
+
if (component) return Promise.resolve({ value: component.props.value });
|
|
422
|
+
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Process the server function names and return a dictionary of functions
|
|
429
|
+
* @param id the component id
|
|
430
|
+
* @param server_fns the server function names
|
|
431
|
+
* @param app the client instance
|
|
432
|
+
* @returns the actual server functions
|
|
433
|
+
*/
|
|
434
|
+
export function process_server_fn(
|
|
435
|
+
id: number,
|
|
436
|
+
server_fns: string[] | undefined,
|
|
437
|
+
app: client_return
|
|
438
|
+
): ServerFunctions {
|
|
439
|
+
if (!server_fns) {
|
|
440
|
+
return {};
|
|
441
|
+
}
|
|
442
|
+
return server_fns.reduce((acc, fn: string) => {
|
|
443
|
+
acc[fn] = async (...args: any[]) => {
|
|
444
|
+
if (args.length === 1) {
|
|
445
|
+
args = args[0];
|
|
446
|
+
}
|
|
447
|
+
const result = await app.component_server(id, fn, args);
|
|
448
|
+
return result;
|
|
449
|
+
};
|
|
450
|
+
return acc;
|
|
451
|
+
}, {} as ServerFunctions);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Gathers the props for a component
|
|
456
|
+
* @param id the ID of the component
|
|
457
|
+
* @param props the props of the component
|
|
458
|
+
* @param dependencies the component's dependencies
|
|
459
|
+
* @param additional any additional props to include
|
|
460
|
+
* @returns the gathered props as an object with `shared_props` and `props` keys
|
|
461
|
+
*/
|
|
462
|
+
function gather_props(
|
|
463
|
+
id: number,
|
|
464
|
+
props: ComponentMeta["props"],
|
|
465
|
+
dependencies: [Set<number>, Set<number>],
|
|
466
|
+
client: client_return,
|
|
467
|
+
additional: Record<string, unknown> = {}
|
|
468
|
+
): {
|
|
469
|
+
shared_props: SharedProps;
|
|
470
|
+
props: Record<string, unknown>;
|
|
471
|
+
} {
|
|
472
|
+
const _shared_props: Partial<SharedProps> = {};
|
|
473
|
+
const _props: Record<string, unknown> = {};
|
|
474
|
+
for (const key in props) {
|
|
475
|
+
// For Tabs (or any component that already has an id prop)
|
|
476
|
+
// Set the id to the props so that it doesn't get overwritten
|
|
477
|
+
if (key === "id" || key === "autoscroll") {
|
|
478
|
+
_props[key] = props[key];
|
|
479
|
+
} else if (allowed_shared_props.includes(key as keyof SharedProps)) {
|
|
480
|
+
const _key = key as keyof SharedProps;
|
|
481
|
+
_shared_props[_key] = props[key];
|
|
482
|
+
if (_key === "server_fns") {
|
|
483
|
+
_shared_props.server = process_server_fn(id, props.server_fns, client);
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
486
|
+
_props[key] = props[key];
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
for (const key in additional) {
|
|
491
|
+
if (allowed_shared_props.includes(key as keyof SharedProps)) {
|
|
492
|
+
const _key = key as keyof SharedProps;
|
|
493
|
+
_shared_props[_key] = additional[key];
|
|
494
|
+
} else {
|
|
495
|
+
_props[key] = additional[key];
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
_shared_props.client = client;
|
|
500
|
+
_shared_props.id = id;
|
|
501
|
+
_shared_props.interactive = determine_interactivity(
|
|
502
|
+
id,
|
|
503
|
+
_shared_props.interactive,
|
|
504
|
+
_props.value,
|
|
505
|
+
dependencies
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
_shared_props.load_component = (
|
|
509
|
+
name: string,
|
|
510
|
+
variant: "base" | "component" | "example"
|
|
511
|
+
) =>
|
|
512
|
+
get_component(
|
|
513
|
+
name,
|
|
514
|
+
"",
|
|
515
|
+
_shared_props.root || "",
|
|
516
|
+
variant
|
|
517
|
+
) as LoadingComponent;
|
|
518
|
+
|
|
519
|
+
_shared_props.visible =
|
|
520
|
+
_shared_props.visible === undefined ? true : _shared_props.visible;
|
|
521
|
+
_shared_props.loading_status = {};
|
|
522
|
+
|
|
523
|
+
return { shared_props: _shared_props as SharedProps, props: _props };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function handle_visibility(
|
|
527
|
+
node: ProcessedComponentMeta,
|
|
528
|
+
root: string
|
|
529
|
+
): ProcessedComponentMeta {
|
|
530
|
+
// Check if the node is visible
|
|
531
|
+
if (node.props.shared_props.visible && !node.component) {
|
|
532
|
+
const result: ProcessedComponentMeta = {
|
|
533
|
+
...node,
|
|
534
|
+
component: get_component(node.type, node.component_class_id, root),
|
|
535
|
+
children: []
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
if (node.children) {
|
|
539
|
+
result.children = node.children.map((child) =>
|
|
540
|
+
handle_visibility(child, root)
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
return result;
|
|
544
|
+
} else {
|
|
545
|
+
return node;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function set_visibility_for_updated_node(
|
|
550
|
+
node: ProcessedComponentMeta,
|
|
551
|
+
id: number,
|
|
552
|
+
visible: boolean
|
|
553
|
+
): ProcessedComponentMeta {
|
|
554
|
+
if (node.id == id) {
|
|
555
|
+
node.props.shared_props.visible = visible;
|
|
556
|
+
}
|
|
557
|
+
return node;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function _untrack(
|
|
561
|
+
node: ProcessedComponentMeta,
|
|
562
|
+
components_to_register: Set<number>
|
|
563
|
+
): void {
|
|
564
|
+
components_to_register.delete(node.id);
|
|
565
|
+
if (node.children) {
|
|
566
|
+
node.children.forEach((child) => _untrack(child, components_to_register));
|
|
567
|
+
}
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function untrack_children_of_invisible_parents(
|
|
572
|
+
node: ProcessedComponentMeta,
|
|
573
|
+
root: string,
|
|
574
|
+
components_to_register: Set<number>
|
|
575
|
+
): ProcessedComponentMeta {
|
|
576
|
+
// Check if the node is visible
|
|
577
|
+
if (node.props.shared_props.visible !== true) {
|
|
578
|
+
_untrack(node, components_to_register);
|
|
579
|
+
}
|
|
580
|
+
return node;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function handle_empty_forms(
|
|
584
|
+
node: ProcessedComponentMeta,
|
|
585
|
+
root: string,
|
|
586
|
+
components_to_register: Set<number>
|
|
587
|
+
): ProcessedComponentMeta {
|
|
588
|
+
// Check if the node is visible
|
|
589
|
+
if (node.type === "form") {
|
|
590
|
+
const all_children_invisible = node.children.every(
|
|
591
|
+
(child) => child.props.shared_props.visible === false
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
if (all_children_invisible) {
|
|
595
|
+
node.props.shared_props.visible = false;
|
|
596
|
+
components_to_register.delete(node.id);
|
|
597
|
+
return node;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return node;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function translate_props(
|
|
605
|
+
node: ProcessedComponentMeta,
|
|
606
|
+
root: string
|
|
607
|
+
): ProcessedComponentMeta {
|
|
608
|
+
const supported_props = [
|
|
609
|
+
"description",
|
|
610
|
+
"info",
|
|
611
|
+
"title",
|
|
612
|
+
"placeholder",
|
|
613
|
+
"value",
|
|
614
|
+
"label"
|
|
615
|
+
];
|
|
616
|
+
for (const attr of Object.keys(node.props.shared_props)) {
|
|
617
|
+
if (supported_props.includes(attr as string)) {
|
|
618
|
+
// @ts-ignore
|
|
619
|
+
node.props.shared_props[attr] = translate_if_needed(
|
|
620
|
+
node.props.shared_props[attr as keyof SharedProps]
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
for (const attr of Object.keys(node.props.props)) {
|
|
625
|
+
if (supported_props.includes(attr as string)) {
|
|
626
|
+
node.props.props[attr] = translate_if_needed(node.props.props[attr]);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return node;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function apply_initial_tabs(
|
|
633
|
+
node: ProcessedComponentMeta,
|
|
634
|
+
root: string,
|
|
635
|
+
initial_tabs: Record<number, Tab[]>
|
|
636
|
+
): ProcessedComponentMeta {
|
|
637
|
+
if (node.type === "tabs" && node.id in initial_tabs) {
|
|
638
|
+
const tabs = initial_tabs[node.id].sort((a, b) => a.order! - b.order!);
|
|
639
|
+
node.props.props.initial_tabs = tabs;
|
|
640
|
+
}
|
|
641
|
+
return node;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function _gather_initial_tabs(
|
|
645
|
+
node: ProcessedComponentMeta,
|
|
646
|
+
initial_tabs: Record<number, Tab[]>,
|
|
647
|
+
parent_tab_id: number | null,
|
|
648
|
+
order: number | null
|
|
649
|
+
): void {
|
|
650
|
+
if (parent_tab_id !== null && node.type === "tabitem") {
|
|
651
|
+
if (!(parent_tab_id in initial_tabs)) {
|
|
652
|
+
initial_tabs[parent_tab_id] = [];
|
|
653
|
+
}
|
|
654
|
+
if (!("id" in node.props.props)) {
|
|
655
|
+
node.props.props.id = node.id;
|
|
656
|
+
}
|
|
657
|
+
initial_tabs[parent_tab_id].push({
|
|
658
|
+
label: node.props.shared_props.label as string,
|
|
659
|
+
id: node.props.props.id as string,
|
|
660
|
+
elem_id: node.props.shared_props.elem_id,
|
|
661
|
+
visible: node.props.shared_props.visible as boolean,
|
|
662
|
+
interactive: node.props.shared_props.interactive,
|
|
663
|
+
scale: node.props.shared_props.scale || null
|
|
664
|
+
});
|
|
665
|
+
node.props.props.order = order;
|
|
666
|
+
}
|
|
667
|
+
if (node.children) {
|
|
668
|
+
node.children.forEach((child, i) => {
|
|
669
|
+
_gather_initial_tabs(
|
|
670
|
+
child,
|
|
671
|
+
initial_tabs,
|
|
672
|
+
node.type === "tabs" ? node.id : null,
|
|
673
|
+
node.type === "tabs" ? i : null
|
|
674
|
+
);
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function gather_initial_tabs(
|
|
681
|
+
node: ProcessedComponentMeta,
|
|
682
|
+
initial_tabs: Record<number, Tab[]>
|
|
683
|
+
): void {
|
|
684
|
+
function single_visit<U extends ProcessedComponentMeta>(node: U): void {
|
|
685
|
+
if ("children" in node && node.children.length > 0) {
|
|
686
|
+
node.children?.forEach((child) =>
|
|
687
|
+
_gather_initial_tabs(
|
|
688
|
+
child,
|
|
689
|
+
initial_tabs,
|
|
690
|
+
node.type === "tabs" ? node.id : null,
|
|
691
|
+
null
|
|
692
|
+
)
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return single_visit(node);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function find_node_by_id(
|
|
700
|
+
tree: ProcessedComponentMeta,
|
|
701
|
+
id: number
|
|
702
|
+
): ProcessedComponentMeta | null {
|
|
703
|
+
if (tree.id === id) {
|
|
704
|
+
return tree;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (tree.children) {
|
|
708
|
+
for (const child of tree.children) {
|
|
709
|
+
const result = find_node_by_id(child, id);
|
|
710
|
+
if (result) {
|
|
711
|
+
return result;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return null;
|
|
717
|
+
}
|