0.37.7: surface folder-upload diagnostics (find why it does nothing)

Folder upload still reported as doing nothing, and without browser access
the failure point is invisible. Make every outcome visible on-screen:

- onChange: if the folder picker returns 0 files, toast an error; otherwise
  toast "Starting folder upload: N file(s)…" so it's clear the upload fired
  (independent of the progress bar rendering).
- Per-file failures are no longer swallowed: capture the first error and
  show it in the result toast ("Uploaded X, Y failed — <path>: <reason>").

This pinpoints whether the picker returns nothing, the upload never starts,
or the requests fail (and why). Frontend-only; all 3 images pushed 0.37.7.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-21 21:30:46 +00:00
co-authored by Claude Opus 4.8
parent 415ebb733a
commit 5c46e40866
3 changed files with 14 additions and 7 deletions
+1 -1
View File
@@ -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.6" APP_VERSION = "0.37.7"
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "stackpilot-frontend", "name": "stackpilot-frontend",
"private": true, "private": true,
"version": "0.37.6", "version": "0.37.7",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+12 -5
View File
@@ -189,6 +189,7 @@ export function Files() {
let ok = 0; let ok = 0;
let failed = 0; let failed = 0;
let doneBytes = 0; // bytes of fully-completed files let doneBytes = 0; // bytes of fully-completed files
let firstError = ""; // surfaced so failures aren't silent
// Show the bar immediately — small files may finish before the browser // Show the bar immediately — small files may finish before the browser
// emits any upload-progress event, so don't wait for the first one. // emits any upload-progress event, so don't wait for the first one.
setProgress({ setProgress({
@@ -213,15 +214,16 @@ export function Files() {
}); });
}); });
ok += 1; ok += 1;
} catch { } catch (err) {
failed += 1; failed += 1;
if (!firstError) firstError = `${rel}: ${apiErrorMessage(err)}`;
} }
doneBytes += f.size; doneBytes += f.size;
} }
return { ok, failed }; return { ok, failed, firstError };
}, },
onSuccess: ({ ok, failed }, files) => { onSuccess: ({ ok, failed, firstError }, files) => {
if (failed) toast.warning(`Uploaded ${ok} file(s), ${failed} failed`); if (failed) toast.error(`Uploaded ${ok} file(s), ${failed} failed${firstError}`);
else toast.success(`Uploaded ${ok} file(s)`); else toast.success(`Uploaded ${ok} file(s)`);
revealIfHidden( revealIfHidden(
...files.map((f) => (f as File & { webkitRelativePath?: string }).webkitRelativePath || f.name), ...files.map((f) => (f as File & { webkitRelativePath?: string }).webkitRelativePath || f.name),
@@ -368,7 +370,12 @@ export function Files() {
multiple multiple
onChange={(e) => { onChange={(e) => {
const files = Array.from(e.target.files ?? []); const files = Array.from(e.target.files ?? []);
if (files.length) uploadFolder.mutate(files); if (!files.length) {
toast.error("Folder selection returned no files (nothing to upload).");
return;
}
toast.info(`Starting folder upload: ${files.length} file(s)…`);
uploadFolder.mutate(files);
}} }}
/> />
</> </>