@gradio/core 1.2.0 → 1.3.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/src/Blocks.svelte +33 -1
  3. package/dist/src/api_docs/ApiBanner.svelte +6 -2
  4. package/dist/src/api_docs/ApiBanner.svelte.d.ts +1 -1
  5. package/dist/src/api_docs/ApiDocs.svelte +41 -25
  6. package/dist/src/api_docs/CodeSnippet.svelte +67 -170
  7. package/dist/src/api_docs/CodeSnippet.svelte.d.ts +2 -6
  8. package/dist/src/api_docs/CopyMarkdown.svelte +7 -2
  9. package/dist/src/api_docs/CopyMarkdown.svelte.d.ts +1 -1
  10. package/dist/src/api_docs/InstallSnippet.svelte +6 -1
  11. package/dist/src/api_docs/InstallSnippet.svelte.d.ts +1 -1
  12. package/dist/src/api_docs/ParametersSnippet.svelte +6 -1
  13. package/dist/src/api_docs/ParametersSnippet.svelte.d.ts +1 -1
  14. package/dist/src/api_docs/RecordingSnippet.svelte +6 -1
  15. package/dist/src/api_docs/RecordingSnippet.svelte.d.ts +1 -1
  16. package/dist/src/api_docs/ResponseSnippet.svelte +6 -1
  17. package/dist/src/api_docs/ResponseSnippet.svelte.d.ts +1 -1
  18. package/dist/src/api_docs/SkillSnippet.svelte +125 -0
  19. package/dist/src/api_docs/SkillSnippet.svelte.d.ts +20 -0
  20. package/dist/src/api_docs/img/skill.svg +10 -0
  21. package/dist/src/api_docs/utils.d.ts +0 -1
  22. package/dist/src/api_docs/utils.js +0 -22
  23. package/dist/src/dependency.d.ts +3 -1
  24. package/dist/src/dependency.js +19 -1
  25. package/dist/src/init.svelte.js +81 -19
  26. package/package.json +22 -22
  27. package/src/Blocks.svelte +33 -1
  28. package/src/api_docs/ApiBanner.svelte +6 -2
  29. package/src/api_docs/ApiDocs.svelte +41 -25
  30. package/src/api_docs/CodeSnippet.svelte +67 -170
  31. package/src/api_docs/CopyMarkdown.svelte +7 -2
  32. package/src/api_docs/InstallSnippet.svelte +6 -1
  33. package/src/api_docs/ParametersSnippet.svelte +6 -1
  34. package/src/api_docs/RecordingSnippet.svelte +6 -1
  35. package/src/api_docs/ResponseSnippet.svelte +6 -1
  36. package/src/api_docs/SkillSnippet.svelte +125 -0
  37. package/src/api_docs/img/skill.svg +10 -0
  38. package/src/api_docs/utils.ts +0 -25
  39. package/src/dependency.ts +19 -1
  40. package/src/init.svelte.ts +95 -19
@@ -4,7 +4,12 @@
4
4
  export let is_running: boolean;
5
5
  export let endpoint_returns: any;
6
6
  export let js_returns: any;
7
- export let current_language: "python" | "javascript" | "bash" | "mcp";
7
+ export let current_language:
8
+ | "python"
9
+ | "javascript"
10
+ | "bash"
11
+ | "skill"
12
+ | "mcp";
8
13
  </script>
9
14
 
10
15
  <h4>
@@ -15,7 +15,7 @@ declare const ResponseSnippet: $$__sveltets_2_IsomorphicComponent<{
15
15
  is_running: boolean;
16
16
  endpoint_returns: any;
17
17
  js_returns: any;
18
- current_language: "python" | "javascript" | "bash" | "mcp";
18
+ current_language: "python" | "javascript" | "bash" | "skill" | "mcp";
19
19
  }, {
20
20
  [evt: string]: CustomEvent<any>;
21
21
  }, {}, {}, string>;
