@gradio/core 1.0.0-dev.0 → 1.0.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 (109) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/src/Blocks.svelte +534 -1001
  5. package/dist/src/Blocks.svelte.d.ts +32 -45
  6. package/dist/src/Embed.svelte +82 -55
  7. package/dist/src/Embed.svelte.d.ts +39 -30
  8. package/dist/src/Login.svelte +33 -29
  9. package/dist/src/Login.svelte.d.ts +21 -19
  10. package/dist/src/MountComponents.svelte +19 -25
  11. package/dist/src/MountComponents.svelte.d.ts +5 -28
  12. package/dist/src/{init.d.ts → _init.d.ts} +5 -4
  13. package/dist/src/{init.js → _init.js} +31 -108
  14. package/dist/src/api_docs/ApiBanner.svelte +12 -8
  15. package/dist/src/api_docs/ApiBanner.svelte.d.ts +22 -20
  16. package/dist/src/api_docs/ApiDocs.svelte +356 -247
  17. package/dist/src/api_docs/ApiDocs.svelte.d.ts +27 -24
  18. package/dist/src/api_docs/ApiRecorder.svelte +6 -3
  19. package/dist/src/api_docs/ApiRecorder.svelte.d.ts +19 -17
  20. package/dist/src/api_docs/CodeSnippet.svelte +122 -48
  21. package/dist/src/api_docs/CodeSnippet.svelte.d.ts +29 -25
  22. package/dist/src/api_docs/CopyButton.svelte +69 -13
  23. package/dist/src/api_docs/CopyButton.svelte.d.ts +18 -16
  24. package/dist/src/api_docs/CopyMarkdown.svelte +734 -0
  25. package/dist/src/api_docs/CopyMarkdown.svelte.d.ts +37 -0
  26. package/dist/src/api_docs/EndpointDetail.svelte +81 -23
  27. package/dist/src/api_docs/EndpointDetail.svelte.d.ts +23 -18
  28. package/dist/src/api_docs/IconArrowUpRight.svelte +34 -0
  29. package/dist/src/api_docs/IconArrowUpRight.svelte.d.ts +20 -0
  30. package/dist/src/api_docs/IconCaret.svelte +39 -0
  31. package/dist/src/api_docs/IconCaret.svelte.d.ts +20 -0
  32. package/dist/src/api_docs/IconHuggingChat.svelte +62 -0
  33. package/dist/src/api_docs/IconHuggingChat.svelte.d.ts +20 -0
  34. package/dist/src/api_docs/InputPayload.svelte +17 -11
  35. package/dist/src/api_docs/InputPayload.svelte.d.ts +25 -23
  36. package/dist/src/api_docs/InstallSnippet.svelte +9 -6
  37. package/dist/src/api_docs/InstallSnippet.svelte.d.ts +18 -16
  38. package/dist/src/api_docs/MCPSnippet.svelte +139 -126
  39. package/dist/src/api_docs/MCPSnippet.svelte.d.ts +60 -58
  40. package/dist/src/api_docs/NoApi.svelte +7 -4
  41. package/dist/src/api_docs/NoApi.svelte.d.ts +20 -18
  42. package/dist/src/api_docs/ParametersSnippet.svelte +8 -6
  43. package/dist/src/api_docs/ParametersSnippet.svelte.d.ts +21 -19
  44. package/dist/src/api_docs/PercentileChart.svelte +125 -0
  45. package/dist/src/api_docs/PercentileChart.svelte.d.ts +22 -0
  46. package/dist/src/api_docs/RecordingSnippet.svelte +124 -110
  47. package/dist/src/api_docs/RecordingSnippet.svelte.d.ts +24 -22
  48. package/dist/src/api_docs/ResponseSnippet.svelte +7 -5
  49. package/dist/src/api_docs/ResponseSnippet.svelte.d.ts +21 -19
  50. package/dist/src/api_docs/Settings.svelte +73 -62
  51. package/dist/src/api_docs/Settings.svelte.d.ts +25 -23
  52. package/dist/src/api_docs/SettingsBanner.svelte +11 -8
  53. package/dist/src/api_docs/SettingsBanner.svelte.d.ts +20 -18
  54. package/dist/src/api_docs/TryButton.svelte +5 -3
  55. package/dist/src/api_docs/TryButton.svelte.d.ts +19 -17
  56. package/dist/src/api_docs/img/IconCheck.svelte +33 -0
  57. package/dist/src/api_docs/img/IconCheck.svelte.d.ts +26 -0
  58. package/dist/src/api_docs/img/IconCopy.svelte +40 -0
  59. package/dist/src/api_docs/img/IconCopy.svelte.d.ts +26 -0
  60. package/dist/src/api_docs/img/clear.svelte.d.ts +22 -21
  61. package/dist/src/dependency.d.ts +145 -0
  62. package/dist/src/dependency.js +668 -0
  63. package/dist/src/init.svelte.d.ts +78 -0
  64. package/dist/src/init.svelte.js +469 -0
  65. package/dist/src/init_utils.d.ts +32 -0
  66. package/dist/src/init_utils.js +73 -0
  67. package/dist/src/lang/en.json +10 -1
  68. package/dist/src/lang/get_lang_names.js +0 -3
  69. package/dist/src/lang/ru.json +10 -1
  70. package/dist/src/stores.d.ts +0 -21
  71. package/dist/src/stories/I18nMultiLanguageTestComponent.svelte +5 -3
  72. package/dist/src/stories/I18nMultiLanguageTestComponent.svelte.d.ts +16 -14
  73. package/dist/src/stories/I18nTestSetup.svelte +14 -10
  74. package/dist/src/stories/I18nTestSetup.svelte.d.ts +18 -16
  75. package/dist/src/types.d.ts +31 -26
  76. package/index.ts +1 -1
  77. package/package.json +62 -63
  78. package/src/Blocks.svelte +360 -1063
  79. package/src/MountComponents.svelte +17 -27
  80. package/src/{init.ts → _init.ts} +49 -126
  81. package/src/api_docs/ApiDocs.svelte +84 -62
  82. package/src/api_docs/CodeSnippet.svelte +83 -24
  83. package/src/api_docs/CopyButton.svelte +61 -7
  84. package/src/api_docs/CopyMarkdown.svelte +734 -0
  85. package/src/api_docs/EndpointDetail.svelte +73 -17
  86. package/src/api_docs/IconArrowUpRight.svelte +34 -0
  87. package/src/api_docs/IconCaret.svelte +39 -0
  88. package/src/api_docs/IconHuggingChat.svelte +62 -0
  89. package/src/api_docs/MCPSnippet.svelte +44 -73
  90. package/src/api_docs/ParametersSnippet.svelte +1 -1
  91. package/src/api_docs/PercentileChart.svelte +125 -0
  92. package/src/api_docs/ResponseSnippet.svelte +1 -1
  93. package/src/api_docs/Settings.svelte +11 -11
  94. package/src/api_docs/img/IconCheck.svelte +33 -0
  95. package/src/api_docs/img/IconCopy.svelte +40 -0
  96. package/src/dependency.ts +909 -0
  97. package/src/init.svelte.ts +717 -0
  98. package/src/init_utils.ts +99 -0
  99. package/src/lang/en.json +10 -1
  100. package/src/lang/get_lang_names.js +0 -3
  101. package/src/lang/ru.json +10 -1
  102. package/src/stores.ts +22 -22
  103. package/src/types.ts +55 -43
  104. package/dist/src/Render.svelte +0 -105
  105. package/dist/src/Render.svelte.d.ts +0 -31
  106. package/dist/src/RenderComponent.svelte +0 -72
  107. package/dist/src/RenderComponent.svelte.d.ts +0 -33
  108. package/src/Render.svelte +0 -126
  109. package/src/RenderComponent.svelte +0 -91
