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."""
|
"""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",
|
"name": "stackpilot-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.37.2",
|
"version": "0.37.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export function Files() {
|
|||||||
kind: "upload" | "download";
|
kind: "upload" | "download";
|
||||||
indeterminate?: boolean;
|
indeterminate?: boolean;
|
||||||
loaded?: number;
|
loaded?: number;
|
||||||
|
detail?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const fileInput = useRef<HTMLInputElement>(null);
|
const fileInput = useRef<HTMLInputElement>(null);
|
||||||
const folderInput = useRef<HTMLInputElement>(null);
|
const folderInput = useRef<HTMLInputElement>(null);
|
||||||
@@ -119,7 +120,12 @@ export function Files() {
|
|||||||
mutationFn: (file: File) => {
|
mutationFn: (file: File) => {
|
||||||
setProgress({ label: file.name, pct: 0, kind: "upload" });
|
setProgress({ label: file.name, pct: 0, kind: "upload" });
|
||||||
return filesApi.upload(path, file, false, "", host, (pct) =>
|
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) => {
|
onSuccess: (r) => {
|
||||||
@@ -145,23 +151,30 @@ export function Files() {
|
|||||||
const uploadFolder = useMutation({
|
const uploadFolder = useMutation({
|
||||||
mutationFn: async (files: File[]) => {
|
mutationFn: async (files: File[]) => {
|
||||||
const total = files.length;
|
const total = files.length;
|
||||||
|
const totalBytes = files.reduce<number>((s, f) => s + f.size, 0);
|
||||||
let ok = 0;
|
let ok = 0;
|
||||||
let failed = 0;
|
let failed = 0;
|
||||||
|
let doneBytes = 0; // bytes of fully-completed files
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const f = files[i];
|
const f = files[i];
|
||||||
const rel = (f as File & { webkitRelativePath?: string }).webkitRelativePath || f.name;
|
const rel = (f as File & { webkitRelativePath?: string }).webkitRelativePath || f.name;
|
||||||
try {
|
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({
|
setProgress({
|
||||||
label: `${f.name} (${i + 1}/${total})`,
|
label: f.name,
|
||||||
pct: ((i + filePct / 100) / total) * 100,
|
// 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",
|
kind: "upload",
|
||||||
})
|
detail: `${i + 1} / ${total} files · ${formatBytes(sent)} / ${formatBytes(totalBytes)}`,
|
||||||
);
|
});
|
||||||
|
});
|
||||||
ok += 1;
|
ok += 1;
|
||||||
} catch {
|
} catch {
|
||||||
failed += 1;
|
failed += 1;
|
||||||
}
|
}
|
||||||
|
doneBytes += f.size;
|
||||||
}
|
}
|
||||||
return { ok, failed };
|
return { ok, failed };
|
||||||
},
|
},
|
||||||
@@ -355,6 +368,9 @@ export function Files() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{progress.detail && (
|
||||||
|
<div className="text-[11px] tabular-nums text-slate-400">{progress.detail}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user