@gradio/core 0.22.0 → 0.23.1
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 +42 -0
- package/dist/src/Blocks.svelte +83 -19
- package/dist/src/RenderComponent.svelte +1 -0
- package/dist/src/api_docs/ApiDocs.svelte +125 -66
- package/dist/src/api_docs/CodeSnippet.svelte +8 -10
- package/dist/src/api_docs/CodeSnippet.svelte.d.ts +1 -2
- package/dist/src/api_docs/EndpointDetail.svelte +12 -13
- package/dist/src/api_docs/EndpointDetail.svelte.d.ts +1 -2
- package/dist/src/init.d.ts +8 -1
- package/dist/src/init.js +32 -11
- package/package.json +50 -47
- package/src/Blocks.svelte +94 -21
- package/src/RenderComponent.svelte +1 -0
- package/src/api_docs/ApiDocs.svelte +137 -66
- package/src/api_docs/CodeSnippet.svelte +8 -10
- package/src/api_docs/EndpointDetail.svelte +12 -13
- package/src/init.ts +48 -13
|
@@ -52,6 +52,21 @@
|
|
|
52
52
|
export let api_calls: Payload[] = [];
|
|
53
53
|
let current_language: "python" | "javascript" | "bash" | "mcp" = "python";
|
|
54
54
|
|
|
55
|
+
function set_query_param(key: string, value: string) {
|
|
56
|
+
const url = new URL(window.location.href);
|
|
57
|
+
url.searchParams.set(key, value);
|
|
58
|
+
history.replaceState(null, "", url.toString());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function get_query_param(key: string): string | null {
|
|
62
|
+
const url = new URL(window.location.href);
|
|
63
|
+
return url.searchParams.get(key);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function is_valid_language(lang: string | null): boolean {
|
|
67
|
+
return ["python", "javascript", "bash", "mcp"].includes(lang ?? "");
|
|
68
|
+
}
|
|
69
|
+
|
|
55
70
|
const langs = [
|
|
56
71
|
["python", "Python", python],
|
|
57
72
|
["javascript", "JavaScript", javascript],
|
|
@@ -112,11 +127,30 @@
|
|
|
112
127
|
}
|
|
113
128
|
|
|
114
129
|
let tools: Tool[] = [];
|
|
130
|
+
let headers: string[] = [];
|
|
131
|
+
let mcp_json_sse: any;
|
|
132
|
+
let mcp_json_stdio: any;
|
|
133
|
+
let file_data_present = false;
|
|
134
|
+
|
|
135
|
+
const upload_file_mcp_server = {
|
|
136
|
+
command: "uvx",
|
|
137
|
+
args: [
|
|
138
|
+
"--from",
|
|
139
|
+
"gradio[mcp]",
|
|
140
|
+
"gradio",
|
|
141
|
+
"upload-mcp",
|
|
142
|
+
root,
|
|
143
|
+
"<UPLOAD_DIRECTORY>"
|
|
144
|
+
]
|
|
145
|
+
};
|
|
115
146
|
|
|
116
147
|
async function fetchMcpTools() {
|
|
117
148
|
try {
|
|
118
149
|
const response = await fetch(`${root}gradio_api/mcp/schema`);
|
|
119
150
|
const schema = await response.json();
|
|
151
|
+
file_data_present = schema
|
|
152
|
+
.map((tool: any) => tool.meta?.file_data_present)
|
|
153
|
+
.some((present: boolean) => present);
|
|
120
154
|
|
|
121
155
|
tools = schema.map((tool: any) => ({
|
|
122
156
|
name: tool.name,
|
|
@@ -124,6 +158,62 @@
|
|
|
124
158
|
parameters: tool.inputSchema?.properties || {},
|
|
125
159
|
expanded: false
|
|
126
160
|
}));
|
|
161
|
+
headers = schema.map((tool: any) => tool.meta?.headers || []).flat();
|
|
162
|
+
if (headers.length > 0) {
|
|
163
|
+
mcp_json_sse = {
|
|
164
|
+
mcpServers: {
|
|
165
|
+
gradio: {
|
|
166
|
+
url: mcp_server_url,
|
|
167
|
+
headers: headers.reduce((accumulator, current_key) => {
|
|
168
|
+
// @ts-ignore
|
|
169
|
+
accumulator[current_key] = "<YOUR_HEADER_VALUE>";
|
|
170
|
+
return accumulator;
|
|
171
|
+
}, {})
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
mcp_json_stdio = {
|
|
176
|
+
mcpServers: {
|
|
177
|
+
gradio: {
|
|
178
|
+
command: "npx",
|
|
179
|
+
args: [
|
|
180
|
+
"mcp-remote",
|
|
181
|
+
mcp_server_url,
|
|
182
|
+
"--transport",
|
|
183
|
+
"sse-only",
|
|
184
|
+
...headers
|
|
185
|
+
.map((header) => [
|
|
186
|
+
"--header",
|
|
187
|
+
`${header}: <YOUR_HEADER_VALUE>`
|
|
188
|
+
])
|
|
189
|
+
.flat()
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
} else {
|
|
195
|
+
mcp_json_sse = {
|
|
196
|
+
mcpServers: {
|
|
197
|
+
gradio: {
|
|
198
|
+
url: mcp_server_url
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
mcp_json_stdio = {
|
|
203
|
+
mcpServers: {
|
|
204
|
+
gradio: {
|
|
205
|
+
command: "npx",
|
|
206
|
+
args: ["mcp-remote", mcp_server_url, "--transport", "sse-only"]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
if (file_data_present) {
|
|
211
|
+
mcp_json_sse.mcpServers.upload_files_to_gradio =
|
|
212
|
+
upload_file_mcp_server;
|
|
213
|
+
mcp_json_stdio.mcpServers.upload_files_to_gradio =
|
|
214
|
+
upload_file_mcp_server;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
127
217
|
} catch (error) {
|
|
128
218
|
console.error("Failed to fetch MCP tools:", error);
|
|
129
219
|
tools = [];
|
|
@@ -136,12 +226,24 @@
|
|
|
136
226
|
window.parentIFrame?.scrollTo(0, 0);
|
|
137
227
|
}
|
|
138
228
|
|
|
229
|
+
const lang_param = get_query_param("lang");
|
|
230
|
+
if (is_valid_language(lang_param)) {
|
|
231
|
+
current_language = lang_param as "python" | "javascript" | "bash" | "mcp";
|
|
232
|
+
}
|
|
233
|
+
|
|
139
234
|
// Check MCP server status and fetch tools if active
|
|
140
235
|
fetch(mcp_server_url)
|
|
141
236
|
.then((response) => {
|
|
142
237
|
mcp_server_active = response.ok;
|
|
143
238
|
if (mcp_server_active) {
|
|
144
239
|
fetchMcpTools();
|
|
240
|
+
if (!is_valid_language(lang_param)) {
|
|
241
|
+
current_language = "mcp";
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
if (!is_valid_language(lang_param)) {
|
|
245
|
+
current_language = "python";
|
|
246
|
+
}
|
|
145
247
|
}
|
|
146
248
|
})
|
|
147
249
|
.catch(() => {
|
|
@@ -177,7 +279,10 @@
|
|
|
177
279
|
<li
|
|
178
280
|
class="snippet
|
|
179
281
|
{current_language === language ? 'current-lang' : 'inactive-lang'}"
|
|
180
|
-
on:click={() =>
|
|
282
|
+
on:click={() => {
|
|
283
|
+
current_language = language;
|
|
284
|
+
set_query_param("lang", language);
|
|
285
|
+
}}
|
|
181
286
|
>
|
|
182
287
|
<img src={img} alt="" />
|
|
183
288
|
{display_name}
|
|
@@ -235,7 +340,7 @@
|
|
|
235
340
|
<div class="mcp-url">
|
|
236
341
|
<label
|
|
237
342
|
><span class="status-indicator active">●</span>MCP Server
|
|
238
|
-
URL</label
|
|
343
|
+
URL (SSE)</label
|
|
239
344
|
>
|
|
240
345
|
<div class="textbox">
|
|
241
346
|
<input type="text" readonly value={mcp_server_url} />
|
|
@@ -294,45 +399,42 @@
|
|
|
294
399
|
</div>
|
|
295
400
|
<p> </p>
|
|
296
401
|
|
|
297
|
-
<strong>
|
|
402
|
+
<strong>SSE Transport</strong>: To add this MCP to clients that
|
|
298
403
|
support SSE (e.g. Cursor, Windsurf, Cline), simply add the
|
|
299
|
-
following configuration to your MCP config
|
|
404
|
+
following configuration to your MCP config.
|
|
300
405
|
<p> </p>
|
|
301
406
|
<Block>
|
|
302
407
|
<code>
|
|
303
408
|
<div class="copy">
|
|
304
409
|
<CopyButton
|
|
305
|
-
code={JSON.stringify(
|
|
306
|
-
{
|
|
307
|
-
mcpServers: {
|
|
308
|
-
gradio: {
|
|
309
|
-
url: mcp_server_url
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
},
|
|
313
|
-
null,
|
|
314
|
-
2
|
|
315
|
-
)}
|
|
410
|
+
code={JSON.stringify(mcp_json_sse, null, 2)}
|
|
316
411
|
/>
|
|
317
412
|
</div>
|
|
318
413
|
<div>
|
|
319
|
-
<pre>{JSON.stringify(
|
|
320
|
-
{
|
|
321
|
-
mcpServers: {
|
|
322
|
-
gradio: {
|
|
323
|
-
url: mcp_server_url
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
},
|
|
327
|
-
null,
|
|
328
|
-
2
|
|
329
|
-
)}</pre>
|
|
414
|
+
<pre>{JSON.stringify(mcp_json_sse, null, 2)}</pre>
|
|
330
415
|
</div>
|
|
331
416
|
</code>
|
|
332
417
|
</Block>
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
418
|
+
{#if file_data_present}
|
|
419
|
+
<p> </p>
|
|
420
|
+
<em>Note about files</em>: Gradio MCP servers that have files
|
|
421
|
+
as inputs need the files as URLs, so the
|
|
422
|
+
<code>upload_files_to_gradio</code>
|
|
423
|
+
tool is included for your convenience. This tool can upload files
|
|
424
|
+
located in the specified <code>UPLOAD_DIRECTORY</code>
|
|
425
|
+
argument (an absolute path in your local machine) or any of its
|
|
426
|
+
subdirectories to the Gradio app. You can omit this tool if you
|
|
427
|
+
are fine manually uploading files yourself and providing the URLs.
|
|
428
|
+
Before using this tool, you must have
|
|
429
|
+
<a
|
|
430
|
+
href="https://docs.astral.sh/uv/getting-started/installation/"
|
|
431
|
+
target="_blank">uv installed</a
|
|
432
|
+
>.
|
|
433
|
+
<p> </p>
|
|
434
|
+
{/if}
|
|
435
|
+
|
|
436
|
+
<strong>STDIO Transport</strong>: For clients that only support
|
|
437
|
+
stdio (e.g. Claude Desktop), first
|
|
336
438
|
<a href="https://nodejs.org/en/download/" target="_blank"
|
|
337
439
|
>install Node.js</a
|
|
338
440
|
>. Then, you can use the following command:
|
|
@@ -341,43 +443,11 @@
|
|
|
341
443
|
<code>
|
|
342
444
|
<div class="copy">
|
|
343
445
|
<CopyButton
|
|
344
|
-
code={JSON.stringify(
|
|
345
|
-
{
|
|
346
|
-
mcpServers: {
|
|
347
|
-
gradio: {
|
|
348
|
-
command: "npx",
|
|
349
|
-
args: [
|
|
350
|
-
"mcp-remote",
|
|
351
|
-
mcp_server_url,
|
|
352
|
-
"--transport",
|
|
353
|
-
"sse-only"
|
|
354
|
-
]
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
null,
|
|
359
|
-
2
|
|
360
|
-
)}
|
|
446
|
+
code={JSON.stringify(mcp_json_stdio, null, 2)}
|
|
361
447
|
/>
|
|
362
448
|
</div>
|
|
363
449
|
<div>
|
|
364
|
-
<pre>{JSON.stringify(
|
|
365
|
-
{
|
|
366
|
-
mcpServers: {
|
|
367
|
-
gradio: {
|
|
368
|
-
command: "npx",
|
|
369
|
-
args: [
|
|
370
|
-
"mcp-remote",
|
|
371
|
-
mcp_server_url,
|
|
372
|
-
"--transport",
|
|
373
|
-
"sse-only"
|
|
374
|
-
]
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
},
|
|
378
|
-
null,
|
|
379
|
-
2
|
|
380
|
-
)}</pre>
|
|
450
|
+
<pre>{JSON.stringify(mcp_json_stdio, null, 2)}</pre>
|
|
381
451
|
</div>
|
|
382
452
|
</code>
|
|
383
453
|
</Block>
|
|
@@ -453,21 +523,22 @@
|
|
|
453
523
|
{/if}
|
|
454
524
|
|
|
455
525
|
{#if current_language !== "mcp"}
|
|
456
|
-
{#each dependencies as dependency
|
|
526
|
+
{#each dependencies as dependency}
|
|
457
527
|
{#if dependency.show_api && info.named_endpoints["/" + dependency.api_name]}
|
|
458
528
|
<div class="endpoint-container">
|
|
459
529
|
<CodeSnippet
|
|
460
|
-
named={true}
|
|
461
530
|
endpoint_parameters={info.named_endpoints[
|
|
462
531
|
"/" + dependency.api_name
|
|
463
532
|
].parameters}
|
|
464
533
|
{dependency}
|
|
465
|
-
{dependency_index}
|
|
466
534
|
{current_language}
|
|
467
535
|
{root}
|
|
468
536
|
{space_id}
|
|
469
537
|
{username}
|
|
470
538
|
api_prefix={app.api_prefix}
|
|
539
|
+
api_description={info.named_endpoints[
|
|
540
|
+
"/" + dependency.api_name
|
|
541
|
+
].description}
|
|
471
542
|
/>
|
|
472
543
|
|
|
473
544
|
<ParametersSnippet
|
|
@@ -15,14 +15,13 @@
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export let dependency: Dependency;
|
|
18
|
-
export let dependency_index: number;
|
|
19
18
|
export let root: string;
|
|
20
19
|
export let api_prefix: string;
|
|
21
20
|
export let space_id: string | null;
|
|
22
21
|
export let endpoint_parameters: any;
|
|
23
|
-
export let named: boolean;
|
|
24
22
|
export let username: string | null;
|
|
25
23
|
export let current_language: "python" | "javascript" | "bash";
|
|
24
|
+
export let api_description: string | null = null;
|
|
26
25
|
|
|
27
26
|
let python_code: HTMLElement;
|
|
28
27
|
let js_code: HTMLElement;
|
|
@@ -42,11 +41,10 @@
|
|
|
42
41
|
</script>
|
|
43
42
|
|
|
44
43
|
<div class="container">
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
{/if}
|
|
44
|
+
<EndpointDetail
|
|
45
|
+
api_name={dependency.api_name}
|
|
46
|
+
description={api_description}
|
|
47
|
+
/>
|
|
50
48
|
{#if current_language === "python"}
|
|
51
49
|
<Block>
|
|
52
50
|
<code>
|
|
@@ -99,9 +97,9 @@ const example{component} = await response_{i}.blob();
|
|
|
99
97
|
const client = await Client.connect(<span class="token string"
|
|
100
98
|
>"{space_id || root}"</span
|
|
101
99
|
>{#if username !== null}, {auth: ["{username}", **password**]}{/if});
|
|
102
|
-
const result = await client.predict(
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
const result = await client.predict(<span class="api-name"
|
|
101
|
+
>"/{dependency.api_name}"</span
|
|
102
|
+
>, { <!--
|
|
105
103
|
-->{#each endpoint_parameters as { label, parameter_name, type, python_type, component, example_input, serializer }, i}<!--
|
|
106
104
|
-->{#if blob_components.includes(component)}<!--
|
|
107
105
|
-->
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
export let api_name: string | null = null;
|
|
3
|
-
export let
|
|
4
|
-
export let named: boolean;
|
|
3
|
+
export let description: string | null = null;
|
|
5
4
|
</script>
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
{:else}
|
|
13
|
-
<h3>
|
|
14
|
-
fn_index:
|
|
15
|
-
<span class="post">{fn_index}</span>
|
|
16
|
-
</h3>
|
|
17
|
-
{/if}
|
|
6
|
+
<h3>
|
|
7
|
+
API name:
|
|
8
|
+
<span class="post">{"/" + api_name}</span>
|
|
9
|
+
<span class="desc">{description}</span>
|
|
10
|
+
</h3>
|
|
18
11
|
|
|
19
12
|
<style>
|
|
20
13
|
h3 {
|
|
@@ -34,4 +27,10 @@
|
|
|
34
27
|
color: var(--color-accent);
|
|
35
28
|
font-weight: var(--weight-semibold);
|
|
36
29
|
}
|
|
30
|
+
|
|
31
|
+
.desc {
|
|
32
|
+
color: var(--body-text-color-subdued);
|
|
33
|
+
font-size: var(--text-lg);
|
|
34
|
+
margin-top: var(--size-1);
|
|
35
|
+
}
|
|
37
36
|
</style>
|
package/src/init.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { writable, type Writable, get } from "svelte/store";
|
|
2
|
+
import { dequal } from "dequal";
|
|
2
3
|
|
|
3
4
|
import type {
|
|
4
5
|
ComponentMeta,
|
|
@@ -28,8 +29,15 @@ const raf = is_browser
|
|
|
28
29
|
* Create a store with the layout and a map of targets
|
|
29
30
|
* @returns A store with the layout and a map of targets
|
|
30
31
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
export function create_components(
|
|
33
|
+
{
|
|
34
|
+
initial_layout = undefined
|
|
35
|
+
}: {
|
|
36
|
+
initial_layout: ComponentMeta | undefined;
|
|
37
|
+
} = {
|
|
38
|
+
initial_layout: undefined
|
|
39
|
+
}
|
|
40
|
+
): {
|
|
33
41
|
layout: Writable<ComponentMeta>;
|
|
34
42
|
targets: Writable<TargetMap>;
|
|
35
43
|
update_value: (updates: UpdateTransaction[]) => void;
|
|
@@ -56,6 +64,7 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
|
|
|
56
64
|
root: string;
|
|
57
65
|
dependencies: Dependency[];
|
|
58
66
|
}) => void;
|
|
67
|
+
value_change: (cb: (id: number, value: any) => void) => void;
|
|
59
68
|
} {
|
|
60
69
|
let _component_map: Map<number, ComponentMeta>;
|
|
61
70
|
|
|
@@ -73,6 +82,12 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
|
|
|
73
82
|
let keys_per_render_id: Record<number, (string | number)[]> = {};
|
|
74
83
|
let _rootNode: ComponentMeta;
|
|
75
84
|
|
|
85
|
+
let value_change_cb: ((id: number, value: any) => void) | null = null;
|
|
86
|
+
|
|
87
|
+
function value_change(cb: (id: number, value: any) => void): void {
|
|
88
|
+
value_change_cb = cb;
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
// Store current layout and root for dynamic visibility recalculation
|
|
77
92
|
let current_layout: LayoutNode;
|
|
78
93
|
let current_root: string;
|
|
@@ -114,8 +129,14 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
|
|
|
114
129
|
if (instance_map) {
|
|
115
130
|
// re-render in reload mode
|
|
116
131
|
components.forEach((c) => {
|
|
117
|
-
if (c.props.value == null && c.
|
|
118
|
-
|
|
132
|
+
if (c.props.value == null && c.key) {
|
|
133
|
+
// If the component has a key, we preserve its value by finding a matching instance with the same key
|
|
134
|
+
const matching_instance = Object.values(instance_map).find(
|
|
135
|
+
(instance) => instance.key === c.key
|
|
136
|
+
);
|
|
137
|
+
if (matching_instance) {
|
|
138
|
+
c.props.value = matching_instance.props.value;
|
|
139
|
+
}
|
|
119
140
|
}
|
|
120
141
|
});
|
|
121
142
|
}
|
|
@@ -126,7 +147,6 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
|
|
|
126
147
|
pending_updates = [];
|
|
127
148
|
constructor_map = new Map();
|
|
128
149
|
_component_map = new Map();
|
|
129
|
-
|
|
130
150
|
instance_map = {};
|
|
131
151
|
|
|
132
152
|
// Store current layout and root for dynamic visibility recalculation
|
|
@@ -321,19 +341,12 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
|
|
|
321
341
|
const constructor_key = instance.component_class_id || instance.type;
|
|
322
342
|
let component_constructor = constructor_map.get(constructor_key);
|
|
323
343
|
|
|
324
|
-
// Only load component if it was preloaded (i.e., it's visible)
|
|
325
344
|
if (component_constructor) {
|
|
326
345
|
instance.component = (await component_constructor)?.default;
|
|
327
346
|
}
|
|
328
|
-
// If component wasn't preloaded, leave it unloaded for now
|
|
329
|
-
// It will be loaded later when/if it becomes visible
|
|
330
347
|
}
|
|
331
348
|
instance.parent = parent;
|
|
332
349
|
|
|
333
|
-
// if (instance.type === "timer") {
|
|
334
|
-
// console.log("timer", instance, constructor_map);
|
|
335
|
-
// }
|
|
336
|
-
|
|
337
350
|
if (instance.type === "dataset") {
|
|
338
351
|
instance.props.component_map = get_component(
|
|
339
352
|
instance.type,
|
|
@@ -500,6 +513,7 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
|
|
|
500
513
|
const instance = instance_map[update.id];
|
|
501
514
|
if (!instance) continue;
|
|
502
515
|
let new_value;
|
|
516
|
+
const old_value = instance.props[update.prop];
|
|
503
517
|
if (update.value instanceof Map) new_value = new Map(update.value);
|
|
504
518
|
else if (update.value instanceof Set)
|
|
505
519
|
new_value = new Set(update.value);
|
|
@@ -509,6 +523,14 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
|
|
|
509
523
|
new_value = { ...update.value };
|
|
510
524
|
else new_value = update.value;
|
|
511
525
|
instance.props[update.prop] = new_value;
|
|
526
|
+
|
|
527
|
+
if (
|
|
528
|
+
update.prop === "value" &&
|
|
529
|
+
!is_visible(instance) &&
|
|
530
|
+
!dequal(old_value, new_value)
|
|
531
|
+
) {
|
|
532
|
+
value_change_cb?.(update.id, new_value);
|
|
533
|
+
}
|
|
512
534
|
}
|
|
513
535
|
}
|
|
514
536
|
return layout;
|
|
@@ -625,7 +647,8 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
|
|
|
625
647
|
loading_status,
|
|
626
648
|
scheduled_updates: update_scheduled_store,
|
|
627
649
|
create_layout: create_layout,
|
|
628
|
-
rerender_layout
|
|
650
|
+
rerender_layout,
|
|
651
|
+
value_change
|
|
629
652
|
};
|
|
630
653
|
}
|
|
631
654
|
|
|
@@ -1088,3 +1111,15 @@ export function preload_all_components(
|
|
|
1088
1111
|
|
|
1089
1112
|
return constructor_map;
|
|
1090
1113
|
}
|
|
1114
|
+
|
|
1115
|
+
function is_visible(component: ComponentMeta): boolean {
|
|
1116
|
+
if (
|
|
1117
|
+
typeof component.props.visible === "boolean" &&
|
|
1118
|
+
component.props.visible === false
|
|
1119
|
+
) {
|
|
1120
|
+
return false;
|
|
1121
|
+
} else if (component.parent) {
|
|
1122
|
+
return is_visible(component.parent);
|
|
1123
|
+
}
|
|
1124
|
+
return true;
|
|
1125
|
+
}
|