| <script context="module" lang="ts"> | |
| import { writable } from "svelte/store"; | |
| import { mount_css } from "@gradio/core"; | |
| import type { Client as ClientType } from "@gradio/client"; | |
| import type { ComponentMeta, Dependency, LayoutNode } from "@gradio/core"; | |
| declare let GRADIO_VERSION: string; | |
| declare let BUILD_MODE: string; | |
| interface Config { | |
| auth_required?: true; | |
| auth_message: string; | |
| components: ComponentMeta[]; | |
| css: string | null; | |
| js: string | null; | |
| head: string | null; | |
| dependencies: Dependency[]; | |
| dev_mode: boolean; | |
| enable_queue: boolean; | |
| layout: LayoutNode; | |
| mode: "blocks" | "interface"; | |
| root: string; | |
| theme: string; | |
| title: string; | |
| version: string; | |
| space_id: string | null; | |
| is_colab: boolean; | |
| show_api: boolean; | |
| stylesheets?: string[]; | |
| path: string; | |
| app_id?: string; | |
| fill_height?: boolean; | |
| fill_width?: boolean; | |
| theme_hash?: number; | |
| username: string | null; | |
| } | |
| let id = -1; | |
| function create_intersection_store(): { | |
| register: (n: number, el: HTMLDivElement) => void; | |
| subscribe: (typeof intersecting)["subscribe"]; | |
| } { | |
| const intersecting = writable<Record<string, boolean>>({}); | |
| const els = new Map<HTMLDivElement, number>(); | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach((entry) => { | |
| if (entry.isIntersecting) { | |
| let _el: number | undefined = els.get(entry.target as HTMLDivElement); | |
| if (_el !== undefined) | |
| intersecting.update((s) => ({ ...s, [_el as number]: true })); | |
| } | |
| }); | |
| }); | |
| function register(_id: number, el: HTMLDivElement): void { | |
| els.set(el, _id); | |
| observer.observe(el); | |
| } | |
| return { register, subscribe: intersecting.subscribe }; | |
| } | |
| </script> | |
| <script lang="ts"> | |
| import { onMount, createEventDispatcher, onDestroy } from "svelte"; | |
| import type { SpaceStatus } from "@gradio/client"; | |
| import { Embed } from "@gradio/core"; | |
| import type { ThemeMode } from "@gradio/core"; | |
| import { StatusTracker } from "@gradio/statustracker"; | |
| import { _ } from "svelte-i18n"; | |
| import { setupi18n } from "@gradio/core"; | |
| import { Client } from "@gradio/client"; | |
| import { page } from "$app/stores"; | |
| import { init } from "@huggingface/space-header"; | |
| import { browser } from "$app/environment"; | |
| setupi18n(); | |
| const dispatch = createEventDispatcher(); | |
| export let data; | |
| export let autoscroll = false; | |
| export let version = GRADIO_VERSION; | |
| export let initial_height: string; | |
| export let app_mode = true; | |
| export let is_embed = false; | |
| export let theme_mode: ThemeMode | null = "system"; | |
| export let control_page_title = true; | |
| export let container: boolean; | |
| let stream: EventSource; | |
| // These utilities are exported to be injectable for the Wasm version. | |
| // export let Client: typeof ClientType; | |
| export let space: string | null; | |
| // export let host: string | null; | |
| // export let src: string | null; | |
| let _id = id++; | |
| let loader_status: "pending" | "error" | "complete" | "generating" = | |
| "complete"; | |
| let app_id: string | null = null; | |
| let wrapper: HTMLDivElement; | |
| let ready = false; | |
| let render_complete = false; | |
| $: config = data.config; | |
| let loading_text = $_("common.loading") + "..."; | |
| let active_theme_mode: ThemeMode; | |
| let intersecting: ReturnType<typeof create_intersection_store> = { | |
| register: () => {}, | |
| subscribe: writable({}).subscribe | |
| }; | |
| $: if (config?.app_id) { | |
| app_id = config.app_id; | |
| } | |
| let status: SpaceStatus = { | |
| message: "", | |
| load_status: "pending", | |
| status: "sleeping", | |
| detail: "SLEEPING" | |
| }; | |
| let app: ClientType = data.app; | |
| let css_ready = false; | |
| function handle_status(_status: SpaceStatus): void { | |
| status = _status; | |
| } | |
| //@ts-ignore | |
| let gradio_dev_mode = ""; | |
| onMount(async () => { | |
| // active_theme_mode = handle_theme_mode(wrapper); | |
| //@ts-ignore | |
| config = data.config; | |
| window.gradio_config = config; | |
| window.gradio_config = data.config; | |
| config = data.config; | |
| if (!app.config) { | |
| throw new Error("Could not resolve app config"); | |
| } | |
| window.__gradio_space__ = config.space_id; | |
| gradio_dev_mode = window?.__GRADIO_DEV__; | |
| status = { | |
| message: "", | |
| load_status: "complete", | |
| status: "running", | |
| detail: "RUNNING" | |
| }; | |
| css_ready = true; | |
| window.__is_colab__ = config.is_colab; | |
| dispatch("loaded"); | |
| if (config.dev_mode) { | |
| setTimeout(() => { | |
| const { host } = new URL(data.api_url); | |
| let url = new URL(`http://${host}${app.api_prefix}/dev/reload`); | |
| stream = new EventSource(url); | |
| stream.addEventListener("error", async (e) => { | |
| new_message_fn("Error reloading app", "error"); | |
| // @ts-ignore | |
| console.error(JSON.parse(e.data)); | |
| }); | |
| stream.addEventListener("reload", async (event) => { | |
| app.close(); | |
| app = await Client.connect(data.api_url, { | |
| status_callback: handle_status, | |
| with_null_state: true, | |
| events: ["data", "log", "status", "render"] | |
| }); | |
| if (!app.config) { | |
| throw new Error("Could not resolve app config"); | |
| } | |
| config = app.config; | |
| window.__gradio_space__ = config.space_id; | |
| }); | |
| }, 200); | |
| } | |
| }); | |
| let new_message_fn: (message: string, type: string) => void; | |
| onMount(async () => { | |
| intersecting = create_intersection_store(); | |
| intersecting.register(_id, wrapper); | |
| }); | |
| $: if (render_complete) { | |
| wrapper.dispatchEvent( | |
| new CustomEvent("render", { | |
| bubbles: true, | |
| cancelable: false, | |
| composed: true | |
| }) | |
| ); | |
| } | |
| $: app?.config && | |
| browser && | |
| mount_space_header(app?.config?.space_id, is_embed); | |
| let spaceheader: HTMLElement | undefined; | |
| async function mount_space_header( | |
| space_id: string | null | undefined, | |
| is_embed: boolean | |
| ): Promise<void> { | |
| if (space_id && !is_embed && window.self === window.top) { | |
| if (spaceheader) { | |
| spaceheader.remove(); | |
| spaceheader = undefined; | |
| } | |
| const header = await init(space_id); | |
| if (header) spaceheader = header.element; | |
| } | |
| } | |
| onDestroy(() => { | |
| spaceheader?.remove(); | |
| }); | |
| // function handle_theme_mode(target: HTMLDivElement): "light" | "dark" { | |
| // let new_theme_mode: ThemeMode; | |
| // const url = new URL(window.location.toString()); | |
| // const url_color_mode: ThemeMode | null = url.searchParams.get( | |
| // "__theme" | |
| // ) as ThemeMode | null; | |
| // new_theme_mode = theme_mode || url_color_mode || "system"; | |
| // if (new_theme_mode === "dark" || new_theme_mode === "light") { | |
| // apply_theme(target, new_theme_mode); | |
| // } else { | |
| // new_theme_mode = sync_system_theme(target); | |
| // } | |
| // return new_theme_mode; | |
| // } | |
| // function sync_system_theme(target: HTMLDivElement): "light" | "dark" { | |
| // const theme = update_scheme(); | |
| // window | |
| // ?.matchMedia("(prefers-color-scheme: dark)") | |
| // ?.addEventListener("change", update_scheme); | |
| // function update_scheme(): "light" | "dark" { | |
| // let _theme: "light" | "dark" = window?.matchMedia?.( | |
| // "(prefers-color-scheme: dark)" | |
| // ).matches | |
| // ? "dark" | |
| // : "light"; | |
| // apply_theme(target, _theme); | |
| // return _theme; | |
| // } | |
| // return theme; | |
| // } | |
| // function apply_theme(target: HTMLDivElement, theme: "dark" | "light"): void { | |
| // const dark_class_element = is_embed ? target.parentElement! : document.body; | |
| // const bg_element = is_embed ? target : target.parentElement!; | |
| // bg_element.style.background = "var(--body-background-fill)"; | |
| // if (theme === "dark") { | |
| // dark_class_element.classList.add("dark"); | |
| // } else { | |
| // dark_class_element.classList.remove("dark"); | |
| // } | |
| // } | |
| </script> | |
| <svelte:head> | |
| <link rel="stylesheet" href={"./theme.css?v=" + config?.theme_hash} /> | |
| {#if config.head} | |
| {@html config.head} | |
| {/if} | |
| </svelte:head> | |
| <Embed | |
| display={container && is_embed} | |
| {is_embed} | |
| info={false} | |
| {version} | |
| {initial_height} | |
| {space} | |
| loaded={loader_status === "complete"} | |
| fill_width={config?.fill_width || false} | |
| bind:wrapper | |
| > | |
| {#if config?.auth_required} | |
| <svelte:component | |
| this={data.Render} | |
| auth_message={config.auth_message} | |
| root={config.root} | |
| space_id={space} | |
| {app_mode} | |
| /> | |
| {:else if config && app} | |
| <svelte:component | |
| this={data.Render} | |
| {app} | |
| {...config} | |
| fill_height={!is_embed && config.fill_height} | |
| theme_mode={active_theme_mode} | |
| {control_page_title} | |
| target={wrapper} | |
| {autoscroll} | |
| bind:ready | |
| bind:render_complete | |
| bind:add_new_message={new_message_fn} | |
| show_footer={!is_embed} | |
| {app_mode} | |
| {version} | |
| search_params={$page.url.searchParams} | |
| initial_layout={data.layout} | |
| /> | |
| {/if} | |
| </Embed> | |
| <!-- <style> | |
| .error { | |
| position: relative; | |
| padding: var(--size-4); | |
| color: var(--body-text-color); | |
| text-align: center; | |
| } | |
| .error > * { | |
| margin-top: var(--size-4); | |
| } | |
| a { | |
| color: var(--link-text-color); | |
| } | |
| a:hover { | |
| color: var(--link-text-color-hover); | |
| text-decoration: underline; | |
| } | |
| a:visited { | |
| color: var(--link-text-color-visited); | |
| } | |
| a:active { | |
| color: var(--link-text-color-active); | |
| } | |
| </style> --> | |