@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
package/src/dependency.ts CHANGED
@@ -205,8 +205,10 @@ export class DependencyManager {
205
205
  get_state_cb: GetStateCallback;
206
206
  rerender_cb: RerenderCallback;
207
207
  log_cb: LogCallback;
208
+ on_connection_lost_cb: () => void;
208
209
 
209
210
  loading_stati = new LoadingStatus();
211
+ connection_lost = false;
210
212
 
211
213
  constructor(
212
214
  dependencies: IDependency[],
@@ -226,13 +228,15 @@ export class DependencyManager {
226
228
  duration?: number | null,
227
229
  visible?: boolean
228
230
  ) => void,
229
- add_to_api_calls: (payload: Payload) => void
231
+ add_to_api_calls: (payload: Payload) => void,
232
+ on_connection_lost_cb: () => void
230
233
  ) {
231
234
  this.add_to_api_calls = add_to_api_calls;
232
235
  this.log_cb = log_cb;
233
236
  this.update_state_cb = update_state_cb;
234
237
  this.get_state_cb = get_state_cb;
235
238
  this.rerender_cb = rerender_cb;
239
+ this.on_connection_lost_cb = on_connection_lost_cb;
236
240
  this.client = client;
237
241
  this.reload(
238
242
  dependencies,
@@ -318,6 +322,7 @@ export class DependencyManager {
318
322
  * @returns a value if there is no backend fn, a 'submission' if there is a backend fn, or null if there is no dependency
319
323
  */
320
324
  async dispatch(event_meta: DispatchFunction | DispatchEvent): Promise<void> {
325
+ if (this.connection_lost) return;
321
326
  let deps: Dependency[] | undefined;
322
327
  if (event_meta.type === "fn") {
323
328
  const dep = this.dependencies_by_fn.get(event_meta.fn_index!);
@@ -484,6 +489,19 @@ export class DependencyManager {
484
489
  });
485
490
  this.update_loading_stati_state();
486
491
  } else if (result.stage === "error") {
492
+ if (result.broken || result.session_not_found) {
493
+ if (!this.connection_lost) {
494
+ this.connection_lost = true;
495
+ this.on_connection_lost_cb();
496
+ }
497
+ this.loading_stati.update({
498
+ status: "complete",
499
+ fn_index: dep.id,
500
+ stream_state: null
501
+ });
502
+ this.update_loading_stati_state();
503
+ break submit_loop;
504
+ }
487
505
  if (Array.isArray(result?.message)) {
488
506
  result.message.forEach((m: ValidationError, i) => {
489
507
  this.update_state_cb(
@@ -67,6 +67,7 @@ export class AppTree {
67
67
 
68
68
  #get_callbacks = new Map<number, get_data_type>();
69
69
  #set_callbacks = new Map<number, set_data_type>();
70
+ #pending_updates = new Map<number, Record<string, unknown>>();
70
71
  #event_dispatcher: (id: number, event: string, data: unknown) => void;
71
72
  component_ids: number[];
72
73
  initial_tabs: Record<number, Tab[]> = {};
@@ -195,6 +196,21 @@ export class AppTree {
195
196
  this.#set_callbacks.set(id, _set_data);
196
197
  this.#get_callbacks.set(id, _get_data);
197
198
  this.components_to_register.delete(id);
199
+
200
+ // Apply any pending updates that were stored while the component
201
+ // was not yet mounted (e.g. hidden in an inactive tab).
202
+ // We must apply AFTER tick() so that the Gradio class's $effect
203
+ // (which syncs from node props) has already run. Otherwise the
204
+ // $effect would overwrite the values we set here.
205
+ const pending = this.#pending_updates.get(id);
206
+ if (pending) {
207
+ this.#pending_updates.delete(id);
208
+ tick().then(() => {
209
+ const _set = this.#set_callbacks.get(id);
210
+ if (_set) _set(pending);
211
+ });
212
+ }
213
+
198
214
  if (this.components_to_register.size === 0 && !this.resolved) {
199
215
  this.resolved = true;
200
216
  this.ready_resolve();
@@ -429,11 +445,24 @@ export class AppTree {
429
445
  const old_value = node?.props.props.value;
430
446
  // @ts-ignore
431
447
  const new_props = create_props_shared_props(new_state);
432
- node!.props.shared_props = {
433
- ...node?.props.shared_props,
434
- ...new_props.shared_props
435
- };
436
- node!.props.props = { ...node?.props.props, ...new_props.props };
448
+ // Modify props in-place instead of replacing the entire object.
449
+ // Replacing with a new object via spread can cause Svelte 5's
450
+ // deep $state proxy to lose track of the values during async
451
+ // component mounting/revival.
452
+ for (const key in new_props.shared_props) {
453
+ // @ts-ignore
454
+ node!.props.shared_props[key] = new_props.shared_props[key];
455
+ }
456
+ for (const key in new_props.props) {
457
+ // @ts-ignore
458
+ node!.props.props[key] = new_props.props[key];
459
+ }
460
+
461
+ // Also store as pending so the value can be applied via _set_data
462
+ // when the component eventually mounts and registers
463
+ const existing = this.#pending_updates.get(id) || {};
464
+ this.#pending_updates.set(id, { ...existing, ...new_state });
465
+
437
466
  if ("value" in new_state && !dequal(old_value, new_state.value)) {
438
467
  this.#event_dispatcher(id, "change", null);
439
468
  }
@@ -469,28 +498,73 @@ export class AppTree {
469
498
  }
470
499
 
471
500
  async render_previously_invisible_children(id: number) {
472
- this.root = this.traverse(this.root!, [
473
- (node) => {
474
- if (node.id === id) {
475
- make_visible_if_not_rendered(node, this.#hidden_on_startup);
476
- }
477
- return node;
478
- },
479
- (node) => handle_visibility(node, this.#config.api_url)
480
- ]);
501
+ const node = find_node_by_id(this.root!, id);
502
+ if (!node) return;
503
+
504
+ // Check if this node or any of its descendants need to be made visible.
505
+ // If not, skip entirely to avoid unnecessary reactive updates
506
+ // from mutating the tree through the $state proxy.
507
+ if (
508
+ !this.#hidden_on_startup.has(node.id) &&
509
+ !has_hidden_descendants(node, this.#hidden_on_startup)
510
+ ) {
511
+ return;
512
+ }
513
+
514
+ make_visible_if_not_rendered(node, this.#hidden_on_startup, true);
515
+ load_components(node, this.#config.api_url);
481
516
  }
482
517
  }
483
518
 
484
519
  function make_visible_if_not_rendered(
485
520
  node: ProcessedComponentMeta,
486
- hidden_on_startup: Set<number>
521
+ hidden_on_startup: Set<number>,
522
+ is_target_node = false
487
523
  ): void {
488
524
  node.props.shared_props.visible = hidden_on_startup.has(node.id)
489
525
  ? true
490
526
  : node.props.shared_props.visible;
491
- node.children.forEach((child) => {
492
- make_visible_if_not_rendered(child, hidden_on_startup);
493
- });
527
+
528
+ if (node.type === "tabs") {
529
+ const selectedId =
530
+ node.props.props.selected ?? node.props.props.initial_tabs?.[0]?.id;
531
+ node.children.forEach((child) => {
532
+ if (
533
+ child.type === "tabitem" &&
534
+ (child.props.props.id === selectedId || child.id === selectedId)
535
+ ) {
536
+ make_visible_if_not_rendered(child, hidden_on_startup, false);
537
+ }
538
+ });
539
+ } else if (
540
+ node.type === "accordion" &&
541
+ node.props.props.open === false &&
542
+ !is_target_node
543
+ ) {
544
+ // Don't recurse into closed accordion content
545
+ } else {
546
+ node.children.forEach((child) => {
547
+ make_visible_if_not_rendered(child, hidden_on_startup, false);
548
+ });
549
+ }
550
+ }
551
+
552
+ function has_hidden_descendants(
553
+ node: ProcessedComponentMeta,
554
+ hidden_on_startup: Set<number>
555
+ ): boolean {
556
+ for (const child of node.children) {
557
+ if (hidden_on_startup.has(child.id)) return true;
558
+ if (has_hidden_descendants(child, hidden_on_startup)) return true;
559
+ }
560
+ return false;
561
+ }
562
+
563
+ function load_components(node: ProcessedComponentMeta, api_url: string): void {
564
+ if (node.props.shared_props.visible && !node.component) {
565
+ node.component = get_component(node.type, node.component_class_id, api_url);
566
+ }
567
+ node.children.forEach((child) => load_components(child, api_url));
494
568
  }
495
569
 
496
570
  /**
@@ -719,8 +793,10 @@ function _gather_initial_tabs(
719
793
  if (!("id" in node.props.props)) {
720
794
  node.props.props.id = node.id;
721
795
  }
796
+ const i18n = node.props.props.i18n as ((str: string) => string) | undefined;
797
+ const raw_label = node.props.shared_props.label as string;
722
798
  initial_tabs[parent_tab_id].push({
723
- label: node.props.shared_props.label as string,
799
+ label: i18n ? i18n(raw_label) : raw_label,
724
800
  id: node.props.props.id as string,
725
801
  elem_id: node.props.shared_props.elem_id,
726
802
  visible: node.props.shared_props.visible as boolean,