0.37.3: byte-accurate upload progress + file counter for folder uploads

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 "<i> / <n> files ·
  <sent> / <total>" and the bar shows the current file name.
- Single file: added the same byte detail ("<sent> / <size>").
- 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 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-21 20:41:42 +00:00
co-authored by Claude Opus 4.8
parent 5ac9f15de4
commit a43e6b48f0
3 changed files with 24 additions and 8 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
"""Single source of truth for the StackPilot release version."""
APP_VERSION = "0.37.2"
APP_VERSION = "0.37.3"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "stackpilot-frontend",
"private": true,
"version": "0.37.2",
"version": "0.37.3",
"type": "module",
"scripts": {
"dev": "vite",
+22 -6
View File
@@ -89,6 +89,7 @@ export function Files() {
kind: "upload" | "download";
indeterminate?: boolean;
loaded?: number;
detail?: string;
} | null>(null);
const fileInput = useRef<HTMLInputElement>(null);
const folderInput = useRef<HTMLInputElement>(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<number>((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() {
/>
)}
</div>
{progress.detail && (
<div className="text-[11px] tabular-nums text-slate-400">{progress.detail}</div>
)}
</div>
)}