@gradio/core 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +201 -0
  3. package/blocks.ts +1 -0
  4. package/index.ts +4 -0
  5. package/login.ts +1 -0
  6. package/package.json +83 -0
  7. package/public/favicon.png +0 -0
  8. package/public/static/img/Bunny.obj +7474 -0
  9. package/public/static/img/Duck.glb +0 -0
  10. package/public/static/img/api-logo.svg +4 -0
  11. package/public/static/img/camera.svg +1 -0
  12. package/public/static/img/clear.svg +67 -0
  13. package/public/static/img/edit.svg +39 -0
  14. package/public/static/img/javascript.svg +16 -0
  15. package/public/static/img/logo.svg +19 -0
  16. package/public/static/img/logo_error.svg +134 -0
  17. package/public/static/img/python.svg +20 -0
  18. package/public/static/img/undo-solid.svg +1 -0
  19. package/src/Blocks.svelte +792 -0
  20. package/src/Embed.svelte +197 -0
  21. package/src/Login.stories.svelte +33 -0
  22. package/src/Login.svelte +111 -0
  23. package/src/MountComponents.svelte +30 -0
  24. package/src/Render.svelte +103 -0
  25. package/src/RenderComponent.svelte +67 -0
  26. package/src/api_docs/ApiBanner.svelte +118 -0
  27. package/src/api_docs/ApiDocs.svelte +418 -0
  28. package/src/api_docs/ApiRecorder.svelte +75 -0
  29. package/src/api_docs/CodeSnippet.svelte +198 -0
  30. package/src/api_docs/CopyButton.svelte +17 -0
  31. package/src/api_docs/EndpointDetail.svelte +37 -0
  32. package/src/api_docs/InputPayload.svelte +155 -0
  33. package/src/api_docs/InstallSnippet.svelte +59 -0
  34. package/src/api_docs/NoApi.svelte +74 -0
  35. package/src/api_docs/ParametersSnippet.svelte +106 -0
  36. package/src/api_docs/RecordingSnippet.svelte +224 -0
  37. package/src/api_docs/ResponseSnippet.svelte +97 -0
  38. package/src/api_docs/TryButton.svelte +19 -0
  39. package/src/api_docs/img/api-logo.svg +4 -0
  40. package/src/api_docs/img/bash.svg +8 -0
  41. package/src/api_docs/img/clear.svelte +19 -0
  42. package/src/api_docs/img/javascript.svg +16 -0
  43. package/src/api_docs/img/python.svg +20 -0
  44. package/src/api_docs/index.ts +2 -0
  45. package/src/api_docs/utils.ts +143 -0
  46. package/src/css.ts +116 -0
  47. package/src/gradio_helper.ts +7 -0
  48. package/src/i18n.test.ts +27 -0
  49. package/src/i18n.ts +36 -0
  50. package/src/images/lightning.svg +2 -0
  51. package/src/images/logo.svg +19 -0
  52. package/src/images/play.svg +2 -0
  53. package/src/images/spaces.svg +7 -0
  54. package/src/init.test.ts +521 -0
  55. package/src/init.ts +590 -0
  56. package/src/lang/BCP47_codes.js +58 -0
  57. package/src/lang/README.md +11 -0
  58. package/src/lang/ar.json +16 -0
  59. package/src/lang/ca.json +19 -0
  60. package/src/lang/ckb.json +108 -0
  61. package/src/lang/de.json +16 -0
  62. package/src/lang/en.json +118 -0
  63. package/src/lang/es.json +17 -0
  64. package/src/lang/eu.json +16 -0
  65. package/src/lang/fa.json +16 -0
  66. package/src/lang/fr.json +30 -0
  67. package/src/lang/he.json +16 -0
  68. package/src/lang/hi.json +16 -0
  69. package/src/lang/ja.json +16 -0
  70. package/src/lang/ko.json +16 -0
  71. package/src/lang/lt.json +16 -0
  72. package/src/lang/nl.json +16 -0
  73. package/src/lang/pl.json +16 -0
  74. package/src/lang/pt-BR.json +19 -0
  75. package/src/lang/ru.json +118 -0
  76. package/src/lang/ta.json +16 -0
  77. package/src/lang/tr.json +16 -0
  78. package/src/lang/uk.json +16 -0
  79. package/src/lang/ur.json +16 -0
  80. package/src/lang/uz.json +15 -0
  81. package/src/lang/zh-CN.json +115 -0
  82. package/src/lang/zh-TW.json +16 -0
  83. package/src/s-blocks.ts +1 -0
  84. package/src/s-login.ts +1 -0
  85. package/src/stores.ts +165 -0
  86. package/src/types.ts +106 -0
  87. package/src/vite-env-override.d.ts +20 -0
