Skip to content

Dragging a non-image file leaves a shell-escaped path in the composer that no file tool can open #1206

Description

@aphelion31

Summary

Dragging a non-image file into the TUI leaves the composer holding a shell-escaped path, which no file tool can open. Drop /tmp/a b/c d.txt and the composer gets /tmp/a\ b/c\ d.txt. That is correct shell quoting but it is not a path: Read/Edit resolve it literally, look for a directory component containing a backslash, and fail.

So the user drags a file in and the agent cannot read it. Images are unaffected (they become [image N] attachments); this only bites .txt/.rs/.pdf/etc.

Environment

  • macOS 15, aarch64
  • jcode v0.84.0 and current master

Reproduction

$ mkdir -p "/tmp/jcode drop repro" && echo content > "/tmp/jcode drop repro/my notes.txt"

Drag that file from VS Code into the composer. It types:

/tmp/jcode\ drop\ repro/my\ notes.txt
$ [ -f '/tmp/jcode\ drop\ repro/my\ notes.txt' ] && echo opens || echo "does not open"
does not open
$ [ -f '/tmp/jcode drop repro/my notes.txt' ] && echo opens
opens

Root cause

parse_dropped_paths already does the hard part: it resolves shell escapes, '...', "...", and file:// URLs, and verifies each token is an existing file. Those resolved paths are just never written back for non-image drops.

promote_dropped_images (crates/jcode-tui/src/tui/app/input.rs) returns early unless every dropped path is an image:

pub(super) fn promote_dropped_images(app: &mut App) -> bool {
    let Some(images) = dropped_image_files(&app.input) else {
        return false;   // <- a dropped .txt lands here, composer left escaped
    };

dropped_image_files maps over the paths and ?-returns on the first non-image, so one text file discards the whole batch.

Suggested fix

When the composer is exactly dropped paths, rewrite it to the verified paths parse_dropped_paths already produced. Since it confirmed each file exists, the rewrite is lossless. Guards that matter:

  • only fire when the whole composer parses as dropped paths, so prose merely containing a path is never rewritten
  • no-op when the path is already clean, so a typed path does not burn an undo entry
  • keep multi-file drops quoted so they stay separable

Branch with a fix and tests, if useful as a reference: https://github.com/aphelion31/jcode/tree/fix/macos-clipboard-image-applescript

The tests assert Path::is_file() on the resulting composer rather than a string shape, so they pin the actual requirement (openable). Verified against real files whose directory and filename both contain spaces:

[PASS] R1 vscode shell-escaped   -> /tmp/.../my notes.txt
[PASS] R2 single-quoted          [PASS] R3 double-quoted
[PASS] R4 file:// URL            [PASS] R5 clean path untouched
[PASS] R6 prose not rewritten    [PASS] R7 image drop still attaches
[PASS] R8 multi-file separable   [PASS] R9 bracketed-paste route

Neutering the fix flips exactly R1-R4 to FAIL while the guard cases R5-R9 stay PASS, so the tests bind to the fix rather than passing incidentally. cargo test --test e2e is unaffected: 55 pass, and the 3 failures are pre-existing (target/selfdev/jcode missing in my checkout) and reproduce identically on the parent commit.

Notes / non-goals

Deliberately not handled, since neither can be distinguished from a legitimate filename:

  • ~/... — the shell expands it before the terminal sends it, so a real drop never produces it
  • percent-encoded paths without a file:// scheme — indistinguishable from a filename that genuinely contains %20

Filing as an issue rather than a PR because pull_request_creation_policy is collaborators_only, so POST /repos/1jehuang/jcode/pulls returns aphelion31 does not have the correct permissions to execute CreatePullRequest. Happy to open one if that ever changes, or you're welcome to take the diff and rewrite it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    autonomous: clearHands-off: unambiguous bug, obvious fix, no decisions. Don't even look - an agent can fully solve.bugSomething isn't workingtriage: reproducibleClear repro + clear fix path

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions