diff --git a/backend/version.py b/backend/version.py index cf4db34..83dd98f 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.3" +APP_VERSION = "0.37.4" diff --git a/frontend/package.json b/frontend/package.json index aac6442..25fd7e8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "stackpilot-frontend", "private": true, - "version": "0.37.3", + "version": "0.37.4", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/pages/Files.tsx b/frontend/src/pages/Files.tsx index 93496a0..11a2b3a 100644 --- a/frontend/src/pages/Files.tsx +++ b/frontend/src/pages/Files.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Folder, @@ -92,7 +92,22 @@ export function Files() { detail?: string; } | null>(null); const fileInput = useRef(null); - const folderInput = useRef(null); + const folderInput = useRef(null); + + // Callback ref for the folder . Enables directory selection + // imperatively: setting `webkitdirectory` via JSX is unreliable (React + // doesn't reliably apply the non-standard attribute), and without it the + // picker is a plain file picker where no folder can be chosen — so "Upload + // folder" silently does nothing. A callback ref also runs whenever the input + // mounts (e.g. once `isAdmin` resolves), which a one-shot effect would miss. + const folderInputRef = useCallback((el: HTMLInputElement | null) => { + folderInput.current = el; + if (el) { + el.setAttribute("webkitdirectory", ""); + el.setAttribute("directory", ""); + el.setAttribute("mozdirectory", ""); + } + }, []); const agents = useQuery({ queryKey: ["agents"], @@ -155,6 +170,14 @@ export function Files() { let ok = 0; let failed = 0; let doneBytes = 0; // bytes of fully-completed files + // Show the bar immediately — small files may finish before the browser + // emits any upload-progress event, so don't wait for the first one. + setProgress({ + label: files[0]?.name ?? "", + pct: 0, + kind: "upload", + detail: `0 / ${total} files · ${formatBytes(0)} / ${formatBytes(totalBytes)}`, + }); for (let i = 0; i < files.length; i++) { const f = files[i]; const rel = (f as File & { webkitRelativePath?: string }).webkitRelativePath || f.name; @@ -315,13 +338,12 @@ export function Files() { if (f) upload.mutate(f); }} /> + {/* webkitdirectory is set imperatively via the callback ref. */} )} onChange={(e) => { const files = Array.from(e.target.files ?? []); if (files.length) uploadFolder.mutate(files);