@gradio/core 0.23.0 → 0.23.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @gradio/core
2
2
 
3
+ ## 0.23.2
4
+
5
+ ### Fixes
6
+
7
+ - [#11632](https://github.com/gradio-app/gradio/pull/11632) [`8f19bff`](https://github.com/gradio-app/gradio/commit/8f19bffd04a73d4fc1080f5854caa0361516f3af) - ensure i18n is initialised before displaying localised loading text. Thanks @pngwn!
8
+
9
+ ### Dependency updates
10
+
11
+ - @gradio/textbox@0.10.18
12
+
13
+ ## 0.23.1
14
+
15
+ ### Features
16
+
17
+ - [#11619](https://github.com/gradio-app/gradio/pull/11619) [`33c5d2b`](https://github.com/gradio-app/gradio/commit/33c5d2bb8214c0d7a90daca0f1eaf96411a52e79) - Add a query param for the selected language & make MCP the default option when `mcp_server` is enabled. Thanks @abidlabs!
18
+ - [#11615](https://github.com/gradio-app/gradio/pull/11615) [`e2b66d7`](https://github.com/gradio-app/gradio/commit/e2b66d718f3a8f57b6ee224502849ee737b1b120) - fix change events for hidden components. Thanks @pngwn!
19
+
20
+ ### Fixes
21
+
22
+ - [#11599](https://github.com/gradio-app/gradio/pull/11599) [`c39d373`](https://github.com/gradio-app/gradio/commit/c39d3739bf2494ad13556174757cdd56060f033e) - Ensure component visibility is correctly propagated to all components. Thanks @copilot-swe-agent!
23
+
3
24
  ## 0.23.0
4
25
 
5
26
  ### Features
@@ -47,8 +47,11 @@ let {
47
47
  loading_status,
48
48
  scheduled_updates,
49
49
  create_layout,
50
- rerender_layout
51
- } = create_components(initial_layout);
50
+ rerender_layout,
51
+ value_change
52
+ } = create_components({
53
+ initial_layout
54
+ });
52
55
  $:
53
56
  components, layout, dependencies, root, app, fill_height, target, run();
54
57
  $: {
@@ -674,6 +677,14 @@ async function handle_mount() {
674
677
  });
675
678
  render_complete = true;
676
679
  }
680
+ value_change((id, value) => {
681
+ const deps = $targets[id]?.["change"];
682
+ deps?.forEach((dep_id) => {
683
+ requestAnimationFrame(() => {
684
+ wait_then_trigger_api_call(dep_id, id, value);
685
+ });
686
+ });
687
+ });
677
688
  const handle_load_triggers = () => {
678
689
  dependencies.forEach((dep) => {
679
690
  if (dep.targets.some((dep2) => dep2[1] === "load")) {
@@ -65,6 +65,7 @@ $:
65
65
  {elem_id}
66
66
  {elem_classes}
67
67
  {target}
68
+ {visible}
68
69
  {...$$restProps}
69
70
  {theme_mode}
70
71
  {root}
@@ -35,6 +35,18 @@ if (!root.endsWith("/")) {
35
35
  }
36
36
  export let api_calls = [];
37
37
  let current_language = "python";
38
+ function set_query_param(key, value) {
39
+ const url = new URL(window.location.href);
40
+ url.searchParams.set(key, value);
41
+ history.replaceState(null, "", url.toString());
42
+ }
43
+ function get_query_param(key) {
44
+ const url = new URL(window.location.href);
45
+ return url.searchParams.get(key);
46
+ }
47
+ function is_valid_language(lang) {
48
+ return ["python", "javascript", "bash", "mcp"].includes(lang ?? "");
49
+ }
38
50
  const langs = [
39
51
  ["python", "Python", python],
40
52
  ["javascript", "JavaScript", javascript],
@@ -152,10 +164,21 @@ onMount(() => {
152
164
  if ("parentIFrame" in window) {
153
165
  window.parentIFrame?.scrollTo(0, 0);
154
166
  }
167
+ const lang_param = get_query_param("lang");
168
+ if (is_valid_language(lang_param)) {
169
+ current_language = lang_param;
170
+ }
155
171
  fetch(mcp_server_url).then((response) => {
156
172
  mcp_server_active = response.ok;
157
173
  if (mcp_server_active) {
158
174
  fetchMcpTools();
175
+ if (!is_valid_language(lang_param)) {
176
+ current_language = "mcp";
177
+ }
178
+ } else {
179
+ if (!is_valid_language(lang_param)) {
180
+ current_language = "python";
181
+ }
159
182
  }
160
183
  }).catch(() => {
161
184
  mcp_server_active = false;
@@ -189,7 +212,10 @@ onMount(() => {
189
212
  <li
190
213
  class="snippet
191
214
  {current_language === language ? 'current-lang' : 'inactive-lang'}"
192
- on:click={() => (current_language = language)}
215
+ on:click={() => {
216
+ current_language = language;
217
+ set_query_param("lang", language);
218
+ }}
193
219
  >
194
220
  <img src={img} alt="" />
195
221
  {display_name}
package/dist/src/i18n.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { addMessages, init, getLocaleFromNavigator, locale, register, waitLocale } from "svelte-i18n";
2
2
  import { formatter } from "./gradio_helper";
3
+ import { loading } from "./lang/loading";
3
4
  const lang_map = {
4
5
  ar: "العربية",
5
6
  ca: "Català",
@@ -166,4 +167,9 @@ export function load_translations(translations) {
166
167
  catch (e) {
167
168
  console.error("Error loading translations:", e);
168
169
  }
170
+ for (const lang in loading) {
171
+ addMessages(lang, {
172
+ common: { loading: loading[lang] }
173
+ });
174
+ }
169
175
  }
@@ -7,7 +7,13 @@ export interface UpdateTransaction {
7
7
  value: any;
8
8
  prop: string;
9
9
  }
10
- export declare function create_components(initial_layout: ComponentMeta | undefined): {
10
+ /**
11
+ * Create a store with the layout and a map of targets
12
+ * @returns A store with the layout and a map of targets
13
+ */
14
+ export declare function create_components({ initial_layout }?: {
15
+ initial_layout: ComponentMeta | undefined;
16
+ }): {
11
17
  layout: Writable<ComponentMeta>;
12
18
  targets: Writable<TargetMap>;
13
19
  update_value: (updates: UpdateTransaction[]) => void;
@@ -34,6 +40,7 @@ export declare function create_components(initial_layout: ComponentMeta | undefi
34
40
  root: string;
35
41
  dependencies: Dependency[];
36
42
  }) => void;
43
+ value_change: (cb: (id: number, value: any) => void) => void;
37
44
  };
38
45
  /** An async version of 'new Function' */
39
46
  export declare const AsyncFunction: new (...args: string[]) => (...args: any[]) => Promise<any>;
package/dist/src/init.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { writable, get } from "svelte/store";
2
+ import { dequal } from "dequal";
2
3
  import { load_component } from "virtual:component-loader";
3
4
  import { create_loading_status_store } from "./stores";
4
5
  import { _ } from "svelte-i18n";
@@ -11,8 +12,9 @@ const raf = is_browser
11
12
  * Create a store with the layout and a map of targets
12
13
  * @returns A store with the layout and a map of targets
13
14
  */
14
- let has_run = new Set();
15
- export function create_components(initial_layout) {
15
+ export function create_components({ initial_layout = undefined } = {
16
+ initial_layout: undefined
17
+ }) {
16
18
  let _component_map;
17
19
  let target_map = writable({});
18
20
  let _target_map = {};
@@ -26,6 +28,10 @@ export function create_components(initial_layout) {
26
28
  let app;
27
29
  let keys_per_render_id = {};
28
30
  let _rootNode;
31
+ let value_change_cb = null;
32
+ function value_change(cb) {
33
+ value_change_cb = cb;
34
+ }
29
35
  // Store current layout and root for dynamic visibility recalculation
30
36
  let current_layout;
31
37
  let current_root;
@@ -182,17 +188,11 @@ export function create_components(initial_layout) {
182
188
  if (!instance.component) {
183
189
  const constructor_key = instance.component_class_id || instance.type;
184
190
  let component_constructor = constructor_map.get(constructor_key);
185
- // Only load component if it was preloaded (i.e., it's visible)
186
191
  if (component_constructor) {
187
192
  instance.component = (await component_constructor)?.default;
188
193
  }
189
- // If component wasn't preloaded, leave it unloaded for now
190
- // It will be loaded later when/if it becomes visible
191
194
  }
192
195
  instance.parent = parent;
193
- // if (instance.type === "timer") {
194
- // console.log("timer", instance, constructor_map);
195
- // }
196
196
  if (instance.type === "dataset") {
197
197
  instance.props.component_map = get_component(instance.type, instance.component_class_id, root, components, instance.props.components).example_components;
198
198
  }
@@ -304,6 +304,7 @@ export function create_components(initial_layout) {
304
304
  if (!instance)
305
305
  continue;
306
306
  let new_value;
307
+ const old_value = instance.props[update.prop];
307
308
  if (update.value instanceof Map)
308
309
  new_value = new Map(update.value);
309
310
  else if (update.value instanceof Set)
@@ -317,6 +318,11 @@ export function create_components(initial_layout) {
317
318
  else
318
319
  new_value = update.value;
319
320
  instance.props[update.prop] = new_value;
321
+ if (update.prop === "value" &&
322
+ !is_visible(instance) &&
323
+ !dequal(old_value, new_value)) {
324
+ value_change_cb?.(update.id, new_value);
325
+ }
320
326
  }
321
327
  }
322
328
  return layout;
@@ -411,7 +417,8 @@ export function create_components(initial_layout) {
411
417
  loading_status,
412
418
  scheduled_updates: update_scheduled_store,
413
419
  create_layout: create_layout,
414
- rerender_layout
420
+ rerender_layout,
421
+ value_change
415
422
  };
416
423
  }
417
424
  /** An async version of 'new Function' */
@@ -725,3 +732,13 @@ export function preload_all_components(components, root) {
725
732
  });
726
733
  return constructor_map;
727
734
  }
735
+ function is_visible(component) {
736
+ if (typeof component.props.visible === "boolean" &&
737
+ component.props.visible === false) {
738
+ return false;
739
+ }
740
+ else if (component.parent) {
741
+ return is_visible(component.parent);
742
+ }
743
+ return true;
744
+ }
@@ -9,6 +9,8 @@ const __dirname = path.dirname(__filename);
9
9
  const langs = fs.readdirSync(path.join(__dirname, "..", "lang"));
10
10
 
11
11
  let lang_names = {};
12
+ let lang_loading = {};
13
+
12
14
  for (const lang of langs) {
13
15
  if (lang.endsWith(".json")) {
14
16
  const lang_text = fs.readFileSync(
@@ -17,7 +19,10 @@ for (const lang of langs) {
17
19
  );
18
20
  const lang_data = JSON.parse(lang_text.trim());
19
21
  lang_names[lang.split(".")[0]] = lang_data._name;
22
+ lang_loading[lang.split(".")[0]] =
23
+ lang_data?.common?.loading || "Loading...";
20
24
  }
21
25
  }
22
26
 
23
27
  console.log(lang_names);
28
+ console.log(lang_loading);
@@ -0,0 +1,33 @@
1
+ export declare const loading: {
2
+ ar: string;
3
+ ca: string;
4
+ ckb: string;
5
+ de: string;
6
+ en: string;
7
+ es: string;
8
+ eu: string;
9
+ fa: string;
10
+ fi: string;
11
+ fr: string;
12
+ he: string;
13
+ hi: string;
14
+ ja: string;
15
+ ko: string;
16
+ lt: string;
17
+ nb: string;
18
+ nl: string;
19
+ pl: string;
20
+ "pt-BR": string;
21
+ pt: string;
22
+ ro: string;
23
+ ru: string;
24
+ sv: string;
25
+ ta: string;
26
+ th: string;
27
+ tr: string;
28
+ uk: string;
29
+ ur: string;
30
+ uz: string;
31
+ "zh-CN": string;
32
+ "zh-TW": string;
33
+ };
@@ -0,0 +1,33 @@
1
+ export const loading = {
2
+ ar: "جاري التحميل",
3
+ ca: "S'està carregant",
4
+ ckb: "بارکردن",
5
+ de: "Laden",
6
+ en: "Loading",
7
+ es: "Cargando",
8
+ eu: "Kargatzen",
9
+ fa: "در حال بارگذاری",
10
+ fi: "Ladataan",
11
+ fr: "Chargement",
12
+ he: "טוען",
13
+ hi: "लोड हो रहा है",
14
+ ja: "読み込み中",
15
+ ko: "로딩 중",
16
+ lt: "Kraunama",
17
+ nb: "Laster",
18
+ nl: "Laden",
19
+ pl: "Ładowanie",
20
+ "pt-BR": "Carregando",
21
+ pt: "A carregar",
22
+ ro: "Se încarcă",
23
+ ru: "Загрузка",
24
+ sv: "Laddar",
25
+ ta: "ஏற்றுகிறது",
26
+ th: "กำลังโหลด",
27
+ tr: "Yükleniyor",
28
+ uk: "Завантаження",
29
+ ur: "لوڈ ہو رہا ہے",
30
+ uz: "Yuklanmoqda",
31
+ "zh-CN": "加载中",
32
+ "zh-TW": "載入中"
33
+ };
package/package.json CHANGED
@@ -1,61 +1,61 @@
1
1
  {
2
2
  "name": "@gradio/core",
3
- "version": "0.23.0",
3
+ "version": "0.23.2",
4
4
  "type": "module",
5
5
  "devDependencies": {
6
6
  "@gradio/atoms": "^0.16.3",
7
- "@gradio/audio": "^0.17.21",
8
7
  "@gradio/accordion": "^0.5.20",
9
8
  "@gradio/annotatedimage": "^0.9.25",
9
+ "@gradio/audio": "^0.17.21",
10
10
  "@gradio/box": "^0.2.21",
11
11
  "@gradio/button": "^0.5.7",
12
+ "@gradio/browserstate": "^0.3.2",
12
13
  "@gradio/chatbot": "^0.26.18",
13
14
  "@gradio/checkbox": "^0.4.26",
14
15
  "@gradio/checkboxgroup": "^0.6.25",
15
16
  "@gradio/client": "^1.15.6",
16
17
  "@gradio/code": "^0.14.11",
17
- "@gradio/colorpicker": "^0.4.25",
18
- "@gradio/dataset": "^0.4.27",
19
- "@gradio/datetime": "^0.3.18",
20
- "@gradio/column": "^0.2.1",
18
+ "@gradio/dataset": "^0.4.28",
21
19
  "@gradio/dataframe": "^0.18.3",
20
+ "@gradio/datetime": "^0.3.18",
21
+ "@gradio/colorpicker": "^0.4.25",
22
22
  "@gradio/downloadbutton": "^0.4.7",
23
23
  "@gradio/dropdown": "^0.9.25",
24
- "@gradio/fallback": "^0.4.25",
24
+ "@gradio/column": "^0.2.1",
25
25
  "@gradio/file": "^0.12.24",
26
+ "@gradio/fallback": "^0.4.25",
26
27
  "@gradio/fileexplorer": "^0.5.35",
27
28
  "@gradio/form": "^0.2.21",
28
- "@gradio/group": "^0.2.0",
29
29
  "@gradio/gallery": "^0.15.27",
30
+ "@gradio/group": "^0.2.0",
30
31
  "@gradio/highlightedtext": "^0.9.8",
31
32
  "@gradio/html": "^0.6.17",
32
33
  "@gradio/icons": "^0.12.0",
33
34
  "@gradio/image": "^0.22.13",
34
- "@gradio/imageeditor": "^0.16.1",
35
35
  "@gradio/imageslider": "^0.2.9",
36
+ "@gradio/imageeditor": "^0.16.1",
36
37
  "@gradio/json": "^0.5.27",
37
- "@gradio/browserstate": "^0.3.2",
38
- "@gradio/model3d": "^0.14.20",
39
- "@gradio/markdown": "^0.13.18",
40
38
  "@gradio/label": "^0.5.17",
39
+ "@gradio/markdown": "^0.13.18",
40
+ "@gradio/model3d": "^0.14.21",
41
+ "@gradio/multimodaltextbox": "^0.10.13",
41
42
  "@gradio/nativeplot": "^0.7.2",
42
43
  "@gradio/number": "^0.6.2",
43
44
  "@gradio/paramviewer": "^0.7.13",
44
- "@gradio/multimodaltextbox": "^0.10.13",
45
- "@gradio/plot": "^0.9.20",
46
45
  "@gradio/radio": "^0.7.8",
47
- "@gradio/row": "^0.2.1",
46
+ "@gradio/plot": "^0.9.20",
47
+ "@gradio/simpledropdown": "^0.3.25",
48
48
  "@gradio/sidebar": "^0.1.18",
49
49
  "@gradio/simpletextbox": "^0.3.26",
50
+ "@gradio/row": "^0.2.1",
50
51
  "@gradio/simpleimage": "^0.8.35",
51
52
  "@gradio/sketchbox": "^0.6.13",
53
+ "@gradio/statustracker": "^0.10.15",
52
54
  "@gradio/slider": "^0.6.14",
53
- "@gradio/simpledropdown": "^0.3.25",
54
55
  "@gradio/state": "^0.1.2",
55
- "@gradio/statustracker": "^0.10.15",
56
- "@gradio/tabs": "^0.4.5",
57
- "@gradio/textbox": "^0.10.17",
58
56
  "@gradio/tabitem": "^0.5.0",
57
+ "@gradio/tabs": "^0.4.5",
58
+ "@gradio/textbox": "^0.10.18",
59
59
  "@gradio/theme": "^0.4.0",
60
60
  "@gradio/timer": "^0.4.5",
61
61
  "@gradio/upload": "^0.16.11",
@@ -94,5 +94,8 @@
94
94
  "type": "git",
95
95
  "url": "git+https://github.com/gradio-app/gradio.git",
96
96
  "directory": "js/core"
97
+ },
98
+ "dependencies": {
99
+ "dequal": "^2.0.2"
97
100
  }
98
101
  }
package/src/Blocks.svelte CHANGED
@@ -69,8 +69,11 @@
69
69
  loading_status,
70
70
  scheduled_updates,
71
71
  create_layout,
72
- rerender_layout
73
- } = create_components(initial_layout);
72
+ rerender_layout,
73
+ value_change
74
+ } = create_components({
75
+ initial_layout
76
+ });
74
77
 
75
78
  $: components, layout, dependencies, root, app, fill_height, target, run();
76
79
 
@@ -845,6 +848,16 @@
845
848
  render_complete = true;
846
849
  }
847
850
 
851
+ value_change((id, value) => {
852
+ const deps = $targets[id]?.["change"];
853
+
854
+ deps?.forEach((dep_id) => {
855
+ requestAnimationFrame(() => {
856
+ wait_then_trigger_api_call(dep_id, id, value);
857
+ });
858
+ });
859
+ });
860
+
848
861
  const handle_load_triggers = (): void => {
849
862
  dependencies.forEach((dep) => {
850
863
  if (dep.targets.some((dep) => dep[1] === "load")) {
@@ -81,6 +81,7 @@
81
81
  {elem_id}
82
82
  {elem_classes}
83
83
  {target}
84
+ {visible}
84
85
  {...$$restProps}
85
86
  {theme_mode}
86
87
  {root}
@@ -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],
@@ -211,12 +226,24 @@
211
226
  window.parentIFrame?.scrollTo(0, 0);
212
227
  }
213
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
+
214
234
  // Check MCP server status and fetch tools if active
215
235
  fetch(mcp_server_url)
216
236
  .then((response) => {
217
237
  mcp_server_active = response.ok;
218
238
  if (mcp_server_active) {
219
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
+ }
220
247
  }
221
248
  })
222
249
  .catch(() => {
@@ -252,7 +279,10 @@
252
279
  <li
253
280
  class="snippet
254
281
  {current_language === language ? 'current-lang' : 'inactive-lang'}"
255
- on:click={() => (current_language = language)}
282
+ on:click={() => {
283
+ current_language = language;
284
+ set_query_param("lang", language);
285
+ }}
256
286
  >
257
287
  <img src={img} alt="" />
258
288
  {display_name}
package/src/i18n.test.ts CHANGED
@@ -17,6 +17,9 @@ import {
17
17
  changeLocale,
18
18
  is_translation_metadata
19
19
  } from "./i18n";
20
+ import { loading } from "./lang/loading";
21
+
22
+ const loading_count = Object.keys(loading).length;
20
23
 
21
24
  vi.mock("svelte-i18n", () => ({
22
25
  locale: { set: vi.fn() },
@@ -117,7 +120,7 @@ describe("i18n", () => {
117
120
 
118
121
  load_translations({ processed_langs: {}, custom_translations });
119
122
 
120
- expect(mockAddMessages).toHaveBeenCalledTimes(2);
123
+ expect(mockAddMessages).toHaveBeenCalledTimes(loading_count + 2);
121
124
  expect(mockAddMessages).toHaveBeenCalledWith(
122
125
  "en",
123
126
  custom_translations.en
package/src/i18n.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  waitLocale
8
8
  } from "svelte-i18n";
9
9
  import { formatter } from "./gradio_helper";
10
+ import { loading } from "./lang/loading";
10
11
 
11
12
  const lang_map = {
12
13
  ar: "العربية",
@@ -237,4 +238,10 @@ export function load_translations(translations: {
237
238
  } catch (e) {
238
239
  console.error("Error loading translations:", e);
239
240
  }
241
+
242
+ for (const lang in loading) {
243
+ addMessages(lang, {
244
+ common: { loading: loading[lang as keyof typeof loading] }
245
+ });
246
+ }
240
247
  }
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
- let has_run = new Set<number>();
32
- export function create_components(initial_layout: ComponentMeta | undefined): {
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;
@@ -132,7 +147,6 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
132
147
  pending_updates = [];
133
148
  constructor_map = new Map();
134
149
  _component_map = new Map();
135
-
136
150
  instance_map = {};
137
151
 
138
152
  // Store current layout and root for dynamic visibility recalculation
@@ -327,19 +341,12 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
327
341
  const constructor_key = instance.component_class_id || instance.type;
328
342
  let component_constructor = constructor_map.get(constructor_key);
329
343
 
330
- // Only load component if it was preloaded (i.e., it's visible)
331
344
  if (component_constructor) {
332
345
  instance.component = (await component_constructor)?.default;
333
346
  }
334
- // If component wasn't preloaded, leave it unloaded for now
335
- // It will be loaded later when/if it becomes visible
336
347
  }
337
348
  instance.parent = parent;
338
349
 
339
- // if (instance.type === "timer") {
340
- // console.log("timer", instance, constructor_map);
341
- // }
342
-
343
350
  if (instance.type === "dataset") {
344
351
  instance.props.component_map = get_component(
345
352
  instance.type,
@@ -506,6 +513,7 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
506
513
  const instance = instance_map[update.id];
507
514
  if (!instance) continue;
508
515
  let new_value;
516
+ const old_value = instance.props[update.prop];
509
517
  if (update.value instanceof Map) new_value = new Map(update.value);
510
518
  else if (update.value instanceof Set)
511
519
  new_value = new Set(update.value);
@@ -515,6 +523,14 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
515
523
  new_value = { ...update.value };
516
524
  else new_value = update.value;
517
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
+ }
518
534
  }
519
535
  }
520
536
  return layout;
@@ -631,7 +647,8 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
631
647
  loading_status,
632
648
  scheduled_updates: update_scheduled_store,
633
649
  create_layout: create_layout,
634
- rerender_layout
650
+ rerender_layout,
651
+ value_change
635
652
  };
636
653
  }
637
654
 
@@ -1094,3 +1111,15 @@ export function preload_all_components(
1094
1111
 
1095
1112
  return constructor_map;
1096
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
+ }
@@ -9,6 +9,8 @@ const __dirname = path.dirname(__filename);
9
9
  const langs = fs.readdirSync(path.join(__dirname, "..", "lang"));
10
10
 
11
11
  let lang_names = {};
12
+ let lang_loading = {};
13
+
12
14
  for (const lang of langs) {
13
15
  if (lang.endsWith(".json")) {
14
16
  const lang_text = fs.readFileSync(
@@ -17,7 +19,10 @@ for (const lang of langs) {
17
19
  );
18
20
  const lang_data = JSON.parse(lang_text.trim());
19
21
  lang_names[lang.split(".")[0]] = lang_data._name;
22
+ lang_loading[lang.split(".")[0]] =
23
+ lang_data?.common?.loading || "Loading...";
20
24
  }
21
25
  }
22
26
 
23
27
  console.log(lang_names);
28
+ console.log(lang_loading);
@@ -0,0 +1,33 @@
1
+ export const loading = {
2
+ ar: "جاري التحميل",
3
+ ca: "S'està carregant",
4
+ ckb: "بارکردن",
5
+ de: "Laden",
6
+ en: "Loading",
7
+ es: "Cargando",
8
+ eu: "Kargatzen",
9
+ fa: "در حال بارگذاری",
10
+ fi: "Ladataan",
11
+ fr: "Chargement",
12
+ he: "טוען",
13
+ hi: "लोड हो रहा है",
14
+ ja: "読み込み中",
15
+ ko: "로딩 중",
16
+ lt: "Kraunama",
17
+ nb: "Laster",
18
+ nl: "Laden",
19
+ pl: "Ładowanie",
20
+ "pt-BR": "Carregando",
21
+ pt: "A carregar",
22
+ ro: "Se încarcă",
23
+ ru: "Загрузка",
24
+ sv: "Laddar",
25
+ ta: "ஏற்றுகிறது",
26
+ th: "กำลังโหลด",
27
+ tr: "Yükleniyor",
28
+ uk: "Завантаження",
29
+ ur: "لوڈ ہو رہا ہے",
30
+ uz: "Yuklanmoqda",
31
+ "zh-CN": "加载中",
32
+ "zh-TW": "載入中"
33
+ };