Skip to content

fix(web): programs can copy to the clipboard from the terminal (OSC 52) - #22470

Open
SunkenInTime wants to merge 1 commit into
pingdotgg:mainfrom
SunkenInTime:fix/terminal-osc52-copy
Open

SunkenInTime wants to merge 1 commit into
pingdotgg:mainfrom
SunkenInTime:fix/terminal-osc52-copy

Conversation

@SunkenInTime

@SunkenInTime SunkenInTime commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

A program in the terminal copies "osc52 works" with OSC 52, then Ctrl+V in the composer. Isolated dev build, Edge, Windows 11, PowerShell terminal.

main this PR
before: the paste gives the old clipboard after: the paste gives the copied text

Problem

Programs inside a terminal copy with the OSC 52 escape sequence. That covers Claude Code's /copy and copy-on-select, tmux with set-clipboard, Vim and Neovim, and printf '\033]52;c;…\a' over SSH. T3's terminal ignored the sequence, so the program reported a copy and the clipboard kept its old contents. #8361 has reports from Windows desktop with a remote SSH environment, macOS and iPad. A Windows user replied to us on Twitter: "Copy/paste never behaves as expected, and it doesn't seem to play nicely with tmux when the remote machine is Ubuntu."

Change

The pinned libghostty-vt C API has no clipboard callback, so osc52.ts adds a small streaming scanner that watches live output next to Ghostty. Ghostty still gets every byte unchanged. The scanner:

  • handles sequences split across chunks, ended by BEL or ESC , and aborted by CAN or SUB
  • maps the c, p and s selections, or none, to the one browser clipboard, and decodes the payload as UTF-8
  • never answers reads (?), since a reply would hand this client's clipboard to whatever process asked
  • stops buffering past 1 MiB of base64 and drops that sequence

The surface reports a copy only while its terminal is focused and visible, so a background terminal can't overwrite the clipboard. History replay goes through resetAndWrite, which resets the scanner, so reattaching never copies old output again. Only the thread terminal drawer is wired up. The provider sign-in terminal sends reconnect catch-up through the live path, so it is left out.

Clients: web and desktop share this code. Mobile has its own native terminal and isn't covered here.

Scope and approval

A fix for #8361, a triaged bug. The policy choices that come with OSC 52 (write-only, focused terminal only) are the conservative ones, and docs/user/terminal.md says so in one paragraph.

Verification

  • The live run above, with this PR and on main, same build otherwise: the clipboard changed from a sentinel to osc52 works only with the PR.
  • vp test run apps/web/src/terminal/ghostty/osc52.test.ts apps/web/src/terminal/ghostty/surface.test.ts apps/web/src/terminal/ghostty/core.test.ts: 115 passed. The scanner tests feed the stream whole and one character at a time, with both terminators, a title sequence and a read request mixed in, and an oversized payload followed by a normal copy. The surface test checks that an unfocused terminal, replayed history and a copy split across a replay don't write, and that a read gets no reply. Removing the focus check or the replay reset fails it.
  • vp lint on the touched files and vp run typecheck in apps/web are clean.
  • GPT-6 Astra reviewed it and fuzzed the framing at 381 split points, including tmux passthrough, bad base64, invalid UTF-8 and OSC 8 links next to it. Per 64 KiB chunk in Node, it measured 0.006 ms for plain output and 0.25 ms for ANSI-heavy output.

Not covered:

  • A copy larger than about 380 KiB of text. The drawer's 512 KiB display buffer trims the output before the scanner sees it, and the copy is lost. A copy inside an output burst over 512 KiB is lost the same way.
  • A /copy whose window loses focus before the sequence arrives is ignored by design.
  • Safari, and plain http over a Tailscale IP, can block clipboard writes that don't follow a click or key press. The user gets an error toast.
  • C1 ST (U+009C) isn't treated as a terminator.
  • Not run with Claude Code's /copy or tmux themselves. They emit the same sequence as the printf test.

Made with Claude Opus 5.5 in T3 Code (Claude Code harness), reviewed by GPT-6 Astra (Codex).

Fixes #8361

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Oct 20, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — This PR adds automatic OSC 52 clipboard writes to the focused terminal, including a new streaming parser and system clipboard side effect. It also introduces a line-level lint suppression, so the behavior and static-analysis exception warrant human review.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3b65b731-9275-44a1-9d5d-2f27f9c77949
📥 Commits

