@gradio/core 1.0.1 → 1.0.2

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.
@@ -1,6 +1,8 @@
1
1
  import { determine_interactivity, get_component, get_inputs_outputs } from "./init_utils";
2
2
  import { translate_if_needed } from "./i18n";
3
3
  import { tick } from "svelte";
4
+ import { dequal } from "dequal";
5
+ import {} from "@gradio/utils";
4
6
  import { allowed_shared_props } from "@gradio/utils";
5
7
  import { Client } from "@gradio/client";
6
8
  const type_map = {
@@ -21,6 +23,7 @@ export class AppTree {
21
23
  client;
22
24
  /** the root node of the processed layout tree */
23
25
  root = $state();
26
+ root_untracked;
24
27
  /** a set of all component IDs that are inputs to dependencies */
25
28
  #input_ids = new Set();
26
29
  /** a set of all component IDs that are outputs of dependencies */
@@ -29,13 +32,15 @@ export class AppTree {
29
32
  #pending_components = [];
30
33
  #get_callbacks = new Map();
31
34
  #set_callbacks = new Map();
35
+ #event_dispatcher;
32
36
  component_ids;
33
37
  initial_tabs = {};
34
38
  components_to_register = new Set();
35
39
  ready;
36
40
  ready_resolve;
37
41
  resolved = false;
38
- constructor(components, layout, dependencies, config, app, reactive_formatter) {
42
+ #hidden_on_startup = new Set();
43
+ constructor(components, layout, dependencies, config, app, reactive_formatter, event_dispatcher) {
39
44
  this.ready = new Promise((resolve) => {
40
45
  this.ready_resolve = resolve;
41
46
  });
@@ -66,6 +71,8 @@ export class AppTree {
66
71
  this.initial_tabs = {};
67
72
  gather_initial_tabs(this.root, this.initial_tabs);
68
73
  this.postprocess(this.root);
74
+ this.#event_dispatcher = event_dispatcher;
75
+ this.root_untracked = this.root;
69
76
  }
70
77
  reload(components, layout, dependencies, config) {
71
78
  this.#layout_payload = layout;
@@ -126,7 +133,8 @@ export class AppTree {
126
133
  (node) => handle_empty_forms(node, this.components_to_register),
127
134
  (node) => translate_props(node),
128
135
  (node) => apply_initial_tabs(node, this.initial_tabs),
129
- (node) => this.find_attached_events(node, this.#dependency_payload)
136
+ (node) => this.find_attached_events(node, this.#dependency_payload),
137
+ (node) => untrack_children_of_closed_accordions_or_inactive_tabs(node, this.components_to_register, this.#hidden_on_startup)
130
138
  ]);
131
139
  }
132
140
  find_attached_events(node, dependencies) {
@@ -211,7 +219,8 @@ export class AppTree {
211
219
  : null,
212
220
  key: component.key,
213
221
  rendered_in: component.rendered_in,
214
- documentation: component.documentation
222
+ documentation: component.documentation,
223
+ original_visibility: processed_props.shared_props.visible
215
224
  };
216
225
  return node;
217
226
  }
@@ -230,6 +239,15 @@ export class AppTree {
230
239
  }
231
240
  n.children = subtree.children;
232
241
  }
242
+ async update_visibility(node, new_state) {
243
+ node.children.forEach((child) => {
244
+ const _set_data = this.#set_callbacks.get(child.id);
245
+ if (_set_data) {
246
+ _set_data(new_state);
247
+ }
248
+ this.update_visibility(child, new_state);
249
+ });
250
+ }
233
251
  /*
234
252
  * Updates the state of a component by its ID
235
253
  * @param id the ID of the component to update
@@ -248,20 +266,44 @@ export class AppTree {
248
266
  this.root = this.traverse(this.root, [
249
267
  //@ts-ignore
250
268
  (n) => set_visibility_for_updated_node(n, id, new_state.visible),
269
+ //@ts-ignore
270
+ (n) => update_parent_visibility(n, id, new_state.visible),
251
271
  (n) => handle_visibility(n, this.#config.api_url)
252
272
  ]);
273
+ await tick();
253
274
  already_updated_visibility = true;
254
275
  }
255
276
  const _set_data = this.#set_callbacks.get(id);
256
- if (!_set_data)
257
- return;
258
- _set_data(new_state);
277
+ if (!_set_data) {
278
+ const old_value = node?.props.props.value;
279
+ // @ts-ignore
280
+ const new_props = create_props_shared_props(new_state);
281
+ node.props.shared_props = {
282
+ ...node?.props.shared_props,
283
+ ...new_props.shared_props
284
+ };
285
+ node.props.props = { ...node?.props.props, ...new_props.props };
286
+ if ("value" in new_state && !dequal(old_value, new_state.value)) {
287
+ this.#event_dispatcher(id, "change", null);
288
+ }
289
+ }
290
+ else if (_set_data) {
291
+ _set_data(new_state);
292
+ }
259
293
  if (!check_visibility || already_updated_visibility)
260
294
  return;
261
295
  // need to let the UI settle before traversing again
262
296
  // otherwise there could be
263
297
  await tick();
264
- this.root = this.traverse(this.root, (n) => handle_visibility(n, this.#config.api_url));
298
+ // Update the visibility in a way that does not
299
+ // re-render the root/tree. Doing that would nuke
300
+ // any values currently in the UI.
301
+ // @ts-ignore
302
+ await this.update_visibility(node, new_state);
303
+ const parent_node = find_parent(this.root, id);
304
+ if (parent_node)
305
+ // @ts-ignore
306
+ update_parent_visibility(parent_node, id, new_state.visible);
265
307
  }
266
308
  /**
267
309
  * Gets the current state of a component by its ID
@@ -270,15 +312,34 @@ export class AppTree {
270
312
  */
271
313
  async get_state(id) {
272
314
  const _get_data = this.#get_callbacks.get(id);
273
- const component = this.#component_payload.find((c) => c.id === id);
315
+ const component = find_node_by_id(this.root, id);
274
316
  if (!_get_data && !component)
275
317
  return null;
276
318
  if (_get_data)
277
319
  return await _get_data();
278
320
  if (component)
279
- return Promise.resolve({ value: component.props.value });
321
+ return Promise.resolve({ value: component.props.props.value });
280
322
  return null;
281
323
  }
324
+ async render_previously_invisible_children(id) {
325
+ this.root = this.traverse(this.root, [
326
+ (node) => {
327
+ if (node.id === id) {
328
+ make_visible_if_not_rendered(node, this.#hidden_on_startup);
329
+ }
330
+ return node;
331
+ },
332
+ (node) => handle_visibility(node, this.#config.api_url)
333
+ ]);
334
+ }
335
+ }
336
+ function make_visible_if_not_rendered(node, hidden_on_startup) {
337
+ node.props.shared_props.visible = hidden_on_startup.has(node.id)
338
+ ? true
339
+ : node.props.shared_props.visible;
340
+ node.children.forEach((child) => {
341
+ make_visible_if_not_rendered(child, hidden_on_startup);
342
+ });
282
343
  }
283
344
  /**
284
345
  * Process the server function names and return a dictionary of functions
@@ -302,15 +363,7 @@ export function process_server_fn(id, server_fns, app) {
302
363
  return acc;
303
364
  }, {});
304
365
  }
305
- /**
306
- * Gathers the props for a component
307
- * @param id the ID of the component
308
- * @param props the props of the component
309
- * @param dependencies the component's dependencies
310
- * @param additional any additional props to include
311
- * @returns the gathered props as an object with `shared_props` and `props` keys
312
- */
313
- function gather_props(id, props, dependencies, client, api_url, additional = {}) {
366
+ function create_props_shared_props(props) {
314
367
  const _shared_props = {};
315
368
  const _props = {};
316
369
  for (const key in props) {
@@ -322,14 +375,24 @@ function gather_props(id, props, dependencies, client, api_url, additional = {})
322
375
  else if (allowed_shared_props.includes(key)) {
323
376
  const _key = key;
324
377
  _shared_props[_key] = props[key];
325
- if (_key === "server_fns") {
326
- _shared_props.server = process_server_fn(id, props.server_fns, client);
327
- }
328
378
  }
329
379
  else {
330
380
  _props[key] = props[key];
331
381
  }
332
382
  }
383
+ return { shared_props: _shared_props, props: _props };
384
+ }
385
+ /**
386
+ * Gathers the props for a component
387
+ * @param id the ID of the component
388
+ * @param props the props of the component
389
+ * @param dependencies the component's dependencies
390
+ * @param additional any additional props to include
391
+ * @returns the gathered props as an object with `shared_props` and `props` keys
392
+ */
393
+ function gather_props(id, props, dependencies, client, api_url, additional = {}) {
394
+ const { shared_props: _shared_props, props: _props } = create_props_shared_props(props);
395
+ _shared_props.server = process_server_fn(id, props.server_fns, client);
333
396
  for (const key in additional) {
334
397
  if (allowed_shared_props.includes(key)) {
335
398
  const _key = key;
@@ -385,6 +448,38 @@ function untrack_children_of_invisible_parents(node, components_to_register) {
385
448
  }
386
449
  return node;
387
450
  }
451
+ function mark_component_invisible_if_visible(node, hidden_on_startup) {
452
+ if (node.props.shared_props.visible === true) {
453
+ hidden_on_startup.add(node.id);
454
+ node.props.shared_props.visible = false;
455
+ }
456
+ node.children.forEach((child) => {
457
+ mark_component_invisible_if_visible(child, hidden_on_startup);
458
+ });
459
+ return node;
460
+ }
461
+ function untrack_children_of_closed_accordions_or_inactive_tabs(node, components_to_register, hidden_on_startup) {
462
+ // Check if the node is an accordion or tabs
463
+ if (node.type === "accordion" && node.props.props.open === false) {
464
+ _untrack(node, components_to_register);
465
+ if (node.children) {
466
+ node.children.forEach((child) => {
467
+ mark_component_invisible_if_visible(child, hidden_on_startup);
468
+ });
469
+ }
470
+ }
471
+ if (node.type === "tabs") {
472
+ node.children.forEach((child) => {
473
+ if (child.type === "tabitem" &&
474
+ child.props.props.id !==
475
+ (node.props.props.selected || node.props.props.initial_tabs[0].id)) {
476
+ _untrack(child, components_to_register);
477
+ mark_component_invisible_if_visible(child, hidden_on_startup);
478
+ }
479
+ });
480
+ }
481
+ return node;
482
+ }
388
483
  function handle_empty_forms(node, components_to_register) {
389
484
  // Check if the node is visible
390
485
  if (node.type === "form") {
@@ -397,6 +492,27 @@ function handle_empty_forms(node, components_to_register) {
397
492
  }
398
493
  return node;
399
494
  }
495
+ function update_parent_visibility(node, child_made_visible, visibility_state) {
496
+ // This function was added to address a tricky situation:
497
+ // Form components are wrapped in a Form component automatically.
498
+ // If all the children of the Form are invisible, the Form itself is marked invisible.
499
+ // in AppTree.postprocess -> handle_empty_forms
500
+ // This is to avoid rendering empty forms in the UI. They look ugly.
501
+ // So what happens when a child inside the Form is made visible again?
502
+ // The Form needs to become visible again too.
503
+ // If the child is made invisible, the form should be too if all other children are invisible.
504
+ // However, we are not doing this now since what we want to do is fetch the latest visibility of all
505
+ // the children from the UI. However, get_data only returns the props, not the shared props.
506
+ if (node.type === "form" &&
507
+ node.children.length &&
508
+ node.children.some((child) => child.id === child_made_visible)) {
509
+ if (visibility_state === true)
510
+ node.props.shared_props.visible = true;
511
+ else if (!visibility_state && node.children.length === 1)
512
+ node.props.shared_props.visible = "hidden";
513
+ }
514
+ return node;
515
+ }
400
516
  function translate_props(node) {
401
517
  const supported_props = [
402
518
  "description",
@@ -424,6 +540,9 @@ function apply_initial_tabs(node, initial_tabs) {
424
540
  const tabs = initial_tabs[node.id].sort((a, b) => a.order - b.order);
425
541
  node.props.props.initial_tabs = tabs;
426
542
  }
543
+ else if (node.type === "tabitem") {
544
+ node.props.props.component_id = node.id;
545
+ }
427
546
  return node;
428
547
  }
429
548
  function _gather_initial_tabs(node, initial_tabs, parent_tab_id, order) {
@@ -440,7 +559,8 @@ function _gather_initial_tabs(node, initial_tabs, parent_tab_id, order) {
440
559
  elem_id: node.props.shared_props.elem_id,
441
560
  visible: node.props.shared_props.visible,
442
561
  interactive: node.props.shared_props.interactive,
443
- scale: node.props.shared_props.scale || null
562
+ scale: node.props.shared_props.scale || null,
563
+ component_id: node.id
444
564
  });
445
565
  node.props.props.order = order;
446
566
  }
@@ -473,3 +593,17 @@ function find_node_by_id(tree, id) {
473
593
  }
474
594
  return null;
475
595
  }
596
+ function find_parent(tree, id) {
597
+ if (tree.children) {
598
+ for (const child of tree.children) {
599
+ if (child.id === id) {
600
+ return tree;
601
+ }
602
+ const result = find_parent(child, id);
603
+ if (result) {
604
+ return result;
605
+ }
606
+ }
607
+ }
608
+ return null;
609
+ }
@@ -28,6 +28,7 @@ export interface ProcessedComponentMeta {
28
28
  component_class_id: string;
29
29
  key: string | number | null;
30
30
  rendered_in?: number;
31
+ original_visibility: boolean | "hidden";
31
32
  }
32
33
  /** Dictates whether a dependency is continous and/or a generator */
33
34
  export interface DependencyTypes {
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
1
  {
2
2
  "name": "@gradio/core",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "devDependencies": {
6
- "@gradio/annotatedimage": "^0.10.1",
6
+ "@gradio/accordion": "^0.5.26",
7
7
  "@gradio/atoms": "^0.19.0",
8
+ "@gradio/annotatedimage": "^0.10.1",
8
9
  "@gradio/box": "^0.2.26",
10
+ "@gradio/audio": "^0.21.0",
9
11
  "@gradio/browserstate": "^0.3.3",
10
- "@gradio/button": "^0.5.14",
11
- "@gradio/checkbox": "^0.5.0",
12
+ "@gradio/button": "^0.6.0",
12
13
  "@gradio/chatbot": "^0.28.0",
13
- "@gradio/client": "^2.0.0",
14
- "@gradio/audio": "^0.20.0",
15
- "@gradio/checkboxgroup": "^0.8.0",
16
- "@gradio/accordion": "^0.5.25",
17
- "@gradio/colorpicker": "^0.5.0",
14
+ "@gradio/checkbox": "^0.5.1",
18
15
  "@gradio/code": "^0.16.0",
19
- "@gradio/dataframe": "^0.21.0",
16
+ "@gradio/checkboxgroup": "^0.8.0",
17
+ "@gradio/colorpicker": "^0.5.1",
20
18
  "@gradio/column": "^0.3.0",
19
+ "@gradio/client": "^2.0.0",
21
20
  "@gradio/dataset": "^0.5.0",
21
+ "@gradio/dataframe": "^0.21.1",
22
+ "@gradio/dropdown": "^0.10.7",
22
23
  "@gradio/datetime": "^0.3.23",
23
- "@gradio/downloadbutton": "^0.4.13",
24
- "@gradio/dropdown": "^0.10.6",
24
+ "@gradio/downloadbutton": "^0.4.14",
25
25
  "@gradio/fallback": "^0.4.30",
26
26
  "@gradio/file": "^0.13.1",
27
- "@gradio/form": "^0.2.27",
28
27
  "@gradio/fileexplorer": "^0.5.42",
29
- "@gradio/gallery": "^0.15.35",
28
+ "@gradio/form": "^0.2.27",
29
+ "@gradio/gallery": "^0.15.36",
30
30
  "@gradio/group": "^0.3.0",
31
- "@gradio/highlightedtext": "^0.9.14",
32
- "@gradio/html": "^0.8.0",
31
+ "@gradio/highlightedtext": "^0.9.15",
32
+ "@gradio/html": "^0.8.1",
33
33
  "@gradio/image": "^0.24.0",
34
- "@gradio/imageeditor": "^0.18.2",
35
34
  "@gradio/icons": "^0.15.0",
36
- "@gradio/imageslider": "^0.3.1",
35
+ "@gradio/imageeditor": "^0.18.2",
37
36
  "@gradio/json": "^0.5.32",
38
- "@gradio/markdown": "^0.13.23",
37
+ "@gradio/imageslider": "^0.3.1",
39
38
  "@gradio/label": "^0.5.22",
39
+ "@gradio/markdown": "^0.13.23",
40
40
  "@gradio/model3d": "^0.15.1",
41
- "@gradio/multimodaltextbox": "^0.11.0",
42
- "@gradio/nativeplot": "^0.9.0",
41
+ "@gradio/multimodaltextbox": "^0.11.1",
43
42
  "@gradio/number": "^0.7.2",
44
- "@gradio/paramviewer": "^0.9.0",
45
43
  "@gradio/plot": "^0.9.25",
44
+ "@gradio/nativeplot": "^0.9.1",
45
+ "@gradio/paramviewer": "^0.9.0",
46
46
  "@gradio/radio": "^0.8.0",
47
47
  "@gradio/row": "^0.3.0",
48
48
  "@gradio/sidebar": "^0.1.24",
49
+ "@gradio/simpleimage": "^0.9.1",
49
50
  "@gradio/simpledropdown": "^0.3.30",
50
51
  "@gradio/simpletextbox": "^0.3.31",
51
- "@gradio/slider": "^0.7.0",
52
- "@gradio/simpleimage": "^0.9.1",
52
+ "@gradio/slider": "^0.7.1",
53
53
  "@gradio/state": "^0.2.0",
54
- "@gradio/tabitem": "^0.6.2",
55
- "@gradio/tabs": "^0.5.2",
56
- "@gradio/textbox": "^0.12.0",
54
+ "@gradio/tabitem": "^0.6.3",
57
55
  "@gradio/statustracker": "^0.12.0",
56
+ "@gradio/textbox": "^0.12.1",
57
+ "@gradio/tabs": "^0.5.3",
58
58
  "@gradio/theme": "^0.5.0",
59
59
  "@gradio/timer": "^0.4.6",
60
- "@gradio/upload": "^0.17.2",
60
+ "@gradio/uploadbutton": "^0.9.14",
61
61
  "@gradio/utils": "^0.10.4",
62
- "@gradio/uploadbutton": "^0.9.13",
63
- "@gradio/video": "^0.17.0",
64
- "@gradio/vibeeditor": "^0.3.1"
62
+ "@gradio/vibeeditor": "^0.3.1",
63
+ "@gradio/video": "^0.18.0",
64
+ "@gradio/upload": "^0.17.2"
65
65
  },
66
66
  "msw": {
67
67
  "workerDirectory": "public"
package/src/Blocks.svelte CHANGED
@@ -104,27 +104,6 @@
104
104
  }
105
105
  });
106
106
 
107
- let app_tree = new AppTree(
108
- components,
109
- layout,
110
- dependencies,
111
- {
112
- root,
113
- theme: theme_mode,
114
- version,
115
- api_prefix,
116
- max_file_size,
117
- autoscroll
118
- },
119
- app,
120
- $reactive_formatter
121
- );
122
-
123
- setContext(GRADIO_ROOT, {
124
- register: app_tree.register_component.bind(app_tree),
125
- dispatcher: gradio_event_dispatcher
126
- });
127
-
128
107
  let messages: (ToastMessage & { fn_index: number })[] = $state([]);
129
108
 
130
109
  function gradio_event_dispatcher(
@@ -142,6 +121,12 @@
142
121
  new_message("Warning", data as string, -1, event, 10, true);
143
122
  } else if (event === "info") {
144
123
  new_message("Info", data as string, -1, event, 10, true);
124
+ } else if (event === "gradio_expand" || event === "gradio_tab_select") {
125
+ const id_ =
126
+ event === "gradio_expand"
127
+ ? id
128
+ : (data as { component_id: number }).component_id;
129
+ app_tree.render_previously_invisible_children(id_);
145
130
  } else if (event == "clear_status") {
146
131
  app_tree.update_state(
147
132
  id,
@@ -173,6 +158,28 @@
173
158
  }
174
159
  }
175
160
 
161
+ let app_tree = new AppTree(
162
+ components,
163
+ layout,
164
+ dependencies,
165
+ {
166
+ root,
167
+ theme: theme_mode,
168
+ version,
169
+ api_prefix,
170
+ max_file_size,
171
+ autoscroll
172
+ },
173
+ app,
174
+ $reactive_formatter,
175
+ gradio_event_dispatcher
176
+ );
177
+
178
+ setContext(GRADIO_ROOT, {
179
+ register: app_tree.register_component.bind(app_tree),
180
+ dispatcher: gradio_event_dispatcher
181
+ });
182
+
176
183
  let api_calls: Payload[] = $state([]);
177
184
  let last_api_call: Payload | null = $state(null);
178
185
  // We need a callback to add to api_calls from the DependencyManager
@@ -322,8 +329,10 @@
322
329
 
323
330
  const MESSAGE_QUOTE_RE = /^'([^]+)'$/;
324
331
 
325
- const DUPLICATE_MESSAGE = $_("blocks.long_requests_queue");
326
- const MOBILE_QUEUE_WARNING = $_("blocks.connection_can_break");
332
+ const DUPLICATE_MESSAGE = $reactive_formatter("blocks.long_requests_queue");
333
+ const MOBILE_QUEUE_WARNING = $reactive_formatter(
334
+ "blocks.connection_can_break"
335
+ );
327
336
  const LOST_CONNECTION_MESSAGE =
328
337
  "Connection to the server was lost. Attempting reconnection...";
329
338
  const CHANGED_CONNECTION_MESSAGE =
@@ -331,7 +340,9 @@
331
340
  const RECONNECTION_MESSAGE = "Connection re-established.";
332
341
  const SESSION_NOT_FOUND_MESSAGE =
333
342
  "Session not found - this is likely because the machine you were connected to has changed. <a href=''>Refresh the page</a> to continue.";
334
- const WAITING_FOR_INPUTS_MESSAGE = $_("blocks.waiting_for_inputs");
343
+ const WAITING_FOR_INPUTS_MESSAGE = $reactive_formatter(
344
+ "blocks.waiting_for_inputs"
345
+ );
335
346
  const SHOW_DUPLICATE_MESSAGE_ON_ETA = 15;
336
347
  const SHOW_MOBILE_QUEUE_WARNING_ON_ETA = 10;
337
348
  let is_mobile_device = false;
@@ -440,11 +451,11 @@
440
451
  class="show-api"
441
452
  >
442
453
  {#if app.config?.mcp_server}
443
- {$_("errors.use_via_api_or_mcp")}
454
+ {$reactive_formatter("errors.use_via_api_or_mcp")}
444
455
  {:else}
445
- {$_("errors.use_via_api")}
456
+ {$reactive_formatter("errors.use_via_api")}
446
457
  {/if}
447
- <img src={api_logo} alt={$_("common.logo")} />
458
+ <img src={api_logo} alt={$reactive_formatter("common.logo")} />
448
459
  </button>
449
460
  {/if}
450
461
  {#if footer_links.includes("gradio")}
@@ -455,8 +466,8 @@
455
466
  target="_blank"
456
467
  rel="noreferrer"
457
468
  >
458
- {$_("common.built_with_gradio")}
459
- <img src={logo} alt={$_("common.logo")} />
469
+ {$reactive_formatter("common.built_with_gradio")}
470
+ <img src={logo} alt={$reactive_formatter("common.logo")} />
460
471
  </a>
461
472
  {/if}
462
473
  <button
@@ -466,8 +477,11 @@
466
477
  }}
467
478
  class="record"
468
479
  >
469
- {$_("common.stop_recording")}
470
- <img src={record_stop} alt={$_("common.stop_recording")} />
480
+ {$reactive_formatter("common.stop_recording")}
481
+ <img
482
+ src={record_stop}
483
+ alt={$reactive_formatter("common.stop_recording")}
484
+ />
471
485
  </button>
472
486
  <div class="divider">·</div>
473
487
  {#if footer_links.includes("settings")}
@@ -481,8 +495,11 @@
481
495
  }}
482
496
  class="settings"
483
497
  >
484
- {$_("common.settings")}
485
- <img src={settings_logo} alt={$_("common.settings")} />
498
+ {$reactive_formatter("common.settings")}
499
+ <img
500
+ src={settings_logo}
501
+ alt={$reactive_formatter("common.settings")}
502
+ />
486
503
  </button>
487
504
  {/if}
488
505
  </footer>
@@ -552,15 +569,16 @@
552
569
  this={Settings}
553
570
  bind:allow_zoom
554
571
  bind:allow_video_trim
555
- on:close={() => {
572
+ onclose={() => {
556
573
  set_settings_visible(false);
557
574
  }}
558
- on:start_recording={() => {
575
+ start_recording={() => {
559
576
  screen_recording();
560
577
  }}
561
578
  pwa_enabled={app.config.pwa}
562
579
  {root}
563
580
  {space_id}
581
+ i18n={$reactive_formatter}
564
582
  />
565
583
  </div>
566
584
  </div>