@gradio/core 0.1.0-beta.1 → 0.1.0-beta.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/package.json CHANGED
@@ -1,64 +1,64 @@
1
1
  {
2
2
  "name": "@gradio/core",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.2",
4
4
  "type": "module",
5
5
  "devDependencies": {
6
- "@gradio/accordion": "^0.3.23-beta.1",
7
- "@gradio/annotatedimage": "^0.7.2-beta.1",
8
- "@gradio/audio": "^0.14.0-beta.1",
9
- "@gradio/atoms": "^0.8.1-beta.1",
10
- "@gradio/button": "^0.3.0-beta.1",
11
- "@gradio/box": "^0.1.25-beta.1",
12
- "@gradio/chatbot": "^0.14.0-beta.1",
13
- "@gradio/checkbox": "^0.4.0-beta.1",
14
- "@gradio/checkboxgroup": "^0.6.0-beta.1",
15
- "@gradio/client": "^1.6.0-beta.1",
16
- "@gradio/colorpicker": "^0.3.13-beta.1",
17
- "@gradio/code": "^0.9.1-beta.1",
6
+ "@gradio/accordion": "^0.3.23-beta.2",
7
+ "@gradio/atoms": "^0.9.0-beta.2",
8
+ "@gradio/annotatedimage": "^0.8.0-beta.2",
9
+ "@gradio/audio": "^0.14.0-beta.2",
10
+ "@gradio/box": "^0.2.0-beta.0",
11
+ "@gradio/button": "^0.3.0-beta.2",
12
+ "@gradio/checkbox": "^0.4.0-beta.2",
13
+ "@gradio/chatbot": "^0.14.0-beta.2",
14
+ "@gradio/checkboxgroup": "^0.6.0-beta.2",
15
+ "@gradio/client": "^1.6.0-beta.2",
16
+ "@gradio/code": "^0.10.0-beta.2",
17
+ "@gradio/dataframe": "^0.11.0-beta.2",
18
+ "@gradio/colorpicker": "^0.3.13-beta.2",
18
19
  "@gradio/column": "^0.2.0-beta.0",
19
- "@gradio/dataframe": "^0.10.1-beta.1",
20
- "@gradio/dataset": "^0.2.5-beta.1",
21
- "@gradio/downloadbutton": "^0.1.28-beta.1",
22
- "@gradio/datetime": "^0.1.4-beta.1",
23
- "@gradio/dropdown": "^0.7.13-beta.1",
24
- "@gradio/fallback": "^0.3.13-beta.1",
25
- "@gradio/file": "^0.9.4-beta.1",
26
- "@gradio/fileexplorer": "^0.4.19-beta.1",
27
- "@gradio/form": "^0.1.25-beta.1",
28
- "@gradio/gallery": "^0.13.0-beta.1",
29
- "@gradio/highlightedtext": "^0.7.7-beta.1",
20
+ "@gradio/dataset": "^0.2.5-beta.2",
21
+ "@gradio/downloadbutton": "^0.1.28-beta.2",
22
+ "@gradio/dropdown": "^0.7.13-beta.2",
23
+ "@gradio/fallback": "^0.3.13-beta.2",
24
+ "@gradio/datetime": "^0.1.4-beta.2",
25
+ "@gradio/fileexplorer": "^0.5.0-beta.2",
26
+ "@gradio/file": "^0.10.0-beta.2",
27
+ "@gradio/form": "^0.1.25-beta.2",
28
+ "@gradio/gallery": "^0.13.0-beta.2",
30
29
  "@gradio/group": "^0.2.0-beta.1",
31
- "@gradio/html": "^0.3.6-beta.1",
32
- "@gradio/icons": "^0.8.0-beta.1",
33
- "@gradio/imageeditor": "^0.11.0-beta.1",
34
- "@gradio/image": "^0.16.0-beta.1",
35
- "@gradio/json": "^0.4.1-beta.1",
36
- "@gradio/label": "^0.3.13-beta.1",
37
- "@gradio/markdown": "^0.9.4-beta.1",
38
- "@gradio/multimodaltextbox": "^0.6.0-beta.1",
39
- "@gradio/model3d": "^0.12.2-beta.1",
40
- "@gradio/number": "^0.4.13-beta.1",
41
- "@gradio/nativeplot": "^0.3.2-beta.1",
42
- "@gradio/paramviewer": "^0.4.22-beta.1",
43
- "@gradio/plot": "^0.6.5-beta.1",
44
- "@gradio/radio": "^0.6.0-beta.1",
45
- "@gradio/row": "^0.1.5-beta.0",
46
- "@gradio/simpledropdown": "^0.2.13-beta.1",
47
- "@gradio/simpleimage": "^0.7.1-beta.1",
48
- "@gradio/slider": "^0.5.0-beta.1",
49
- "@gradio/simpletextbox": "^0.2.13-beta.1",
50
- "@gradio/state": "^0.1.2-beta.0",
51
- "@gradio/statustracker": "^0.8.0-beta.1",
52
- "@gradio/tabitem": "^0.3.0-beta.1",
53
- "@gradio/tabs": "^0.3.0-beta.1",
54
- "@gradio/textbox": "^0.7.0-beta.1",
30
+ "@gradio/highlightedtext": "^0.7.7-beta.2",
31
+ "@gradio/html": "^0.4.0-beta.2",
32
+ "@gradio/icons": "^0.8.0-beta.2",
33
+ "@gradio/image": "^0.16.0-beta.2",
34
+ "@gradio/json": "^0.5.0-beta.2",
35
+ "@gradio/imageeditor": "^0.11.0-beta.2",
36
+ "@gradio/model3d": "^0.13.0-beta.2",
37
+ "@gradio/label": "^0.3.13-beta.2",
38
+ "@gradio/multimodaltextbox": "^0.6.0-beta.2",
39
+ "@gradio/markdown": "^0.10.0-beta.2",
40
+ "@gradio/nativeplot": "^0.4.0-beta.2",
41
+ "@gradio/paramviewer": "^0.4.22-beta.2",
42
+ "@gradio/plot": "^0.6.5-beta.2",
43
+ "@gradio/number": "^0.4.13-beta.2",
44
+ "@gradio/row": "^0.2.0-beta.1",
45
+ "@gradio/radio": "^0.6.0-beta.2",
46
+ "@gradio/simpledropdown": "^0.2.13-beta.2",
47
+ "@gradio/simpleimage": "^0.8.0-beta.2",
48
+ "@gradio/simpletextbox": "^0.2.13-beta.2",
49
+ "@gradio/slider": "^0.5.0-beta.2",
50
+ "@gradio/statustracker": "^0.8.0-beta.2",
51
+ "@gradio/tabitem": "^0.3.0-beta.2",
52
+ "@gradio/state": "^0.1.2",
53
+ "@gradio/tabs": "^0.3.0-beta.2",
54
+ "@gradio/textbox": "^0.7.0-beta.2",
55
+ "@gradio/timer": "^0.3.3-beta.2",
55
56
  "@gradio/theme": "^0.3.0-beta.1",
56
- "@gradio/timer": "^0.3.3-beta.1",
57
- "@gradio/upload": "^0.12.4-beta.1",
58
- "@gradio/uploadbutton": "^0.6.19-beta.1",
59
- "@gradio/utils": "^0.7.0-beta.1",
60
- "@gradio/video": "^0.11.0-beta.1",
61
- "@gradio/wasm": "^0.13.1-beta.1"
57
+ "@gradio/upload": "^0.13.0-beta.2",
58
+ "@gradio/uploadbutton": "^0.7.0-beta.2",
59
+ "@gradio/utils": "^0.7.0-beta.2",
60
+ "@gradio/video": "^0.11.0-beta.2",
61
+ "@gradio/wasm": "^0.14.0-beta.2"
62
62
  },
63
63
  "msw": {
64
64
  "workerDirectory": "public"
package/src/Blocks.svelte CHANGED
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { tick } from "svelte";
2
+ import { tick, onMount } from "svelte";
3
3
  import { _ } from "svelte-i18n";
4
4
  import { Client } from "@gradio/client";
5
5
 
@@ -21,7 +21,7 @@
21
21
  import type {
22
22
  LogMessage,
23
23
  RenderMessage,
24
- StatusMessage
24
+ StatusMessage,
25
25
  } from "@gradio/client";
26
26
 
27
27
  setupi18n();
@@ -45,8 +45,11 @@
45
45
  export let fill_height = false;
46
46
  export let ready: boolean;
47
47
  export let username: string | null;
48
+ export let api_prefix: string;
49
+ export let max_file_size: number;
50
+ export let initial_layout: LayoutNode | undefined = undefined;
48
51
 
49
- const {
52
+ let {
50
53
  layout: _layout,
51
54
  targets,
52
55
  update_value,
@@ -57,27 +60,35 @@
57
60
  loading_status,
58
61
  scheduled_updates,
59
62
  create_layout,
60
- rerender_layout
63
+ rerender_layout,
61
64
  } = create_components();
62
65
 
63
- $: create_layout({
64
- components,
65
- layout,
66
- dependencies,
67
- root: root + app.api_prefix,
68
- app,
69
- options: {
70
- fill_height
71
- }
72
- });
66
+ // @ts-ignore
67
+ $_layout = initial_layout;
68
+
69
+ $: components, layout, dependencies, root, app, fill_height, target, run();
73
70
 
74
71
  $: {
75
72
  ready = !!$_layout;
76
73
  }
77
74
 
78
- let params = new URLSearchParams(window.location.search);
79
- let api_docs_visible = params.get("view") === "api" && show_api;
80
- let api_recorder_visible = params.get("view") === "api-recorder" && show_api;
75
+ async function run(): Promise<void> {
76
+ await create_layout({
77
+ components,
78
+ layout,
79
+ dependencies,
80
+ root: root + api_prefix,
81
+ app,
82
+ options: {
83
+ fill_height,
84
+ },
85
+ });
86
+ }
87
+
88
+ export let search_params: URLSearchParams;
89
+ let api_docs_visible = search_params.get("view") === "api" && show_api;
90
+ let api_recorder_visible =
91
+ search_params.get("view") === "api-recorder" && show_api;
81
92
  function set_api_docs_visible(visible: boolean): void {
82
93
  api_recorder_visible = false;
83
94
  api_docs_visible = visible;
@@ -99,7 +110,7 @@
99
110
  return {
100
111
  id: outputs[i],
101
112
  prop: "value_is_output",
102
- value: true
113
+ value: true,
103
114
  };
104
115
  });
105
116
 
@@ -122,7 +133,7 @@
122
133
  updates.push({
123
134
  id: outputs[i],
124
135
  prop: update_key,
125
- value: update_value
136
+ value: update_value,
126
137
  });
127
138
  }
128
139
  }