@@ -0,0 +1,792 @@
1
+ <script lang="ts">
2
+ import { tick } from "svelte";
3
+ import { _ } from "svelte-i18n";
4
+ import { Client } from "@gradio/client";
5
+
6
+ import type { LoadingStatus, LoadingStatusCollection } from "./stores";
7
+
8
+ import type { ComponentMeta, Dependency, LayoutNode } from "./types";
9
+ import type { UpdateTransaction } from "./init";
10
+ import { setupi18n } from "./i18n";
11
+ import { ApiDocs, ApiRecorder } from "./api_docs/";
12
+ import type { ThemeMode, Payload } from "./types";
13
+ import { Toast } from "@gradio/statustracker";
14
+ import type { ToastMessage } from "@gradio/statustracker";
15
+ import type { ShareData } from "@gradio/utils";
16
+ import MountComponents from "./MountComponents.svelte";
17
+
18
+ import logo from "./images/logo.svg";
19
+ import api_logo from "./api_docs/img/api-logo.svg";
20
+ import { create_components, AsyncFunction } from "./init";
21
+ import type {
22
+ LogMessage,
23
+ RenderMessage,
24
+ StatusMessage
25
+ } from "@gradio/client";
26
+
27
+ setupi18n();
28
+
29
+ export let root: string;
30
+ export let components: ComponentMeta[];
31
+ export let layout: LayoutNode;
32
+ export let dependencies: Dependency[];
33
+ export let title = "Gradio";
34
+ export let target: HTMLElement;
35
+ export let autoscroll: boolean;
36
+ export let show_api = true;
37
+ export let show_footer = true;
38
+ export let control_page_title = false;
39
+ export let app_mode: boolean;
40
+ export let theme_mode: ThemeMode;
41
+ export let app: Awaited<ReturnType<typeof Client.connect>>;
42
+ export let space_id: string | null;
43
+ export let version: string;
44
+ export let js: string | null;
45
+ export let fill_height = false;
46
+ export let ready: boolean;
47
+ export let username: string | null;
48
+
49
+ const {
50
+ layout: _layout,
51
+ targets,
52
+ update_value,
53
+ get_data,
54
+ loading_status,
55
+ scheduled_updates,
56
+ create_layout,
57
+ rerender_layout
58
+ } = create_components();
59
+
60
+ $: create_layout({
61
+ components,
62
+ layout,
63
+ dependencies,
64
+ root,
65
+ app,
66
+ options: {
67
+ fill_height
68
+ }
69
+ });
70
+
71
+ $: {
72
+ ready = !!$_layout;
73
+ }
74
+
75
+ let params = new URLSearchParams(window.location.search);
76
+ let api_docs_visible = params.get("view") === "api" && show_api;
77
+ let api_recorder_visible = params.get("view") === "api-recorder" && show_api;
78
+ function set_api_docs_visible(visible: boolean): void {
79
+ api_recorder_visible = false;
80
+ api_docs_visible = visible;
81
+ let params = new URLSearchParams(window.location.search);
82
+ if (visible) {
83
+ params.set("view", "api");
84
+ } else {
85
+ params.delete("view");
86
+ }
87
+ history.replaceState(null, "", "?" + params.toString());
88
+ }
89
+ let api_calls: Payload[] = [];
90
+
91
+ export let render_complete = false;
92
+ async function handle_update(data: any, fn_index: number): Promise<void> {
93
+ const outputs = dependencies.find((dep) => dep.id == fn_index)!.outputs;
94
+
95
+ const meta_updates = data?.map((value: any, i: number) => {
96
+ return {
97
+ id: outputs[i],
98
+ prop: "value_is_output",
99
+ value: true
100
+ };
101
+ });
102
+
103
+ update_value(meta_updates);
104
+
105
+ await tick();
106
+
107
+ const updates: UpdateTransaction[] = [];
108
+
109
+ data?.forEach((value: any, i: number) => {
110
+ if (
111
+ typeof value === "object" &&
112
+ value !== null &&
113
+ value.__type__ === "update"
114
+ ) {
115
+ for (const [update_key, update_value] of Object.entries(value)) {
116
+ if (update_key === "__type__") {
117
+ continue;
118
+ } else {
119
+ updates.push({
120
+ id: outputs[i],
121
+ prop: update_key,
122
+ value: update_value
123
+ });
124
+ }
125
+ }
126
+ } else {
127
+ updates.push({
128
+ id: outputs[i],
129
+ prop: "value",
130
+ value
131
+ });
132
+ }
133
+ });
134
+ update_value(updates);
135
+
136
+ await tick();
137
+ }
138
+
139
+ let submit_map: Map<number, ReturnType<typeof app.submit>> = new Map();
140
+
141
+ let messages: (ToastMessage & { fn_index: number })[] = [];
142
+ function new_message(
143
+ message: string,
144
+ fn_index: number,
145
+ type: ToastMessage["type"],
146
+ duration: number | null = 10,
147
+ visible = true
148
+ ): ToastMessage & { fn_index: number } {
149
+ return {
150
+ message,
151
+ fn_index,
152
+ type,
153
+ id: ++_error_id,
154
+ duration,
155
+ visible
156
+ };
157
+ }
158
+
159
+ export function add_new_message(
160
+ message: string,
161
+ type: ToastMessage["type"]
162
+ ): void {
163
+ messages = [new_message(message, -1, type), ...messages];
164
+ }
165
+
166
+ let _error_id = -1;
167
+
168
+ let user_left_page = false;
169
+ document.addEventListener("visibilitychange", function () {
170
+ if (document.visibilityState === "hidden") {
171
+ user_left_page = true;
172
+ }
173
+ });
174
+
175
+ const MESSAGE_QUOTE_RE = /^'([^]+)'$/;
176
+
177
+ const DUPLICATE_MESSAGE = $_("blocks.long_requests_queue");
178
+ const MOBILE_QUEUE_WARNING = $_("blocks.connection_can_break");
179
+ const MOBILE_RECONNECT_MESSAGE = $_("blocks.lost_connection");
180
+ const SHOW_DUPLICATE_MESSAGE_ON_ETA = 15;
181
+ const SHOW_MOBILE_QUEUE_WARNING_ON_ETA = 10;
182
+ const is_mobile_device =
183
+ /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
184
+ navigator.userAgent
185
+ );
186
+ let showed_duplicate_message = false;
187
+ let showed_mobile_warning = false;
188
+
189
+ // as state updates are not synchronous, we need to ensure updates are flushed before triggering any requests
190
+ function wait_then_trigger_api_call(
191
+ dep_index: number,
192
+ trigger_id: number | null = null,
193
+ event_data: unknown = null
194
+ ): void {
195
+ let _unsub = (): void => {};
196
+ function unsub(): void {
197
+ _unsub();
198
+ }
199
+ if ($scheduled_updates) {
200
+ _unsub = scheduled_updates.subscribe((updating) => {
201
+ if (!updating) {
202
+ trigger_api_call(dep_index, trigger_id, event_data);
203
+ unsub();
204
+ }
205
+ });
206
+ } else {
207
+ trigger_api_call(dep_index, trigger_id, event_data);
208
+ }
209
+ }
210
+
211
+ async function trigger_api_call(
212
+ dep_index: number,
213
+ trigger_id: number | null = null,
214
+ event_data: unknown = null
215
+ ): Promise<void> {
216
+ let dep = dependencies.find((dep) => dep.id === dep_index)!;
217
+
218
+ const current_status = loading_status.get_status_for_fn(dep_index);
219
+ messages = messages.filter(({ fn_index }) => fn_index !== dep_index);
220
+ if (current_status === "pending" || current_status === "generating") {
221
+ dep.pending_request = true;
222
+ }
223
+
224
+ let payload: Payload = {
225
+ fn_index: dep_index,
226
+ data: await Promise.all(dep.inputs.map((id) => get_data(id))),
227
+ event_data: dep.collects_event_data ? event_data : null,
228
+ trigger_id: trigger_id
229
+ };
230
+
231
+ if (dep.frontend_fn) {
232
+ dep
233
+ .frontend_fn(
234
+ payload.data.concat(
235
+ await Promise.all(dep.outputs.map((id) => get_data(id)))
236
+ )
237
+ )
238
+ .then((v: unknown[]) => {
239
+ if (dep.backend_fn) {
240
+ payload.data = v;
241
+ make_prediction(payload);
242
+ } else {
243
+ handle_update(v, dep_index);
244
+ }
245
+ });
246
+ } else if (dep.types.cancel && dep.cancels) {
247
+ await Promise.all(
248
+ dep.cancels.map(async (fn_index) => {
249
+ const submission = submit_map.get(fn_index);
250
+ submission?.cancel();
251
+ return submission;
252
+ })
253
+ );
254
+ } else {
255
+ if (dep.backend_fn) {
256
+ if (dep.trigger_mode === "once") {
257
+ if (!dep.pending_request) make_prediction(payload);
258
+ } else if (dep.trigger_mode === "multiple") {
259
+ make_prediction(payload);
260
+ } else if (dep.trigger_mode === "always_last") {
261
+ if (!dep.pending_request) {
262
+ make_prediction(payload);
263
+ } else {
264
+ dep.final_event = payload;
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ async function make_prediction(payload: Payload): Promise<void> {
271
+ if (api_recorder_visible) {
272
+ api_calls = [...api_calls, JSON.parse(JSON.stringify(payload))];
273
+ }
274
+
275
+ let submission: ReturnType<typeof app.submit>;
276
+ try {
277
+ submission = app.submit(
278
+ payload.fn_index,
279
+ payload.data as unknown[],
280
+ payload.event_data,
281
+ payload.trigger_id
282
+ );
283
+ } catch (e) {
284
+ const fn_index = 0; // Mock value for fn_index
285
+ messages = [new_message(String(e), fn_index, "error"), ...messages];
286
+ loading_status.update({
287
+ status: "error",
288
+ fn_index,
289
+ eta: 0,
290
+ queue: false,
291
+ queue_position: null
292
+ });
293
+ set_status($loading_status);
294
+ return;
295
+ }
296
+
297
+ submit_map.set(dep_index, submission);
298
+
299
+ for await (const message of submission) {
300
+ if (message.type === "data") {
301
+ handle_data(message);
302
+ } else if (message.type === "render") {
303
+ handle_render(message);
304
+ } else if (message.type === "status") {
305
+ handle_status_update(message);
306
+ } else if (message.type === "log") {
307
+ handle_log(message);
308
+ }
309
+ }
310
+
311
+ function handle_data(message: Payload): void {
312
+ const { data, fn_index } = message;
313
+ if (dep.pending_request && dep.final_event) {
314
+ dep.pending_request = false;
315
+ make_prediction(dep.final_event);
316
+ }
317
+ dep.pending_request = false;
318
+ handle_update(data, fn_index);
319
+ set_status($loading_status);
320
+ }
321
+
322
+ function handle_render(message: RenderMessage): void {
323
+ const { data } = message;
324
+ let _components: ComponentMeta[] = data.components;
325
+ let render_layout: LayoutNode = data.layout;
326
+ let _dependencies: Dependency[] = data.dependencies;
327
+ let render_id = data.render_id;
328
+
329
+ let deps_to_remove: number[] = [];
330
+ dependencies.forEach((dep, i) => {
331
+ if (dep.rendered_in === render_id) {
332
+ deps_to_remove.push(i);
333
+ }
334
+ });
335
+ deps_to_remove.reverse().forEach((i) => {
336
+ dependencies.splice(i, 1);
337
+ });
338
+ _dependencies.forEach((dep) => {
339
+ dependencies.push(dep);
340
+ });
341
+
342
+ rerender_layout({
343
+ components: _components,
344
+ layout: render_layout,
345
+ root: root,
346
+ dependencies: dependencies,
347
+ render_id: render_id
348
+ });
349
+ }
350
+
351
+ function handle_log(msg: LogMessage): void {
352
+ const { log, fn_index, level, duration, visible } = msg;
353
+ messages = [
354
+ new_message(log, fn_index, level, duration, visible),
355
+ ...messages
356
+ ];
357
+ }
358
+
359
+ function handle_status_update(message: StatusMessage): void {
360
+ const { fn_index, ...status } = message;
361
+ //@ts-ignore
362
+ loading_status.update({
363
+ ...status,
364
+ status: status.stage,
365
+ progress: status.progress_data,
366
+ fn_index
367
+ });
368
+ set_status($loading_status);
369
+ if (
370
+ !showed_duplicate_message &&
371
+ space_id !== null &&
372
+ status.position !== undefined &&
373
+ status.position >= 2 &&
374
+ status.eta !== undefined &&
375
+ status.eta > SHOW_DUPLICATE_MESSAGE_ON_ETA
376
+ ) {
377
+ showed_duplicate_message = true;
378
+ messages = [
379
+ new_message(DUPLICATE_MESSAGE, fn_index, "warning"),
380
+ ...messages
381
+ ];
382
+ }
383
+ if (
384
+ !showed_mobile_warning &&
385
+ is_mobile_device &&
386
+ status.eta !== undefined &&
387
+ status.eta > SHOW_MOBILE_QUEUE_WARNING_ON_ETA
388
+ ) {
389
+ showed_mobile_warning = true;
390
+ messages = [
391
+ new_message(MOBILE_QUEUE_WARNING, fn_index, "warning"),
392
+ ...messages
393
+ ];
394
+ }
395
+
396
+ if (status.stage === "complete") {
397
+ status.changed_state_ids?.forEach((id) => {
398
+ dependencies
399
+ .filter((dep) => dep.targets.some(([_id, _]) => _id === id))
400
+ .forEach((dep) => {
401
+ wait_then_trigger_api_call(dep.id, payload.trigger_id);
402
+ });
403
+ });
404
+ dependencies.forEach(async (dep) => {
405
+ if (dep.trigger_after === fn_index) {
406
+ wait_then_trigger_api_call(dep.id, payload.trigger_id);
407
+ }
408
+ });
409
+
410
+ // submission.destroy();
411
+ }
412
+ if (status.broken && is_mobile_device && user_left_page) {
413
+ window.setTimeout(() => {
414
+ messages = [
415
+ new_message(MOBILE_RECONNECT_MESSAGE, fn_index, "error"),
416
+ ...messages
417
+ ];
418
+ }, 0);
419
+ wait_then_trigger_api_call(dep.id, payload.trigger_id, event_data);
420
+ user_left_page = false;
421
+ } else if (status.stage === "error") {
422
+ if (status.message) {
423
+ const _message = status.message.replace(
424
+ MESSAGE_QUOTE_RE,
425
+ (_, b) => b
426
+ );
427
+ messages = [
428
+ new_message(
429
+ _message,
430
+ fn_index,
431
+ "error",
432
+ status.duration,
433
+ status.visible
434
+ ),
435
+ ...messages
436
+ ];
437
+ }
438
+ dependencies.map(async (dep) => {
439
+ if (
440
+ dep.trigger_after === fn_index &&
441
+ !dep.trigger_only_on_success
442
+ ) {
443
+ wait_then_trigger_api_call(dep.id, payload.trigger_id);
444
+ }
445
+ });
446
+ }
447
+ }
448
+ }
449
+ }
450
+
451
+ function trigger_share(title: string | undefined, description: string): void {
452
+ if (space_id === null) {
453
+ return;
454
+ }
455
+ const discussion_url = new URL(
456
+ `https://huggingface.co/spaces/${space_id}/discussions/new`
457
+ );
458
+ if (title !== undefined && title.length > 0) {
459
+ discussion_url.searchParams.set("title", title);
460
+ }
461
+ discussion_url.searchParams.set("description", description);
462
+ window.open(discussion_url.toString(), "_blank");
463
+ }
464
+
465
+ function handle_error_close(e: Event & { detail: number }): void {
466
+ const _id = e.detail;
467
+ messages = messages.filter((m) => m.id !== _id);
468
+ }
469
+
470
+ const is_external_url = (link: string | null): boolean =>
471
+ !!(link && new URL(link, location.href).origin !== location.origin);
472
+
473
+ async function handle_mount(): Promise<void> {
474
+ if (js) {
475
+ let blocks_frontend_fn = new AsyncFunction(
476
+ `let result = await (${js})();
477
+ return (!Array.isArray(result)) ? [result] : result;`
478
+ );
479
+ await blocks_frontend_fn();
480
+ }
481
+
482
+ await tick();
483
+
484
+ var a = target.getElementsByTagName("a");
485
+
486
+ for (var i = 0; i < a.length; i++) {
487
+ const _target = a[i].getAttribute("target");
488
+ const _link = a[i].getAttribute("href");
489
+
490
+ // only target anchor tags with external links
491
+ if (is_external_url(_link) && _target !== "_blank")
492
+ a[i].setAttribute("target", "_blank");
493
+ }
494
+
495
+ // handle load triggers
496
+ dependencies.forEach((dep) => {
497
+ if (dep.targets.some((dep) => dep[1] === "load")) {
498
+ wait_then_trigger_api_call(dep.id);
499
+ }
500
+ });
501
+
502
+ if (render_complete) return;
503
+
504
+ target.addEventListener("prop_change", (e: Event) => {
505
+ if (!isCustomEvent(e)) throw new Error("not a custom event");
506
+ const { id, prop, value } = e.detail;
507
+ update_value([{ id, prop, value }]);
508
+ });
509
+ target.addEventListener("gradio", (e: Event) => {
510
+ if (!isCustomEvent(e)) throw new Error("not a custom event");
511
+
512
+ const { id, event, data } = e.detail;
513
+
514
+ if (event === "share") {
515
+ const { title, description } = data as ShareData;
516
+ trigger_share(title, description);
517
+ } else if (event === "error" || event === "warning") {
518
+ messages = [new_message(data, -1, event), ...messages];
519
+ } else if (event == "clear_status") {
520
+ update_status(id, "complete", data);
521
+ } else {
522
+ const deps = $targets[id]?.[event];
523
+
524
+ deps?.forEach((dep_id) => {
525
+ requestAnimationFrame(() => {
526
+ wait_then_trigger_api_call(dep_id, id, data);
527
+ });
528
+ });
529
+ }
530
+ });
531
+
532
+ render_complete = true;
533
+ }
534
+
535
+ $: set_status($loading_status);
536
+
537
+ function update_status(
538
+ id: number,
539
+ status: "error" | "complete" | "pending",
540
+ data: LoadingStatus
541
+ ): void {
542
+ data.status = status;
543
+ update_value([
544
+ {
545
+ id,
546
+ prop: "loading_status",
547
+ value: data
548
+ }
549
+ ]);
550
+ }
551
+
552
+ function set_status(statuses: LoadingStatusCollection): void {
553
+ let updates: {
554
+ id: number;
555
+ prop: string;
556
+ value: LoadingStatus;
557
+ }[] = [];
558
+ Object.entries(statuses).forEach(([id, loading_status]) => {
559
+ let dependency = dependencies.find(
560
+ (dep) => dep.id == loading_status.fn_index
561
+ );
562
+ if (dependency === undefined) {
563
+ return;
564
+ }
565
+ loading_status.scroll_to_output = dependency.scroll_to_output;
566
+ loading_status.show_progress = dependency.show_progress;
567
+ updates.push({
568
+ id: parseInt(id),
569
+ prop: "loading_status",
570
+ value: loading_status
571
+ });
572
+ });
573
+
574
+ const inputs_to_update = loading_status.get_inputs_to_update();
575
+ const additional_updates = Array.from(inputs_to_update).map(
576
+ ([id, pending_status]) => {
577
+ return {
578
+ id,
579
+ prop: "pending",
580
+ value: pending_status === "pending"
581
+ };
582
+ }
583
+ );
584
+
585
+ update_value([...updates, ...additional_updates]);
586
+ }
587
+
588
+ function isCustomEvent(event: Event): event is CustomEvent {
589
+ return "detail" in event;
590
+ }
591
+ </script>
592
+
593
+ <svelte:head>
594
+ {#if control_page_title}
595
+ <title>{title}</title>
596
+ {/if}
597
+ </svelte:head>
598
+
599
+ <div class="wrap" style:min-height={app_mode ? "100%" : "auto"}>
600
+ <div class="contain" style:flex-grow={app_mode ? "1" : "auto"}>
601
+ {#if $_layout && app.config}
602
+ <MountComponents
603
+ rootNode={$_layout}
604
+ {root}
605
+ {target}
606
+ {theme_mode}
607
+ on:mount={handle_mount}
608
+ {version}
609
+ {autoscroll}
610
+ max_file_size={app.config.max_file_size}
611
+ client={app}
612
+ />
613
+ {/if}
614
+ </div>
615
+
616
+ {#if show_footer}
617
+ <footer>
618
+ {#if show_api}
619
+ <button
620
+ on:click={() => {
621
+ set_api_docs_visible(!api_docs_visible);
622
+ }}
623
+ class="show-api"
624
+ >
625
+ {$_("errors.use_via_api")}
626
+ <img src={api_logo} alt={$_("common.logo")} />
627
+ </button>
628
+ <div>·</div>
629
+ {/if}
630
+ <a
631
+ href="https://gradio.app"
632
+ class="built-with"
633
+ target="_blank"
634
+ rel="noreferrer"
635
+ >
636
+ {$_("common.built_with_gradio")}
637
+ <img src={logo} alt={$_("common.logo")} />
638
+ </a>
639
+ </footer>
640
+ {/if}
641
+ </div>
642
+
643
+ {#if api_recorder_visible}
644
+ <!-- TODO: fix -->
645
+ <!-- svelte-ignore a11y-click-events-have-key-events-->
646
+ <!-- svelte-ignore a11y-no-static-element-interactions-->
647
+ <div
648
+ id="api-recorder-container"
649
+ on:click={() => {
650
+ set_api_docs_visible(true);
651
+ api_recorder_visible = false;
652
+ }}
653
+ >
654
+ <ApiRecorder {api_calls} {dependencies} />
655
+ </div>
656
+ {/if}
657
+
658
+ {#if api_docs_visible && $_layout}
659
+ <div class="api-docs">
660
+ <!-- TODO: fix -->
661
+ <!-- svelte-ignore a11y-click-events-have-key-events-->
662
+ <!-- svelte-ignore a11y-no-static-element-interactions-->
663
+ <div
664
+ class="backdrop"
665
+ on:click={() => {
666
+ set_api_docs_visible(false);
667
+ }}
668
+ />
669
+ <div class="api-docs-wrap">
670
+ <ApiDocs
671
+ root_node={$_layout}
672
+ on:close={(event) => {
673
+ set_api_docs_visible(false);
674
+ api_calls = [];
675
+ api_recorder_visible = event.detail.api_recorder_visible;
676
+ }}
677
+ {dependencies}
678
+ {root}
679
+ {app}
680
+ {space_id}
681
+ {api_calls}
682
+ {username}
683
+ />
684
+ </div>
685
+ </div>
686
+ {/if}
687
+
688
+ {#if messages}
689
+ <Toast {messages} on:close={handle_error_close} />
690
+ {/if}
691
+
692
+ <style>
693
+ .wrap {
694
+ display: flex;
695
+ flex-grow: 1;
696
+ flex-direction: column;
697
+ width: var(--size-full);
698
+ font-weight: var(--body-text-weight);
699
+ font-size: var(--body-text-size);
700
+ }
701
+
702
+ .contain {
703
+ display: flex;
704
+ flex-direction: column;
705
+ }
706
+
707
+ footer {
708
+ display: flex;
709
+ justify-content: center;
710
+ margin-top: var(--size-4);
711
+ color: var(--body-text-color-subdued);
712
+ }
713
+
714
+ footer > * + * {
715
+ margin-left: var(--size-2);
716
+ }
717
+
718
+ .show-api {
719
+ display: flex;
720
+ align-items: center;
721
+ }
722
+ .show-api:hover {
723
+ color: var(--body-text-color);
724
+ }
725
+
726
+ .show-api img {
727
+ margin-right: var(--size-1);
728
+ margin-left: var(--size-2);
729
+ width: var(--size-3);
730
+ }
731
+
732
+ .built-with {
733
+ display: flex;
734
+ align-items: center;
735
+ }
736
+
737
+ .built-with:hover {
738
+ color: var(--body-text-color);
739
+ }
740
+
741
+ .built-with img {
742
+ margin-right: var(--size-1);
743
+ margin-left: var(--size-1);
744
+ margin-bottom: 1px;
745
+ width: var(--size-4);
746
+ }
747
+
748
+ .api-docs {
749
+ display: flex;
750
+ position: fixed;
751
+ top: 0;
752
+ right: 0;
753
+ z-index: var(--layer-top);
754
+ background: rgba(0, 0, 0, 0.5);
755
+ width: var(--size-screen);
756
+ height: var(--size-screen-h);
757
+ }
758
+
759
+ .backdrop {
760
+ flex: 1 1 0%;
761
+ -webkit-backdrop-filter: blur(4px);
762
+ backdrop-filter: blur(4px);
763
+ }
764
+
765
+ .api-docs-wrap {
766
+ box-shadow: var(--shadow-drop-lg);
767
+ background: var(--background-fill-primary);
768
+ overflow-x: hidden;
769
+ overflow-y: auto;
770
+ }
771
+
772
+ @media (--screen-md) {
773
+ .api-docs-wrap {
774
+ border-top-left-radius: var(--radius-lg);
775
+ border-bottom-left-radius: var(--radius-lg);
776
+ width: 950px;
777
+ }
778
+ }
779
+
780
+ @media (--screen-xxl) {
781
+ .api-docs-wrap {
782
+ width: 1150px;
783
+ }
784
+ }
785
+
786
+ #api-recorder-container {
787
+ position: fixed;
788
+ left: 10px;
789
+ bottom: 10px;
790
+ z-index: 1000;
791
+ }
792
+ </style>