gradio / js /app /src /Index.svelte
mindmime's picture
Upload folder using huggingface_hub
a03b3ba verified
<script context="module" lang="ts">
import { writable } from "svelte/store";
import { mount_css as default_mount_css, prefix_css } from "./css";
import type { ComponentMeta, Dependency, LayoutNode } from "./types";
declare let BUILD_MODE: string;
interface Config {
auth_required: boolean | undefined;
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;
}
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 };
}
const intersecting = create_intersection_store();
</script>
<script lang="ts">
import { onMount, setContext } from "svelte";
import type { api_factory, SpaceStatus } from "@gradio/client";
import Embed from "./Embed.svelte";
import type { ThemeMode } from "./types";
import { StatusTracker } from "@gradio/statustracker";
import { _ } from "svelte-i18n";
import { setupi18n } from "./i18n";
import type { WorkerProxy } from "@gradio/wasm";
import { setWorkerProxyContext } from "@gradio/wasm/svelte";
setupi18n();
export let autoscroll: boolean;
export let version: string;
export let initial_height: string;
export let app_mode: boolean;
export let is_embed: boolean;
export let theme_mode: ThemeMode | null = "system";
export let control_page_title: boolean;
export let container: boolean;
export let info: boolean;
export let eager: boolean;
let eventSource: EventSource;
// These utilities are exported to be injectable for the Wasm version.
export let mount_css: typeof default_mount_css = default_mount_css;
export let client: ReturnType<typeof api_factory>["client"];
export let upload_files: ReturnType<typeof api_factory>["upload_files"];
export let worker_proxy: WorkerProxy | undefined = undefined;
if (worker_proxy) {
setWorkerProxyContext(worker_proxy);
worker_proxy.addEventListener("progress-update", (event) => {
loading_text = (event as CustomEvent).detail + "...";
});
}
export let fetch_implementation: typeof fetch = fetch;
setContext("fetch_implementation", fetch_implementation);
export let EventSource_factory: (url: URL) => EventSource = (url) =>
new EventSource(url);
setContext("EventSource_factory", EventSource_factory);
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" =
"pending";
let app_id: string | null = null;
let wrapper: HTMLDivElement;
let ready = false;
let render_complete = false;
let config: Config;
let loading_text = $_("common.loading") + "...";
let active_theme_mode: ThemeMode;
let api_url: string;
$: if (config?.app_id) {
app_id = config.app_id;
}
let css_text_stylesheet: HTMLStyleElement | null = null;
async function mount_custom_css(css_string: string | null): Promise<void> {
if (css_string) {
css_text_stylesheet = prefix_css(
css_string,
version,
css_text_stylesheet || undefined
);
}
await mount_css(config.root + "/theme.css", document.head);
if (!config.stylesheets) return;
await Promise.all(
config.stylesheets.map((stylesheet) => {
let absolute_link =
stylesheet.startsWith("http:") || stylesheet.startsWith("https:");
if (absolute_link) {
return mount_css(stylesheet, document.head);
}
return fetch(config.root + "/" + stylesheet)
.then((response) => response.text())
.then((css_string) => {
prefix_css(css_string, version);
});
})
);
}
async function add_custom_html_head(
head_string: string | null
): Promise<void> {
if (head_string) {
const parser = new DOMParser();
const parsed_head_html = Array.from(
parser.parseFromString(head_string, "text/html").head.children
);
if (parsed_head_html) {
for (let head_element of parsed_head_html) {
let newElement = document.createElement(head_element.tagName);
Array.from(head_element.attributes).forEach((attr) => {
newElement.setAttribute(attr.name, attr.value);
});
newElement.textContent = head_element.textContent;
document.head.appendChild(newElement);
}
}
}
}
function handle_darkmode(target: HTMLDivElement): "light" | "dark" {
let url = new URL(window.location.toString());
let url_color_mode: ThemeMode | null = url.searchParams.get(
"__theme"
) as ThemeMode | null;
active_theme_mode = theme_mode || url_color_mode || "system";
if (active_theme_mode === "dark" || active_theme_mode === "light") {
darkmode(target, active_theme_mode);
} else {
active_theme_mode = use_system_theme(target);
}
return active_theme_mode;
}
function use_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";
darkmode(target, _theme);
return _theme;
}
return theme;
}
function darkmode(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");
}
}
let status: SpaceStatus = {
message: "",
load_status: "pending",
status: "sleeping",
detail: "SLEEPING"
};
let app: Awaited<ReturnType<typeof client>>;
let css_ready = false;
function handle_status(_status: SpaceStatus): void {
status = _status;
}
onMount(async () => {
if (window.__gradio_mode__ !== "website") {
active_theme_mode = handle_darkmode(wrapper);
}
//@ts-ignore
const gradio_dev_mode = window.__GRADIO_DEV__;
//@ts-ignore
const server_port = window.__GRADIO__SERVER_PORT__;
api_url =
BUILD_MODE === "dev" || gradio_dev_mode === "dev"
? `http://localhost:${
typeof server_port === "number" ? server_port : 7860
}`
: host || space || src || location.origin;
app = await client(api_url, {
status_callback: handle_status,
normalise_files: false
});
config = app.config;
window.__gradio_space__ = config.space_id;
status = {
message: "",
load_status: "complete",
status: "running",
detail: "RUNNING"
};
await mount_custom_css(config.css);
await add_custom_html_head(config.head);
css_ready = true;
window.__is_colab__ = config.is_colab;
if (config.dev_mode) {
setTimeout(() => {
const { host } = new URL(api_url);
let url = new URL(`http://${host}/dev/reload`);
eventSource = new EventSource(url);
eventSource.onmessage = async function (event) {
if (event.data === "CHANGE") {
app = await client(api_url, {
status_callback: handle_status,
normalise_files: false
});
config = app.config;
window.__gradio_space__ = config.space_id;
await mount_custom_css(config.css);
}
};
}, 200);
}
});
setContext("upload_files", upload_files);
$: loader_status =
!ready && status.load_status !== "error"
? "pending"
: !ready && status.load_status === "error"
? "error"
: status.load_status;
$: config && (eager || $intersecting[_id]) && load_demo();
let Blocks: typeof import("./Blocks.svelte").default;
let Login: typeof import("./Login.svelte").default;
async function get_blocks(): Promise<void> {
Blocks = (await import("./Blocks.svelte")).default;
}
async function get_login(): Promise<void> {
Login = (await import("./Login.svelte")).default;
}
function load_demo(): void {
if (config.auth_required) get_login();
else get_blocks();
}
type error_types =
| "NO_APP_FILE"
| "CONFIG_ERROR"
| "BUILD_ERROR"
| "RUNTIME_ERROR"
| "PAUSED";
// todo @hannahblair: translate these messages
const discussion_message = {
readable_error: {
NO_APP_FILE: $_("errors.no_app_file"),
CONFIG_ERROR: $_("errors.config_error"),
BUILD_ERROR: $_("errors.build_error"),
RUNTIME_ERROR: $_("errors.runtime_error"),
PAUSED: $_("errors.space_paused")
} as const,
title(error: error_types): string {
return encodeURIComponent($_("errors.space_not_working"));
},
description(error: error_types, site: string): string {
return encodeURIComponent(
`Hello,\n\nFirstly, thanks for creating this space!\n\nI noticed that the space isn't working correctly because there is ${
this.readable_error[error] || "an error"
}.\n\nIt would be great if you could take a look at this because this space is being embedded on ${site}.\n\nThanks!`
);
}
};
onMount(async () => {
intersecting.register(_id, wrapper);
});
$: if (render_complete) {
wrapper.dispatchEvent(
new CustomEvent("render", {
bubbles: true,
cancelable: false,
composed: true
})
);
}
</script>
<Embed
display={container && is_embed}
{is_embed}
info={!!space && info}
{version}
{initial_height}
{space}
loaded={loader_status === "complete"}
bind:wrapper
>
{#if (loader_status === "pending" || loader_status === "error") && !(config && config?.auth_required)}
<StatusTracker
absolute={!is_embed}
status={loader_status}
timer={false}
queue_position={null}
queue_size={null}
translucent={true}
{loading_text}
i18n={$_}
{autoscroll}
>
<!-- todo: translate message text -->
<div class="error" slot="error">
<p><strong>{status?.message || ""}</strong></p>
{#if (status.status === "space_error" || status.status === "paused") && status.discussions_enabled}
<p>
Please <a
href="https://huggingface.co/spaces/{space}/discussions/new?title={discussion_message.title(
status?.detail
)}&description={discussion_message.description(
status?.detail,
location.origin
)}"
>
contact the author of the space</a
> to let them know.
</p>
{:else}
<p>{$_("errors.contact_page_author")}</p>
{/if}
</div>
</StatusTracker>
{/if}
{#if config?.auth_required && Login}
<Login
auth_message={config.auth_message}
root={config.root}
space_id={space}
{app_mode}
/>
{:else if config && Blocks && css_ready}
<Blocks
{app}
{...config}
theme_mode={active_theme_mode}
{control_page_title}
target={wrapper}
{autoscroll}
bind:ready
bind:render_complete
show_footer={!is_embed}
{app_mode}
{version}
/>
{/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>