Skip to content

Comments

feat(tui): improve clipboard paste and drag-and-drop file handling on Linux#14552

Open
RobertWsp wants to merge 7 commits intoanomalyco:devfrom
RobertWsp:feat/tui-paste-and-drop
Open

feat(tui): improve clipboard paste and drag-and-drop file handling on Linux#14552
RobertWsp wants to merge 7 commits intoanomalyco:devfrom
RobertWsp:feat/tui-paste-and-drop

Conversation

@RobertWsp
Copy link

@RobertWsp RobertWsp commented Feb 21, 2026

Issue for this PR

Closes #14555
Also addresses #4668, #6331, #10154

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Fixes TUI paste and drag-and-drop to properly handle multiple file formats and terminal emulators on Linux.

clipboard.ts — Dynamic MIME discovery: The previous implementation hardcoded image/png as the only clipboard format. This replaces it with a priority-ordered probe (PNG → JPEG → WebP → GIF → BMP) that reads the first available format from the system clipboard via xclip/xsel. Falls back gracefully when no image is available.

prompt/index.tsx — Rewritten onPaste handler: The core change. When text is pasted, the handler now checks if the pasted content looks like file paths before treating it as plain text:

  1. Multi-line paths (newline-separated, from Nautilus/Thunar file managers): splits by newline, validates each line is an existing path via Filesystem.exists(), then routes images to pasteImage() and other files to the new pasteFile().

  2. Single-line space-separated paths (WezTerm DroppedFile format): WezTerm joins multiple dropped file paths with spaces and backslash-escapes spaces within filenames (quote_dropped_files = "SpacesOnly" is the Linux default). A new shellTokens() function parses this format by splitting on unescaped spaces and unescaping \ .

  3. Single file path: detected with synchronous existsSync() check for fast-path routing.

  4. pasteFile(): new function that handles non-image attachments (PDFs, SVGs, code files) by creating [File: name.ext] virtual tokens, and directories as [Dir: dirname/].

Critical fix — synchronous event.preventDefault(): onPaste is async, but event.preventDefault() must be called before any await to prevent the browser's default paste behavior (inserting raw text) from executing during the async gap. This is called immediately after the synchronous heuristic check (all tokens look like paths), before any filesystem I/O.

The all-or-nothing validation ensures regular text paste is unaffected: if ANY token doesn't look like a path or doesn't exist on disk, the entire paste falls through to normal text insertion.

How did you verify your code works?

Tested on Zorin OS 18 (Ubuntu Noble, X11) with WezTerm nightly 20260117-154428-05343b38:

  • Multi-file drag-and-drop from Nautilus → only virtual tokens appear, no raw paths
  • Single file drag-and-drop → only virtual token appears
  • Filenames with spaces and special characters (e.g. Projeto (12).png) parsed correctly
  • Clipboard paste (Ctrl+V) with PNG and JPEG images
  • Regular text paste unaffected
  • Non-existent paths fall through to text paste

Screenshots / recordings

N/A — TUI terminal behavior, not a visual UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

event.preventDefault() must be called synchronously before yielding
to the event loop. The previous code called it after awaiting
Filesystem.exists(), allowing the browser default paste to insert raw
file paths into the textarea alongside the virtual extmarks.

Fix: add a synchronous heuristic check (paths start with /, ~/, or
file://) to call preventDefault() immediately, then validate existence
asynchronously. If async validation fails, manually insert text as
fallback since the default paste was already prevented.
Single file paths for PDFs, code files, and directories were falling
through to the text handler because only image MIME types were checked.
Uses existsSync for synchronous preventDefault before any await.
WezTerm sends dropped files as space-separated paths with backslash-
escaped spaces (quote_dropped_files=SpacesOnly), not newline-separated.
Add shellTokens() parser to handle all WezTerm quoting modes and
integrate it into onPaste so multi-file DnD routes to pasteImage/
pasteFile correctly. Also removes debug logging from prior session.
Move event.preventDefault() before await in the WezTerm space-separated
DnD branch to prevent raw path text from being inserted during the async
gap. Add text fallback when file existence check fails.
@github-actions github-actions bot added needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Feb 21, 2026
@github-actions
Copy link
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions
Copy link
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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.

[FEATURE]: improve TUI clipboard paste and drag-and-drop file handling on Linux

1 participant