Repository navigation
fix(dropzone): keep dropped files alive across the handle await (GH-91) - #93
Merged
Merged
Conversation
Chromium invalidates DataTransfer access as soon as the drop handler's first await resolves (drag_controller.cc sets the access policy to kNumb on return), so DataTransfer.files() returns an empty list after that point. handleDrop awaited Promise.all(getAsFileSystemHandle()) before reading e.dataTransfer.files, so the file list was always empty by the time onFilesSelected ran in Chrome/Edge/Brave/Electron -- drops silently did nothing while the file picker (no await in between) worked fine. Extract snapshotDroppedFiles() (src/lib/helpers/drop-files.ts), a pure helper that reads dt.files and starts the getAsFileSystemHandle() promises synchronously, before any await. handleDrop is now a thin caller. onFilesSelected's prop type changes from FileList to readonly File[]; update the three +page.svelte callers accordingly (handleFilesSelected, handleSidebarImport, and the file-input handler) and drop the now-pointless new DataTransfer() round-trips that only existed to keep producing a FileList. Regression test: tests/drop-zone/snapshot-dropped-files.test.ts models the Chromium behaviour with a fake DataTransfer whose .files getter returns [] once an async boundary has been crossed, and asserts the snapshot still has the files afterwards. Closes #91
Electron 32 removed the non-standard File#path property that getElectronFilePath relied on (project is on electron ^39.8.6), so a file dropped or picked in the desktop app never got a path, and fell back to the reselect-on-next-launch flow instead of persisting. Expose webUtils.getPathForFile via the preload script's contextBridge (electron/preload.cjs), declare it on ElectronAPI (src/app.d.ts), and make getElectronFilePath prefer window.electronAPI.getPathForFile(file) over the removed 'path' in file check, which is kept only as a fallback for older Electron builds. getPathForFile returns '' (not undefined) for a File not backed by a real path, so that is normalized to undefined at the same call site. Not runnable on this host: no display server, so the Electron app itself cannot be launched to exercise this manually. Verified via lint/check/build/unit-test only; webUtils.getPathForFile's presence and signature were confirmed against node_modules/electron/electron.d.ts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
FileDropZone.svelte'shandleDropawaitedPromise.all(getAsFileSystemHandle())before readinge.dataTransfer.files. Chromium invalidatesDataTransferaccess as soon as the drop handler's firstawaitresolves (drag_controller.ccsets the access policy tokNumbon return), so.filesreturns an empty list past that point — drops silently did nothing in Chrome/Edge/Brave/Electron while the file picker (no intervening await) kept working. Introduced in #66 when the handler becameasync; the existing comment only protected.items, not.files.Fix
Snapshot
dt.filesand start the handle promises synchronously, before anyawait, in a new pure helpersnapshotDroppedFiles(src/lib/helpers/drop-files.ts).handleDropis now a thin caller.onFilesSelected's prop type moved fromFileListtoreadonly File[]; updated the three+page.sveltecallers and dropped thenew DataTransfer()round-trips that only existed to keep producing aFileList.Second commit:
getElectronFilePathrelied onFile#path, removed in Electron 32 (this project shipselectron ^39.8.6), so dropped/picked files in the desktop app never got a path for persistence. Now preferswindow.electronAPI.getPathForFile(backed bywebUtils.getPathForFile, exposed viacontextBridgein the preload script), falling back to the old'path' in filecheck. Not runnable on the CI/dev host that produced this PR (no display server) — verified via lint/check/build/unit tests only.Mutation evidence (RED → GREEN) for
tests/drop-zone/snapshot-dropped-files.test.tsRED — temporarily changed
snapshotDroppedFilestoawait Promise.resolve()before readingdt.files, reproducing the bug the helper exists to prevent:(The mutation makes
snapshotDroppedFilesreturn a Promise instead of the snapshot object, since the fakeDataTransferin the test isn't awaited by the test's synchronous calls — which is itself proof the mutation broke the "no await before reading files" contract the test enforces.)GREEN — reverted the mutation:
Gates run locally
npm ci— clean installnpm run lint— Checked 134 files, no fixes appliednpm run check— svelte-check found 0 errors and 0 warningsnpm test— Test Files 5 passed (5), Tests 61 passed (61)npm run build— succeedsnpm run test:global-searchneeds Playwright browsers, which cannot launch on this host (no display) — left to CI to prove that leg.Closes #91