@@ -1,867 +1,412 @@
1
- <script>import { tick, onMount } from "svelte";
2
- import { _ } from "svelte-i18n";
3
- import { Client } from "@gradio/client";
4
- import { writable } from "svelte/store";
5
- import { setupi18n } from "./i18n";
6
- import { Toast } from "@gradio/statustracker";
7
- import MountComponents from "./MountComponents.svelte";
8
- import { prefix_css } from "./css";
9
- import logo from "./images/logo.svg";
10
- import api_logo from "./api_docs/img/api-logo.svg";
11
- import settings_logo from "./api_docs/img/settings-logo.svg";
12
- import record_stop from "./api_docs/img/record-stop.svg";
13
- import { create_components, AsyncFunction } from "./init";
14
- import * as screen_recorder from "./screen_recorder";
15
- export let root;
16
- export let components;
17
- export let layout;
18
- export let dependencies;
19
- export let title = "Gradio";
20
- export let target;
21
- export let autoscroll;
22
- export let footer_links = ["gradio", "settings", "api"];
23
- export let control_page_title = false;
24
- export let app_mode;
25
- export let theme_mode;
26
- export let app;
27
- export let space_id;
28
- export let version;
29
- export let js;
30
- export let fill_height = false;
31
- export let ready;
32
- export let username;
33
- export let api_prefix = "";
34
- export let max_file_size = void 0;
35
- export let initial_layout = void 0;
36
- export let css = null;
37
- export let vibe_mode = false;
38
- let broken_connection = false;
39
- let {
40
- layout: _layout,
41
- targets,
42
- update_value,
43
- get_data,
44
- modify_stream,
45
- get_stream_state,
46
- set_time_limit,
47
- loading_status,
48
- scheduled_updates,
49
- create_layout,
50
- rerender_layout,
51
- value_change
52
- } = create_components({
53
- initial_layout
54
- });
55
- $: components, layout, dependencies, root, app, fill_height, target, run();
56
- $: {
57
- ready = !!$_layout;
58
- }
59
- let old_dependencies = dependencies;
60
- $: if (dependencies !== old_dependencies && render_complete && !layout_creating) {
61
- handle_load_triggers();
62
- old_dependencies = dependencies;
63
- }
64
- let vibe_editor_width = 350;
65
- async function run() {
66
- await setupi18n(app.config?.i18n_translations || void 0);
67
- layout_creating = true;
68
- await create_layout({
69
- components,
70
- layout,
71
- dependencies,
72
- root: root + api_prefix,
73
- app,
74
- options: {
75
- fill_height
76
- }
77
- });
78
- layout_creating = false;
79
- }
80
- export let search_params;
81
- let api_docs_visible = search_params.get("view") === "api" && footer_links.includes("api");
82
- let settings_visible = search_params.get("view") === "settings";
83
- let api_recorder_visible = search_params.get("view") === "api-recorder" && footer_links.includes("api");
84
- let allow_zoom = true;
85
- let allow_video_trim = true;
86
- let ApiDocs = null;
87
- let ApiRecorder = null;
88
- let Settings = null;
89
- let VibeEditor = null;
90
- async function loadApiDocs() {
91
- if (!ApiDocs || !ApiRecorder) {
92
- const api_docs_module = await import("./api_docs/ApiDocs.svelte");
93
- const api_recorder_module = await import("./api_docs/ApiRecorder.svelte");
94
- if (!ApiDocs) ApiDocs = api_docs_module.default;
95
- if (!ApiRecorder) ApiRecorder = api_recorder_module.default;
96
- }
97
- }
98
- async function loadApiRecorder() {
99
- if (!ApiRecorder) {
100
- const api_recorder_module = await import("./api_docs/ApiRecorder.svelte");
101
- ApiRecorder = api_recorder_module.default;
102
- }
103
- }
104
- async function loadSettings() {
105
- if (!Settings) {
106
- const settings_module = await import("./api_docs/Settings.svelte");
107
- Settings = settings_module.default;
108
- }
109
- }
110
- async function loadVibeEditor() {
111
- if (!VibeEditor) {
112
- const vibe_editor_module = await import("@gradio/vibeeditor");
113
- VibeEditor = vibe_editor_module.default;
114
- }
115
- }
116
- async function set_api_docs_visible(visible) {
117
- api_recorder_visible = false;
118
- if (visible) {
119
- await loadApiDocs();
120
- }
121
- api_docs_visible = visible;
122
- let params = new URLSearchParams(window.location.search);
123
- if (visible) {
124
- params.set("view", "api");
125
- } else {
126
- params.delete("view");
127
- }
128
- history.replaceState(null, "", "?" + params.toString());
129
- }
130
- async function set_settings_visible(visible) {
131
- if (visible) {
132
- await loadSettings();
133
- }
134
- let params = new URLSearchParams(window.location.search);
135
- if (visible) {
136
- params.set("view", "settings");
137
- } else {
138
- params.delete("view");
139
- }
140
- history.replaceState(null, "", "?" + params.toString());
141
- settings_visible = !settings_visible;
142
- }
143
- let api_calls = [];
144
- let layout_creating = false;
145
- export let render_complete = false;
146
- async function handle_update(data, fn_index) {
147
- const dep = dependencies.find((dep2) => dep2.id === fn_index);
148
- const input_type = components.find(
149
- (comp) => comp.id === dep?.inputs[0]
150
- )?.type;
151
- if (allow_zoom && dep && input_type !== "dataset") {
152
- if (dep && dep.inputs && dep.inputs.length > 0 && $is_screen_recording) {
153
- screen_recorder.zoom(true, dep.inputs, 1);
154
- }
155
- if (dep && dep.outputs && dep.outputs.length > 0 && $is_screen_recording) {
156
- screen_recorder.zoom(false, dep.outputs, 2);
157
- }
158
- }
159
- if (!dep) {
160
- return;
161
- }
162
- const outputs = dep.outputs;
163
- const meta_updates = data?.map((value, i) => {
164
- return {
165
- id: outputs[i],
166
- prop: "value_is_output",
167
- value: true
168
- };
169
- });
170
- update_value(meta_updates);
171
- await tick();
172
- const updates = [];
173
- data?.forEach((value, i) => {
174
- if (typeof value === "object" && value !== null && value.__type__ === "update") {
175
- for (const [update_key, update_value2] of Object.entries(value)) {
176
- if (update_key === "__type__") {
177
- continue;
178
- } else {
179
- updates.push({
180
- id: outputs[i],
181
- prop: update_key,
182
- value: update_value2
183
- });
184
- }
185
- }
186
- } else {
187
- updates.push({
188
- id: outputs[i],
189
- prop: "value",
190
- value
191
- });
192
- }
193
- });
194
- update_value(updates);
195
- updates.forEach((update) => {
196
- const component = components.find((comp) => comp.id === update.id);
197
- if (component && component.type === "navbar") {
198
- import("./navbar_store").then(({ navbar_config }) => {
199
- navbar_config.update((current) => ({
200
- ...current,
201
- [update.prop]: update.value
202
- }));
203
- });
204
- }
205
- });
206
- await tick();
207
- }
208
- let submit_map = /* @__PURE__ */ new Map();
209
- let messages = [];
210
- function new_message(title2, message, fn_index, type, duration = 10, visible = true) {
211
- return {
212
- title: title2,
213
- message,
214
- fn_index,
215
- type,
216
- id: ++_error_id,
217
- duration,
218
- visible
219
- };
220
- }
221
- export function add_new_message(title2, message, type) {
222
- messages = [new_message(title2, message, -1, type), ...messages];
223
- }
224
- let _error_id = -1;
225
- const MESSAGE_QUOTE_RE = /^'([^]+)'$/;
226
- const DUPLICATE_MESSAGE = $_("blocks.long_requests_queue");
227
- const MOBILE_QUEUE_WARNING = $_("blocks.connection_can_break");
228
- const LOST_CONNECTION_MESSAGE = "Connection to the server was lost. Attempting reconnection...";
229
- const CHANGED_CONNECTION_MESSAGE = "Reconnected to server, but the server has changed. You may need to <a href=''>refresh the page</a>.";
230
- const RECONNECTION_MESSAGE = "Connection re-established.";
231
- const SESSION_NOT_FOUND_MESSAGE = "Session not found - this is likely because the machine you were connected to has changed. <a href=''>Refresh the page</a> to continue.";
232
- const WAITING_FOR_INPUTS_MESSAGE = $_("blocks.waiting_for_inputs");
233
- const SHOW_DUPLICATE_MESSAGE_ON_ETA = 15;
234
- const SHOW_MOBILE_QUEUE_WARNING_ON_ETA = 10;
235
- let is_mobile_device = false;
236
- let showed_duplicate_message = false;
237
- let showed_mobile_warning = false;
238
- let inputs_waiting = [];
239
- function wait_then_trigger_api_call(dep_index, trigger_id = null, event_data = null) {
240
- let _unsub = () => {
241
- };
242
- function unsub() {
243
- _unsub();
244
- }
245
- if ($scheduled_updates) {
246
- _unsub = scheduled_updates.subscribe((updating) => {
247
- if (!updating) {
248
- tick().then(() => {
249
- trigger_api_call(dep_index, trigger_id, event_data);
250
- unsub();
251
- });
252
- }
253
- });
254
- } else {
255
- trigger_api_call(dep_index, trigger_id, event_data);
256
- }
257
- }
258
- async function get_component_value_or_event_data(component_id, trigger_id, event_data) {
259
- if (component_id === trigger_id && event_data && event_data.is_value_data === true) {
260
- return event_data.value;
261
- }
262
- return get_data(component_id);
263
- }
264
- async function trigger_api_call(dep_index, trigger_id = null, event_data = null) {
265
- const _dep = dependencies.find((dep2) => dep2.id === dep_index);
266
- if (_dep === void 0) {
267
- return;
268
- }
269
- const dep = _dep;
270
- if (inputs_waiting.length > 0) {
271
- for (const input of inputs_waiting) {
272
- if (dep.inputs.includes(input)) {
273
- add_new_message("Warning", WAITING_FOR_INPUTS_MESSAGE, "warning");
274
- return;
275
- }
276
- }
277
- }
278
- const current_status = loading_status.get_status_for_fn(dep_index);
279
- messages = messages.filter(({ fn_index }) => fn_index !== dep_index);
280
- if (current_status === "pending" || current_status === "generating") {
281
- dep.pending_request = true;
282
- }
283
- let payload = {
284
- fn_index: dep_index,
285
- data: await Promise.all(
286
- dep.inputs.map(
287
- (id) => get_component_value_or_event_data(id, trigger_id, event_data)
288
- )
289
- ),
290
- event_data: dep.collects_event_data ? event_data : null,
291
- trigger_id
292
- };
293
- if (dep.frontend_fn && typeof dep.frontend_fn !== "boolean") {
294
- dep.frontend_fn(
295
- payload.data.concat(
296
- await Promise.all(dep.outputs.map((id) => get_data(id)))
297
- )
298
- ).then((v) => {
299
- if (dep.backend_fn) {
300
- payload.data = v;
301
- trigger_prediction(dep, payload);
302
- } else {
303
- handle_update(v, dep_index);
304
- }
305
- });
306
- } else if (dep.types.cancel && dep.cancels) {
307
- await Promise.all(
308
- dep.cancels.map(async (fn_index) => {
309
- const submission = submit_map.get(fn_index);
310
- submission?.cancel();
311
- return submission;
312
- })
313
- );
314
- } else {
315
- if (dep.backend_fn) {
316
- if (dep.js_implementation) {
317
- let js_fn = new AsyncFunction(
318
- `let result = await (${dep.js_implementation})(...arguments);
319
- return (!Array.isArray(result)) ? [result] : result;`
320
- );
321
- js_fn(...payload.data).then((js_result) => {
322
- handle_update(js_result, dep_index);
323
- payload.js_implementation = true;
324
- }).catch((error) => {
325
- console.error(error);
326
- payload.js_implementation = false;
327
- });
328
- }
329
- trigger_prediction(dep, payload);
330
- }
331
- }
332
- function trigger_prediction(dep2, payload2) {
333
- if (dep2.trigger_mode === "once") {
334
- if (!dep2.pending_request)
335
- make_prediction(payload2, dep2.connection == "stream");
336
- } else if (dep2.trigger_mode === "multiple") {
337
- make_prediction(payload2, dep2.connection == "stream");
338
- } else if (dep2.trigger_mode === "always_last") {
339
- if (!dep2.pending_request) {
340
- make_prediction(payload2, dep2.connection == "stream");
341
- } else {
342
- dep2.final_event = payload2;
343
- }
344
- }
345
- }
346
- async function reconnect() {
347
- const connection_status = await app.reconnect();
348
- if (connection_status === "broken") {
349
- setTimeout(reconnect, 1e3);
350
- } else if (connection_status === "changed") {
351
- broken_connection = false;
352
- messages = [
353
- new_message(
354
- "Changed Connection",
355
- CHANGED_CONNECTION_MESSAGE,
356
- -1,
357
- "info",
358
- 3,
359
- true
360
- ),
361
- ...messages.map(
362
- (m) => m.message === LOST_CONNECTION_MESSAGE ? { ...m, visible: false } : m
363
- )
364
- ];
365
- } else if (connection_status === "connected") {
366
- broken_connection = false;
367
- messages = [
368
- new_message(
369
- "Reconnected",
370
- RECONNECTION_MESSAGE,
371
- -1,
372
- "success",
373
- null,
374
- true
375
- ),
376
- ...messages.map(
377
- (m) => m.message === LOST_CONNECTION_MESSAGE ? { ...m, visible: false } : m
378
- )
379
- ];
380
- }
381
- }
382
- async function make_prediction(payload2, streaming = false) {
383
- if (allow_video_trim) {
384
- screen_recorder.markRemoveSegmentStart();
385
- }
386
- if (api_recorder_visible) {
387
- api_calls = [...api_calls, JSON.parse(JSON.stringify(payload2))];
388
- }
389
- let submission;
390
- app.set_current_payload(payload2);
391
- if (streaming) {
392
- if (!submit_map.has(dep_index)) {
393
- dep.inputs.forEach((id) => modify_stream(id, "waiting"));
394
- } else if (submit_map.has(dep_index) && dep.inputs.some((id) => get_stream_state(id) === "waiting")) {
395
- return;
396
- } else if (submit_map.has(dep_index) && dep.inputs.some((id) => get_stream_state(id) === "open")) {
397
- await app.post_data(
398
- // @ts-ignore
399
- `${app.config.root + app.config.api_prefix}/stream/${submit_map.get(dep_index).event_id()}`,
400
- { ...payload2, session_hash: app.session_hash }
401
- );
402
- return;
403
- }
404
- }
405
- try {
406
- submission = app.submit(
407
- payload2.fn_index,
408
- payload2.data,
409
- payload2.event_data,
410
- payload2.trigger_id
411
- );
412
- } catch (e) {
413
- const fn_index = 0;
414
- if (app.closed) return;
415
- messages = [
416
- new_message("Error", String(e), fn_index, "error"),
417
- ...messages
418
- ];
419
- loading_status.update({
420
- status: "error",
421
- fn_index,
422
- eta: 0,
423
- queue: false,
424
- queue_position: null
425
- });
426
- set_status($loading_status);
427
- return;
428
- }
429
- submit_map.set(dep_index, submission);
430
- for await (const message of submission) {
431
- if (payload2.js_implementation) {
432
- return;
433
- }
434
- if (message.type === "data") {
435
- handle_data(message);
436
- } else if (message.type === "render") {
437
- handle_render(message);
438
- } else if (message.type === "status") {
439
- handle_status_update(message);
440
- } else if (message.type === "log") {
441
- handle_log(message);
442
- }
443
- }
444
- function handle_data(message) {
445
- const { data, fn_index } = message;
446
- if (dep.pending_request && dep.final_event) {
447
- dep.pending_request = false;
448
- make_prediction(dep.final_event, dep.connection == "stream");
449
- }
450
- dep.pending_request = false;
451
- handle_update(data, fn_index);
452
- set_status($loading_status);
453
- }
454
- function handle_render(message) {
455
- const { data } = message;
456
- let _components = data.components;
457
- let render_layout = data.layout;
458
- let _dependencies = data.dependencies;
459
- let render_id = data.render_id;
460
- let deps_to_remove = [];
461
- dependencies.forEach((old_dep, i) => {
462
- if (old_dep.rendered_in === dep.render_id) {
463
- deps_to_remove.push(i);
464
- }
465
- });
466
- deps_to_remove.reverse().forEach((i) => {
467
- dependencies.splice(i, 1);
468
- });
469
- _dependencies.forEach((dep2) => {
470
- dependencies.push(dep2);
471
- });
472
- rerender_layout({
473
- components: _components,
474
- layout: render_layout,
475
- root: root + api_prefix,
476
- dependencies,
477
- render_id
478
- });
479
- _dependencies.forEach((dep2) => {
480
- if (dep2.targets.some((dep3) => dep3[1] === "load")) {
481
- wait_then_trigger_api_call(dep2.id);
482
- }
483
- });
484
- }
485
- function handle_log(msg) {
486
- const { title: title2, log, fn_index, level, duration, visible } = msg;
487
- messages = [
488
- new_message(title2, log, fn_index, level, duration, visible),
489
- ...messages
490
- ];
491
- }
492
- function open_stream_events(status, id, dep2) {
493
- if (status.original_msg === "process_starts" && dep2.connection === "stream") {
494
- modify_stream(id, "open");
495
- }
496
- }
497
- function handle_status_update(message) {
498
- if (message.code === "validation_error") {
499
- const dep2 = dependencies.find((dep3) => dep3.id === message.fn_index);
500
- if (dep2 === void 0 || message.message === void 0 || typeof message.message === "string") {
501
- return;
502
- }
503
- const validation_error_data = [];
504
- message.message.forEach((message2, i) => {
505
- if (message2.is_valid) {
506
- return;
507
- }
508
- validation_error_data.push({
509
- id: dep2.inputs[i],
510
- prop: "validation_error",
511
- value: message2.message
512
- });
513
- validation_error_data.push({
514
- id: dep2.inputs[i],
515
- prop: "loading_status",
516
- value: { validation_error: message2.message }
517
- });
518
- });
519
- if (validation_error_data.length > 0) {
520
- update_value(validation_error_data);
521
- loading_status.update({
522
- status: "complete",
523
- fn_index: message.fn_index,
524
- eta: 0,
525
- queue: false,
526
- queue_position: null
527
- });
528
- set_status($loading_status);
529
- return;
530
- }
531
- }
532
- if (message.broken && !broken_connection) {
533
- messages = [
534
- new_message(
535
- "Broken Connection",
536
- LOST_CONNECTION_MESSAGE,
537
- -1,
538
- "error",
539
- null,
540
- true
541
- ),
542
- ...messages
543
- ];
544
- broken_connection = true;
545
- setTimeout(reconnect, 1e3);
546
- }
547
- if (message.session_not_found) {
548
- messages = [
549
- new_message(
550
- "Session Not Found",
551
- SESSION_NOT_FOUND_MESSAGE,
552
- -1,
553
- "error",
554
- null,
555
- true
556
- ),
557
- ...messages
558
- ];
559
- }
560
- const { fn_index, ...status } = message;
561
- if (status.stage === "streaming" && status.time_limit) {
562
- dep.inputs.forEach((id) => {
563
- set_time_limit(id, status.time_limit);
564
- });
565
- }
566
- dep.inputs.forEach((id) => {
567
- open_stream_events(message, id, dep);
568
- });
569
- loading_status.update({
570
- ...status,
571
- time_limit: status.time_limit,
572
- status: status.stage,
573
- progress: status.progress_data,
574
- fn_index
575
- });
576
- set_status($loading_status);
577
- if (!showed_duplicate_message && space_id !== null && status.position !== void 0 && status.position >= 2 && status.eta !== void 0 && status.eta > SHOW_DUPLICATE_MESSAGE_ON_ETA) {
578
- showed_duplicate_message = true;
579
- messages = [
580
- new_message("Warning", DUPLICATE_MESSAGE, fn_index, "warning"),
581
- ...messages
582
- ];
583
- }
584
- if (!showed_mobile_warning && is_mobile_device && status.eta !== void 0 && status.eta > SHOW_MOBILE_QUEUE_WARNING_ON_ETA) {
585
- showed_mobile_warning = true;
586
- messages = [
587
- new_message("Warning", MOBILE_QUEUE_WARNING, fn_index, "warning"),
588
- ...messages
589
- ];
590
- }
591
- if (status.stage === "complete" || status.stage === "generating") {
592
- const deps_triggered_by_state = /* @__PURE__ */ new Set();
593
- status.changed_state_ids?.forEach((id) => {
594
- dependencies.filter((dep2) => dep2.targets.some(([_id, _2]) => _id === id)).forEach((dep2) => {
595
- deps_triggered_by_state.add(dep2);
596
- });
597
- });
598
- deps_triggered_by_state.forEach((dep2) => {
599
- wait_then_trigger_api_call(dep2.id, payload2.trigger_id);
600
- });
601
- }
602
- if (status.stage === "complete") {
603
- dependencies.forEach(async (dep2) => {
604
- if (dep2.trigger_after === fn_index && !dep2.trigger_only_on_failure) {
605
- wait_then_trigger_api_call(dep2.id, payload2.trigger_id);
606
- }
607
- });
608
- dep.inputs.forEach((id) => {
609
- modify_stream(id, "closed");
610
- });
611
- submit_map.delete(dep_index);
612
- }
613
- if (status.stage === "error" && !broken_connection && !message.session_not_found) {
614
- if (status.message && typeof status.message === "string") {
615
- const _message = status.message.replace(
616
- MESSAGE_QUOTE_RE,
617
- (_2, b) => b
618
- );
619
- const _title = status.title ?? "Error";
620
- messages = [
621
- new_message(
622
- _title,
623
- _message,
624
- fn_index,
625
- "error",
626
- status.duration,
627
- status.visible
628
- ),
629
- ...messages
630
- ];
631
- }
632
- dependencies.map(async (dep2) => {
633
- if (dep2.trigger_after === fn_index && (!dep2.trigger_only_on_success || dep2.trigger_only_on_failure)) {
634
- wait_then_trigger_api_call(dep2.id, payload2.trigger_id);
635
- }
636
- });
637
- }
638
- }
639
- if (allow_video_trim) {
640
- screen_recorder.markRemoveSegmentEnd();
641
- }
642
- }
643
- }
644
- function trigger_share(title2, description) {
645
- if (space_id === null) {
646
- return;
647
- }
648
- const discussion_url = new URL(
649
- `https://huggingface.co/spaces/${space_id}/discussions/new`
650
- );
651
- if (title2 !== void 0 && title2.length > 0) {
652
- discussion_url.searchParams.set("title", title2);
653
- }
654
- discussion_url.searchParams.set("description", description);
655
- window.open(discussion_url.toString(), "_blank");
656
- }
657
- function handle_error_close(e) {
658
- const _id = e.detail;
659
- messages = messages.filter((m) => m.id !== _id);
660
- }
661
- const is_external_url = (link) => !!(link && new URL(link, location.href).origin !== location.origin);
662
- async function handle_mount() {
663
- if (js) {
664
- let blocks_frontend_fn = new AsyncFunction(
665
- `let result = await (${js})();
666
- return (!Array.isArray(result)) ? [result] : result;`
667
- );
668
- await blocks_frontend_fn();
669
- }
670
- await tick();
671
- var a = target.getElementsByTagName("a");
672
- for (var i = 0; i < a.length; i++) {
673
- const _target = a[i].getAttribute("target");
674
- const _link = a[i].getAttribute("href");
675
- if (is_external_url(_link) && _target !== "_blank")
676
- a[i].setAttribute("target", "_blank");
677
- }
678
- handle_load_triggers();
679
- if (!target || render_complete) return;
680
- target.addEventListener("prop_change", (e) => {
681
- if (!isCustomEvent(e)) throw new Error("not a custom event");
682
- const { id, prop, value } = e.detail;
683
- if (prop === "value") {
684
- update_value([
685
- {
686
- id,
687
- prop: "loading_status",
688
- value: { validation_error: void 0 }
689
- }
690
- ]);
691
- }
692
- update_value([{ id, prop, value }]);
693
- if (prop === "input_ready" && value === false) {
694
- inputs_waiting.push(id);
695
- }
696
- if (prop === "input_ready" && value === true) {
697
- inputs_waiting = inputs_waiting.filter((item) => item !== id);
698
- }
699
- });
700
- target.addEventListener("gradio", (e) => {
701
- if (!isCustomEvent(e)) throw new Error("not a custom event");
702
- const { id, event, data } = e.detail;
703
- if (event === "share") {
704
- const { title: title2, description } = data;
705
- trigger_share(title2, description);
706
- } else if (event === "error") {
707
- messages = [new_message("Error", data, -1, event), ...messages];
708
- } else if (event === "warning") {
709
- messages = [new_message("Warning", data, -1, event), ...messages];
710
- } else if (event === "info") {
711
- messages = [new_message("Info", data, -1, event), ...messages];
712
- } else if (event == "clear_status") {
713
- update_status(id, "complete", data);
714
- } else if (event == "close_stream") {
715
- const deps = $targets[id]?.[data];
716
- deps?.forEach((dep_id) => {
717
- if (submit_map.has(dep_id)) {
718
- const url = `${app.config.root + app.config.api_prefix}/stream/${submit_map.get(dep_id).event_id()}`;
719
- app.post_data(`${url}/close`, {});
720
- }
721
- });
722
- } else {
723
- const deps = $targets[id]?.[event];
724
- deps?.forEach((dep_id) => {
725
- requestAnimationFrame(() => {
726
- wait_then_trigger_api_call(dep_id, id, data);
727
- });
728
- });
729
- }
730
- });
731
- render_complete = true;
732
- }
733
- value_change((id, value) => {
734
- const deps = $targets[id]?.["change"];
735
- deps?.forEach((dep_id) => {
736
- requestAnimationFrame(() => {
737
- wait_then_trigger_api_call(dep_id, id, value);
738
- });
739
- });
740
- });
741
- const handle_load_triggers = () => {
742
- dependencies.forEach((dep) => {
743
- if (dep.targets.some((dep2) => dep2[1] === "load")) {
744
- wait_then_trigger_api_call(dep.id);
745
- }
746
- });
747
- };
748
- $: set_status($loading_status);
749
- function update_status(id, status, data) {
750
- data.status = status;
751
- update_value([
752
- {
753
- id,
754
- prop: "loading_status",
755
- value: data
756
- }
757
- ]);
758
- }
759
- function set_status(statuses) {
760
- let updates = [];
761
- Object.entries(statuses).forEach(([id, loading_status2]) => {
762
- if (app.closed && loading_status2.status === "error") {
763
- return;
764
- }
765
- let dependency = dependencies.find(
766
- (dep) => dep.id == loading_status2.fn_index
767
- );
768
- if (dependency === void 0) {
769
- return;
770
- }
771
- loading_status2.scroll_to_output = dependency.scroll_to_output;
772
- loading_status2.show_progress = dependency.show_progress;
773
- updates.push({
774
- id: parseInt(id),
775
- prop: "loading_status",
776
- value: loading_status2
777
- });
778
- });
779
- const inputs_to_update = loading_status.get_inputs_to_update();
780
- const additional_updates = Array.from(inputs_to_update).map(
781
- ([id, pending_status]) => {
782
- return {
783
- id,
784
- prop: "pending",
785
- value: pending_status === "pending"
786
- };
787
- }
788
- );
789
- update_value([...updates, ...additional_updates]);
790
- }
791
- function isCustomEvent(event) {
792
- return "detail" in event;
793
- }
794
- let is_screen_recording = writable(false);
795
- onMount(() => {
796
- is_mobile_device = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
797
- navigator.userAgent
798
- );
799
- screen_recorder.initialize(
800
- root,
801
- (title2, message, type) => {
802
- add_new_message(title2, message, type);
803
- },
804
- (isRecording) => {
805
- $is_screen_recording = isRecording;
806
- }
807
- );
808
- const handleVibeEditorResize = (event) => {
809
- vibe_editor_width = event.detail.width;
810
- };
811
- window.addEventListener(
812
- "vibeEditorResize",
813
- handleVibeEditorResize
814
- );
815
- if (api_docs_visible) {
816
- loadApiDocs();
817
- }
818
- if (api_recorder_visible) {
819
- loadApiRecorder();
820
- }
821
- if (settings_visible) {
822
- loadSettings();
823
- }
824
- if (vibe_mode) {
825
- loadVibeEditor();
826
- }
827
- });
828
- function screen_recording() {
829
- if ($is_screen_recording) {
830
- screen_recorder.stopRecording();
831
- } else {
832
- screen_recorder.startRecording();
833
- }
834
- }
835
- let footer_height = 0;
836
- let root_container;
837
- function get_root_node(container) {
838
- if (!container) return null;
839
- return container.children[container.children.length - 1];
840
- }
841
- function handle_resize() {
842
- if ("parentIFrame" in window) {
843
- const box = root_container.children[0].getBoundingClientRect();
844
- if (!box) return;
845
- window.parentIFrame?.size(box.bottom + footer_height + 32);
846
- }
847
- }
848
- onMount(() => {
849
- if ("parentIFrame" in window) {
850
- window.parentIFrame?.autoResize(false);
851
- }
852
- const mut = new MutationObserver(handle_resize);
853
- const res = new ResizeObserver(handle_resize);
854
- mut.observe(root_container, {
855
- childList: true,
856
- subtree: true,
857
- attributes: true
858
- });
859
- res.observe(root_container);
860
- return () => {
861
- mut.disconnect();
862
- res.disconnect();
863
- };
864
- });
1
+ <script lang="ts">
2
+ import { tick, onMount, setContext, settled, untrack } from "svelte";
3
+ import { _ } from "svelte-i18n";
4
+ import { Client } from "@gradio/client";
5
+ import { writable } from "svelte/store";
6
+
7
+ // import type { LoadingStatus, LoadingStatusCollection } from "./stores";
8
+
9
+ import type {
10
+ ComponentMeta,
11
+ Dependency as IDependency,
12
+ LayoutNode
13
+ } from "./types";
14
+ // import type { UpdateTransaction } from "./_init";
15
+ import { setupi18n } from "./i18n";
16
+ import type { ThemeMode, Payload } from "./types";
17
+ import { Toast } from "@gradio/statustracker";
18
+ import type { ToastMessage } from "@gradio/statustracker";
19
+ import { type ShareData, type ValueData, GRADIO_ROOT } from "@gradio/utils";
20
+
21
+ import MountComponents from "./MountComponents.svelte";
22
+ import { prefix_css } from "./css";
23
+ import { reactive_formatter } from "./gradio_helper";
24
+
25
+ import type ApiDocsInterface from "./api_docs/ApiDocs.svelte";
26
+ import type ApiRecorderInterface from "./api_docs/ApiRecorder.svelte";
27
+ import type SettingsInterface from "./api_docs/Settings.svelte";
28
+ // import type { ComponentType } from "svelte";
29
+
30
+ import logo from "./images/logo.svg";
31
+ import api_logo from "./api_docs/img/api-logo.svg";
32
+ import settings_logo from "./api_docs/img/settings-logo.svg";
33
+ import record_stop from "./api_docs/img/record-stop.svg";
34
+ import { AppTree } from "./init.svelte";
35
+ // import type {
36
+ // LogMessage,
37
+ // RenderMessage,
38
+ // StatusMessage,
39
+ // } from "@gradio/client";
40
+ import * as screen_recorder from "./screen_recorder";
41
+
42
+ import { DependencyManager } from "./dependency";
43
+
44
+ let {
45
+ root,
46
+ components,
47
+ layout,
48
+ dependencies,
49
+ title,
50
+ target,
51
+ autoscroll,
52
+ footer_links,
53
+ control_page_title,
54
+ app_mode,
55
+ theme_mode,
56
+ app,
57
+ space_id,
58
+ version,
59
+ js,
60
+ fill_height,
61
+ username,
62
+ api_prefix,
63
+ max_file_size,
64
+ initial_layout,
65
+ css,
66
+ vibe_mode,
67
+ search_params,
68
+ render_complete = false,
69
+ ready = $bindable(false),
70
+ reload_count = $bindable(0),
71
+ add_new_message = $bindable()
72
+ }: {
73
+ root: string;
74
+ components: ComponentMeta[];
75
+ layout: LayoutNode;
76
+ dependencies: IDependency[];
77
+ title: string;
78
+ target: HTMLElement;
79
+ autoscroll: boolean;
80
+ footer_links: string[];
81
+ control_page_title: boolean;
82
+ app_mode: boolean;
83
+ theme_mode: ThemeMode;
84
+ app: Awaited<ReturnType<typeof Client.connect>>;
85
+ space_id: string | null;
86
+ version: string;
87
+ js: string | null;
88
+ fill_height: boolean;
89
+ username: string | null;
90
+ api_prefix: string;
91
+ max_file_size: number | undefined;
92
+ initial_layout: ComponentMeta | undefined;
93
+ css: string | null | undefined;
94
+ vibe_mode: boolean;
95
+ search_params: URLSearchParams;
96
+ render_complete: boolean;
97
+ ready: boolean;
98
+ reload_count: number;
99
+ add_new_message: (title: string, message: string, type: string) => void;
100
+ } = $props();
101
+
102
+ components.forEach((comp) => {
103
+ if (!comp.props.i18n) {
104
+ comp.props.i18n = $reactive_formatter;
105
+ }
106
+ });
107
+
108
+ let app_tree = new AppTree(
109
+ components,
110
+ layout,
111
+ dependencies,
112
+ {
113
+ root,
114
+ theme: theme_mode,
115
+ version,
116
+ api_prefix,
117
+ max_file_size,
118
+ autoscroll
119
+ },
120
+ app,
121
+ $reactive_formatter
122
+ );
123
+
124
+ setContext(GRADIO_ROOT, {
125
+ register: app_tree.register_component.bind(app_tree),
126
+ dispatcher: gradio_event_dispatcher
127
+ });
128
+
129
+ let messages: (ToastMessage & { fn_index: number })[] = $state([]);
130
+
131
+ function gradio_event_dispatcher(
132
+ id: number,
133
+ event: string,
134
+ data: unknown
135
+ ): void {
136
+ if (event === "share") {
137
+ const { title, description } = data as ShareData;
138
+ // trigger_share(title, description);
139
+ // TODO: lets combine all of the into a log type with levels
140
+ } else if (event === "error") {
141
+ new_message("Error", data, -1, event, 10, true);
142
+ } else if (event === "warning") {
143
+ new_message("Warning", data, -1, event, 10, true);
144
+ } else if (event === "info") {
145
+ new_message("Info", data, -1, event, 10, true);
146
+ } else if (event == "clear_status") {
147
+ app_tree.update_state(
148
+ id,
149
+ {
150
+ loading_status: {}
151
+ },
152
+ false
153
+ );
154
+ dep_manager.clear_loading_status(id);
155
+ // TODO: the loading_status store should handle this via a method
156
+ // update_status(id, "complete", data);
157
+ } else if (event == "close_stream") {
158
+ dep_manager.close_stream(id);
159
+ } else {
160
+ // Tabs are a bit weird. The Tabs component dispatches 'select' events
161
+ // but the target id corresponds to the child Tab component that was selected.
162
+ // So the id we get from the dispatcher belongs to the Tabs,
163
+ // so we need to pull out the correct id here.
164
+ if (event === "select" && id in app_tree.initial_tabs) {
165
+ // this is the id of the selected tab
166
+ id = data.id;
167
+ }
168
+ dep_manager.dispatch({
169
+ type: "event",
170
+ event_name: event,
171
+ target_id: id,
172
+ event_data: data
173
+ });
174
+ }
175
+ }
176
+
177
+ let api_calls: Payload[] = $state([]);
178
+ let last_api_call: Payload | null = $state(null);
179
+ // We need a callback to add to api_calls from the DependencyManager
180
+ // We can't update a state variable from inside the DependencyManager because
181
+ // svelte won't see it and won't update the UI.
182
+ let add_to_api_calls = (payload: Payload): void => {
183
+ last_api_call = payload;
184
+ if (!api_recorder_visible) return;
185
+ api_calls = [...api_calls, last_api_call];
186
+ };
187
+
188
+ let dep_manager = new DependencyManager(
189
+ dependencies,
190
+ app,
191
+ app_tree.update_state.bind(app_tree),
192
+ app_tree.get_state.bind(app_tree),
193
+ app_tree.rerender.bind(app_tree),
194
+ new_message,
195
+ add_to_api_calls
196
+ );
197
+
198
+ $effect(() => {
199
+ reload_count;
200
+ untrack(() => {
201
+ app_tree.reload(components, layout, dependencies, {
202
+ root,
203
+ theme: theme_mode,
204
+ version,
205
+ api_prefix,
206
+ max_file_size,
207
+ autoscroll
208
+ });
209
+ dep_manager.reload(
210
+ dependencies,
211
+ app_tree.update_state.bind(app_tree),
212
+ app_tree.get_state.bind(app_tree),
213
+ app_tree.rerender.bind(app_tree),
214
+ app
215
+ );
216
+ });
217
+ });
218
+
219
+ let vibe_editor_width = 350;
220
+
221
+ // export let
222
+ let api_docs_visible = $derived(
223
+ search_params.get("view") === "api" && footer_links.includes("api")
224
+ );
225
+ let settings_visible = $derived(search_params.get("view") === "settings");
226
+ let api_recorder_visible = $derived(
227
+ search_params.get("view") === "api-recorder" && footer_links.includes("api")
228
+ );
229
+ let allow_zoom = true;
230
+ let allow_video_trim = true;
231
+
232
+ // Lazy component loading state
233
+ let ApiDocs: ComponentType<ApiDocsInterface> | null = null;
234
+ let ApiRecorder: ComponentType<ApiRecorderInterface> | null = null;
235
+ let Settings: ComponentType<SettingsInterface> | null = null;
236
+ let VibeEditor: any = $state(null);
237
+
238
+ async function loadApiDocs(): Promise<void> {
239
+ if (!ApiDocs || !ApiRecorder) {
240
+ const api_docs_module = await import("./api_docs/ApiDocs.svelte");
241
+ const api_recorder_module = await import("./api_docs/ApiRecorder.svelte");
242
+ if (!ApiDocs) ApiDocs = api_docs_module.default;
243
+ if (!ApiRecorder) ApiRecorder = api_recorder_module.default;
244
+ }
245
+ }
246
+
247
+ async function loadApiRecorder(): Promise<void> {
248
+ if (!ApiRecorder) {
249
+ const api_recorder_module = await import("./api_docs/ApiRecorder.svelte");
250
+ ApiRecorder = api_recorder_module.default;
251
+ }
252
+ }
253
+
254
+ async function loadSettings(): Promise<void> {
255
+ if (!Settings) {
256
+ const settings_module = await import("./api_docs/Settings.svelte");
257
+ Settings = settings_module.default;
258
+ }
259
+ }
260
+
261
+ async function loadVibeEditor(): Promise<void> {
262
+ if (!VibeEditor) {
263
+ const vibe_editor_module = await import("@gradio/vibeeditor");
264
+ VibeEditor = vibe_editor_module.default;
265
+ }
266
+ }
267
+
268
+ async function set_api_docs_visible(visible: boolean): Promise<void> {
269
+ api_recorder_visible = false;
270
+ if (visible) {
271
+ await loadApiDocs();
272
+ }
273
+ api_docs_visible = visible;
274
+ let params = new URLSearchParams(window.location.search);
275
+ if (visible) {
276
+ params.set("view", "api");
277
+ } else {
278
+ params.delete("view");
279
+ }
280
+ history.replaceState(null, "", "?" + params.toString());
281
+ }
282
+
283
+ async function set_settings_visible(visible: boolean): Promise<void> {
284
+ if (visible) {
285
+ await loadSettings();
286
+ }
287
+ let params = new URLSearchParams(window.location.search);
288
+ if (visible) {
289
+ params.set("view", "settings");
290
+ } else {
291
+ params.delete("view");
292
+ }
293
+ history.replaceState(null, "", "?" + params.toString());
294
+ settings_visible = !settings_visible;
295
+ }
296
+
297
+ let layout_creating = false;
298
+ //
299
+
300
+ function new_message(
301
+ title: string,
302
+ message: string,
303
+ fn_index: number,
304
+ type: ToastMessage["type"],
305
+ duration: number | null = 10,
306
+ visible = false
307
+ ): void {
308
+ if (!visible) return;
309
+ messages.push({
310
+ title,
311
+ message,
312
+ fn_index,
313
+ type,
314
+ id: ++_error_id,
315
+ duration,
316
+ visible
317
+ });
318
+ }
319
+
320
+ add_new_message = new_message;
321
+
322
+ let _error_id = -1;
323
+
324
+ const MESSAGE_QUOTE_RE = /^'([^]+)'$/;
325
+
326
+ const DUPLICATE_MESSAGE = $_("blocks.long_requests_queue");
327
+ const MOBILE_QUEUE_WARNING = $_("blocks.connection_can_break");
328
+ const LOST_CONNECTION_MESSAGE =
329
+ "Connection to the server was lost. Attempting reconnection...";
330
+ const CHANGED_CONNECTION_MESSAGE =
331
+ "Reconnected to server, but the server has changed. You may need to <a href=''>refresh the page</a>.";
332
+ const RECONNECTION_MESSAGE = "Connection re-established.";
333
+ const SESSION_NOT_FOUND_MESSAGE =
334
+ "Session not found - this is likely because the machine you were connected to has changed. <a href=''>Refresh the page</a> to continue.";
335
+ const WAITING_FOR_INPUTS_MESSAGE = $_("blocks.waiting_for_inputs");
336
+ const SHOW_DUPLICATE_MESSAGE_ON_ETA = 15;
337
+ const SHOW_MOBILE_QUEUE_WARNING_ON_ETA = 10;
338
+ let is_mobile_device = false;
339
+ let showed_duplicate_message = false;
340
+ let showed_mobile_warning = false;
341
+ let inputs_waiting: number[] = [];
342
+
343
+ // as state updates are not synchronous, we need to ensure updates are flushed before triggering any requests
344
+
345
+ let is_screen_recording = writable(false);
346
+
347
+ let footer_height = 0;
348
+
349
+ let root_container: HTMLElement;
350
+
351
+ function get_root_node(container: HTMLElement | null): HTMLElement | null {
352
+ if (!container) return null;
353
+ return container.children[container.children.length - 1] as HTMLElement;
354
+ }
355
+
356
+ function handle_resize(): void {
357
+ if ("parentIFrame" in window) {
358
+ const box = root_container.children[0].getBoundingClientRect();
359
+ if (!box) return;
360
+ window.parentIFrame?.size(box.bottom + footer_height + 32);
361
+ }
362
+ }
363
+
364
+ function screen_recording(): void {
365
+ if ($is_screen_recording) {
366
+ screen_recorder.stopRecording();
367
+ } else {
368
+ screen_recorder.startRecording();
369
+ }
370
+ }
371
+
372
+ onMount(() => {
373
+ is_mobile_device =
374
+ /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
375
+ navigator.userAgent
376
+ );
377
+
378
+ if ("parentIFrame" in window) {
379
+ window.parentIFrame?.autoResize(false);
380
+ }
381
+
382
+ const mut = new MutationObserver(handle_resize);
383
+ const res = new ResizeObserver(handle_resize);
384
+
385
+ mut.observe(root_container, {
386
+ childList: true,
387
+ subtree: true,
388
+ attributes: true
389
+ });
390
+ res.observe(root_container);
391
+
392
+ app_tree.ready.then(() => {
393
+ ready = true;
394
+ dep_manager.dispatch_load_events();
395
+ });
396
+
397
+ if (vibe_mode) {
398
+ void loadVibeEditor();
399
+ }
400
+
401
+ return () => {
402
+ mut.disconnect();
403
+ res.disconnect();
404
+ };
405
+ });
406
+
407
+ function handle_close(id: number): void {
408
+ messages = messages.filter((m) => m.id !== id);
409
+ }
865
410
  </script>
