Skip to content

feat: paste files into a Finder folder with desktop_insert, confirmed by their names appearing there (#3) - #114

Merged
iamnbutler merged 2 commits into
mainfrom
feat/finder-paste
Oct 17, 2026
Merged

iamnbutler merged 2 commits into
mainfrom
feat/finder-paste

Conversation

@iamnbutler

@iamnbutler iamnbutler commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

The #3 follow-up: desktop_insert with format: "files" now accepts a Finder folder window as its receiver. It pastes the files with Command-V and confirms the paste when every name appears in the folder, then restores the clipboard.

How it works (native/patches/peekaboo-finder-paste.patch, last in the stack)

  • Route fixed before input. A files payload into a Finder window takes the folder route. The snapshot's focus must not be a text field, text area, search field or combo box (a name being edited, or search). Otherwise the request is refused, since that receiver would take the paste as text.
  • Folder. The host resolves the folder from the focused element's window: AXDocument, then AXURL, with short AX messaging timeouts. The result must be a file URL naming an existing directory. A window that reports neither is refused before input.
  • Names. Each file's basename must be unique, ignoring case, and absent from the folder by lstat. If it isn't, the request is refused with PASTE_NAME_EXISTS before the clipboard is touched, so Finder's Replace / Keep Both dialog never opens. The check runs again before the clipboard write and in the delivery validator, just before Command-V.
  • Proof. The paste counts as seen once every name is present in the folder, by a host-side filesystem read. The host polls for 5 s after delivery.
    • When seen, the result is completed with consumption: "observed", folder and copy: "started". That is reported separately from consumption: Finder may still be copying contents.
    • The prior clipboard is restored.
    • The same proof backs the paste reservation's pending check while the clipboard still holds the paste's claim, so a slow copy releases the reservation once the names appear.
    • With clipboard: "replace", the clipboard is left replaced, as for other receivers.
  • Delivery. Delivery and the guarded background-window route are unchanged from the text and attachment paste. No new Bridge operation; the wire count is unchanged.
  • Client. The client maps the name refusal (its message ends with FinderPasteRefusal.suffix) to PASTE_NAME_EXISTS with a hint, and passes folder and copy through.

Also

  • The desktop_insert description gains one sentence on Finder. docs/tools.md's "images and files" details gain a Finder bullet, and the "Copy files between Finder folders" recipe now uses desktop_insert.
  • CHANGELOG [Unreleased] → Added bullet.
  • Two live checks (npm run live-check -- --only 114): a 2-file paste into the fixture folder (completed, copy: "started", both files on disk, known clipboard text restored), and a name collision (refused with PASTE_NAME_EXISTS, existing file unchanged, clipboard change count unchanged). They're gated on the build's description mentioning PASTE_NAME_EXISTS.

Verified

  • npm test (70) and npm run check pass. The local native build passes.
  • Peekaboo FinderFolderPasteTests (2 tests: name presence including a dangling symlink, and the refusal suffix) and ClipboardAttachmentWriteTests (3) pass. They ran isolated, with no helpers left running.

Pending: screen locked

Nothing has run live yet. Unverified assumptions, to be checked by the live checks:

  • Finder windows expose AXDocument or AXURL as a file URL.
  • Finder's file list is the snapshot's focused element after a row click.
  • The guarded background paste route works on Finder's window chrome. If it doesn't, Finder needs to be frontmost (desktop_focus first, as the recipe says).

iamnbutler added a commit that referenced this pull request Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant