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:
co-authored by
Claude Opus 4.8
parent
5ac9f15de4
commit
a43e6b48f0
+1
-1
@@ -1,3 +1,3 @@
|
||||
"""Single source of truth for the StackPilot release version."""
|
||||
|
||||
APP_VERSION = "0.37.2"
|
||||
APP_VERSION = "0.37.3"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "stackpilot-frontend",
|
||||
"private": true,
|
||||
"version": "0.37.2",
|
||||
"version": "0.37.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user