@@ -130,7 +141,7 @@
130
141
  updates.push({
131
142
  id: outputs[i],
132
143
  prop: "value",
133
- value
144
+ value,
134
145
  });
135
146
  }
136
147
  });
@@ -147,7 +158,7 @@
147
158
  fn_index: number,
148
159
  type: ToastMessage["type"],
149
160
  duration: number | null = 10,
150
- visible = true
161
+ visible = true,
151
162
  ): ToastMessage & { fn_index: number } {
152
163
  return {
153
164
  message,
@@ -155,13 +166,13 @@
155
166
  type,
156
167
  id: ++_error_id,
157
168
  duration,
158
- visible
169
+ visible,
159
170
  };
160
171
  }
161
172
 
162
173
  export function add_new_message(
163
174
  message: string,
164
- type: ToastMessage["type"]
175
+ type: ToastMessage["type"],
165
176
  ): void {
166
177
  messages = [new_message(message, -1, type), ...messages];
167
178
  }
@@ -169,31 +180,25 @@
169
180
  let _error_id = -1;
170
181
 
171
182
  let user_left_page = false;
172
- document.addEventListener("visibilitychange", function () {
173
- if (document.visibilityState === "hidden") {
174
- user_left_page = true;
175
- }
176
- });
177
183
 
178
184
  const MESSAGE_QUOTE_RE = /^'([^]+)'$/;
