Skip to content

fix(shell): paste through terminal.paste() and stop swallowing native paste - #1007

Open
mayankdebnath wants to merge 3 commits into
siteboon:mainfrom
mayankdebnath:fix/shell-paste-bracketed
Open

fix(shell): paste through terminal.paste() and stop swallowing native paste#1007
mayankdebnath wants to merge 3 commits into
siteboon:mainfrom
mayankdebnath:fix/shell-paste-bracketed

Conversation

@mayankdebnath

@mayankdebnath mayankdebnath commented Jul 12, 2026

Copy link
Copy Markdown

Summary

Fixes #1006 — two independent paste bugs in the Shell terminal:

  1. Multi-line pastes executed line-by-line. Both paste paths (Ctrl/Cmd+V
    handler and the mobile shortcuts-panel Paste button) sent raw clipboard text
    as a socket input message, bypassing xterm's bracketed-paste wrapping
    (DECSET 2004) and \n\r normalization. Apps that enable bracketed paste
    — Claude Code's CLI, modern shells, vim — treated every newline as Enter:
    pasting a 5-line prompt into claude submitted it 5 times.
  2. Cmd+V / Ctrl+Shift+V pasted nothing on plain-HTTP deployments. The key
    handler unconditionally cancelled the chord but only injected text when
    navigator.clipboard.readText exists — which it doesn't outside secure
    contexts, i.e. on the common self-hosted http://<lan-ip>:3001 setup. The
    cancelled keydown also suppressed the browser's native paste event that
    would have reached xterm's hidden textarea and pasted fine. (Right-click →
    Paste always worked, because only the keydown was intercepted.)

The fix

  • Route both paths through terminal.paste(text). xterm applies bracketed-
    paste wrapping and newline normalization, then emits onData, which already
    flows to the pty through the existing socket input subscription — so the wire
    protocol is unchanged.
  • In the key handler, only intercept the paste chord when the async clipboard
    API is actually usable; otherwise return true so the native paste event
    reaches xterm's textarea (xterm handles bracketed paste for that path
    itself). Plain Ctrl+V (no Shift) intentionally remains xterm's literal ^V,
    matching normal terminal semantics.

Two files, no dependency or protocol changes.

Verification

  • npm run lint (0 errors), npm run typecheck, npm run build, full
    node:test suite — all pass.
  • Behavior of terminal.paste() confirmed against the bundled xterm 5.5.0
    source: replace(/\r?\n/g, "\r") plus \x1b[200~…\x1b[201~ wrapping when
    DECSET 2004 is active, emitted via the normal data event.

Related history: #767 (macOS Cmd+V paste failing in the terminal).

Summary by CodeRabbit

  • Bug Fixes
    • Improved terminal paste handling when clipboard access is unavailable by allowing native paste to proceed.
    • Updated desktop and mobile paste actions to preserve terminal formatting, including bracketed-paste behavior and newline normalization.
    • Improved compatibility with terminal applications that rely on standard paste semantics.

… paste

Both shell paste paths (the Ctrl/Cmd+V handler and the mobile shortcuts-panel
Paste button) sent raw clipboard text as a socket input message, bypassing
xterm's bracketed-paste wrapping (DECSET 2004) and newline normalization —
multi-line pastes submitted or executed line-by-line in Claude Code's CLI,
shells, and vim. Route both through terminal.paste(), whose onData output
flows to the pty via the existing socket input subscription.

The key handler also unconditionally cancelled the paste chord even when
navigator.clipboard.readText was unavailable (plain-HTTP deployments have no
navigator.clipboard), pasting nothing while suppressing the browser's native
paste event that would have reached xterm's hidden textarea. Only intercept
the chord when the async clipboard API is actually usable; otherwise let the
native path handle it.

Fixes siteboon#1006
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ccdb1129-4cc0-4507-9634-eab43bdc0212

📥 Commits

Reviewing files that changed from the base of the PR and between 5884573 and b8b719d.

📒 Files selected for processing (2)
  • src/components/shell/hooks/useShellTerminal.ts
  • src/components/shell/view/subcomponents/TerminalShortcutsPanel.tsx

📝 Walkthrough

Walkthrough

Changes

Terminal paste handling

Layer / File(s) Summary
Route clipboard text through xterm
src/components/shell/hooks/useShellTerminal.ts, src/components/shell/view/subcomponents/TerminalShortcutsPanel.tsx
Keyboard paste falls back to native handling when the Clipboard API is unavailable; available clipboard text and mobile shortcut pastes now use xterm’s paste() path for bracketed-paste wrapping and newline normalization.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant PasteHandler
  participant ClipboardAPI
  participant Xterm
  participant PTY
  User->>PasteHandler: Paste shortcut or mobile paste
  PasteHandler->>ClipboardAPI: readText()
  ClipboardAPI-->>PasteHandler: clipboard text
  PasteHandler->>Xterm: paste(text)
  Xterm->>PTY: normalized bracketed input
Loading

Suggested reviewers: viper151

Poem

I’m a rabbit with paste in my paws,
Lines now hop through xterm’s laws.
Brackets wrap, newlines behave,
HTTP paste gets a native wave.
One neat nibble, safely sent—
Hoppy shells are glad again!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main shell paste behavior changes.
Linked Issues check ✅ Passed The changes address #1006 by routing both paste paths through terminal.paste() and allowing native paste when the Clipboard API is unavailable.
Out of Scope Changes check ✅ Passed The PR stays narrowly focused on shell paste handling and only modifies the two relevant shell components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI review requested due to automatic review settings July 31, 2026 10:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes terminal paste behavior in the Shell by ensuring pasted text flows through xterm’s terminal.paste() path (to get bracketed-paste wrapping and newline normalization) and by avoiding swallowing paste key chords when the async Clipboard API isn’t available (notably on plain-HTTP deployments).

Changes:

  • Mobile shortcuts “Paste” button now uses terminal.paste(text) instead of sending raw input over the socket.
  • Desktop paste key handler now allows native paste to proceed when navigator.clipboard.readText is unavailable, and otherwise uses terminal.paste(text) to preserve bracketed paste semantics.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/components/shell/view/subcomponents/TerminalShortcutsPanel.tsx Routes mobile Paste action through terminal.paste() to preserve bracketed paste + newline normalization.
src/components/shell/hooks/useShellTerminal.ts Updates paste key handling to avoid blocking native paste on environments without the async Clipboard API and to use terminal.paste() when available.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 216 to +220
(event.ctrlKey || event.metaKey) &&
event.key?.toLowerCase() === 'v'
) {
// Without the async clipboard API (common on self-hosted plain-http
// deployments, where navigator.clipboard doesn't exist) there is
Copilot AI review requested due to automatic review settings August 3, 2026 11:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/components/shell/hooks/useShellTerminal.ts:218

  • The paste key handler still triggers on plain Ctrl+V (because it checks event.ctrlKey || event.metaKey), which conflicts with the PR description/issue requirement that plain Ctrl+V should remain xterm's literal ^V and only Cmd+V / Ctrl+Shift+V should paste. This will cause Ctrl+V to paste (and be swallowed on denial) whenever the async clipboard API exists.
      if (
        event.type === 'keydown' &&
        (event.ctrlKey || event.metaKey) &&
        event.key?.toLowerCase() === 'v'
      ) {

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Shell: multi-line pastes execute line-by-line (bracketed paste bypassed) and Cmd+V/Ctrl+Shift+V paste nothing on plain-HTTP deployments

3 participants