@@ -0,0 +1,125 @@
1
+ <script lang="ts">
2
+ import { Block } from "@gradio/atoms";
3
+ import CopyButton from "./CopyButton.svelte";
4
+
5
+ export let space_id: string | null;
6
+
7
+ $: effective_space_id = space_id || "";
8
+ $: skill_id = effective_space_id.replace("/", "-");
9
+ $: install_gradio = "pip install --upgrade gradio";
10
+ $: install_cmd_claude = `gradio skills add ${effective_space_id} --claude`;
11
+ $: install_cmd_cursor = `gradio skills add ${effective_space_id} --cursor`;
12
+ $: install_cmd_codex = `gradio skills add ${effective_space_id} --codex`;
13
+
14
+ $: skill_preview = `---
15
+ name: ${skill_id}
16
+ description: Use the ${effective_space_id} Gradio Space via API. Provides Python, JavaScript, and cURL usage examples.
17
+ ---
18
+
19
+ # ${effective_space_id}
20
+
21
+ This skill describes how to use the ${effective_space_id} Gradio Space programmatically.
22
+
23
+ ## API Endpoints
24
+ ...`;
25
+ </script>
26
+
27
+ <div class="skill-content">
28
+ <p class="padded">1. Make sure you are using the latest version of Gradio:</p>
29
+ <Block>
30
+ <code>
31
+ <div class="copy">
32
+ <CopyButton code={install_gradio} />
33
+ </div>
34
+ <div>
35
+ <pre>$ {install_gradio}</pre>
36
+ </div>
37
+ </code>
38
+ </Block>
39
+
40
+ <p class="padded">
41
+ 2. Install the usage of this Space as a Skill for your coding agent by
42
+ running this in your terminal:
43
+ </p>
44
+ <Block>
45
+ <code>
46
+ <div class="copy">
47
+ <CopyButton code={install_cmd_claude} />
48
+ </div>
49
+ <div>
50
+ <pre>$ {install_cmd_claude}</pre>
51
+ </div>
52
+ </code>
53
+ </Block>
54
+ <p class="hint">
55
+ Instead of <span class="inline-code">--claude</span>, you can use
56
+ <span class="inline-code">--cursor</span>,
57
+ <span class="inline-code">--codex</span>, or
58
+ <span class="inline-code">--opencode</span>. Combine flags to install for
59
+ multiple agents. Use <span class="inline-code">--global</span> to install user-level
60
+ instead of per-project.
61
+ </p>
62
+
63
+ <p class="padded">
64
+ 3. This will add a skill to your coding agent that describes how to use this
65
+ Space via Python, JavaScript, and cURL API. The skill will look like this:
66
+ </p>
67
+ <Block>
68
+ <code>
69
+ <div class="copy">
70
+ <CopyButton code={skill_preview} />
71
+ </div>
72
+ <div>
73
+ <pre>{skill_preview}</pre>
74
+ </div>
75
+ </code>
76
+ </Block>
77
+ </div>
78
+
79
+ <style>
80
+ .skill-content {
81
+ margin-top: var(--size-2);
82
+ }
83
+
84
+ p.padded {
85
+ padding: 15px 0px;
86
+ font-size: var(--text-lg);
87
+ }
88
+
89
+ .hint {
90
+ margin-top: var(--size-2);
91
+ color: var(--body-text-color);
92
+ opacity: 0.8;
93
+ font-size: var(--text-md);
94
+ line-height: 1.6;
95
+ }
96
+
97
+ .inline-code {
98
+ display: inline;
99
+ font-family: var(--font-mono);
100
+ font-size: var(--text-md);
101
+ background: var(--background-fill-secondary);
102
+ padding: 1px 4px;
103
+ border-radius: var(--radius-sm);
104
+ }
105
+
106
+ code pre {
107
+ overflow-x: auto;
108
+ color: var(--body-text-color);
109
+ font-family: var(--font-mono);
110
+ tab-size: 2;
111
+ }
112
+
113
+ code {
114
+ position: relative;
115
+ display: block;
116
+ }
117
+
118
+ .copy {
119
+ position: absolute;
120
+ top: 0;
121
+ right: 0;
122
+ margin-top: -5px;
123
+ margin-right: -5px;
124
+ }
125
+ </style>
@@ -0,0 +1,20 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: Props & {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const SkillSnippet: $$__sveltets_2_IsomorphicComponent<{
15
+ space_id: string | null;
16
+ }, {
17
+ [evt: string]: CustomEvent<any>;
18
+ }, {}, {}, string>;
19
+ type SkillSnippet = InstanceType<typeof SkillSnippet>;
20
+ export default SkillSnippet;
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="14 22 122 106" style="background-color: #ffffff;">
2
+ <rect x="22" y="28" width="106" height="94" rx="8" fill="none" stroke="#374151" stroke-width="6"/>
3
+ <rect x="22" y="28" width="106" height="20" rx="8" fill="#374151" fill-opacity="0.08"/>
4
+ <circle cx="38" cy="38" r="4" fill="#ff5f57"/>
5
+ <circle cx="52" cy="38" r="4" fill="#febc2e"/>
6
+ <circle cx="66" cy="38" r="4" fill="#28c840"/>
7
+ <text x="38" y="76" fill="#374151" font-family="monospace" font-size="22" font-weight="bold">&gt;_</text>
8
+ <rect x="38" y="88" width="50" height="4" rx="2" fill="#374151" fill-opacity="0.35"/>
9
+ <rect x="38" y="100" width="35" height="4" rx="2" fill="#374151" fill-opacity="0.2"/>
10
+ </svg>
@@ -1,4 +1,3 @@
1
1
  export declare function represent_value(value: string, type: string | undefined, lang?: "js" | "py" | "bash" | null): string | null | number | boolean | Record<string, unknown>;
2
- export declare function is_potentially_nested_file_data(obj: any): boolean;
3
2
  export declare function format_latency(val: number): string;
4
3
  export declare function get_color_from_success_rate(success_rate: number): string;
@@ -48,28 +48,6 @@ export function represent_value(value, type, lang = null) {
48
48
  }
49
49
  return stringify_except_file_function(value);
50
50
  }
