← back to Exo
fix: add download/resume buttons to pending downloads (#1581)
e8c13371682cf035fa1c245dca7c040cc7baddec · 2026-02-23 10:20:35 -0800 · Alex Cheema
## Summary
- Adds a **resume button** (download icon) to paused pending downloads
(those with partial progress)
- Adds a **download button** to not-started pending downloads
- Both buttons call the existing `startDownload()` function which
handles both new downloads and resuming partial ones
- Previously, paused downloads only showed a "paused" label with no
action, and not-started downloads showed "..." with no way to trigger
them
## Test plan
- [ ] Build dashboard (`cd dashboard && npm run build`)
- [ ] Start exo, navigate to Downloads tab
- [ ] Verify paused downloads show a clickable resume (download arrow)
icon below the progress bar
- [ ] Verify not-started pending downloads show a clickable download
icon
- [ ] Click both button types and confirm downloads start/resume
> Note: Screenshot could not be captured because the dashboard requires
the exo backend API to render, and exo has a pre-existing
`Keypair.generate()` startup bug on main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Files touched
M dashboard/src/routes/downloads/+page.svelte
Diff
commit e8c13371682cf035fa1c245dca7c040cc7baddec
Author: Alex Cheema <41707476+AlexCheema@users.noreply.github.com>
Date: Mon Feb 23 10:20:35 2026 -0800
fix: add download/resume buttons to pending downloads (#1581)
## Summary
- Adds a **resume button** (download icon) to paused pending downloads
(those with partial progress)
- Adds a **download button** to not-started pending downloads
- Both buttons call the existing `startDownload()` function which
handles both new downloads and resuming partial ones
- Previously, paused downloads only showed a "paused" label with no
action, and not-started downloads showed "..." with no way to trigger
them
## Test plan
- [ ] Build dashboard (`cd dashboard && npm run build`)
- [ ] Start exo, navigate to Downloads tab
- [ ] Verify paused downloads show a clickable resume (download arrow)
icon below the progress bar
- [ ] Verify not-started pending downloads show a clickable download
icon
- [ ] Click both button types and confirm downloads start/resume
> Note: Screenshot could not be captured because the dashboard requires
the exo backend API to render, and exo has a pre-existing
`Keypair.generate()` startup bug on main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
---
dashboard/src/routes/downloads/+page.svelte | 86 ++++++++++++++++++++++-------
1 file changed, 66 insertions(+), 20 deletions(-)
diff --git a/dashboard/src/routes/downloads/+page.svelte b/dashboard/src/routes/downloads/+page.svelte
index c0781c19..c69c7635 100644
--- a/dashboard/src/routes/downloads/+page.svelte
+++ b/dashboard/src/routes/downloads/+page.svelte
@@ -469,11 +469,11 @@
<td class="px-4 py-3 text-center align-middle">
{#if cell.kind === "completed"}
<div
- class="flex flex-col items-center gap-0.5"
+ class="flex flex-col items-center gap-1"
title="Completed ({formatBytes(cell.totalBytes)})"
>
<svg
- class="w-5 h-5 text-green-400"
+ class="w-7 h-7 text-green-400"
viewBox="0 0 20 20"
fill="currentColor"
>
@@ -483,18 +483,18 @@
clip-rule="evenodd"
></path>
</svg>
- <span class="text-[10px] text-exo-light-gray/70"
+ <span class="text-xs text-exo-light-gray/70"
>{formatBytes(cell.totalBytes)}</span
>
<button
type="button"
- class="text-exo-light-gray/40 hover:text-red-400 transition-colors mt-0.5"
+ class="text-exo-light-gray/40 hover:text-red-400 transition-colors mt-0.5 cursor-pointer"
onclick={() =>
deleteDownload(col.nodeId, row.modelId)}
title="Delete from this node"
>
<svg
- class="w-3.5 h-3.5"
+ class="w-5 h-5"
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
@@ -517,11 +517,11 @@
cell.speed,
)} - ETA {formatEta(cell.etaMs)}"
>
- <span class="text-exo-yellow text-xs font-medium"
+ <span class="text-exo-yellow text-sm font-medium"
>{clampPercent(cell.percentage).toFixed(1)}%</span
>
<div
- class="w-14 h-1.5 bg-exo-black/60 rounded-sm overflow-hidden"
+ class="w-16 h-2 bg-exo-black/60 rounded-sm overflow-hidden"
>
<div
class="h-full bg-gradient-to-r from-exo-yellow to-exo-yellow/70 transition-all duration-300"
@@ -530,25 +530,25 @@
).toFixed(1)}%"
></div>
</div>
- <span class="text-[9px] text-exo-light-gray/60"
+ <span class="text-[10px] text-exo-light-gray/60"
>{formatSpeed(cell.speed)}</span
>
</div>
{:else if cell.kind === "pending"}
<div
- class="flex flex-col items-center gap-0.5"
+ class="flex flex-col items-center gap-1"
title={cell.downloaded > 0
- ? `${formatBytes(cell.downloaded)} / ${formatBytes(cell.total)} downloaded`
+ ? `${formatBytes(cell.downloaded)} / ${formatBytes(cell.total)} downloaded (paused)`
: "Download pending"}
>
{#if cell.downloaded > 0 && cell.total > 0}
- <span class="text-exo-light-gray/70 text-[10px]"
+ <span class="text-exo-light-gray/70 text-xs"
>{formatBytes(cell.downloaded)} / {formatBytes(
cell.total,
)}</span
>
<div
- class="w-full h-1 bg-white/10 rounded-full overflow-hidden"
+ class="w-full h-1.5 bg-white/10 rounded-full overflow-hidden"
>
<div
class="h-full bg-exo-light-gray/40 rounded-full"
@@ -558,9 +558,55 @@
).toFixed(1)}%"
></div>
</div>
- <span class="text-exo-light-gray/40 text-[9px]"
- >paused</span
+ {#if row.shardMetadata}
+ <button
+ type="button"
+ class="text-exo-light-gray/50 hover:text-exo-yellow transition-colors cursor-pointer"
+ onclick={() =>
+ startDownload(col.nodeId, row.shardMetadata!)}
+ title="Resume download on this node"
+ >
+ <svg
+ class="w-5 h-5"
+ viewBox="0 0 20 20"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ >
+ <path
+ d="M10 3v10m0 0l-3-3m3 3l3-3M3 17h14"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ ></path>
+ </svg>
+ </button>
+ {:else}
+ <span class="text-exo-light-gray/40 text-[10px]"
+ >paused</span
+ >
+ {/if}
+ {:else if row.shardMetadata}
+ <button
+ type="button"
+ class="text-exo-light-gray/30 hover:text-exo-yellow transition-colors cursor-pointer"
+ onclick={() =>
+ startDownload(col.nodeId, row.shardMetadata!)}
+ title="Start download on this node"
>
+ <svg
+ class="w-6 h-6"
+ viewBox="0 0 20 20"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ >
+ <path
+ d="M10 3v10m0 0l-3-3m3 3l3-3M3 17h14"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ ></path>
+ </svg>
+ </button>
{:else}
<span class="text-exo-light-gray/50 text-sm">...</span
>
@@ -568,11 +614,11 @@
</div>
{:else if cell.kind === "failed"}
<div
- class="flex flex-col items-center gap-0.5"
+ class="flex flex-col items-center gap-1"
title="Download failed"
>
<svg
- class="w-5 h-5 text-red-400"
+ class="w-7 h-7 text-red-400"
viewBox="0 0 20 20"
fill="currentColor"
>
@@ -585,13 +631,13 @@
{#if row.shardMetadata}
<button
type="button"
- class="text-exo-light-gray/40 hover:text-exo-yellow transition-colors"
+ class="text-exo-light-gray/40 hover:text-exo-yellow transition-colors cursor-pointer"
onclick={() =>
startDownload(col.nodeId, row.shardMetadata!)}
title="Retry download on this node"
>
<svg
- class="w-3.5 h-3.5"
+ class="w-5 h-5"
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
@@ -617,13 +663,13 @@
{#if row.shardMetadata}
<button
type="button"
- class="text-exo-light-gray/30 hover:text-exo-yellow transition-colors mt-0.5 opacity-0 group-hover:opacity-100"
+ class="text-exo-light-gray/30 hover:text-exo-yellow transition-colors mt-0.5 opacity-0 group-hover:opacity-100 cursor-pointer"
onclick={() =>
startDownload(col.nodeId, row.shardMetadata!)}
title="Download to this node"
>
<svg
- class="w-3.5 h-3.5"
+ class="w-5 h-5"
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
← 7024ddcf fix: detect completed downloads by checking final file exist
·
back to Exo
·
fix: suppress closure errors in runnersupervisor and force s c90a0cec →