179
185
 
180
186
  const DUPLICATE_MESSAGE = $_("blocks.long_requests_queue");
181
187
  const MOBILE_QUEUE_WARNING = $_("blocks.connection_can_break");
182
188
  const MOBILE_RECONNECT_MESSAGE = $_("blocks.lost_connection");
189
+ const WAITING_FOR_INPUTS_MESSAGE = $_("blocks.waiting_for_inputs");
183
190
  const SHOW_DUPLICATE_MESSAGE_ON_ETA = 15;
184
191
  const SHOW_MOBILE_QUEUE_WARNING_ON_ETA = 10;
185
- const is_mobile_device =
186
- /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
187
- navigator.userAgent
188
- );
192
+ let is_mobile_device = false;
189
193
  let showed_duplicate_message = false;
190
194
  let showed_mobile_warning = false;
195
+ let inputs_waiting: number[] = [];
191
196
 
192
197
  // as state updates are not synchronous, we need to ensure updates are flushed before triggering any requests
193
198
  function wait_then_trigger_api_call(
194
199
  dep_index: number,
195
200
  trigger_id: number | null = null,
196
- event_data: unknown = null
201
+ event_data: unknown = null,
197
202
  ): void {
198
203
  let _unsub = (): void => {};
199
204
  function unsub(): void {
@@ -214,7 +219,7 @@
214
219
  async function get_component_value_or_event_data(
215
220
  component_id: number,
216
221
  trigger_id: number | null,
217
- event_data: unknown
222
+ event_data: unknown,
218
223
  ): Promise<any> {
219
224
  if (
220
225
  component_id === trigger_id &&
@@ -230,10 +235,17 @@
230
235
  async function trigger_api_call(
231
236
  dep_index: number,
232
237
  trigger_id: number | null = null,
233
- event_data: unknown = null
238
+ event_data: unknown = null,
234
239
  ): Promise<void> {
235
240
  let dep = dependencies.find((dep) => dep.id === dep_index)!;
236
-
241
+ if (inputs_waiting.length > 0) {
242
+ for (const input of inputs_waiting) {
243
+ if (dep.inputs.includes(input)) {
244
+ add_new_message(WAITING_FOR_INPUTS_MESSAGE, "warning");
245
+ return;
246
+ }
247
+ }
248
+ }
237
249
  const current_status = loading_status.get_status_for_fn(dep_index);
238
250
  messages = messages.filter(({ fn_index }) => fn_index !== dep_index);
239
251
  if (current_status === "pending" || current_status === "generating") {
@@ -244,24 +256,24 @@
244
256
  fn_index: dep_index,
245
257
  data: await Promise.all(
246
258
  dep.inputs.map((id) =>
247
- get_component_value_or_event_data(id, trigger_id, event_data)
248
- )
259
+ get_component_value_or_event_data(id, trigger_id, event_data),
260
+ ),
249
261
  ),
250
262
  event_data: dep.collects_event_data ? event_data : null,
251
- trigger_id: trigger_id
263
+ trigger_id: trigger_id,
252
264
  };
253
265
 
254
266
  if (dep.frontend_fn) {
255
267
  dep
256
268
  .frontend_fn(
257
269
  payload.data.concat(
258
- await Promise.all(dep.outputs.map((id) => get_data(id)))
259
- )
270
+ await Promise.all(dep.outputs.map((id) => get_data(id))),
271
+ ),
260
272
  )
261
273
  .then((v: unknown[]) => {
262
274
  if (dep.backend_fn) {
263
275
  payload.data = v;
264
- make_prediction(payload);
276
+ trigger_prediction(dep, payload);
265
277
  } else {
266
278
  handle_update(v, dep_index);
267
279
  }
@@ -272,28 +284,32 @@
272
284
  const submission = submit_map.get(fn_index);
273
285
  submission?.cancel();
274
286
  return submission;
275
- })
287
+ }),
276
288
  );
277
289
  } else {
278
290
  if (dep.backend_fn) {
279
- if (dep.trigger_mode === "once") {
280
- if (!dep.pending_request)
281
- make_prediction(payload, dep.connection == "stream");
282
- } else if (dep.trigger_mode === "multiple") {
291
+ trigger_prediction(dep, payload);
292
+ }
293
+ }
294
+
295
+ function trigger_prediction(dep: Dependency, payload: Payload): void {
296
+ if (dep.trigger_mode === "once") {
297
+ if (!dep.pending_request)
283
298
  make_prediction(payload, dep.connection == "stream");
284
- } else if (dep.trigger_mode === "always_last") {
285
- if (!dep.pending_request) {
286
- make_prediction(payload, dep.connection == "stream");
287
- } else {
288
- dep.final_event = payload;
289
- }
299
+ } else if (dep.trigger_mode === "multiple") {
300
+ make_prediction(payload, dep.connection == "stream");
301
+ } else if (dep.trigger_mode === "always_last") {
302
+ if (!dep.pending_request) {
303
+ make_prediction(payload, dep.connection == "stream");
304
+ } else {
305
+ dep.final_event = payload;
290
306
  }
291
307
  }
292
308
  }
293
-
309
+ $: console.log({ app });
294
310
  async function make_prediction(
295
311
  payload: Payload,
296
- streaming = false
312
+ streaming = false,
297
313
  ): Promise<void> {
298
314
  if (api_recorder_visible) {
299
315
  api_calls = [...api_calls, JSON.parse(JSON.stringify(payload))];
@@ -315,8 +331,8 @@
315
331
  ) {
316
332
  await app.post_data(
317
333
  // @ts-ignore
318
- `${app.config.root}/stream/${submit_map.get(dep_index).event_id()}`,
319
- { ...payload, session_hash: app.session_hash }
334
+ `${app.config.root + app.config.api_prefix}/stream/${submit_map.get(dep_index).event_id()}`,
335
+ { ...payload, session_hash: app.session_hash },
320
336
  );
321
337
  return;
322
338
  }
@@ -326,7 +342,7 @@
326
342
  payload.fn_index,
327
343
  payload.data as unknown[],
328
344
  payload.event_data,
329
- payload.trigger_id
345
+ payload.trigger_id,
330
346
  );
331
347
  } catch (e) {
332
348
  const fn_index = 0; // Mock value for fn_index
@@ -336,7 +352,7 @@
336
352
  fn_index,
337
353
  eta: 0,
338
354
  queue: false,
339
- queue_position: null
355
+ queue_position: null,
340
356
  });