866
411
 
867
412
  <svelte:head>
@@ -880,167 +425,155 @@ onMount(() => {
880
425
  bind:this={root_container}
881
426
  style:margin-right={vibe_mode ? `${vibe_editor_width}px` : "0"}
882
427
  >
883
- {#if $_layout && app.config}
884
- <MountComponents
885
- rootNode={$_layout}
886
- {root}
887
- {target}
888
- {theme_mode}
889
- on:mount={handle_mount}
890
- {version}
891
- {autoscroll}
892
- {max_file_size}
893
- client={app}
894
- />
895
- {/if}
896
- </div>
428
+ <MountComponents node={app_tree.root} />
897
429
 
898
- {#if footer_links.length > 0}
899
- <footer bind:clientHeight={footer_height}>
900
- {#if footer_links.includes("api")}
430
+ {#if footer_links.length > 0}
431
+ <footer bind:clientHeight={footer_height}>
432
+ {#if footer_links.includes("api")}
433
+ <button
434
+ on:click={() => {
435
+ set_api_docs_visible(!api_docs_visible);
436
+ }}
437
+ on:mouseenter={() => {
438
+ loadApiDocs();
439
+ loadApiRecorder();
440
+ }}
441
+ class="show-api"
442
+ >
443
+ {#if app.config?.mcp_server}
444
+ {$_("errors.use_via_api_or_mcp")}
445
+ {:else}
446
+ {$_("errors.use_via_api")}
447
+ {/if}
448
+ <img src={api_logo} alt={$_("common.logo")} />
449
+ </button>
450
+ {/if}
451
+ {#if footer_links.includes("gradio")}
452
+ <div class="divider show-api-divider">·</div>
453
+ <a
454
+ href="https://gradio.app"
455
+ class="built-with"
456
+ target="_blank"
457
+ rel="noreferrer"
458
+ >
459
+ {$_("common.built_with_gradio")}
460
+ <img src={logo} alt={$_("common.logo")} />
461
+ </a>
462
+ {/if}
901
463
  <button
464
+ class:hidden={!$is_screen_recording}
902
465
  on:click={() => {
903
- set_api_docs_visible(!api_docs_visible);
466
+ screen_recording();
904
467
  }}
905
- on:mouseenter={() => {
906
- loadApiDocs();
907
- loadApiRecorder();
908
- }}
909
- class="show-api"
468
+ class="record"
910
469
  >
911
- {#if app.config?.mcp_server}
912
- {$_("errors.use_via_api_or_mcp")}
913
- {:else}
914
- {$_("errors.use_via_api")}
915
- {/if}
916
- <img src={api_logo} alt={$_("common.logo")} />
470
+ {$_("common.stop_recording")}
471
+ <img src={record_stop} alt={$_("common.stop_recording")} />
917
472
  </button>
918
- {/if}
919
- {#if footer_links.includes("gradio")}
920
- <div class="divider show-api-divider">·</div>
921
- <a
922
- href="https://gradio.app"
923
- class="built-with"
924
- target="_blank"
925
- rel="noreferrer"
926
- >
927
- {$_("common.built_with_gradio")}
928
- <img src={logo} alt={$_("common.logo")} />
929
- </a>
930
- {/if}
931
- <button
932
- class:hidden={!$is_screen_recording}
933
- on:click={() => {
934
- screen_recording();
935
- }}
936
- class="record"
937
- >
938
- {$_("common.stop_recording")}
939
- <img src={record_stop} alt={$_("common.stop_recording")} />
940
- </button>
941
- <div class="divider">·</div>
942
- {#if footer_links.includes("settings")}
943
- <div class="divider" class:hidden={!$is_screen_recording}>·</div>
944
- <button
945
- on:click={() => {
946
- set_settings_visible(!settings_visible);
947
- }}
948
- on:mouseenter={() => {
949
- loadSettings();
950
- }}
951
- class="settings"
952
- >
953
- {$_("common.settings")}
954
- <img src={settings_logo} alt={$_("common.settings")} />
955
- </button>
956
- {/if}
957
- </footer>
958
- {/if}
959
- </div>
960
-
961
- {#if api_recorder_visible && ApiRecorder}
962
- <!-- TODO: fix -->
963
- <!-- svelte-ignore a11y-click-events-have-key-events-->
964
- <!-- svelte-ignore a11y-no-static-element-interactions-->
965
- <div
966
- id="api-recorder-container"
967
- on:click={() => {
968
- set_api_docs_visible(true);
969
- api_recorder_visible = false;
970
- }}
971
- >
972
- <svelte:component this={ApiRecorder} {api_calls} {dependencies} />
473
+ <div class="divider">·</div>
474
+ {#if footer_links.includes("settings")}
475
+ <div class="divider" class:hidden={!$is_screen_recording}>·</div>
476
+ <button
477
+ on:click={() => {
478
+ set_settings_visible(!settings_visible);
479
+ }}
480
+ on:mouseenter={() => {
481
+ loadSettings();
482
+ }}
483
+ class="settings"
484
+ >
485
+ {$_("common.settings")}
486
+ <img src={settings_logo} alt={$_("common.settings")} />
487
+ </button>
488
+ {/if}
489
+ </footer>
490
+ {/if}
973
491
  </div>
974
- {/if}
975
-
976
- {#if api_docs_visible && $_layout && ApiDocs}
977
- <div class="api-docs">
492
+ {#if api_recorder_visible && ApiRecorder}
978
493
  <!-- TODO: fix -->
979
494
  <!-- svelte-ignore a11y-click-events-have-key-events-->
980
495
  <!-- svelte-ignore a11y-no-static-element-interactions-->
981
496
  <div
982
- class="backdrop"
497
+ id="api-recorder-container"
983
498
  on:click={() => {
984
- set_api_docs_visible(false);
499
+ set_api_docs_visible(true);
500
+ api_recorder_visible = false;
985
501
  }}
986
- />
987
- <div class="api-docs-wrap">
988
- <svelte:component
989
- this={ApiDocs}
990
- root_node={$_layout}
991
- on:close={(event) => {
502
+ >
503
+ <svelte:component this={ApiRecorder} {api_calls} {dependencies} />
504
+ </div>
505
+ {/if}
506
+
507
+ {#if api_docs_visible && app_tree.root && ApiDocs}
508
+ <div class="api-docs">
509
+ <!-- TODO: fix -->
510
+ <!-- svelte-ignore a11y-click-events-have-key-events-->
511
+ <!-- svelte-ignore a11y-no-static-element-interactions-->
512
+ <div
513
+ class="backdrop"
514
+ on:click={() => {
992
515
  set_api_docs_visible(false);
993
- api_calls = [];
994
- api_recorder_visible = api_recorder_visible =
995
- event.detail?.api_recorder_visible;
996
516
  }}
997
- {dependencies}
998
- {root}
999
- {app}
1000
- {space_id}
1001
- {api_calls}
1002
- {username}
1003
517
  />
518
+ <div class="api-docs-wrap">
519
+ <svelte:component
520
+ this={ApiDocs}
521
+ root_node={app_tree.root}
522
+ on:close={(event) => {
523
+ set_api_docs_visible(false);
524
+ api_calls = [];
525
+ api_recorder_visible = api_recorder_visible =
526
+ event.detail?.api_recorder_visible;
527
+ }}
528
+ {dependencies}
529
+ {root}
530
+ {app}
531
+ {space_id}
532
+ {api_calls}
533
+ {username}
534
+ {last_api_call}
535
+ />
536
+ </div>
1004
537
  </div>
1005
- </div>
1006
- {/if}
538
+ {/if}
1007
539
 
1008
- {#if settings_visible && $_layout && app.config && Settings}
1009
- <div class="api-docs">
1010
- <!-- TODO: fix -->
1011
- <!-- svelte-ignore a11y-click-events-have-key-events-->
1012
- <!-- svelte-ignore a11y-no-static-element-interactions-->
1013
- <div
1014
- class="backdrop"
1015
- on:click={() => {
1016
- set_settings_visible(false);
1017
- }}
1018
- />
1019
- <div class="api-docs-wrap">
1020
- <svelte:component
1021
- this={Settings}
1022
- bind:allow_zoom
1023
- bind:allow_video_trim
1024
- on:close={() => {
540
+ {#if settings_visible && app.config && app_tree.root && Settings}
541
+ <div class="api-docs">
542
+ <!-- TODO: fix -->
543
+ <!-- svelte-ignore a11y-click-events-have-key-events-->
544
+ <!-- svelte-ignore a11y-no-static-element-interactions-->
545
+ <div
546
+ class="backdrop"
547
+ on:click={() => {
1025
548
  set_settings_visible(false);
1026
549
  }}
1027
- on:start_recording={() => {
1028
- screen_recording();
1029
- }}
1030
- pwa_enabled={app.config.pwa}
1031
- {root}
1032
- {space_id}
1033
550
  />
551
+ <div class="api-docs-wrap">
552
+ <svelte:component
553
+ this={Settings}
554
+ bind:allow_zoom
555
+ bind:allow_video_trim
556
+ on:close={() => {
557
+ set_settings_visible(false);
558
+ }}
559
+ on:start_recording={() => {
560
+ screen_recording();
561
+ }}
562
+ pwa_enabled={app.config.pwa}
563
+ {root}
564
+ {space_id}
565
+ />
566
+ </div>
1034
567
  </div>
1035
- </div>
1036
- {/if}
568
+ {/if}
1037
569
 
1038
- {#if messages}
1039
- <Toast {messages} on:close={handle_error_close} />
1040
- {/if}
570
+ {#if vibe_mode && VibeEditor}
571
+ <svelte:component this={VibeEditor} {app} {root} />
572
+ {/if}
573
+ </div>
1041
574
 
1042
- {#if vibe_mode && VibeEditor}
1043
- <svelte:component this={VibeEditor} {app} {root} />
575
+ {#if messages}
576
+ <Toast {messages} on_close={handle_close} />
1044
577
  {/if}
1045
578
 
1046
579
  <style>