From 98d756faf613adb51a37e56428e484cc9f63ee43 Mon Sep 17 00:00:00 2001 From: menzelj Date: Sun, 21 Jun 2026 20:52:50 +0000 Subject: [PATCH] 0.37.4: fix folder upload doing nothing (set webkitdirectory reliably) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Upload folder" silently did nothing: the directory-selection attribute was set on the hidden via a JSX spread ({...{webkitdirectory:"", directory:""}}), which React doesn't reliably apply to the DOM โ€” and if isAdmin resolves after first render, a one-shot effect would miss the input mounting entirely. Without the attribute the picker is a plain file picker where no folder can be selected, so the user picks nothing and nothing happens. - Set webkitdirectory/directory/mozdirectory imperatively through a callback ref, which runs whenever the input mounts. folderInput is now a MutableRefObject so the callback can populate it. - Folder upload now shows the progress bar immediately on start (small files can finish before the browser emits any upload-progress event, so don't wait for the first one to render feedback). Frontend-only; all 3 images rebuilt+pushed 0.37.4. Co-Authored-By: Claude Opus 4.8 --- backend/version.py | 2 +- frontend/package.json | 2 +- frontend/src/pages/Files.tsx | 32 +++++++++++++++++++++++++++----- 3 files changed, 29 insertions(+), 7 deletions(-) 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);