341
357
  set_status($loading_status);
342
358
  return;
@@ -360,7 +376,7 @@
360
376
  const { data, fn_index } = message;
361
377
  if (dep.pending_request && dep.final_event) {
362
378
  dep.pending_request = false;
363
- make_prediction(dep.final_event);
379
+ make_prediction(dep.final_event, dep.connection == "stream");
364
380
  }
365
381
  dep.pending_request = false;
366
382
  handle_update(data, fn_index);
@@ -392,7 +408,7 @@
392
408
  layout: render_layout,
393
409
  root: root,
394
410
  dependencies: dependencies,
395
- render_id: render_id
411
+ render_id: render_id,
396
412
  });
397
413
  }
398
414
 
@@ -400,14 +416,14 @@
400
416
  const { log, fn_index, level, duration, visible } = msg;
401
417
  messages = [
402
418
  new_message(log, fn_index, level, duration, visible),
403
- ...messages
419
+ ...messages,
404
420
  ];
405
421
  }
406
422
 
407
423
  function open_stream_events(
408
424
  status: StatusMessage,
409
425
  id: number,
410
- dep: Dependency
426
+ dep: Dependency,
411
427
  ): void {
412
428
  if (
413
429
  status.original_msg === "process_starts" &&
@@ -417,6 +433,7 @@
417
433
  }
418
434
  }
