Skip to content

feat: drag-and-drop file paths into terminals (Terminal.app parity)#1

Open
KickingTheTV wants to merge 1 commit into
simion:mainfrom
KickingTheTV:feat/terminal-drag-drop-paths
Open

feat: drag-and-drop file paths into terminals (Terminal.app parity)#1
KickingTheTV wants to merge 1 commit into
simion:mainfrom
KickingTheTV:feat/terminal-drag-drop-paths

Conversation

@KickingTheTV
Copy link
Copy Markdown

What

Dropping a file (e.g. a screenshot) onto a terminal now inserts the file's escaped path at the prompt, matching macOS Terminal.app / iTerm2. This is the move you reach for to hand an agent a screenshot — drag it in, the path lands where you're typing, and claude/gemini/codex read it from there. Before this, dropping a file did nothing.

Why not HTML5 drag-and-drop

Tauri's native file-drop (the window's dragDropEnabled, which defaults to true) intercepts the OS drag before the WKWebView's HTML5 drop event would fire — and WKWebView won't expose a dropped File's real filesystem path anyway. So the browser DnD route is a dead end on both counts. Tauri's native onDragDropEvent is the only source of the absolute paths (plus a physical-pixel drop point). Nothing happened on drop before simply because that native event had no listener.

Implementation (frontend-only — no Rust, config, or capability changes)

  • src/lib/terminalDrop.ts (new): one app-wide onDragDropEvent listener + a host element → pty-id registry. On drop it walks up from the drop point to the terminal under the cursor and writes the escaped path(s) through the same ipc.ptyWrite channel as a keystroke — so to the agent CLI it's indistinguishable from typing. Drop position is converted physical→CSS px via devicePixelRatio for elementFromPoint.
  • TerminalPane.tsx / AuxTerminal.tsx: register their xterm host on mount, unregister on teardown. The registry stores a getter for the pty id, so a Restart that mints a fresh PTY is picked up without re-registering.
  • main.tsx: initialise the single listener once at startup (idempotent against HMR re-eval).
  • index.css: a layout-safe inset-ring highlight (box-shadow, so it can't perturb xterm's fit()/cell grid) on the terminal under the cursor during a drag.

Paths are backslash-escaped like Terminal.app (spaces, parens, $, & all survive), so they work both at an agent prompt and in a plain shell. Multiple files are space-joined; a trailing space is appended.

Testing

Manually verified on macOS (Tauri 2.11, dev build):

  • Drag a screenshot onto an agent pane → accent ring appears during drag, escaped path lands at the prompt on drop.
  • Works on the scratch shell (AuxTerminal) too.
  • Paths with spaces/parens escape correctly.

Note: the registry approach means any future terminal surface gets drop support by registering its host. The same ptyWrite plumbing could also back a clipboard-image paste later.

🤖 Generated with Claude Code

Dropping a file (e.g. a screenshot) onto a terminal now inserts the file's
escaped path at the prompt, matching macOS Terminal.app / iTerm2. This is the
move you reach for to hand an agent a screenshot — drag it in, the path lands
where you're typing, and claude/gemini/codex read it from there.

Why a new path rather than HTML5 drag-and-drop: Tauri's native file-drop
(window `dragDropEnabled`, default true) intercepts the OS drag before the
WKWebView's `drop` event fires, and WKWebView won't expose a dropped File's
real filesystem path anyway. Tauri's native `onDragDropEvent` is the only
source of the absolute paths (plus a physical-pixel drop point), so nothing
happened on drop before — the event had no listener.

Implementation (frontend-only; no Rust/config/capability changes):
- src/lib/terminalDrop.ts: one app-wide onDragDropEvent listener + a
  host-element -> pty-id registry. On drop it walks up from the drop point to
  the terminal under the cursor and writes the escaped path(s) through the
  same ipc.ptyWrite channel as a keystroke. Drop position is converted from
  physical to CSS px (devicePixelRatio) for elementFromPoint.
- TerminalPane / AuxTerminal: register their xterm host on mount, unregister
  on teardown (getter reads ptyRef lazily so Restart's fresh PTY still works).
- main.tsx: initialise the listener once at startup.
- index.css: a layout-safe inset-ring highlight on the hovered terminal.

Paths are backslash-escaped like Terminal.app (spaces, parens, $, & survive),
so they work both at an agent prompt and in a plain shell. Multiple files are
space-joined; a trailing space is appended.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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