Files: pasting multiple files copied in Finder does nothing (paste-to-upload, browser matrix on Mac) #467

Closed
opened 2026-09-29 17:07:08 +00:00 by kayg · 13 comments
Owner

Bug (owner, 2026-09-29)

"Copying several files to the clipboard in Finder and pasting them in the web app → Files → Documents → a folder does not work."

Work

  1. Establish what each browser delivers on the macOS VM (netbird ssh --no-browser calternal@10.69.69.21, cua-driver; hold flock ~/.local/state/codex-jobs/calternal/macvm.lock; the admin login is in ~/calternal-private/macos-vm/credentials.env, never printed): copy 1 file, then 3 files, then a folder, then mixed types, in Finder (⌘C), and paste (⌘V) into a test page that logs clipboardData.files, clipboardData.items (kind, type, getAsFile(), webkitGetAsEntry()) and clipboardData.types. Test in Safari and Chrome (and Firefox if installed). Also test navigator.clipboard.read() from a keyboard shortcut. Record a matrix: what each browser delivers (file contents vs only names, icons or text/uri-list).
  2. Implement paste-to-upload in Files (the list and grid of the current folder, plus the sidebar folder drop targets): read every file the browser gives (files, plus an items fallback with getAsFile, and folders via webkitGetAsEntry recursion where available), and upload through the existing upload queue with progress and conflict handling (Keep both / Replace / Skip, as Finder does). Paste goes to the current folder, or to the selected folder if one is selected.
  3. Where a browser only gives names or icons (no contents), show a helpful toast rather than doing nothing: "Safari only shares file names when pasting from Finder. Drag the files in instead." Never silently ignore a paste.
  4. Proof: an e2e with synthetic clipboard data in Chromium and WebKit, and the real Mac matrix with screenshots of a successful multi-file paste and of the fallback toast. Web gates in full.
## Bug (owner, 2026-09-29) "Copying several files to the clipboard in Finder and pasting them in the web app → Files → Documents → a folder does not work." ## Work 1. **Establish what each browser delivers** on the macOS VM (`netbird ssh --no-browser calternal@10.69.69.21`, cua-driver; hold `flock ~/.local/state/codex-jobs/calternal/macvm.lock`; the admin login is in `~/calternal-private/macos-vm/credentials.env`, never printed): copy 1 file, then 3 files, then a folder, then mixed types, in Finder (⌘C), and paste (⌘V) into a test page that logs `clipboardData.files`, `clipboardData.items` (kind, type, `getAsFile()`, `webkitGetAsEntry()`) and `clipboardData.types`. Test in **Safari** and **Chrome** (and Firefox if installed). Also test `navigator.clipboard.read()` from a keyboard shortcut. Record a matrix: what each browser delivers (file contents vs only names, icons or `text/uri-list`). 2. **Implement paste-to-upload in Files** (the list and grid of the current folder, plus the sidebar folder drop targets): read every file the browser gives (`files`, plus an `items` fallback with `getAsFile`, and folders via `webkitGetAsEntry` recursion where available), and upload through the existing upload queue with progress and conflict handling (Keep both / Replace / Skip, as Finder does). Paste goes to the current folder, or to the selected folder if one is selected. 3. Where a browser only gives names or icons (no contents), show a helpful toast rather than doing nothing: "Safari only shares file names when pasting from Finder. Drag the files in instead." Never silently ignore a paste. 4. **Proof:** an e2e with synthetic clipboard data in Chromium and WebKit, and the real Mac matrix with screenshots of a successful multi-file paste and of the fallback toast. Web gates in full.
Author
Owner

Starting issue #467 on branch job/paste-467 from base 918b4764ad. I am tracing the existing Files paste, upload queue, and browser test paths before implementation.

Starting issue #467 on branch job/paste-467 from base 918b4764aded5aa385ef784acb8887cf4361bf36. I am tracing the existing Files paste, upload queue, and browser test paths before implementation.
Author
Owner

Finding for #467: apps/web/src/lib/files/FilesBrowser.svelte intercepts Cmd/Ctrl+V and calls pasteItems(path) even when the internal cut buffer is empty, so a native Finder paste event cannot reach the Files view. apps/web/src/lib/files/uploads.svelte.ts collects only DataTransfer.items and defaults tus uploads to rename; it does not use DataTransfer.files as the paste source or expose Replace/Skip choices. The backend already accepts replace as a conflict policy, so this can stay within the existing Files API and upload queue.

Finding for #467: apps/web/src/lib/files/FilesBrowser.svelte intercepts Cmd/Ctrl+V and calls pasteItems(path) even when the internal cut buffer is empty, so a native Finder paste event cannot reach the Files view. apps/web/src/lib/files/uploads.svelte.ts collects only DataTransfer.items and defaults tus uploads to rename; it does not use DataTransfer.files as the paste source or expose Replace/Skip choices. The backend already accepts replace as a conflict policy, so this can stay within the existing Files API and upload queue.
Author
Owner

Mac VM inventory for #467: CuaDriver and Safari are installed, but cua-driver call list_apps reports no Chrome or Firefox. I will add Google Chrome to the VM user Applications folder so the requested Safari and Chrome matrix can run; Firefox is not installed.

Mac VM inventory for #467: CuaDriver and Safari are installed, but `cua-driver call list_apps` reports no Chrome or Firefox. I will add Google Chrome to the VM user Applications folder so the requested Safari and Chrome matrix can run; Firefox is not installed.
Author
Owner

Mac Finder clipboard matrix, 2026-09-29 (Finder ⌘C; test page ⌘V)

Browser One file Three files Folder Mixed folder + PNG navigator.clipboard.read()
Chrome 154.0.8037.58 clipboardData.types is Files; files and items each contain the file bytes; getAsFile() and file entry work. Files; all three names, sizes, MIME types, getAsFile() values, and file entries are present. One File named Folder item with empty MIME type; webkitGetAsEntry() reports isDirectory: true. The folder is an empty-MIME entry with isDirectory: true; Mixed image.png is a 38-byte image/png file. The keyboard shortcut returned one ClipboardItem with no exposed types and no blobs. The browser permission prompt was allowed for the local probe.
Safari clipboardData.types is Files; files and items each contain the file bytes; getAsFile() and file entry work. Files; all three files appear in files and items, with MIME types and file entries. One zero-byte, empty-MIME File named Folder item; webkitGetAsEntry() reports isDirectory: true. The folder entry reports isDirectory: true; Mixed image.png is a 38-byte image/png file. Tried from a page keyboard shortcut. Safari's reload shortcut conflicted with the first binding; the replacement shortcut did not produce a readable record, so Safari's async read result is unconfirmed.

Firefox was not installed on the VM. The native probe screenshots are in the worktree under artifacts/paste-467/ and will be attached with the final evidence.

Mac Finder clipboard matrix, 2026-09-29 (Finder ⌘C; test page ⌘V) | Browser | One file | Three files | Folder | Mixed folder + PNG | navigator.clipboard.read() | | --- | --- | --- | --- | --- | --- | | Chrome 154.0.8037.58 | `clipboardData.types` is `Files`; `files` and `items` each contain the file bytes; `getAsFile()` and file entry work. | `Files`; all three names, sizes, MIME types, `getAsFile()` values, and file entries are present. | One `File` named `Folder item` with empty MIME type; `webkitGetAsEntry()` reports `isDirectory: true`. | The folder is an empty-MIME entry with `isDirectory: true`; `Mixed image.png` is a 38-byte `image/png` file. | The keyboard shortcut returned one `ClipboardItem` with no exposed types and no blobs. The browser permission prompt was allowed for the local probe. | | Safari | `clipboardData.types` is `Files`; `files` and `items` each contain the file bytes; `getAsFile()` and file entry work. | `Files`; all three files appear in `files` and `items`, with MIME types and file entries. | One zero-byte, empty-MIME `File` named `Folder item`; `webkitGetAsEntry()` reports `isDirectory: true`. | The folder entry reports `isDirectory: true`; `Mixed image.png` is a 38-byte `image/png` file. | Tried from a page keyboard shortcut. Safari's reload shortcut conflicted with the first binding; the replacement shortcut did not produce a readable record, so Safari's async read result is unconfirmed. | Firefox was not installed on the VM. The native probe screenshots are in the worktree under `artifacts/paste-467/` and will be attached with the final evidence.
Author
Owner

Finding from the 2026-09-29 Finder matrix: pasting one folder into the Mac probe reports both a FileList item named Folder item and an item whose webkitGetAsEntry() result has isDirectory: true. Safari reports the FileList placeholder as 0 bytes; Chrome reported 128 bytes. collectPaste currently queues every FileList value and also walks directory entries, so it would upload the placeholder as a file beside the folder tree. I am filtering the matching directory placeholder and adding a regression test before the production e2e.

Finding from the 2026-09-29 Finder matrix: pasting one folder into the Mac probe reports both a FileList item named `Folder item` and an item whose `webkitGetAsEntry()` result has `isDirectory: true`. Safari reports the FileList placeholder as 0 bytes; Chrome reported 128 bytes. `collectPaste` currently queues every FileList value and also walks directory entries, so it would upload the placeholder as a file beside the folder tree. I am filtering the matching directory placeholder and adding a regression test before the production e2e.
Author
Owner

Fixed and committed as c88c9d11 (Skip Finder folder placeholder uploads). collectPaste now filters a FileList entry whose name matches a directory entry, then recurses the directory entry. Regression test: Test Files 1 passed (1); Tests 9 passed (9). bun run check: svelte-check found 0 errors and 0 warnings.

Fixed and committed as `c88c9d11` (`Skip Finder folder placeholder uploads`). `collectPaste` now filters a FileList entry whose name matches a directory entry, then recurses the directory entry. Regression test: `Test Files 1 passed (1); Tests 9 passed (9)`. `bun run check`: `svelte-check found 0 errors and 0 warnings`.
Author
Owner

Finder paste e2e finding: the conflict dialog opens, and Keep both passes, but after choosing Replace the download route still returns the seeded old Replace bytes instead of the pasted new replace bytes. I am tracing the queue's per-file policy and will add a regression before the next e2e run.

Finder paste e2e finding: the conflict dialog opens, and Keep both passes, but after choosing Replace the download route still returns the seeded `old Replace` bytes instead of the pasted `new replace` bytes. I am tracing the queue's per-file policy and will add a regression before the next e2e run.
Author
Owner

The production e2e now passes Chromium, including Keep both, Replace (downloaded bytes match the new file), and Skip. WebKit consumes the first synthetic paste but the upload queue does not complete. I am checking the WebKit DataTransfer entry path and adding a getAsFile fallback where the optional entry method throws.

The production e2e now passes Chromium, including Keep both, Replace (downloaded bytes match the new file), and Skip. WebKit consumes the first synthetic paste but the upload queue does not complete. I am checking the WebKit DataTransfer entry path and adding a getAsFile fallback where the optional entry method throws.
Author
Owner

Resuming issue #467 on job/paste-467 from base 369ab6a2f9fc673e3564b94857fbecfeb04df404 (the current origin/dev merge base). The branch already contains six implementation/proof commits; I am reviewing the pending documentation edits, real Mac browser matrix evidence, and final gates.

Resuming issue #467 on `job/paste-467` from base `369ab6a2f9fc673e3564b94857fbecfeb04df404` (the current `origin/dev` merge base). The branch already contains six implementation/proof commits; I am reviewing the pending documentation edits, real Mac browser matrix evidence, and final gates.
Author
Owner

Real Mac proof captured in Safari on 2026-09-29. Finder copied three files into Files → Documents → paste-mac; the app shows one.txt, two.txt and three.txt. A names-only paste shows the Safari fallback toast.

Safari multi-file Finder paste succeeded

Safari names-only Finder paste fallback

Real Mac proof captured in Safari on 2026-09-29. Finder copied three files into Files → Documents → paste-mac; the app shows `one.txt`, `two.txt` and `three.txt`. A names-only paste shows the Safari fallback toast. ![Safari multi-file Finder paste succeeded](https://git.kayg.org/attachments/f80943fd-62e7-44a2-8d62-13a6225fbdb1) ![Safari names-only Finder paste fallback](https://git.kayg.org/attachments/8ce579ef-5ff9-41a0-b2f1-f4176dda300e)
Author
Owner

Finished (#467) — head 8dd910b75de4b07f01970fd9290cf7ba6a3b92aa

Branch job/paste-467, merged with origin/dev (8dd910b75). Not pushed or merged into dev.

Real Mac proof (macOS 27 VM, 2026-09-30, production build)

Four files copied in Finder (select all, Cmd+C: mac-alpha.txt, mac-beta.txt, mac-gamma.txt, mac-delta.png). Then Cmd+V in Files → Documents → . The server listing was checked after each paste.

Browser Finder clipboard seen by the page Result in Files
Safari (macOS 27) Files; all files, with bytes Pass: 4 of 4 uploaded, sizes 36/35/8/36 bytes are correct
Chrome 154.0.8037.58 Files; all files, with bytes Pass: 4 of 4 uploaded. A second paste opens "Some files already exist" (Keep both / Replace / Skip / Cancel). Return = Keep both gives mac-alpha 2.txt and so on (8 files)
Firefox 157.0 Files; only the first file (mac-alpha.txt, 36 bytes), with no text or URI data for the others Partial (browser limit): the first file uploads. The new hint "Firefox pastes only the first file copied in Finder. Drag the files in to upload all of them." is shown (Dismiss, 30 s)

Firefox probe page output: types: ["Files"], files: [mac-alpha.txt 36 B], items: [file text/plain, entry mac-alpha.txt]. The page cannot get the other three files, so the app cannot upload them.

Safari: 4 Finder files pasted
Chrome: 4 Finder files pasted
Chrome: conflict dialog on the second paste
Firefox: first file uploaded + hint
Firefox probe: only one File from a 4-file Finder copy

E2E evidence (production build, synthetic clipboard): desktop light success phone dark names-only fallback. The full 390/820/1440 × light/dark set is in artifacts/paste-467/paste-{success,fallback}-*.png.

Changes in this session

  • a4d5024ef: remove the unused Collected upload type.
  • 3beb6d4db: Firefox-on-macOS single-file hint, with an e2e case. The e2e uses the Firefox UA in Chromium: a 2-file paste shows no hint, and a 1-file paste uploads and shows the hint.
  • a257a3a46: the hint follows the upload toast, so it stacks in front. It has a Dismiss button. On the real Mac, the 4 s confirmation expired behind the upload capsule.

E2E: bun run test:e2e:files-paste (Chromium + WebKit, synthetic multi-file ClipboardEvent)

Version 1.63.0
CSP REPORTS files-paste: 0 across 3 pages
FILES PASTE E2E PASSED

Gates (merged head)

bun run check

Text sizes use shared role tokens.
COMPLETED 1908 FILES 0 ERRORS 0 WARNINGS 0 FILES_WITH_PROBLEMS

bun run test

 Test Files  131 passed (131)
      Tests  851 passed (851)

There are no Rust changes on this branch, so no cargo gates were run.

Decisions (please confirm)

  • Firefox cannot deliver more than one Finder file on paste. We upload the file it gives and show a hint only when Firefox on macOS delivers exactly one file. When one file was really copied, the hint also shows. It is a harmless extra message, and the page cannot tell the two cases apart.
  • Mac test setup: Firefox 157 was installed in ~/Applications on the VM. The Mac proofs used a throwaway local instance. Safari and Firefox used HTTPS with a self-signed cert (click-through only; no trust store change). Chrome used a CDP cookie injection. No product code was changed for the setup.
## Finished (#467) — head `8dd910b75de4b07f01970fd9290cf7ba6a3b92aa` Branch `job/paste-467`, merged with `origin/dev` (`8dd910b75`). Not pushed or merged into dev. ### Real Mac proof (macOS 27 VM, 2026-09-30, production build) Four files copied in Finder (select all, Cmd+C: `mac-alpha.txt`, `mac-beta.txt`, `mac-gamma.txt`, `mac-delta.png`). Then Cmd+V in Files → Documents → <folder>. The server listing was checked after each paste. | Browser | Finder clipboard seen by the page | Result in Files | | --- | --- | --- | | Safari (macOS 27) | `Files`; all files, with bytes | **Pass**: 4 of 4 uploaded, sizes 36/35/8/36 bytes are correct | | Chrome 154.0.8037.58 | `Files`; all files, with bytes | **Pass**: 4 of 4 uploaded. A second paste opens "Some files already exist" (Keep both / Replace / Skip / Cancel). Return = Keep both gives `mac-alpha 2.txt` and so on (8 files) | | Firefox 157.0 | `Files`; **only the first file** (`mac-alpha.txt`, 36 bytes), with no text or URI data for the others | **Partial (browser limit)**: the first file uploads. The new hint "Firefox pastes only the first file copied in Finder. Drag the files in to upload all of them." is shown (Dismiss, 30 s) | Firefox probe page output: `types: ["Files"]`, `files: [mac-alpha.txt 36 B]`, `items: [file text/plain, entry mac-alpha.txt]`. The page cannot get the other three files, so the app cannot upload them. ![Safari: 4 Finder files pasted](https://git.kayg.org/attachments/b833e500-e3a9-49fb-a91e-c2d475fa2ca8) ![Chrome: 4 Finder files pasted](https://git.kayg.org/attachments/e28f0e09-4b9c-40e0-96d5-9f9624cd4e75) ![Chrome: conflict dialog on the second paste](https://git.kayg.org/attachments/1696008b-1bf5-4d69-bd3f-4630d31ec5de) ![Firefox: first file uploaded + hint](https://git.kayg.org/attachments/1155568f-ebfc-4934-9eab-aad7488e57d4) ![Firefox probe: only one File from a 4-file Finder copy](https://git.kayg.org/attachments/541bdaa9-243f-4bcd-85e4-ff949dbe9528) E2E evidence (production build, synthetic clipboard): ![desktop light success](https://git.kayg.org/attachments/2e3f5a83-8fa3-433d-99e6-2cd378d85c5f) ![phone dark names-only fallback](https://git.kayg.org/attachments/bd8c6533-da0a-4c9f-ab6e-7f3e0eaa89bc). The full 390/820/1440 × light/dark set is in `artifacts/paste-467/paste-{success,fallback}-*.png`. ### Changes in this session - `a4d5024ef`: remove the unused `Collected` upload type. - `3beb6d4db`: Firefox-on-macOS single-file hint, with an e2e case. The e2e uses the Firefox UA in Chromium: a 2-file paste shows no hint, and a 1-file paste uploads and shows the hint. - `a257a3a46`: the hint follows the upload toast, so it stacks in front. It has a Dismiss button. On the real Mac, the 4 s confirmation expired behind the upload capsule. ### E2E: `bun run test:e2e:files-paste` (Chromium + WebKit, synthetic multi-file ClipboardEvent) ``` Version 1.63.0 CSP REPORTS files-paste: 0 across 3 pages FILES PASTE E2E PASSED ``` ### Gates (merged head) `bun run check` ``` Text sizes use shared role tokens. COMPLETED 1908 FILES 0 ERRORS 0 WARNINGS 0 FILES_WITH_PROBLEMS ``` `bun run test` ``` Test Files 131 passed (131) Tests 851 passed (851) ``` There are no Rust changes on this branch, so no cargo gates were run. ### Decisions (please confirm) - Firefox cannot deliver more than one Finder file on paste. We upload the file it gives and show a hint only when Firefox on macOS delivers exactly one file. When one file was really copied, the hint also shows. It is a harmless extra message, and the page cannot tell the two cases apart. - Mac test setup: Firefox 157 was installed in `~/Applications` on the VM. The Mac proofs used a throwaway local instance. Safari and Firefox used HTTPS with a self-signed cert (click-through only; no trust store change). Chrome used a CDP cookie injection. No product code was changed for the setup.
Author
Owner

Merged into dev at 412ad2136 and pushed.

Merged into dev at 412ad2136 and pushed.
kayg closed this issue 2026-09-30 02:25:45 +00:00
Author
Owner

Owner confirmed the current behaviour (2026-10-01).

Owner confirmed the current behaviour (2026-10-01).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#467
No description provided.