419
435
 
436
+ /* eslint-disable complexity */
420
437
  function handle_status_update(message: StatusMessage): void {
421
438
  const { fn_index, ...status } = message;
422
439
  if (status.stage === "streaming" && status.time_limit) {
@@ -433,7 +450,7 @@
433
450
  time_limit: status.time_limit,
434
451
  status: status.stage,
435
452
  progress: status.progress_data,
436
- fn_index
453
+ fn_index,
437
454
  });
438
455
  set_status($loading_status);
439
456
  if (
@@ -447,7 +464,7 @@
447
464
  showed_duplicate_message = true;
448
465
  messages = [
449
466
  new_message(DUPLICATE_MESSAGE, fn_index, "warning"),
450
- ...messages
467
+ ...messages,
451
468
  ];
452
469
  }
453
470
  if (
@@ -459,11 +476,11 @@
459
476
  showed_mobile_warning = true;
460
477
  messages = [
461
478
  new_message(MOBILE_QUEUE_WARNING, fn_index, "warning"),
462
- ...messages
479
+ ...messages,
463
480
  ];
464
481
  }
465
482
 
466
- if (status.stage === "complete") {
483
+ if (status.stage === "complete" || status.stage === "generating") {
467
484
  status.changed_state_ids?.forEach((id) => {
468
485
  dependencies
469
486
  .filter((dep) => dep.targets.some(([_id, _]) => _id === id))
@@ -471,6 +488,8 @@
471
488
  wait_then_trigger_api_call(dep.id, payload.trigger_id);
472
489
  });
473
490
  });