51
- export function is_potentially_nested_file_data(obj) {
52
- if (typeof obj === "object" && obj !== null) {
53
- if (obj.hasOwnProperty("url") && obj.hasOwnProperty("meta")) {
54
- if (typeof obj.meta === "object" &&
55
- obj.meta !== null &&
56
- obj.meta._type === "gradio.FileData") {
57
- return true;
58
- }
59
- }
60
- }
61
- if (typeof obj === "object" && obj !== null) {
62
- for (let key in obj) {
63
- if (typeof obj[key] === "object") {
64
- let result = is_potentially_nested_file_data(obj[key]);
65
- if (result) {
66
- return true;
67
- }
68
- }
69
- }
70
- }
71
- return false;
72
- }
73
51
  function simplify_file_data(obj) {
74
52
  if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
75
53
  if ("url" in obj &&
@@ -85,8 +85,10 @@ export declare class DependencyManager {
85
85
  get_state_cb: GetStateCallback;
86
86
  rerender_cb: RerenderCallback;
87
87
  log_cb: LogCallback;
88
+ on_connection_lost_cb: () => void;
88
89
  loading_stati: LoadingStatus;
89
- constructor(dependencies: IDependency[], client: Client, update_state_cb: (id: number, state: Record<string, unknown>, check_visibility?: boolean) => Promise<void>, get_state_cb: (id: number) => Promise<Record<string, unknown> | null>, rerender_cb: (components: ComponentMeta[], layout: LayoutNode) => void, log_cb: (title: string, message: string, fn_index: number, type: ToastMessage["type"], duration?: number | null, visible?: boolean) => void, add_to_api_calls: (payload: Payload) => void);
90
+ connection_lost: boolean;
91
+ constructor(dependencies: IDependency[], client: Client, update_state_cb: (id: number, state: Record<string, unknown>, check_visibility?: boolean) => Promise<void>, get_state_cb: (id: number) => Promise<Record<string, unknown> | null>, rerender_cb: (components: ComponentMeta[], layout: LayoutNode) => void, log_cb: (title: string, message: string, fn_index: number, type: ToastMessage["type"], duration?: number | null, visible?: boolean) => void, add_to_api_calls: (payload: Payload) => void, on_connection_lost_cb: () => void);
90
92
  reload(dependencies: IDependency[], update_state: UpdateStateCallback, get_state: GetStateCallback, rerender: RerenderCallback, client: Client): void;
91
93
  register_loading_stati(deps: Map<number, Dependency>): void;
92
94
  clear_loading_status(component_id: number): void;
@@ -118,13 +118,16 @@ export class DependencyManager {
118
118
  get_state_cb;
119
119
  rerender_cb;
120
120
  log_cb;
121
+ on_connection_lost_cb;
121
122
  loading_stati = new LoadingStatus();
122
- constructor(dependencies, client, update_state_cb, get_state_cb, rerender_cb, log_cb, add_to_api_calls) {
123
+ connection_lost = false;
124
+ constructor(dependencies, client, update_state_cb, get_state_cb, rerender_cb, log_cb, add_to_api_calls, on_connection_lost_cb) {
123
125
  this.add_to_api_calls = add_to_api_calls;
124
126
  this.log_cb = log_cb;
125
127
  this.update_state_cb = update_state_cb;
126
128
  this.get_state_cb = get_state_cb;
127
129
  this.rerender_cb = rerender_cb;
130
+ this.on_connection_lost_cb = on_connection_lost_cb;
128
131
  this.client = client;
129
132
  this.reload(dependencies, update_state_cb, get_state_cb, rerender_cb, client);
130
133
  }
@@ -180,6 +183,8 @@ export class DependencyManager {
180
183
  * @returns a value if there is no backend fn, a 'submission' if there is a backend fn, or null if there is no dependency
181
184
  */
182
185
  async dispatch(event_meta) {
186
+ if (this.connection_lost)
187
+ return;
183
188
  let deps;
184
189
  if (event_meta.type === "fn") {
185
190
  const dep = this.dependencies_by_fn.get(event_meta.fn_index);
@@ -314,6 +319,19 @@ export class DependencyManager {
314
319
  this.update_loading_stati_state();
315
320
  }
316
321
  else if (result.stage === "error") {
322
+ if (result.broken || result.session_not_found) {
323
+ if (!this.connection_lost) {
324
+ this.connection_lost = true;
325
+ this.on_connection_lost_cb();
326
+ }
327
+ this.loading_stati.update({
328
+ status: "complete",
329
+ fn_index: dep.id,
330
+ stream_state: null
331
+ });
332
+ this.update_loading_stati_state();
333
+ break submit_loop;
334
+ }
317
335
  if (Array.isArray(result?.message)) {
318
336
  result.message.forEach((m, i) => {
319
337
  this.update_state_cb(dep.inputs[i], {
@@ -31,6 +31,7 @@ export class AppTree {
31
31
  #pending_components = [];
32
32
  #get_callbacks = new Map();
33
33
  #set_callbacks = new Map();
34
+ #pending_updates = new Map();
34
35
  #event_dispatcher;
35
36
  component_ids;
36
37
  initial_tabs = {};
@@ -110,6 +111,20 @@ export class AppTree {
110
111
  this.#set_callbacks.set(id, _set_data);
111
112
  this.#get_callbacks.set(id, _get_data);
112
113
  this.components_to_register.delete(id);
114
+ // Apply any pending updates that were stored while the component
115
+ // was not yet mounted (e.g. hidden in an inactive tab).
116
+ // We must apply AFTER tick() so that the Gradio class's $effect
117
+ // (which syncs from node props) has already run. Otherwise the
118
+ // $effect would overwrite the values we set here.
119
+ const pending = this.#pending_updates.get(id);
120
+ if (pending) {
121
+ this.#pending_updates.delete(id);
122
+ tick().then(() => {
123
+ const _set = this.#set_callbacks.get(id);
124
+ if (_set)
125
+ _set(pending);
126
+ });
127
+ }
113
128
  if (this.components_to_register.size === 0 && !this.resolved) {
114
129
  this.resolved = true;
115
130
  this.ready_resolve();
@@ -276,11 +291,22 @@ export class AppTree {
276
291
  const old_value = node?.props.props.value;
277
292
  // @ts-ignore
278
293
  const new_props = create_props_shared_props(new_state);
279
- node.props.shared_props = {
280
- ...node?.props.shared_props,
281
- ...new_props.shared_props
282
- };
283
- node.props.props = { ...node?.props.props, ...new_props.props };
294
+ // Modify props in-place instead of replacing the entire object.
295
+ // Replacing with a new object via spread can cause Svelte 5's
296
+ // deep $state proxy to lose track of the values during async
297
+ // component mounting/revival.
298
+ for (const key in new_props.shared_props) {
299
+ // @ts-ignore
300
+ node.props.shared_props[key] = new_props.shared_props[key];
301
+ }
302
+ for (const key in new_props.props) {
303
+ // @ts-ignore
304
+ node.props.props[key] = new_props.props[key];
305
+ }
306
+ // Also store as pending so the value can be applied via _set_data
307
+ // when the component eventually mounts and registers
308
+ const existing = this.#pending_updates.get(id) || {};
309
+ this.#pending_updates.set(id, { ...existing, ...new_state });
284
310
  if ("value" in new_state && !dequal(old_value, new_state.value)) {
285
311
  this.#event_dispatcher(id, "change", null);
286
312
  }
@@ -316,24 +342,58 @@ export class AppTree {
316
342
  return null;
317
343
  }
318
344
  async render_previously_invisible_children(id) {
319
- this.root = this.traverse(this.root, [
320
- (node) => {
321
- if (node.id === id) {
322
- make_visible_if_not_rendered(node, this.#hidden_on_startup);
323
- }
324
- return node;
325
- },
326
- (node) => handle_visibility(node, this.#config.api_url)
327
- ]);
345
+ const node = find_node_by_id(this.root, id);
346
+ if (!node)
347
+ return;
348
+ // Check if this node or any of its descendants need to be made visible.
349
+ // If not, skip entirely to avoid unnecessary reactive updates
350
+ // from mutating the tree through the $state proxy.
351
+ if (!this.#hidden_on_startup.has(node.id) &&
352
+ !has_hidden_descendants(node, this.#hidden_on_startup)) {
353
+ return;
354
+ }
355
+ make_visible_if_not_rendered(node, this.#hidden_on_startup, true);
356
+ load_components(node, this.#config.api_url);
328
357
  }
329
358
  }
330
- function make_visible_if_not_rendered(node, hidden_on_startup) {
359
+ function make_visible_if_not_rendered(node, hidden_on_startup, is_target_node = false) {
331
360
  node.props.shared_props.visible = hidden_on_startup.has(node.id)
332
361
  ? true
333
362
  : node.props.shared_props.visible;
334
- node.children.forEach((child) => {
335
- make_visible_if_not_rendered(child, hidden_on_startup);
336
- });
363
+ if (node.type === "tabs") {
364
+ const selectedId = node.props.props.selected ?? node.props.props.initial_tabs?.[0]?.id;
365
+ node.children.forEach((child) => {
366
+ if (child.type === "tabitem" &&
367
+ (child.props.props.id === selectedId || child.id === selectedId)) {
368
+ make_visible_if_not_rendered(child, hidden_on_startup, false);
369
+ }
370
+ });
371
+ }
372
+ else if (node.type === "accordion" &&
373
+ node.props.props.open === false &&
374
+ !is_target_node) {
375
+ // Don't recurse into closed accordion content
376
+ }
377
+ else {
378
+ node.children.forEach((child) => {
379
+ make_visible_if_not_rendered(child, hidden_on_startup, false);
380
+ });
381
+ }
382
+ }
383
+ function has_hidden_descendants(node, hidden_on_startup) {
384
+ for (const child of node.children) {
385
+ if (hidden_on_startup.has(child.id))
386
+ return true;
387
+ if (has_hidden_descendants(child, hidden_on_startup))
388
+ return true;
389
+ }
390
+ return false;
391
+ }
392
+ function load_components(node, api_url) {
393
+ if (node.props.shared_props.visible && !node.component) {
394
+ node.component = get_component(node.type, node.component_class_id, api_url);
395
+ }
396
+ node.children.forEach((child) => load_components(child, api_url));
337
397
  }
338
398
  /**
339
399
  * Process the server function names and return a dictionary of functions
@@ -492,8 +552,10 @@ function _gather_initial_tabs(node, initial_tabs, parent_tab_id, order) {
492
552
  if (!("id" in node.props.props)) {
493
553
  node.props.props.id = node.id;
494
554
  }
555
+ const i18n = node.props.props.i18n;
556
+ const raw_label = node.props.shared_props.label;
495
557
  initial_tabs[parent_tab_id].push({
496
- label: node.props.shared_props.label,
558
+ label: i18n ? i18n(raw_label) : raw_label,
497
559
  id: node.props.props.id,
498
560
  elem_id: node.props.shared_props.elem_id,
499
561
  visible: node.props.shared_props.visible,
package/package.json CHANGED
@@ -1,63 +1,63 @@
1
1
  {
2
2
  "name": "@gradio/core",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "devDependencies": {
6
+ "@gradio/annotatedimage": "^0.11.3",
6
7
  "@gradio/accordion": "^0.5.31",
7
8
  "@gradio/audio": "^0.22.3",
8
9
  "@gradio/atoms": "^0.22.0",
9
10
  "@gradio/box": "^0.2.30",
10
- "@gradio/annotatedimage": "^0.11.3",
11
11
  "@gradio/browserstate": "^0.3.6",
12
- "@gradio/button": "^0.6.3",
12
+ "@gradio/button": "^0.6.4",
13
+ "@gradio/chatbot": "^0.29.4",
13
14
  "@gradio/checkbox": "^0.6.3",
14
15
  "@gradio/checkboxgroup": "^0.9.3",
15
- "@gradio/client": "^2.0.4",
16
- "@gradio/code": "^0.17.3",
16
+ "@gradio/client": "^2.1.0",
17
17
  "@gradio/colorpicker": "^0.5.6",
18
- "@gradio/dataframe": "^0.21.5",
19
- "@gradio/dataset": "^0.5.4",
20
- "@gradio/chatbot": "^0.29.4",
21
18
  "@gradio/column": "^0.3.2",
22
19
  "@gradio/datetime": "^0.4.3",
20
+ "@gradio/code": "^0.17.3",
21
+ "@gradio/downloadbutton": "^0.4.17",
23
22
  "@gradio/dropdown": "^0.11.4",
23
+ "@gradio/dataset": "^0.5.4",
24
24
  "@gradio/fallback": "^0.4.34",
25
25
  "@gradio/file": "^0.14.3",
26
- "@gradio/form": "^0.3.0",
27
- "@gradio/downloadbutton": "^0.4.17",
28
- "@gradio/gallery": "^0.17.1",
29
26
  "@gradio/fileexplorer": "^0.6.3",
27
+ "@gradio/dataframe": "^0.21.6",
28
+ "@gradio/form": "^0.3.0",
30
29
  "@gradio/group": "^0.3.2",
30
+ "@gradio/gallery": "^0.17.1",
31
31
  "@gradio/highlightedtext": "^0.11.2",
32
- "@gradio/html": "^0.9.3",
32
+ "@gradio/html": "^0.10.0",
33
33
  "@gradio/icons": "^0.15.1",
34
- "@gradio/imageeditor": "^0.18.6",
35
34
  "@gradio/image": "^0.25.3",
35
+ "@gradio/imageeditor": "^0.18.6",
36
36
  "@gradio/label": "^0.6.3",
37
37
  "@gradio/json": "^0.7.2",
38
- "@gradio/imageslider": "^0.4.3",
39
38
  "@gradio/markdown": "^0.13.28",
39
+ "@gradio/imageslider": "^0.4.3",
40
40
  "@gradio/model3d": "^0.16.4",
41
41
  "@gradio/multimodaltextbox": "^0.11.6",
42
- "@gradio/paramviewer": "^0.9.4",
43
42
  "@gradio/nativeplot": "^0.10.2",
44
43
  "@gradio/number": "^0.8.3",
44
+ "@gradio/paramviewer": "^0.9.4",
45
45
  "@gradio/plot": "^0.10.4",
46
- "@gradio/radio": "^0.9.3",
47
46
  "@gradio/row": "^0.3.1",
48
- "@gradio/simpletextbox": "^0.3.36",
49
- "@gradio/simpledropdown": "^0.3.34",
50
47
  "@gradio/sidebar": "^0.2.3",
48
+ "@gradio/simpledropdown": "^0.3.34",
51
49
  "@gradio/simpleimage": "^0.9.5",
52
50
  "@gradio/slider": "^0.7.6",
51
+ "@gradio/simpletextbox": "^0.3.36",
52
+ "@gradio/radio": "^0.9.3",
53
53
  "@gradio/state": "^0.2.2",
54
- "@gradio/tabitem": "^0.6.5",
55
54
  "@gradio/statustracker": "^0.12.4",
56
- "@gradio/tabs": "^0.5.6",
57
- "@gradio/theme": "^0.6.1",
55
+ "@gradio/tabitem": "^0.6.5",
56
+ "@gradio/tabs": "^0.5.7",
58
57
  "@gradio/textbox": "^0.13.4",
59
- "@gradio/upload": "^0.17.6",
58
+ "@gradio/theme": "^0.6.1",
60
59
  "@gradio/timer": "^0.4.8",
60
+ "@gradio/upload": "^0.17.6",
61
61
  "@gradio/uploadbutton": "^0.9.17",
62
62
  "@gradio/utils": "^0.11.3",
63
63
  "@gradio/video": "^0.20.3",
package/src/Blocks.svelte CHANGED
@@ -104,6 +104,7 @@
104
104
  });
105
105
 
106
106
  let messages: (ToastMessage & { fn_index: number })[] = $state([]);
107
+ let reconnect_interval: ReturnType<typeof setInterval> | null = null;
107
108
 
108
109
  function gradio_event_dispatcher(
109
110
  id: number,
@@ -206,6 +207,35 @@
206
207
  api_calls = [...api_calls, last_api_call];
207
208
  };
208
209
 
210
+ function handle_connection_lost(): void {
211
+ messages = messages.filter((m) => m.type !== "error");
212
+
213
+ ++_error_id;
214
+ messages.push({
215
+ title: "Connection Lost",
216
+ message: LOST_CONNECTION_MESSAGE,
217
+ fn_index: -1,
218
+ type: "error",
219
+ id: _error_id,
220
+ duration: null,
221
+ visible: true
222
+ });
223
+
224
+ reconnect_interval = setInterval(async () => {
225
+ try {
226
+ const status = await app.reconnect();
227
+ if (status === "connected" || status === "changed") {
228
+ clearInterval(reconnect_interval!);
229
+ reconnect_interval = null;
230
+ window.location.reload();
231
+ }
232
+ } catch (e) {
233
+ // server still unreachable
234
+ console.debug(e);
235
+ }
236
+ }, 2000);
237
+ }
238
+
209
239
  let dep_manager = new DependencyManager(
210
240
  dependencies,
211
241
  app,
@@ -213,7 +243,8 @@
213
243
  app_tree.get_state.bind(app_tree),
214
244
  app_tree.rerender.bind(app_tree),
215
245
  new_message,
216
- add_to_api_calls
246
+ add_to_api_calls,
247
+ handle_connection_lost
217
248
  );
218
249
 
219
250
  $effect(() => {
@@ -426,6 +457,7 @@
426
457
  return () => {
427
458
  mut.disconnect();
428
459
  res.disconnect();
460
+ if (reconnect_interval) clearInterval(reconnect_interval);
429
461
  };
430
462
  });
431
463
 
@@ -6,8 +6,12 @@
6
6
 
7
7
  export let root: string;
8
8
  export let api_count: number;
9
- export let current_language: "python" | "javascript" | "bash" | "mcp" =
10
- "python";
9
+ export let current_language:
10
+ | "python"
11
+ | "javascript"
12
+ | "bash"
13
+ | "skill"
14
+ | "mcp" = "python";
11
15
 
12
16
  const dispatch = createEventDispatcher();
13
17
  </script>