From 76f36876e425983dff8c5c889ba9c9fd999f0d14 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 16:16:02 +0000 Subject: [PATCH 1/2] Initial plan From dc9aec2bab43e7b6ef2f85205a1d9742109aa78d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 16:19:06 +0000 Subject: [PATCH 2/2] fix(upload): fix multi-file drag-and-drop only uploading one file Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- frontend/static/js/upload.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/frontend/static/js/upload.js b/frontend/static/js/upload.js index 22f8332d..7b95ff62 100644 --- a/frontend/static/js/upload.js +++ b/frontend/static/js/upload.js @@ -214,22 +214,40 @@ async function traverseFileEntry(entry, files) { * Extract all File objects from a DataTransfer, recursively expanding any * dropped directories. Falls back gracefully to dataTransfer.files when the * FileSystem Entry API is unavailable (Safari < 11.1, some mobile browsers). + * + * IMPORTANT: DataTransferItemList is only accessible synchronously during the + * drop event handler. All entries and fallback File objects must be collected + * in a single synchronous pass before any `await`, otherwise the browser clears + * the list after the first yield — causing only the first file to be captured + * when multiple files are dropped. + * * @param {DataTransfer} dataTransfer * @returns {Promise} */ async function getFilesFromDataTransfer(dataTransfer) { if (dataTransfer.items && dataTransfer.items.length > 0) { - const files = []; + // ── Synchronous pass ─────────────────────────────────────────────────── + // Collect all FileSystemEntry objects and any plain File fallbacks NOW, + // before the first `await`, while the DataTransferItemList is still valid. + const entries = []; + const fallbackFiles = []; for (let i = 0; i < dataTransfer.items.length; i++) { const item = dataTransfer.items[i]; const entry = item.webkitGetAsEntry ? item.webkitGetAsEntry() : null; if (entry) { - await traverseFileEntry(entry, files); + entries.push(entry); } else if (item.kind === 'file') { const file = item.getAsFile(); - if (file) files.push(file); + if (file) fallbackFiles.push(file); } } + // ── Asynchronous traversal ───────────────────────────────────────────── + // DataTransferItemList is no longer needed here; we work only with the + // already-captured FileSystemEntry objects and File objects. + const files = [...fallbackFiles]; + for (const entry of entries) { + await traverseFileEntry(entry, files); + } return files; } return Array.from(dataTransfer.files || []);