From a43e6b48f0410a45afcbfc2f36e9a35c3d1258a0 Mon Sep 17 00:00:00 2001 From: menzelj Date: Sun, 21 Jun 2026 20:41:42 +0000 Subject: [PATCH] 0.37.3: byte-accurate upload progress + file counter for folder uploads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed the upload path. Single-file and folder uploads already drove the progress bar, but folder progress was file-COUNT based ((i + filePct)/total), which jumps around when a folder mixes tiny files with large ones and gives no sense of total size. - Folder upload: progress is now byte-weighted (sum of all file sizes), so the bar tracks real transfer. Added a detail line " / files · / " and the bar shows the current file name. - Single file: added the same byte detail (" / "). - Progress component gained an optional detail sub-line (shared by the download bar too). Frontend-only; all 3 images rebuilt+pushed 0.37.3 for tag consistency. Co-Authored-By: Claude Opus 4.8 --- backend/version.py | 2 +- frontend/package.json | 2 +- frontend/src/pages/Files.tsx | 28 ++++++++++++++++++++++------ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/backend/version.py b/backend/version.py index 3e7c20b..cf4db34 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,3 +1,3 @@ """Single source of truth for the StackPilot release version.""" -APP_VERSION = "0.37.2" +APP_VERSION = "0.37.3" diff --git a/frontend/package.json b/frontend/package.json index a92c642..aac6442 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "stackpilot-frontend", "private": true, - "version": "0.37.2", + "version": "0.37.3", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/pages/Files.tsx b/frontend/src/pages/Files.tsx index 9efc8d2..93496a0 100644 --- a/frontend/src/pages/Files.tsx +++ b/frontend/src/pages/Files.tsx @@ -89,6 +89,7 @@ export function Files() { kind: "upload" | "download"; indeterminate?: boolean; loaded?: number; + detail?: string; } | null>(null); const fileInput = useRef(null); const folderInput = useRef(null); @@ -119,7 +120,12 @@ export function Files() { mutationFn: (file: File) => { setProgress({ label: file.name, pct: 0, kind: "upload" }); return filesApi.upload(path, file, false, "", host, (pct) => - setProgress({ label: file.name, pct, kind: "upload" }) + setProgress({ + label: file.name, + pct, + kind: "upload", + detail: `${formatBytes((pct / 100) * file.size)} / ${formatBytes(file.size)}`, + }) ); }, onSuccess: (r) => { @@ -145,23 +151,30 @@ export function Files() { const uploadFolder = useMutation({ mutationFn: async (files: File[]) => { const total = files.length; + const totalBytes = files.reduce((s, f) => s + f.size, 0); let ok = 0; let failed = 0; + let doneBytes = 0; // bytes of fully-completed files for (let i = 0; i < files.length; i++) { const f = files[i]; const rel = (f as File & { webkitRelativePath?: string }).webkitRelativePath || f.name; try { - await filesApi.upload(path, f, true, rel, host, (filePct) => + await filesApi.upload(path, f, true, rel, host, (filePct) => { + const sent = doneBytes + (filePct / 100) * f.size; setProgress({ - label: `${f.name} (${i + 1}/${total})`, - pct: ((i + filePct / 100) / total) * 100, + label: f.name, + // Byte-weighted so the bar tracks real transfer, not file count + // (a folder can mix tiny files with multi-GB ones). + pct: totalBytes ? (sent / totalBytes) * 100 : ((i + filePct / 100) / total) * 100, kind: "upload", - }) - ); + detail: `${i + 1} / ${total} files · ${formatBytes(sent)} / ${formatBytes(totalBytes)}`, + }); + }); ok += 1; } catch { failed += 1; } + doneBytes += f.size; } return { ok, failed }; }, @@ -355,6 +368,9 @@ export function Files() { /> )} + {progress.detail && ( +
{progress.detail}
+ )} )}