From 32669ae82df4e28d2f3e4cfa14bced5fc866bba7 Mon Sep 17 00:00:00 2001 From: Alex Cheema Date: Thu, 19 Feb 2026 04:14:06 -0800 Subject: [PATCH] feat: show ETA on prefill progress bar Track when prefill starts via performance.now() and extrapolate remaining time from observed tokens/sec. Displays "~Xs remaining" (or "~Xm Ys remaining" for longer prompts) next to the percentage. Co-Authored-By: Claude Opus 4.6 --- .../lib/components/PrefillProgressBar.svelte | 22 +++++++++++++++++-- dashboard/src/lib/stores/app.svelte.ts | 3 +++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/dashboard/src/lib/components/PrefillProgressBar.svelte b/dashboard/src/lib/components/PrefillProgressBar.svelte index ea08d11d5..c07be12c9 100644 --- a/dashboard/src/lib/components/PrefillProgressBar.svelte +++ b/dashboard/src/lib/components/PrefillProgressBar.svelte @@ -14,6 +14,21 @@ : 0, ); + const etaText = $derived.by(() => { + if (progress.processed <= 0 || progress.total <= 0) return null; + const elapsedMs = performance.now() - progress.startedAt; + if (elapsedMs < 200) return null; // need a minimum sample window + const tokensPerMs = progress.processed / elapsedMs; + const remainingTokens = progress.total - progress.processed; + const remainingMs = remainingTokens / tokensPerMs; + const remainingSec = Math.ceil(remainingMs / 1000); + if (remainingSec <= 0) return null; + if (remainingSec < 60) return `~${remainingSec}s remaining`; + const mins = Math.floor(remainingSec / 60); + const secs = remainingSec % 60; + return `~${mins}m ${secs}s remaining`; + }); + function formatTokenCount(count: number | undefined): string { if (count == null) return "0"; if (count >= 1000) { @@ -40,8 +55,11 @@ style="width: {percentage}%" > -
- {percentage}% +
+ {etaText ?? ""} + {percentage}%
diff --git a/dashboard/src/lib/stores/app.svelte.ts b/dashboard/src/lib/stores/app.svelte.ts index 3e0074e37..ef9f12072 100644 --- a/dashboard/src/lib/stores/app.svelte.ts +++ b/dashboard/src/lib/stores/app.svelte.ts @@ -276,6 +276,8 @@ export interface TokenData { export interface PrefillProgress { processed: number; total: number; + /** Timestamp (performance.now()) when prefill started. */ + startedAt: number; } export interface Message { @@ -2459,6 +2461,7 @@ class AppStore { this.prefillProgress = { processed: inner.processed_tokens, total: inner.total_tokens, + startedAt: this.prefillProgress?.startedAt ?? performance.now(), }; }, },