491
+ }
492
+ if (status.stage === "complete") {
474
493
  dependencies.forEach(async (dep) => {
475
494
  if (dep.trigger_after === fn_index) {
476
495
  wait_then_trigger_api_call(dep.id, payload.trigger_id);
@@ -485,7 +504,7 @@
485
504
  window.setTimeout(() => {
486
505
  messages = [
487
506
  new_message(MOBILE_RECONNECT_MESSAGE, fn_index, "error"),
488
- ...messages
507
+ ...messages,
489
508
  ];
490
509
  }, 0);
491
510
  wait_then_trigger_api_call(dep.id, payload.trigger_id, event_data);
@@ -494,7 +513,7 @@
494
513
  if (status.message) {
495
514
  const _message = status.message.replace(
496
515
  MESSAGE_QUOTE_RE,
497
- (_, b) => b
516
+ (_, b) => b,
498
517
  );
499
518
  messages = [
500
519
  new_message(
@@ -502,9 +521,9 @@
502
521
  fn_index,
503
522
  "error",
504
523
  status.duration,
505
- status.visible
524
+ status.visible,
506
525
  ),
507
- ...messages
526
+ ...messages,
508
527
  ];
509
528
  }
510
529
  dependencies.map(async (dep) => {
@@ -519,13 +538,14 @@
519
538
  }
520
539
  }
521
540
  }
541
+ /* eslint-enable complexity */
522
542
 
523
543
  function trigger_share(title: string | undefined, description: string): void {
524
544
  if (space_id === null) {
525
545
  return;
526
546
  }
527
547
  const discussion_url = new URL(
528
- `https://huggingface.co/spaces/${space_id}/discussions/new`
548
+ `https://huggingface.co/spaces/${space_id}/discussions/new`,
529
549
  );
530
550
  if (title !== undefined && title.length > 0) {
531
551
  discussion_url.searchParams.set("title", title);
@@ -546,7 +566,7 @@
546
566
  if (js) {
547
567
  let blocks_frontend_fn = new AsyncFunction(
548
568
  `let result = await (${js})();
549
- return (!Array.isArray(result)) ? [result] : result;`
569
+ return (!Array.isArray(result)) ? [result] : result;`,
550
570
  );
551
571
  await blocks_frontend_fn();
552
572
  }
@@ -571,12 +591,18 @@
571
591
  }
572
592
  });
573
593
 
574
- if (render_complete) return;
594
+ if (!target || render_complete) return;
575
595
 
576
596
  target.addEventListener("prop_change", (e: Event) => {
577
597
  if (!isCustomEvent(e)) throw new Error("not a custom event");
578
598
  const { id, prop, value } = e.detail;
579
599
  update_value([{ id, prop, value }]);
600
+ if (prop === "input_ready" && value === false) {
601
+ inputs_waiting.push(id);
602
+ }
603
+ if (prop === "input_ready" && value === true) {
604
+ inputs_waiting = inputs_waiting.filter((item) => item !== id);
605
+ }
580
606
  });
581
607
  target.addEventListener("gradio", (e: Event) => {
582
608
  if (!isCustomEvent(e)) throw new Error("not a custom event");
@@ -596,8 +622,8 @@
596
622
  if (submit_map.has(dep_id)) {
597
623
  app.post_data(
598
624
  // @ts-ignore
599
- `${app.config.root}/stream/${submit_map.get(dep_id).event_id()}/close`,
600
- {}
625
+ `${app.config.root + app.config.api_prefix}/stream/${submit_map.get(dep_id).event_id()}/close`,
626
+ {},
601
627
  );
602
628
  }
603
629
  });
@@ -620,15 +646,15 @@
620
646
  function update_status(
621
647
  id: number,
622
648
  status: "error" | "complete" | "pending",
623
- data: LoadingStatus
649
+ data: LoadingStatus,
624
650
  ): void {
625
651
  data.status = status;
626
652
  update_value([
627
653
  {
628
654
  id,
629
655
  prop: "loading_status",
630
- value: data
631
- }
656
+ value: data,
657
+ },
632
658
  ]);
633
659
  }
634
660
 
@@ -640,7 +666,7 @@
640
666
  }[] = [];
641
667
  Object.entries(statuses).forEach(([id, loading_status]) => {
642
668
  let dependency = dependencies.find(
643
- (dep) => dep.id == loading_status.fn_index
669
+ (dep) => dep.id == loading_status.fn_index,
644
670
  );
645
671
  if (dependency === undefined) {
646
672
  return;
@@ -650,7 +676,7 @@
650
676
  updates.push({
651
677
  id: parseInt(id),
652
678
  prop: "loading_status",
653
- value: loading_status
679
+ value: loading_status,
654
680
  });
655
681
  });
656
682
 
@@ -660,9 +686,9 @@
660
686
  return {
661
687
  id,
662
688
  prop: "pending",
663
- value: pending_status === "pending"
689
+ value: pending_status === "pending",
664
690
  };
665
- }
691
+ },
666
692
  );
667
693
 
668
694
  update_value([...updates, ...additional_updates]);
@@ -671,6 +697,19 @@
671
697
  function isCustomEvent(event: Event): event is CustomEvent {
672
698
  return "detail" in event;
673
699
  }
700
+
701
+ onMount(() => {
702
+ document.addEventListener("visibilitychange", function () {
703
+ if (document.visibilityState === "hidden") {
704
+ user_left_page = true;
705
+ }
706
+ });
707
+
708
+ is_mobile_device =
709
+ /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
710
+ navigator.userAgent,
711
+ );
712
+ });
674
713
  </script>
675
714
 
676
715
  <svelte:head>
@@ -681,19 +720,19 @@
681
720
 
682
721
  <div class="wrap" style:min-height={app_mode ? "100%" : "auto"}>
683
722
  <div class="contain" style:flex-grow={app_mode ? "1" : "auto"}>
684
- {#if $_layout && app.config}
685
- <MountComponents
686
- rootNode={$_layout}
687
- {root}
688
- {target}
689
- {theme_mode}
690
- on:mount={handle_mount}
691
- {version}
692
- {autoscroll}
693
- max_file_size={app.config.max_file_size}
694
- client={app}
695
- />
696
- {/if}
723
+ <!-- {#if $_layout} -->
724
+ <MountComponents
725
+ rootNode={$_layout}
726
+ {root}
727
+ {target}
728
+ {theme_mode}
729
+ on:mount={handle_mount}
730
+ {version}
731
+ {autoscroll}
732
+ {max_file_size}
733
+ client={app}
734
+ />
735
+ <!-- {/if} -->
697
736
  </div>
698
737
 
699
738
  {#if show_footer}