mirror of
https://github.com/gradio-app/gradio.git
synced 2025-01-12 10:34:32 +08:00
828fb9e6ce
* change * changes * add changeset * add changeset * changes * changes * changes * changes --------- Co-authored-by: Ali Abid <ubuntu@ip-172-31-25-241.us-west-2.compute.internal> Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com> Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
425 lines
9.3 KiB
Svelte
425 lines
9.3 KiB
Svelte
<script context="module" lang="ts">
|
|
import { tick } from "svelte";
|
|
import { pretty_si } from "./utils";
|
|
|
|
let items: HTMLDivElement[] = [];
|
|
|
|
let called = false;
|
|
|
|
async function scroll_into_view(
|
|
el: HTMLDivElement,
|
|
enable: boolean | null = true
|
|
): Promise<void> {
|
|
if (
|
|
window.__gradio_mode__ === "website" ||
|
|
(window.__gradio_mode__ !== "app" && enable !== true)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
items.push(el);
|
|
if (!called) called = true;
|
|
else return;
|
|
|
|
await tick();
|
|
|
|
requestAnimationFrame(() => {
|
|
let min = [0, 0];
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
const element = items[i];
|
|
|
|
const box = element.getBoundingClientRect();
|
|
if (i === 0 || box.top + window.scrollY <= min[0]) {
|
|
min[0] = box.top + window.scrollY;
|
|
min[1] = i;
|
|
}
|
|
}
|
|
|
|
window.scrollTo({ top: min[0] - 20, behavior: "smooth" });
|
|
|
|
called = false;
|
|
items = [];
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import { onDestroy } from "svelte";
|
|
|
|
import Loader from "./Loader.svelte";
|
|
import type { LoadingStatus } from "./types";
|
|
import type { I18nFormatter } from "@gradio/utils";
|
|
|
|
export let i18n: I18nFormatter;
|
|
export let eta: number | null = null;
|
|
export let queue_position: number | null;
|
|
export let queue_size: number | null;
|
|
export let status: "complete" | "pending" | "error" | "generating";
|
|
export let scroll_to_output = false;
|
|
export let timer = true;
|
|
export let show_progress: "full" | "minimal" | "hidden" = "full";
|
|
export let message: string | null = null;
|
|
export let progress: LoadingStatus["progress"] | null | undefined = null;
|
|
export let variant: "default" | "center" = "default";
|
|
export let loading_text = "Loading...";
|
|
export let absolute = true;
|
|
export let translucent = false;
|
|
export let border = false;
|
|
export let autoscroll: boolean;
|
|
|
|
let el: HTMLDivElement;
|
|
|
|
let _timer = false;
|
|
let timer_start = 0;
|
|
let timer_diff = 0;
|
|
let old_eta: number | null = null;
|
|
let eta_from_start: number | null = null;
|
|
let message_visible = false;
|
|
let eta_level: number | null = 0;
|
|
let progress_level: (number | undefined)[] | null = null;
|
|
let last_progress_level: number | undefined = undefined;
|
|
let progress_bar: HTMLElement | null = null;
|
|
let show_eta_bar = true;
|
|
|
|
$: eta_level =
|
|
eta_from_start === null || eta_from_start <= 0 || !timer_diff
|
|
? null
|
|
: Math.min(timer_diff / eta_from_start, 1);
|
|
$: if (progress != null) {
|
|
show_eta_bar = false;
|
|
}
|
|
|
|
$: {
|
|
if (progress != null) {
|
|
progress_level = progress.map((p) => {
|
|
if (p.index != null && p.length != null) {
|
|
return p.index / p.length;
|
|
} else if (p.progress != null) {
|
|
return p.progress;
|
|
}
|
|
return undefined;
|
|
});
|
|
} else {
|
|
progress_level = null;
|
|
}
|
|
|
|
if (progress_level) {
|
|
last_progress_level = progress_level[progress_level.length - 1];
|
|
if (progress_bar) {
|
|
if (last_progress_level === 0) {
|
|
progress_bar.style.transition = "0";
|
|
} else {
|
|
progress_bar.style.transition = "150ms";
|
|
}
|
|
}
|
|
} else {
|
|
last_progress_level = undefined;
|
|
}
|
|
}
|
|
|
|
const start_timer = (): void => {
|
|
eta = old_eta = formatted_eta = null;
|
|
timer_start = performance.now();
|
|
timer_diff = 0;
|
|
_timer = true;
|
|
run();
|
|
};
|
|
|
|
function run(): void {
|
|
requestAnimationFrame(() => {
|
|
timer_diff = (performance.now() - timer_start) / 1000;
|
|
if (_timer) run();
|
|
});
|
|
}
|
|
|
|
function stop_timer(): void {
|
|
timer_diff = 0;
|
|
eta = old_eta = formatted_eta = null;
|
|
|
|
if (!_timer) return;
|
|
_timer = false;
|
|
}
|
|
|
|
onDestroy(() => {
|
|
if (_timer) stop_timer();
|
|
});
|
|
|
|
$: {
|
|
if (status === "pending") {
|
|
start_timer();
|
|
} else {
|
|
stop_timer();
|
|
}
|
|
}
|
|
|
|
$: el &&
|
|
scroll_to_output &&
|
|
(status === "pending" || status === "complete") &&
|
|
scroll_into_view(el, autoscroll);
|
|
|
|
let formatted_eta: string | null = null;
|
|
$: {
|
|
if (eta === null) {
|
|
eta = old_eta;
|
|
}
|
|
if (eta != null && old_eta !== eta) {
|
|
eta_from_start = (performance.now() - timer_start) / 1000 + eta;
|
|
formatted_eta = eta_from_start.toFixed(1);
|
|
old_eta = eta;
|
|
}
|
|
}
|
|
let show_message_timeout: NodeJS.Timeout | null = null;
|
|
function close_message(): void {
|
|
message_visible = false;
|
|
if (show_message_timeout !== null) {
|
|
clearTimeout(show_message_timeout);
|
|
}
|
|
}
|
|
$: {
|
|
close_message();
|
|
if (status === "error" && message) {
|
|
message_visible = true;
|
|
}
|
|
}
|
|
$: formatted_timer = timer_diff.toFixed(1);
|
|
</script>
|
|
|
|
<div
|
|
class="wrap {variant} {show_progress}"
|
|
class:hide={!status || status === "complete" || show_progress === "hidden"}
|
|
class:translucent={(variant === "center" &&
|
|
(status === "pending" || status === "error")) ||
|
|
translucent ||
|
|
show_progress === "minimal"}
|
|
class:generating={status === "generating"}
|
|
class:border
|
|
style:position={absolute ? "absolute" : "static"}
|
|
style:padding={absolute ? "0" : "var(--size-8) 0"}
|
|
bind:this={el}
|
|
>
|
|
{#if status === "pending"}
|
|
{#if variant === "default" && show_eta_bar && show_progress === "full"}
|
|
<div
|
|
class="eta-bar"
|
|
style:transform="translateX({(eta_level || 0) * 100 - 100}%)"
|
|
/>
|
|
{/if}
|
|
<div
|
|
class:meta-text-center={variant === "center"}
|
|
class:meta-text={variant === "default"}
|
|
class="progress-text"
|
|
>
|
|
{#if progress}
|
|
{#each progress as p}
|
|
{#if p.index != null}
|
|
{#if p.length != null}
|
|
{pretty_si(p.index || 0)}/{pretty_si(p.length)}
|
|
{:else}
|
|
{pretty_si(p.index || 0)}
|
|
{/if}
|
|
{p.unit} | {" "}
|
|
{/if}
|
|
{/each}
|
|
{:else if queue_position !== null && queue_size !== undefined && queue_position >= 0}
|
|
queue: {queue_position + 1}/{queue_size} |
|
|
{:else if queue_position === 0}
|
|
processing |
|
|
{/if}
|
|
|
|
{#if timer}
|
|
{formatted_timer}{eta ? `/${formatted_eta}` : ""}s
|
|
{/if}
|
|
</div>
|
|
|
|
{#if last_progress_level != null}
|
|
<div class="progress-level">
|
|
<div class="progress-level-inner">
|
|
{#if progress != null}
|
|
{#each progress as p, i}
|
|
{#if p.desc != null || (progress_level && progress_level[i] != null)}
|
|
{#if i !== 0}
|
|
/
|
|
{/if}
|
|
{#if p.desc != null}
|
|
{p.desc}
|
|
{/if}
|
|
{#if p.desc != null && progress_level && progress_level[i] != null}
|
|
-
|
|
{/if}
|
|
{#if progress_level != null}
|
|
{(100 * (progress_level[i] || 0)).toFixed(1)}%
|
|
{/if}
|
|
{/if}
|
|
{/each}
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="progress-bar-wrap">
|
|
<div
|
|
bind:this={progress_bar}
|
|
class="progress-bar"
|
|
style:width="{last_progress_level * 100}%"
|
|
/>
|
|
</div>
|
|
</div>
|
|
{:else if show_progress === "full"}
|
|
<Loader margin={variant === "default"} />
|
|
{/if}
|
|
|
|
{#if !timer}
|
|
<p class="loading">{loading_text}</p>
|
|
{/if}
|
|
{:else if status === "error"}
|
|
<span class="error">{i18n("common.error")}</span>
|
|
<slot name="error" />
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.wrap {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: var(--layer-top);
|
|
transition: opacity 0.1s ease-in-out;
|
|
border-radius: var(--block-radius);
|
|
background: var(--block-background-fill);
|
|
padding: 0 var(--size-6);
|
|
max-height: var(--size-screen-h);
|
|
overflow: hidden;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.wrap.center {
|
|
top: 0;
|
|
right: 0px;
|
|
left: 0px;
|
|
}
|
|
|
|
.wrap.default {
|
|
top: 0px;
|
|
right: 0px;
|
|
bottom: 0px;
|
|
left: 0px;
|
|
}
|
|
|
|
.hide {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.generating {
|
|
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
border: 2px solid var(--color-accent);
|
|
background: transparent;
|
|
}
|
|
|
|
.translucent {
|
|
background: none;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%,
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
.loading {
|
|
z-index: var(--layer-2);
|
|
color: var(--body-text-color);
|
|
}
|
|
.eta-bar {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
transform-origin: left;
|
|
opacity: 0.8;
|
|
z-index: var(--layer-1);
|
|
transition: 10ms;
|
|
background: var(--background-fill-secondary);
|
|
}
|
|
.progress-bar-wrap {
|
|
border: 1px solid var(--border-color-primary);
|
|
background: var(--background-fill-primary);
|
|
width: 55.5%;
|
|
height: var(--size-4);
|
|
}
|
|
.progress-bar {
|
|
transform-origin: left;
|
|
background-color: var(--loader-color);
|
|
width: var(--size-full);
|
|
height: var(--size-full);
|
|
}
|
|
|
|
.progress-level {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 1;
|
|
z-index: var(--layer-2);
|
|
width: var(--size-full);
|
|
}
|
|
|
|
.progress-level-inner {
|
|
margin: var(--size-2) auto;
|
|
color: var(--body-text-color);
|
|
font-size: var(--text-sm);
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
.meta-text {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
z-index: var(--layer-2);
|
|
padding: var(--size-1) var(--size-2);
|
|
font-size: var(--text-sm);
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
.meta-text-center {
|
|
display: flex;
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
justify-content: center;
|
|
align-items: center;
|
|
transform: translateY(var(--size-6));
|
|
z-index: var(--layer-2);
|
|
padding: var(--size-1) var(--size-2);
|
|
font-size: var(--text-sm);
|
|
font-family: var(--font-mono);
|
|
text-align: center;
|
|
}
|
|
|
|
.error {
|
|
box-shadow: var(--shadow-drop);
|
|
border: solid 1px var(--error-border-color);
|
|
border-radius: var(--radius-full);
|
|
background: var(--error-background-fill);
|
|
padding-right: var(--size-4);
|
|
padding-left: var(--size-4);
|
|
color: var(--error-text-color);
|
|
font-weight: var(--weight-semibold);
|
|
font-size: var(--text-lg);
|
|
line-height: var(--line-lg);
|
|
font-family: var(--font);
|
|
}
|
|
|
|
.minimal .progress-text {
|
|
background: var(--block-background-fill);
|
|
}
|
|
|
|
.border {
|
|
border: 1px solid var(--border-color-primary);
|
|
}
|
|
</style>
|