Repository navigation
iOS image paste: deliver path via bracketed paste so Claude Code attaches [Image #N] - #5602
lawrencecchen wants to merge 1 commit into
Conversation
…ches [Image #N] The iOS image-paste RPC (terminal.paste_image) wrote the clipboard image to a temp file on the Mac and injected the shell-escaped path with sendInputResult, which routes through ghostty_surface_text_input. That API treats the bytes as typed text and explicitly does NOT use bracketed paste mode. Claude Code only runs its pasted-image-path detection from its bracketed-paste handler (the text framed by ESC[200~ ... ESC[201~). With the path arriving as typed text, that handler never fires, so the path lands as a literal string and the image is never attached as [Image #N]. Route the injection through the paste path instead (ghostty_surface_text, the same route a desktop clipboard paste or file drop takes), which wraps the bytes in a bracketed paste when the foreground program has enabled DECSET 2004. This matches the desktop drop behavior that already produces [Image #N]. Non-Claude shells are unaffected: a single-line escaped path has no newline, so it passes Ghostty paste protection and pastes the path as before. Adds TerminalSurface.sendTextResult(_:), a paste-path twin of sendInputResult that keeps the rich InputSendResult queue/availability error mapping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis change introduces a new ChangesPaste-path text delivery and mobile image paste integration
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Possibly related PRs
Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (2 errors, 1 warning)
✅ Passed checks (16 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR fixes iOS image paste not being recognized by Claude Code by switching the delivery route for the image file path from
Confidence Score: 4/5The one-line call-site change is safe and the new helper is a straightforward paste-path wrapper with correct error mapping. The fix correctly identifies and addresses the root cause and handles both live and cold-surface paths. The sendTextResult body duplicates sendText entirely, which is the one issue worth tidying before the change drifts. The duplicated logic between sendText and sendTextResult in GhosttyTerminalView.swift is worth a second look. Important Files Changed
Sequence DiagramsequenceDiagram
participant iOS as iOS App
participant TC as TerminalController
participant TS as TerminalSurface
participant G as Ghostty C API
iOS->>TC: terminal.paste_image (base64)
TC->>TC: saveImageData
TC->>TS: sendTextResult(escapedPath)
alt surface live
TS->>G: ghostty_surface_text()
G-->>TS: bracketed paste delivered
TS-->>TC: .sent
TC->>TS: forceRefresh
else surface cold
TS->>TS: enqueuePendingSocketInput(.pasteText)
TS-->>TC: .queued
end
TC-->>iOS: "ok {queued: Bool}"
|
Problem
Pasting a phone-clipboard image into the iOS terminal injected the image's file path as a readable string, but Claude Code did not attach it as
[Image #N]. It just looked like a pasted path.Root cause
The
terminal.paste_imageRPC (TerminalController.v2MobileTerminalPasteImage) writes the forwarded clipboard image to a temp file on the Mac, shell-escapes the path, then injected it withsendInputResult→ghostty_surface_text_input. Per Ghostty's own API docs, that call "is treated like typed text, not a paste ... bracketed paste mode is not used."Claude Code only runs its pasted-image-path detection from its bracketed-paste handler (the text framed by
ESC[200~ … ESC[201~). Its paste handler strips the bracketed-paste artifacts, splits the pasted text on path boundaries, unescapes shell escaping (so a shell-escaped path is accepted), matches/\.(png|jpe?g|gif|webp)$/i, reads the file, and attaches it as[Image #N]. When the path arrives as ordinary typed text (no bracketing), that handler never fires, so the path lands as a literal string and no image is attached. (Verified against the Claude Code binary's input layer:handlePaste → X(Z)path-detection vs the raw-key path, which only runs the detector for single key events over 800 chars.)The escaping/format was never the problem; the delivery route was. The desktop drop/paste path already produces
[Image #N]because it delivers throughghostty_surface_complete_clipboard_request(the paste path, which respects bracketed paste mode).Fix
Route the mobile image-paste injection through Ghostty's paste path (
ghostty_surface_text), the same route a desktop clipboard paste or file drop takes. This wraps the bytes in a bracketed paste when the foreground program has enabled bracketed paste mode (DECSET 2004), so Claude Code's image-path detection runs.TerminalSurface.sendTextResult(_:), a paste-path twin ofsendInputResultthat preserves the richInputSendResultqueue/availability error mapping (and the cold-surface.pasteTextenqueue path, which already flushes via the paste route).v2MobileTerminalPasteImagenow callssendTextResultinstead ofsendInputResult.Non-Claude shells are unaffected: a single-line escaped path has no newline and no
ESC[201~, so it passes Ghostty paste protection and pastes the path exactly as before.The change is entirely Mac-side (the iOS app forwards the image bytes; the Mac does the injection). Stacks on / relates to the merged image-paste work (#5546).
Testing
A meaningful automated test is not practical here: the bug is in the delivery primitive (
ghostty_surface_textvsghostty_surface_text_input), which is a C-API call on a live Ghostty surface, and the user-visible outcome ([Image #N]) requires Claude Code running with auth. Per the repo test-quality policy, no source-shape regression test was added. Verification is on-device dogfood.Dogfood (on device): copy an image to the iPhone clipboard, open a cmux terminal running
claude, paste the image. Previously: the prompt showed a literal file path. Now: the prompt shows[Image #N].🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Note
Medium Risk
Changes terminal input delivery for mobile image paste and adds a new paste-path API; scope is narrow (one RPC + surface helper) but affects how bytes reach the PTY.
Overview
Fixes iOS-forwarded clipboard images not becoming
[Image #N]in Claude Code by changing how the Mac injects the temp file path, not how the path is escaped.Adds
sendTextResult(_:)on the terminal surface as a paste-path counterpart tosendInputResult: it delivers UTF-8 through Ghostty’s paste API (ghostty_surface_textviawriteTextData), so bracketed paste (DECSET 2004) applies when the foreground program has it enabled. It keeps the sameInputSendResultsemantics (queue when the surface is cold, surface/process errors, etc.), including enqueueing.pasteTextfor background surfaces.v2MobileTerminalPasteImagenow callssendTextResultinstead ofsendInputResult, matching desktop clipboard paste / file drop so TUIs like Claude Code run paste-time image-path detection instead of treating the path as typed text.Reviewed by Cursor Bugbot for commit c626c22. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Fixes iOS image paste so Claude Code attaches images as [Image #N] by sending the temp file path via Ghostty’s paste path (bracketed paste) instead of typed text. Other shells paste the escaped path unchanged.
TerminalSurface.sendTextResult(_:)to deliver viaghostty_surface_text(uses bracketed paste when DECSET 2004 is enabled) while preservingInputSendResultsemantics and queueing.v2MobileTerminalPasteImageto usesendTextResultrather thansendInputResult, matching desktop paste/drop behavior.Written for commit c626c22. Summary will update on new commits.
Summary by CodeRabbit
New Features
Improvements