@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
package/src/Blocks.svelte CHANGED
@@ -1,131 +1,239 @@
1
1
  <script lang="ts">
2
- import { tick, onMount } from "svelte";
2
+ import { tick, onMount, setContext, settled, untrack } from "svelte";
3
3
  import { _ } from "svelte-i18n";
4
4
  import { Client } from "@gradio/client";
5
5
  import { writable } from "svelte/store";
6
6
 
7
- import type { LoadingStatus, LoadingStatusCollection } from "./stores";
7
+ // import type { LoadingStatus, LoadingStatusCollection } from "./stores";
8
8
 
9
- import type { ComponentMeta, Dependency, LayoutNode } from "./types";
10
- import type { UpdateTransaction } from "./init";
9
+ import type {
10
+ ComponentMeta,
11
+ Dependency as IDependency,
12
+ LayoutNode
13
+ } from "./types";
14
+ // import type { UpdateTransaction } from "./_init";
11
15
  import { setupi18n } from "./i18n";
12
16
  import type { ThemeMode, Payload } from "./types";
13
17
  import { Toast } from "@gradio/statustracker";
14
18
  import type { ToastMessage } from "@gradio/statustracker";
15
- import type { ShareData, ValueData } from "@gradio/utils";
19
+ import { type ShareData, type ValueData, GRADIO_ROOT } from "@gradio/utils";
20
+
16
21
  import MountComponents from "./MountComponents.svelte";
17
22
  import { prefix_css } from "./css";
23
+ import { reactive_formatter } from "./gradio_helper";
18
24
 
19
- import type ApiDocs from "./api_docs/ApiDocs.svelte";
20
- import type ApiRecorder from "./api_docs/ApiRecorder.svelte";
21
- import type Settings from "./api_docs/Settings.svelte";
22
- import type { ComponentType } from "svelte";
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";
23
29
 
24
30
  import logo from "./images/logo.svg";
25
31
  import api_logo from "./api_docs/img/api-logo.svg";
26
32
  import settings_logo from "./api_docs/img/settings-logo.svg";
27
33
  import record_stop from "./api_docs/img/record-stop.svg";
28
- import { create_components, AsyncFunction } from "./init";
29
- import type {
30
- LogMessage,
31
- RenderMessage,
32
- StatusMessage
33
- } from "@gradio/client";
34
+ import { AppTree } from "./init.svelte";
35
+ // import type {
36
+ // LogMessage,
37
+ // RenderMessage,
38
+ // StatusMessage,
39
+ // } from "@gradio/client";
34
40
  import * as screen_recorder from "./screen_recorder";
35
41
 
36
- export let root: string;
37
- export let components: ComponentMeta[];
38
- export let layout: LayoutNode;
39
- export let dependencies: Dependency[];
40
- export let title = "Gradio";
41
- export let target: HTMLElement;
42
- export let autoscroll: boolean;
43
- export let footer_links = ["gradio", "settings", "api"];
44
- export let control_page_title = false;
45
- export let app_mode: boolean;
46
- export let theme_mode: ThemeMode;
47
- export let app: Awaited<ReturnType<typeof Client.connect>>;
48
- export let space_id: string | null;
49
- export let version: string;
50
- export let js: string | null;
51
- export let fill_height = false;
52
- export let ready: boolean;
53
- export let username: string | null;
54
- export let api_prefix = "";
55
- export let max_file_size: number | undefined = undefined;
56
- export let initial_layout: ComponentMeta | undefined = undefined;
57
- export let css: string | null | undefined = null;
58
- export let vibe_mode = false;
59
- let broken_connection = false;
42
+ import { DependencyManager } from "./dependency";
60
43
 
61
44
  let {
62
- layout: _layout,
63
- targets,
64
- update_value,
65
- get_data,
66
- modify_stream,
67
- get_stream_state,
68
- set_time_limit,
69
- loading_status,
70
- scheduled_updates,
71
- create_layout,
72
- rerender_layout,
73
- value_change
74
- } = create_components({
75
- initial_layout
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
+ }
76
106
  });
77
107
 
78
- $: components, layout, dependencies, root, app, fill_height, target, run();
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
+ });
79
128
 
80
- $: {
81
- ready = !!$_layout;
82
- }
129
+ let messages: (ToastMessage & { fn_index: number })[] = $state([]);
83
130
 
84
- let old_dependencies = dependencies;
85
- $: if (
86
- dependencies !== old_dependencies &&
87
- render_complete &&
88
- !layout_creating
89
- ) {
90
- // re-run load triggers in SSR mode when page changes
91
- handle_load_triggers();
92
- old_dependencies = dependencies;
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
+ }
93
175
  }
94
176
 
95
- let vibe_editor_width = 350;
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
+ };
96
187
 
97
- async function run(): Promise<void> {
98
- await setupi18n(app.config?.i18n_translations || undefined);
99
-
100
- layout_creating = true;
101
- await create_layout({
102
- components,
103
- layout,
104
- dependencies,
105
- root: root + api_prefix,
106
- app,
107
- options: {
108
- fill_height
109
- }
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
+ );
110
216
  });
111
- layout_creating = false;
112
- }
217
+ });
218
+
219
+ let vibe_editor_width = 350;
113
220
 
114
- export let search_params: URLSearchParams;
115
- let api_docs_visible =
116
- search_params.get("view") === "api" && footer_links.includes("api");
117
- let settings_visible = search_params.get("view") === "settings";
118
- let api_recorder_visible =
119
- search_params.get("view") === "api-recorder" &&
120
- footer_links.includes("api");
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
+ );
121
229
  let allow_zoom = true;
122
230
  let allow_video_trim = true;
123
231
 
124
232
  // Lazy component loading state
125
- let ApiDocs: ComponentType<ApiDocs> | null = null;
126
- let ApiRecorder: ComponentType<ApiRecorder> | null = null;
127
- let Settings: ComponentType<Settings> | null = null;
128
- let VibeEditor: ComponentType | null = null;
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);
129
237
 
130
238
  async function loadApiDocs(): Promise<void> {
131
239
  if (!ApiDocs || !ApiRecorder) {
@@ -186,104 +294,19 @@
186
294
  settings_visible = !settings_visible;
187
295
  }
188
296
 
189
- let api_calls: Payload[] = [];
190
-
191
297
  let layout_creating = false;
192
- export let render_complete = false;
193
-
194
- async function handle_update(data: any, fn_index: number): Promise<void> {
195
- const dep = dependencies.find((dep) => dep.id === fn_index);
196
- const input_type = components.find(
197
- (comp) => comp.id === dep?.inputs[0]
198
- )?.type;
199
- if (allow_zoom && dep && input_type !== "dataset") {
200
- if (dep && dep.inputs && dep.inputs.length > 0 && $is_screen_recording) {
201
- screen_recorder.zoom(true, dep.inputs, 1.0);
202
- }
298
+ //
203
299
 
204
- if (
205
- dep &&
206
- dep.outputs &&
207
- dep.outputs.length > 0 &&
208
- $is_screen_recording
209
- ) {
210
- screen_recorder.zoom(false, dep.outputs, 2.0);
211
- }
212
- }
213
-
214
- if (!dep) {
215
- return;
216
- }
217
- const outputs = dep.outputs;
218
- const meta_updates = data?.map((value: any, i: number) => {
219
- return {
220
- id: outputs[i],
221
- prop: "value_is_output",
222
- value: true
223
- };
224
- });
225
-
226
- update_value(meta_updates);
227
-
228
- await tick();
229
-
230
- const updates: UpdateTransaction[] = [];
231
-
232
- data?.forEach((value: any, i: number) => {
233
- if (
234
- typeof value === "object" &&
235
- value !== null &&
236
- value.__type__ === "update"
237
- ) {
238
- for (const [update_key, update_value] of Object.entries(value)) {
239
- if (update_key === "__type__") {
240
- continue;
241
- } else {
242
- updates.push({
243
- id: outputs[i],
244
- prop: update_key,
245
- value: update_value
246
- });
247
- }
248
- }
249
- } else {
250
- updates.push({
251
- id: outputs[i],
252
- prop: "value",
253
- value
254
- });
255
- }
256
- });
257
- update_value(updates);
258
-
259
- // Handle navbar updates separately since they need to be updated in the store.
260
- updates.forEach((update) => {
261
- const component = components.find((comp) => comp.id === update.id);
262
- if (component && component.type === "navbar") {
263
- import("./navbar_store").then(({ navbar_config }) => {
264
- navbar_config.update((current) => ({
265
- ...current,
266
- [update.prop]: update.value
267
- }));
268
- });
269
- }
270
- });
271
-
272
- await tick();
273
- }
274
-
275
- let submit_map: Map<number, ReturnType<typeof app.submit>> = new Map();
276
-
277
- let messages: (ToastMessage & { fn_index: number })[] = [];
278
300
  function new_message(
279
301
  title: string,
280
302
  message: string,
281
303
  fn_index: number,
282
304
  type: ToastMessage["type"],
283
305
  duration: number | null = 10,
284
- visible = true
285
- ): ToastMessage & { fn_index: number } {
286
- return {
306
+ visible = false
307
+ ): void {
308
+ if (!visible) return;
309
+ messages.push({
287
310
  title,
288
311
  message,
289
312
  fn_index,
@@ -291,16 +314,10 @@
291
314
  id: ++_error_id,
292
315
  duration,
293
316
  visible
294
- };
317
+ });
295
318
  }
296
319
 
297
- export function add_new_message(
298
- title: string,
299
- message: string,
300
- type: ToastMessage["type"]
301
- ): void {
302
- messages = [new_message(title, message, -1, type), ...messages];
303
- }
320
+ add_new_message = new_message;
304
321
 
305
322
  let _error_id = -1;
306
323
 
@@ -324,742 +341,9 @@
324
341
  let inputs_waiting: number[] = [];
325
342
 
326
343
  // as state updates are not synchronous, we need to ensure updates are flushed before triggering any requests
327
- function wait_then_trigger_api_call(
328
- dep_index: number,
329
- trigger_id: number | null = null,
330
- event_data: unknown = null
331
- ): void {
332
- let _unsub = (): void => {};
333
- function unsub(): void {
334
- _unsub();
335
- }
336
- if ($scheduled_updates) {
337
- _unsub = scheduled_updates.subscribe((updating) => {
338
- if (!updating) {
339
- tick().then(() => {
340
- trigger_api_call(dep_index, trigger_id, event_data);
341
- unsub();
342
- });
343
- }
344
- });
345
- } else {
346
- trigger_api_call(dep_index, trigger_id, event_data);
347
- }
348
- }
349
-
350
- async function get_component_value_or_event_data(
351
- component_id: number,
352
- trigger_id: number | null,
353
- event_data: unknown
354
- ): Promise<any> {
355
- if (
356
- component_id === trigger_id &&
357
- event_data &&
358
- (event_data as ValueData).is_value_data === true
359
- ) {
360
- // @ts-ignore
361
- return event_data.value;
362
- }
363
- return get_data(component_id);
364
- }
365
-
366
- async function trigger_api_call(
367
- dep_index: number,
368
- trigger_id: number | null = null,
369
- event_data: unknown = null
370
- ): Promise<void> {
371
- const _dep = dependencies.find((dep) => dep.id === dep_index);
372
- if (_dep === undefined) {
373
- return;
374
- }
375
- const dep = _dep;
376
- if (inputs_waiting.length > 0) {
377
- for (const input of inputs_waiting) {
378
- if (dep.inputs.includes(input)) {
379
- add_new_message("Warning", WAITING_FOR_INPUTS_MESSAGE, "warning");
380
- return;
381
- }
382
- }
383
- }
384
- const current_status = loading_status.get_status_for_fn(dep_index);
385
- messages = messages.filter(({ fn_index }) => fn_index !== dep_index);
386
- if (current_status === "pending" || current_status === "generating") {
387
- dep.pending_request = true;
388
- }
389
-
390
- let payload: Payload = {
391
- fn_index: dep_index,
392
- data: await Promise.all(
393
- dep.inputs.map((id) =>
394
- get_component_value_or_event_data(id, trigger_id, event_data)
395
- )
396
- ),
397
- event_data: dep.collects_event_data ? event_data : null,
398
- trigger_id: trigger_id
399
- };
400
-
401
- if (dep.frontend_fn && typeof dep.frontend_fn !== "boolean") {
402
- dep
403
- .frontend_fn(
404
- payload.data.concat(
405
- await Promise.all(dep.outputs.map((id) => get_data(id)))
406
- )
407
- )
408
- .then((v: unknown[]) => {
409
- if (dep.backend_fn) {
410
- payload.data = v;
411
- trigger_prediction(dep, payload);
412
- } else {
413
- handle_update(v, dep_index);
414
- }
415
- });
416
- } else if (dep.types.cancel && dep.cancels) {
417
- await Promise.all(
418
- dep.cancels.map(async (fn_index) => {
419
- const submission = submit_map.get(fn_index);
420
- submission?.cancel();
421
- return submission;
422
- })
423
- );
424
- } else {
425
- if (dep.backend_fn) {
426
- if (dep.js_implementation) {
427
- let js_fn = new AsyncFunction(
428
- `let result = await (${dep.js_implementation})(...arguments);
429
- return (!Array.isArray(result)) ? [result] : result;`
430
- );
431
- js_fn(...payload.data)
432
- .then((js_result) => {
433
- handle_update(js_result, dep_index);
434
- payload.js_implementation = true;
435
- })
436
- .catch((error) => {
437
- console.error(error);
438
- payload.js_implementation = false;
439
- });
440
- }
441
- trigger_prediction(dep, payload);
442
- }
443
- }
444
-
445
- function trigger_prediction(dep: Dependency, payload: Payload): void {
446
- if (dep.trigger_mode === "once") {
447
- if (!dep.pending_request)
448
- make_prediction(payload, dep.connection == "stream");
449
- } else if (dep.trigger_mode === "multiple") {
450
- make_prediction(payload, dep.connection == "stream");
451
- } else if (dep.trigger_mode === "always_last") {
452
- if (!dep.pending_request) {
453
- make_prediction(payload, dep.connection == "stream");
454
- } else {
455
- dep.final_event = payload;
456
- }
457
- }
458
- }
459
-
460
- async function reconnect(): Promise<void> {
461
- const connection_status = await app.reconnect();
462
- if (connection_status === "broken") {
463
- setTimeout(reconnect, 1000);
464
- } else if (connection_status === "changed") {
465
- broken_connection = false;
466
- messages = [
467
- new_message(
468
- "Changed Connection",
469
- CHANGED_CONNECTION_MESSAGE,
470
- -1,
471
- "info",
472
- 3,
473
- true
474
- ),
475
- ...messages.map((m) =>
476
- m.message === LOST_CONNECTION_MESSAGE ? { ...m, visible: false } : m
477
- )
478
- ];
479
- } else if (connection_status === "connected") {
480
- broken_connection = false;
481
- messages = [
482
- new_message(
483
- "Reconnected",
484
- RECONNECTION_MESSAGE,
485
- -1,
486
- "success",
487
- null,
488
- true
489
- ),
490
- ...messages.map((m) =>
491
- m.message === LOST_CONNECTION_MESSAGE ? { ...m, visible: false } : m
492
- )
493
- ];
494
- }
495
- }
496
-
497
- async function make_prediction(
498
- payload: Payload,
499
- streaming = false
500
- ): Promise<void> {
501
- if (allow_video_trim) {
502
- screen_recorder.markRemoveSegmentStart();
503
- }
504
- if (api_recorder_visible) {
505
- api_calls = [...api_calls, JSON.parse(JSON.stringify(payload))];
506
- }
507
-
508
- let submission: ReturnType<typeof app.submit>;
509
- app.set_current_payload(payload);
510
- if (streaming) {
511
- if (!submit_map.has(dep_index)) {
512
- dep.inputs.forEach((id) => modify_stream(id, "waiting"));
513
- } else if (
514
- submit_map.has(dep_index) &&
515
- dep.inputs.some((id) => get_stream_state(id) === "waiting")
516
- ) {
517
- return;
518
- } else if (
519
- submit_map.has(dep_index) &&
520
- dep.inputs.some((id) => get_stream_state(id) === "open")
521
- ) {
522
- await app.post_data(
523
- // @ts-ignore
524
- `${app.config.root + app.config.api_prefix}/stream/${submit_map.get(dep_index).event_id()}`,
525
- { ...payload, session_hash: app.session_hash }
526
- );
527
- return;
528
- }
529
- }
530
- try {
531
- submission = app.submit(
532
- payload.fn_index,
533
- payload.data as unknown[],
534
- payload.event_data,
535
- payload.trigger_id
536
- );
537
- } catch (e) {
538
- const fn_index = 0; // Mock value for fn_index
539
- if (app.closed) return; // when a user navigates away in multipage app.
540
- messages = [
541
- new_message("Error", String(e), fn_index, "error"),
542
- ...messages
543
- ];
544
- loading_status.update({
545
- status: "error",
546
- fn_index,
547
- eta: 0,
548
- queue: false,
549
- queue_position: null
550
- });
551
- set_status($loading_status);
552
- return;
553
- }
554
-
555
- submit_map.set(dep_index, submission);
556
-
557
- for await (const message of submission) {
558
- if (payload.js_implementation) {
559
- return;
560
- }
561
- if (message.type === "data") {
562
- handle_data(message);
563
- } else if (message.type === "render") {
564
- handle_render(message);
565
- } else if (message.type === "status") {
566
- handle_status_update(message);
567
- } else if (message.type === "log") {
568
- handle_log(message);
569
- }
570
- }
571
-
572
- function handle_data(message: Payload): void {
573
- const { data, fn_index } = message;
574
- if (dep.pending_request && dep.final_event) {
575
- dep.pending_request = false;
576
- make_prediction(dep.final_event, dep.connection == "stream");
577
- }
578
- dep.pending_request = false;
579
- handle_update(data, fn_index);
580
- set_status($loading_status);
581
- }
582
-
583
- function handle_render(message: RenderMessage): void {
584
- const { data } = message;
585
- let _components: ComponentMeta[] = data.components;
586
- let render_layout: LayoutNode = data.layout;
587
- let _dependencies: Dependency[] = data.dependencies;
588
- let render_id = data.render_id;
589
-
590
- let deps_to_remove: number[] = [];
591
- dependencies.forEach((old_dep, i) => {
592
- if (old_dep.rendered_in === dep.render_id) {
593
- deps_to_remove.push(i);
594
- }
595
- });
596
- deps_to_remove.reverse().forEach((i) => {
597
- dependencies.splice(i, 1);
598
- });
599
- _dependencies.forEach((dep) => {
600
- dependencies.push(dep);
601
- });
602
-
603
- rerender_layout({
604
- components: _components,
605
- layout: render_layout,
606
- root: root + api_prefix,
607
- dependencies: dependencies,
608
- render_id: render_id
609
- });
610
- _dependencies.forEach((dep) => {
611
- if (dep.targets.some((dep) => dep[1] === "load")) {
612
- wait_then_trigger_api_call(dep.id);
613
- }
614
- });
615
- }
616
-
617
- function handle_log(msg: LogMessage): void {
618
- const { title, log, fn_index, level, duration, visible } = msg;
619
- messages = [
620
- new_message(title, log, fn_index, level, duration, visible),
621
- ...messages
622
- ];
623
- }
624
-
625
- function open_stream_events(
626
- status: StatusMessage,
627
- id: number,
628
- dep: Dependency
629
- ): void {
630
- if (
631
- status.original_msg === "process_starts" &&
632
- dep.connection === "stream"
633
- ) {
634
- modify_stream(id, "open");
635
- }
636
- }
637
-
638
- /* eslint-disable complexity */
639
- function handle_status_update(message: StatusMessage): void {
640
- if (message.code === "validation_error") {
641
- const dep = dependencies.find((dep) => dep.id === message.fn_index);
642
- if (
643
- dep === undefined ||
644
- message.message === undefined ||
645
- typeof message.message === "string"
646
- ) {
647
- return;
648
- }
649
-
650
- const validation_error_data: {
651
- id: number;
652
- prop: string;
653
- value: unknown;
654
- }[] = [];
655
-
656
- message.message.forEach((message, i) => {
657
- if (message.is_valid) {
658
- return;
659
- }
660
- validation_error_data.push({
661
- id: dep.inputs[i],
662
- prop: "validation_error",
663
- value: message.message
664
- });
665
-
666
- validation_error_data.push({
667
- id: dep.inputs[i],
668
- prop: "loading_status",
669
- value: { validation_error: message.message }
670
- });
671
- });
672
-
673
- if (validation_error_data.length > 0) {
674
- update_value(validation_error_data);
675
- loading_status.update({
676
- status: "complete",
677
- fn_index: message.fn_index,
678
- eta: 0,
679
- queue: false,
680
- queue_position: null
681
- });
682
- set_status($loading_status);
683
-
684
- return;
685
- }
686
- }
687
- if (message.broken && !broken_connection) {
688
- messages = [
689
- new_message(
690
- "Broken Connection",
691
- LOST_CONNECTION_MESSAGE,
692
- -1,
693
- "error",
694
- null,
695
- true
696
- ),
697
- ...messages
698
- ];
699
-
700
- broken_connection = true;
701
- setTimeout(reconnect, 1000);
702
- }
703
- if (message.session_not_found) {
704
- messages = [
705
- new_message(
706
- "Session Not Found",
707
- SESSION_NOT_FOUND_MESSAGE,
708
- -1,
709
- "error",
710
- null,
711
- true
712
- ),
713
- ...messages
714
- ];
715
- }
716
- const { fn_index, ...status } = message;
717
- if (status.stage === "streaming" && status.time_limit) {
718
- dep.inputs.forEach((id) => {
719
- set_time_limit(id, status.time_limit);
720
- });
721
- }
722
- dep.inputs.forEach((id) => {
723
- open_stream_events(message, id, dep);
724
- });
725
- //@ts-ignore
726
- loading_status.update({
727
- ...status,
728
- time_limit: status.time_limit,
729
- status: status.stage,
730
- progress: status.progress_data,
731
- fn_index
732
- });
733
- set_status($loading_status);
734
- if (
735
- !showed_duplicate_message &&
736
- space_id !== null &&
737
- status.position !== undefined &&
738
- status.position >= 2 &&
739
- status.eta !== undefined &&
740
- status.eta > SHOW_DUPLICATE_MESSAGE_ON_ETA
741
- ) {
742
- showed_duplicate_message = true;
743
- messages = [
744
- new_message("Warning", DUPLICATE_MESSAGE, fn_index, "warning"),
745
- ...messages
746
- ];
747
- }
748
- if (
749
- !showed_mobile_warning &&
750
- is_mobile_device &&
751
- status.eta !== undefined &&
752
- status.eta > SHOW_MOBILE_QUEUE_WARNING_ON_ETA
753
- ) {
754
- showed_mobile_warning = true;
755
- messages = [
756
- new_message("Warning", MOBILE_QUEUE_WARNING, fn_index, "warning"),
757
- ...messages
758
- ];
759
- }
760
-
761
- if (status.stage === "complete" || status.stage === "generating") {
762
- const deps_triggered_by_state: Set<Dependency> = new Set();
763
- status.changed_state_ids?.forEach((id) => {
764
- dependencies
765
- .filter((dep) => dep.targets.some(([_id, _]) => _id === id))
766
- .forEach((dep) => {
767
- deps_triggered_by_state.add(dep);
768
- });
769
- });
770
- deps_triggered_by_state.forEach((dep) => {
771
- wait_then_trigger_api_call(dep.id, payload.trigger_id);
772
- });
773
- }
774
- if (status.stage === "complete") {
775
- dependencies.forEach(async (dep) => {
776
- if (
777
- dep.trigger_after === fn_index &&
778
- !dep.trigger_only_on_failure
779
- ) {
780
- wait_then_trigger_api_call(dep.id, payload.trigger_id);
781
- }
782
- });
783
- dep.inputs.forEach((id) => {
784
- modify_stream(id, "closed");
785
- });
786
- submit_map.delete(dep_index);
787
- }
788
- if (
789
- status.stage === "error" &&
790
- !broken_connection &&
791
- !message.session_not_found
792
- ) {
793
- if (status.message && typeof status.message === "string") {
794
- const _message = status.message.replace(
795
- MESSAGE_QUOTE_RE,
796
- (_, b) => b
797
- );
798
- const _title = status.title ?? "Error";
799
- messages = [
800
- new_message(
801
- _title,
802
- _message,
803
- fn_index,
804
- "error",
805
- status.duration,
806
- status.visible
807
- ),
808
- ...messages
809
- ];
810
- }
811
- dependencies.map(async (dep) => {
812
- if (
813
- dep.trigger_after === fn_index &&
814
- (!dep.trigger_only_on_success || dep.trigger_only_on_failure)
815
- ) {
816
- wait_then_trigger_api_call(dep.id, payload.trigger_id);
817
- }
818
- });
819
- }
820
- }
821
- if (allow_video_trim) {
822
- screen_recorder.markRemoveSegmentEnd();
823
- }
824
- }
825
- }
826
- /* eslint-enable complexity */
827
-
828
- function trigger_share(title: string | undefined, description: string): void {
829
- if (space_id === null) {
830
- return;
831
- }
832
- const discussion_url = new URL(
833
- `https://huggingface.co/spaces/${space_id}/discussions/new`
834
- );
835
- if (title !== undefined && title.length > 0) {
836
- discussion_url.searchParams.set("title", title);
837
- }
838
- discussion_url.searchParams.set("description", description);
839
- window.open(discussion_url.toString(), "_blank");
840
- }
841
-
842
- function handle_error_close(e: Event & { detail: number }): void {
843
- const _id = e.detail;
844
- messages = messages.filter((m) => m.id !== _id);
845
- }
846
-
847
- const is_external_url = (link: string | null): boolean =>
848
- !!(link && new URL(link, location.href).origin !== location.origin);
849
-
850
- async function handle_mount(): Promise<void> {
851
- if (js) {
852
- let blocks_frontend_fn = new AsyncFunction(
853
- `let result = await (${js})();
854
- return (!Array.isArray(result)) ? [result] : result;`
855
- );
856
- await blocks_frontend_fn();
857
- }
858
-
859
- await tick();
860
-
861
- var a = target.getElementsByTagName("a");
862
-
863
- for (var i = 0; i < a.length; i++) {
864
- const _target = a[i].getAttribute("target");
865
- const _link = a[i].getAttribute("href");
866
-
867
- // only target anchor tags with external links
868
- if (is_external_url(_link) && _target !== "_blank")
869
- a[i].setAttribute("target", "_blank");
870
- }
871
- handle_load_triggers();
872
-
873
- if (!target || render_complete) return;
874
-
875
- target.addEventListener("prop_change", (e: Event) => {
876
- if (!isCustomEvent(e)) throw new Error("not a custom event");
877
- const { id, prop, value } = e.detail;
878
- if (prop === "value") {
879
- update_value([
880
- {
881
- id,
882
- prop: "loading_status",
883
- value: { validation_error: undefined }
884
- }
885
- ]);
886
- }
887
- update_value([{ id, prop, value }]);
888
- if (prop === "input_ready" && value === false) {
889
- inputs_waiting.push(id);
890
- }
891
- if (prop === "input_ready" && value === true) {
892
- inputs_waiting = inputs_waiting.filter((item) => item !== id);
893
- }
894
- });
895
- target.addEventListener("gradio", (e: Event) => {
896
- if (!isCustomEvent(e)) throw new Error("not a custom event");
897
-
898
- const { id, event, data } = e.detail;
899
-
900
- if (event === "share") {
901
- const { title, description } = data as ShareData;
902
- trigger_share(title, description);
903
- } else if (event === "error") {
904
- messages = [new_message("Error", data, -1, event), ...messages];
905
- } else if (event === "warning") {
906
- messages = [new_message("Warning", data, -1, event), ...messages];
907
- } else if (event === "info") {
908
- messages = [new_message("Info", data, -1, event), ...messages];
909
- } else if (event == "clear_status") {
910
- update_status(id, "complete", data);
911
- } else if (event == "close_stream") {
912
- const deps = $targets[id]?.[data];
913
- deps?.forEach((dep_id) => {
914
- if (submit_map.has(dep_id)) {
915
- // @ts-ignore
916
- const url = `${app.config.root + app.config.api_prefix}/stream/${submit_map.get(dep_id).event_id()}`;
917
- app.post_data(`${url}/close`, {});
918
- }
919
- });
920
- } else {
921
- const deps = $targets[id]?.[event];
922
-
923
- deps?.forEach((dep_id) => {
924
- requestAnimationFrame(() => {
925
- wait_then_trigger_api_call(dep_id, id, data);
926
- });
927
- });
928
- }
929
- });
930
-
931
- render_complete = true;
932
- }
933
-
934
- value_change((id, value) => {
935
- const deps = $targets[id]?.["change"];
936
-
937
- deps?.forEach((dep_id) => {
938
- requestAnimationFrame(() => {
939
- wait_then_trigger_api_call(dep_id, id, value);
940
- });
941
- });
942
- });
943
-
944
- const handle_load_triggers = (): void => {
945
- dependencies.forEach((dep) => {
946
- if (dep.targets.some((dep) => dep[1] === "load")) {
947
- wait_then_trigger_api_call(dep.id);
948
- }
949
- });
950
- };
951
-
952
- $: set_status($loading_status);
953
-
954
- function update_status(
955
- id: number,
956
- status: "error" | "complete" | "pending",
957
- data: LoadingStatus
958
- ): void {
959
- data.status = status;
960
- update_value([
961
- {
962
- id,
963
- prop: "loading_status",
964
- value: data
965
- }
966
- ]);
967
- }
968
-
969
- function set_status(statuses: LoadingStatusCollection): void {
970
- let updates: {
971
- id: number;
972
- prop: string;
973
- value: LoadingStatus;
974
- }[] = [];
975
- Object.entries(statuses).forEach(([id, loading_status]) => {
976
- if (app.closed && loading_status.status === "error") {
977
- // when a user navigates away in multipage app.
978
- return;
979
- }
980
- let dependency = dependencies.find(
981
- (dep) => dep.id == loading_status.fn_index
982
- );
983
- if (dependency === undefined) {
984
- return;
985
- }
986
- loading_status.scroll_to_output = dependency.scroll_to_output;
987
- loading_status.show_progress = dependency.show_progress;
988
- updates.push({
989
- id: parseInt(id),
990
- prop: "loading_status",
991
- value: loading_status
992
- });
993
- });
994
-
995
- const inputs_to_update = loading_status.get_inputs_to_update();
996
- const additional_updates = Array.from(inputs_to_update).map(
997
- ([id, pending_status]) => {
998
- return {
999
- id,
1000
- prop: "pending",
1001
- value: pending_status === "pending"
1002
- };
1003
- }
1004
- );
1005
-
1006
- update_value([...updates, ...additional_updates]);
1007
- }
1008
-
1009
- function isCustomEvent(event: Event): event is CustomEvent {
1010
- return "detail" in event;
1011
- }
1012
344
 
1013
345
  let is_screen_recording = writable(false);
1014
346
 
1015
- onMount(() => {
1016
- is_mobile_device =
1017
- /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
1018
- navigator.userAgent
1019
- );
1020
-
1021
- screen_recorder.initialize(
1022
- root,
1023
- (title, message, type) => {
1024
- add_new_message(title, message, type);
1025
- },
1026
- (isRecording) => {
1027
- $is_screen_recording = isRecording;
1028
- }
1029
- );
1030
-
1031
- const handleVibeEditorResize = (event: CustomEvent): void => {
1032
- vibe_editor_width = event.detail.width;
1033
- };
1034
-
1035
- window.addEventListener(
1036
- "vibeEditorResize",
1037
- handleVibeEditorResize as EventListener
1038
- );
1039
-
1040
- // Load components if they should be visible on initial page load
1041
- if (api_docs_visible) {
1042
- loadApiDocs();
1043
- }
1044
- if (api_recorder_visible) {
1045
- loadApiRecorder();
1046
- }
1047
- if (settings_visible) {
1048
- loadSettings();
1049
- }
1050
- if (vibe_mode) {
1051
- loadVibeEditor();
1052
- }
1053
- });
1054
-
1055
- function screen_recording(): void {
1056
- if ($is_screen_recording) {
1057
- screen_recorder.stopRecording();
1058
- } else {
1059
- screen_recorder.startRecording();
1060
- }
1061
- }
1062
-
1063
347
  let footer_height = 0;
1064
348
 
1065
349
  let root_container: HTMLElement;
@@ -1077,7 +361,20 @@
1077
361
  }
1078
362
  }
1079
363
 
364
+ function screen_recording(): void {
365
+ if ($is_screen_recording) {
366
+ screen_recorder.stopRecording();
367
+ } else {
368
+ screen_recorder.startRecording();
369
+ }
370
+ }
371
+
1080
372
  onMount(() => {
373
+ is_mobile_device =
374
+ /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
375
+ navigator.userAgent
376
+ );
377
+
1081
378
  if ("parentIFrame" in window) {
1082
379
  window.parentIFrame?.autoResize(false);
1083
380
  }
@@ -1090,14 +387,26 @@
1090
387
  subtree: true,
1091
388
  attributes: true
1092
389
  });
1093
-
1094
390
  res.observe(root_container);
1095
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
+
1096
401
  return () => {
1097
402
  mut.disconnect();
1098
403
  res.disconnect();
1099
404
  };
1100
405
  });
406
+
407
+ function handle_close(id: number): void {
408
+ messages = messages.filter((m) => m.id !== id);
409
+ }
1101
410
  </script>
1102
411
 
1103
412
  <svelte:head>
@@ -1116,167 +425,155 @@
1116
425
  bind:this={root_container}