Reviewing files that changed from the base of the PR and between 7e289ae and 22bd11b.

📒 Files selected for processing: 6
  • apps/web/src/components/ThreadTerminalDrawer.tsx
  • apps/web/src/terminal/ghostty/osc52.test.ts
  • apps/web/src/terminal/ghostty/osc52.ts
  • apps/web/src/terminal/ghostty/surface.test.ts
  • apps/web/src/terminal/ghostty/surface.ts
  • docs/user/terminal.md

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

📝 Walkthrough

The change adds OSC 52 clipboard-write parsing to terminal surfaces. Valid writes from focused, visible surfaces reach clipboard handling. Clipboard copy failures produce an error toast. Documentation describes the behavior and browser gesture restrictions.

Changes: OSC 52 clipboard support

Layer / File(s) Summary
Parse OSC 52 writes
apps/web/src/terminal/ghostty/osc52.ts, apps/web/src/terminal/ghostty/osc52.test.ts
Adds a streaming scanner that validates and decodes clipboard writes, limits payload size, and ignores reads and invalid sequences. Tests cover chunking, termination, decoding, and recovery after oversized payloads.
Forward writes from terminal surfaces
apps/web/src/terminal/ghostty/surface.ts, apps/web/src/terminal/ghostty/surface.test.ts
Adds an optional clipboard callback. The surface forwards valid writes only while focused and visible, and clears pending scanner state when replacing output. Tests cover live, replayed, partial, query, and unfocused output.
Connect writes to clipboard handling
apps/web/src/components/ThreadTerminalDrawer.tsx, docs/user/terminal.md
Connects the surface callback to clipboard copying and reports copy failures with an error toast. Documents OSC 52 behavior and browser gesture restrictions.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant GhosttyTerminalSurface
  participant Osc52Scanner
  participant ThreadTerminalDrawer
  participant Clipboard
  GhosttyTerminalSurface->>Osc52Scanner: Feed terminal output
  Osc52Scanner->>GhosttyTerminalSurface: Callback with decoded text
  GhosttyTerminalSurface->>ThreadTerminalDrawer: Forward clipboard text
  ThreadTerminalDrawer->>Clipboard: Copy text
Loading

Suggested reviewers: juliusmarminge, t3dotgg

Merge Risk: ⚪ Minimal · up to 22bd1

This adds OSC 52 clipboard writes from focused, visible terminals, with a 1 MiB limit, no clipboard reads, and an error toast on copy failure. No actionable merge-blocking risk remains.

Security architecture risk: 🔵 Low · up to 22bd1

Programs producing output in an active terminal can now replace clipboard text. Clipboard reads remain disabled, writes are limited to focused and visible terminal surfaces, and replayed history is excluded. Browser-specific permission and delayed-write behavior remain unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — A producer controlling live output in a viewed terminal gains bounded text-write influence over that client's shared clipboard when the surface gate and platform permissions allow it. This authority is local to each receiving client, but clipboard contents may subsequently be pasted outside the application.

Security Findings and Attack Paths

  • inferred — Malicious terminal output can intentionally replace clipboard text through the newly supported protocol. Further effects, such as executing pasted commands elsewhere, require a separate paste or consumer action; the new callback itself only submits text. This is the feature's authority expansion, not evidence of a verified control bypass.

Trust Boundaries and Controls

  • observed — Application-level authorization occurs at sequence completion through the surface's focus and visibility flags. Replay resets bypass scanning. The existing helper uses navigator.clipboard.writeText when available, uses synchronous execCommand only when that API is unavailable, and reports asynchronous denial rather than retrying through the fallback.

Resilience and Maintainability Implications

  • observed — The callback submits independent asynchronous writes without a terminal-generation token or application-level sequencing. Focus is tracked through input focus and blur events. Source inspection therefore establishes submission-time controls, not cancellation of pending platform operations or their completion ordering. The supplied browser permission test exercises terminal resize grants rather than OSC 52 clipboard permissions.
Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: enabling terminal programs to copy text to the clipboard through OSC 52.
Description check Passed The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the behavior, implementation scope, tests, manual validation, limitations, and review …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the behavior, implementation scope, tests, manual validation, limitations, and review tooling. It also includes before-and-after UI evidence.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

This branch has not been deployed

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

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Copying from the terminal never works — clipboard always keeps its old content (Windows desktop, remote environment)

1 participant