0.37.4: fix folder upload doing nothing (set webkitdirectory reliably)
"Upload folder" silently did nothing: the directory-selection attribute
was set on the hidden <input> 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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a43e6b48f0
commit
98d756faf6
+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.3"
|
APP_VERSION = "0.37.4"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "stackpilot-frontend",
|
"name": "stackpilot-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.37.3",
|
"version": "0.37.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -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 { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
Folder,
|
Folder,
|
||||||
@@ -92,7 +92,22 @@ export function Files() {
|
|||||||
detail?: string;
|
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>(null);
|
||||||
|
|
||||||
|
// Callback ref for the folder <input>. 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({
|
const agents = useQuery({
|
||||||
queryKey: ["agents"],
|
queryKey: ["agents"],
|
||||||
@@ -155,6 +170,14 @@ 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
|
||||||
|
// 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++) {
|
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;
|
||||||
@@ -315,13 +338,12 @@ export function Files() {
|
|||||||
if (f) upload.mutate(f);
|
if (f) upload.mutate(f);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{/* webkitdirectory is set imperatively via the callback ref. */}
|
||||||
<input
|
<input
|
||||||
ref={folderInput}
|
ref={folderInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
className="hidden"
|
className="hidden"
|
||||||
multiple
|
multiple
|
||||||
// webkitdirectory/directory are non-standard but widely supported.
|
|
||||||
{...({ webkitdirectory: "", directory: "" } as Record<string, string>)}
|
|
||||||
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) uploadFolder.mutate(files);
|
||||||
|
|||||||
Reference in New Issue
Block a user