1117
426
  style:margin-right={vibe_mode ? `${vibe_editor_width}px` : "0"}
1118
427
  >
1119
- {#if $_layout && app.config}
1120
- <MountComponents
1121
- rootNode={$_layout}
1122
- {root}
1123
- {target}
1124
- {theme_mode}
1125
- on:mount={handle_mount}
1126
- {version}
1127
- {autoscroll}
1128
- {max_file_size}
1129
- client={app}
1130
- />
1131
- {/if}
1132
- </div>
1133
-
1134
- {#if footer_links.length > 0}
1135
- <footer bind:clientHeight={footer_height}>
1136
- {#if footer_links.includes("api")}
428
+ <MountComponents node={app_tree.root} />
429
+
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}
1137
463
  <button
464
+ class:hidden={!$is_screen_recording}
1138
465
  on:click={() => {
1139
- set_api_docs_visible(!api_docs_visible);
1140
- }}
1141
- on:mouseenter={() => {
1142
- loadApiDocs();
1143
- loadApiRecorder();
466
+ screen_recording();
1144
467
  }}
1145
- class="show-api"
468
+ class="record"
1146
469
  >
1147
- {#if app.config?.mcp_server}
1148
- {$_("errors.use_via_api_or_mcp")}
1149
- {:else}
1150
- {$_("errors.use_via_api")}
1151
- {/if}
1152
- <img src={api_logo} alt={$_("common.logo")} />
470
+ {$_("common.stop_recording")}
471
+ <img src={record_stop} alt={$_("common.stop_recording")} />
1153
472
  </button>
1154
- {/if}
1155
- {#if footer_links.includes("gradio")}
1156
- <div class="divider show-api-divider">·</div>
1157
- <a
1158
- href="https://gradio.app"
1159
- class="built-with"
1160
- target="_blank"
1161
- rel="noreferrer"
1162
- >
1163
- {$_("common.built_with_gradio")}
1164
- <img src={logo} alt={$_("common.logo")} />
1165
- </a>
1166
- {/if}
1167
- <button
1168
- class:hidden={!$is_screen_recording}
1169
- on:click={() => {
1170
- screen_recording();
1171
- }}
1172
- class="record"
1173
- >
1174
- {$_("common.stop_recording")}
1175
- <img src={record_stop} alt={$_("common.stop_recording")} />
1176
- </button>
1177
- <div class="divider">·</div>
1178
- {#if footer_links.includes("settings")}
1179
- <div class="divider" class:hidden={!$is_screen_recording}>·</div>
1180
- <button
1181
- on:click={() => {
1182
- set_settings_visible(!settings_visible);
1183
- }}
1184
- on:mouseenter={() => {
1185
- loadSettings();
1186
- }}
1187
- class="settings"
1188
- >
1189
- {$_("common.settings")}
1190
- <img src={settings_logo} alt={$_("common.settings")} />
1191
- </button>
1192
- {/if}
1193
- </footer>
1194
- {/if}
1195
- </div>
1196
-
1197
- {#if api_recorder_visible && ApiRecorder}
1198
- <!-- TODO: fix -->
1199
- <!-- svelte-ignore a11y-click-events-have-key-events-->
1200
- <!-- svelte-ignore a11y-no-static-element-interactions-->
1201
- <div
1202
- id="api-recorder-container"
1203
- on:click={() => {
1204
- set_api_docs_visible(true);
1205
- api_recorder_visible = false;
1206
- }}
1207
- >
1208
- <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}
1209
491
  </div>
1210
- {/if}
1211
-
1212
- {#if api_docs_visible && $_layout && ApiDocs}
1213
- <div class="api-docs">
492
+ {#if api_recorder_visible && ApiRecorder}
1214
493
  <!-- TODO: fix -->
1215
494
  <!-- svelte-ignore a11y-click-events-have-key-events-->
1216
495
  <!-- svelte-ignore a11y-no-static-element-interactions-->
1217
496
  <div
1218
- class="backdrop"
497
+ id="api-recorder-container"
1219
498
  on:click={() => {
1220
- set_api_docs_visible(false);
499
+ set_api_docs_visible(true);
500
+ api_recorder_visible = false;
1221
501
  }}
1222
- />
1223
- <div class="api-docs-wrap">
1224
- <svelte:component
1225
- this={ApiDocs}
1226
- root_node={$_layout}
1227
- 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={() => {
1228
515
  set_api_docs_visible(false);
1229
- api_calls = [];
1230
- api_recorder_visible = api_recorder_visible =
1231
- event.detail?.api_recorder_visible;
1232
516
  }}
1233
- {dependencies}
1234
- {root}
1235
- {app}
1236
- {space_id}
1237
- {api_calls}
1238
- {username}
1239
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>
1240
537
  </div>
1241
- </div>
1242
- {/if}
538
+ {/if}
1243
539
 
1244
- {#if settings_visible && $_layout && app.config && Settings}
1245
- <div class="api-docs">
1246
- <!-- TODO: fix -->
1247
- <!-- svelte-ignore a11y-click-events-have-key-events-->
1248
- <!-- svelte-ignore a11y-no-static-element-interactions-->
1249
- <div
1250
- class="backdrop"
1251
- on:click={() => {
1252
- set_settings_visible(false);
1253
- }}
1254
- />
1255
- <div class="api-docs-wrap">
1256
- <svelte:component
1257
- this={Settings}
1258
- bind:allow_zoom
1259
- bind:allow_video_trim
1260
- 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={() => {
1261
548
  set_settings_visible(false);
1262
549
  }}
1263
- on:start_recording={() => {
1264
- screen_recording();
1265
- }}
1266
- pwa_enabled={app.config.pwa}
1267
- {root}
1268
- {space_id}
1269
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>
1270
567
  </div>
1271
- </div>
1272
- {/if}
568
+ {/if}
1273
569
 
1274
- {#if messages}
1275
- <Toast {messages} on:close={handle_error_close} />
1276
- {/if}
570
+ {#if vibe_mode && VibeEditor}
571
+ <svelte:component this={VibeEditor} {app} {root} />
572
+ {/if}
573
+ </div>
1277
574
 
1278
- {#if vibe_mode && VibeEditor}
1279
- <svelte:component this={VibeEditor} {app} {root} />
575
+ {#if messages}
576
+ <Toast {messages} on_close={handle_close} />
1280
577
  {/if}
1281
578
 
1282
579
  <style>