Fix TUI copy to use native clipboard with accurate feedback#13929
Fix TUI copy to use native clipboard with accurate feedback#13929warp-dev-github-integration[bot] wants to merge 5 commits into
Conversation
…P-4878) Co-Authored-By: Warp <agent@warp.dev>
…APP-4878) Fold in approval feedback: make explicit that the transport is chosen by environment (SSH -> OSC 52; local -> native arboard w/ OSC 52 fallback), not try-then-fallback (OSC 52 is fire-and-forget with no ack). Note the Linux clipboard-ownership requirement to hold the arboard handle for the TUI's lifetime (ClipboardLease), and prior art (Codex, opencode). Co-Authored-By: Warp <agent@warp.dev>
The headless TUI copy path (selection copy and /export-to-clipboard) used OSC 52 exclusively. Warp's own terminal denies programmatic clipboard writes by default (terminal.osc52_clipboard_access = Deny), so copy silently failed for the common case of running ./script/run-tui inside Warp — yet the TUI still reported "copied to clipboard" because copy_to_clipboard returned Ok as soon as the bytes left the process, regardless of host acceptance. Select the transport up front from the environment instead of relying on an unobservable OSC 52 result: - Remote/SSH sessions (SSH_CONNECTION / SSH_TTY) use OSC 52. - Local sessions write directly to the OS clipboard via arboard, falling back to OSC 52 only when the native backend errors (e.g. headless Linux). copy_to_clipboard now returns a ClipboardCopy outcome (Copied vs SentToTerminal) so the footer hint is truthful: the confirmed-copy hint only shows on a real native write, OSC 52 sends show a distinct best-effort hint, and genuine failures show the failure hint. On Linux (X11/Wayland) the clipboard is served by the owning process, so the arboard handle is retained for the TUI's lifetime (ClipboardLease pattern via a process-lifetime OnceLock<Mutex<Option<Clipboard>>>) so copied text survives while the TUI runs. Verification: added transport-decision regression tests (local_copy_reports_copied, ssh_session_reports_sent_to_terminal, native_failure_falls_back_to_osc52, hard_failure_reports_err); all warp_tui clipboard tests pass; cargo fmt --check and clippy (-D warnings) clean. Co-Authored-By: Warp <agent@warp.dev>
|
@warp-dev-github-integration[bot] I'm starting a first review of this pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR updates the headless TUI clipboard path to prefer native OS clipboard writes for local sessions, use OSC 52 for SSH/fallback cases, and adjust copy/export feedback based on the new outcome.
Concerns
- The new
warp_tuidependency entry does not enablearboard's Wayland backend even though the workspace dependency hasdefault-features = false, so local Wayland sessions can still miss the native clipboard path and fall back to the unconfirmed OSC 52 behavior this PR is trying to avoid. - For this user-facing TUI behavior change, the PR description does not include acceptable TUI verification evidence. Please attach a terminal transcript,
render_to_lines/TuiBuffer::to_linessnapshot diff, or./script/run-tuicapture demonstrating selection copy and/or/export-to-clipboardfeedback working end to end.
Verdict
Found: 0 critical, 2 important, 0 suggestions
Request changes
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
There was a problem hiding this comment.
Overview
This fixes the TUI copy bug cleanly at the root cause: it replaces the OSC-52-only path with an environment-selected transport (native arboard for local sessions, OSC 52 for SSH/remote and as a local fallback), and makes success/failure feedback honest via the ClipboardCopy outcome enum. The transport decision is factored into a testable copy_with seam with an injectable NativeClipboard backend and OSC 52 writer, and the Linux clipboard-ownership issue is handled with a process-lifetime ClipboardLease (OnceLock<Mutex<Option<Clipboard>>>). The implementation matches the committed spec's design decisions and acceptance criteria, and the four new regression tests exercise the full decision matrix (local→Copied, SSH→SentToTerminal, native-error→OSC 52 fallback, hard failure→Err). I checked out the branch and confirmed all 8 warp_tui clipboard tests pass on Linux and the crate compiles. As a headless-TUI change, deterministic unit tests are the correct evidence (no computer_use/GUI proof required, per the repo's TUI verification convention).
Concerns
Blocking: all three Windows CI checks (Run Windows tests, Formatting + Clippy, Verify compilation with release flags) fail — and it is this PR that breaks them. The added spec file agents/specs/APP-4878: fix TUI copy to use native clipboard.md contains a colon (:), which is a reserved/invalid character in Windows file paths. git checkout on the Windows runners fails with error: invalid path '...' (exit code 128) before any compilation runs, so the whole Windows matrix is red. The fix is to rename the file to remove the colon — the sibling spec agents/specs/REMOTE-2160 - macOS video recording for computer use.md uses - and checks out fine, so mirror that (e.g. APP-4878 - fix TUI copy to use native clipboard.md). Because the checkout fails before compiling, Windows has also not actually verified the new arboard dependency edge builds there — that coverage returns once the filename is fixed and CI re-runs.
Non-blocking: the hidden factory-agent metadata block in the PR body still records task_id/linear_issue_id as APP-4878, but the ticket moved to CODE-1883 during the run. This isn't a code issue, but it breaks the automated PR↔task lookup (factory-state/factory-pr-meta find couldn't resolve the PR from CODE-1883). Worth updating the block to the current identifier for traceability.
Verdict
Found: 1 critical, 0 important, 1 suggestion
Request changes
- Rename spec file to use ' - ' separator instead of ':' — a colon is an invalid Windows path character, which broke the Windows CI matrix (git checkout exited 128 before compilation). Mirrors sibling spec naming. - Enable arboard 'wayland-data-control' on Linux/FreeBSD so native clipboard copies work on Wayland sessions (the workspace dep is default-features=false, which otherwise leaves only the X11 backend and forces pure-Wayland copies to fall back to unconfirmed OSC 52). Addresses review feedback on PR #13929 (CODE-1883). Co-Authored-By: Oz <oz-agent@warp.dev>
|
Thanks for the reviews — addressed on rework #1 (
|
There was a problem hiding this comment.
Overview
Second review (rework cycle 1). This iteration resolves every blocking finding from the prior reviews. The Windows-invalid colon in the committed spec filename is gone (agents/specs/APP-4878 - fix TUI copy to use native clipboard.md), so the Windows runners can check the branch out again — CI now shows 0 failing (the previously-red Windows checks are re-running as pending, none failing). The arboard dependency now enables wayland-data-control for Linux/FreeBSD while keeping macOS/Windows text-only, matching the reviewer's suggestion, so pure-Wayland local sessions get the native clipboard path instead of silently falling back to OSC 52. The hidden factory-agent metadata block now records the current identifier (CODE-1883), restoring the automated PR↔task lookup.
The underlying fix remains sound: transport is selected up front from the environment (SSH → OSC 52; local → native arboard with OSC 52 as a last-resort fallback), the honest ClipboardCopy outcome drives truthful footer feedback (Copied → "copied to clipboard", SentToTerminal → "copied via terminal", Err → "failed to copy to clipboard"), and the Linux clipboard-ownership caveat is handled with a process-lifetime ClipboardLease (OnceLock<Mutex<Option<Clipboard>>>, poison-tolerant). The copy_with seam with an injectable NativeClipboard keeps the decision matrix unit-testable. I checked out the head SHA (dfb0a3c) and confirmed the crate builds with the new Wayland dependency edge and all 8 warp_tui clipboard tests pass. As a headless-TUI change, deterministic unit tests are the correct evidence per the repo's TUI verification convention — no GUI computer-use proof required.
Concerns
Non-blocking nits only. The committed spec file is still named/prefixed APP-4878 (agents/specs/APP-4878 - fix TUI copy to use native clipboard.md, and the Linear: line inside it) even though the ticket is now CODE-1883; renaming it would align the on-branch artifact with the current identifier, though it is cosmetic churn at this point. Optionally, the view-level mapping in terminal_session_view.rs (Copied/SentToTerminal/Err → the three hints) could gain a render_to_lines snapshot test per the tui-testing skill; the substantive transport logic is already covered by the four new regression tests, so this is a marginal improvement rather than a gap.
Verdict
Found: 0 critical, 0 important, 2 suggestions
Approve
| # enable `wayland-data-control` so native copies work on Wayland sessions | ||
| # (otherwise they fall back to X11 only, and pure-Wayland copies miss the native | ||
| # path). macOS/Windows have no such backend feature and stay text-only. | ||
| [target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies] |
There was a problem hiding this comment.
is this like target thing a common pattern in the codebase? just want to make sure we're not introducing a new weird dependency structure
There was a problem hiding this comment.
Yes — [target.'cfg(...)'.dependencies] is a well-established pattern in this workspace. For example crates/command/Cargo.toml (cfg(windows) / cfg(unix) / cfg(not(target_family="wasm"))), crates/warpui/Cargo.toml, and crates/warp_core/Cargo.toml, plus ~30 other crates use it. The workspace arboard dep is default-features = false, and we only need wayland-data-control on Linux/FreeBSD, so a target-cfg split is the standard way to vary a feature per platform — no new or weird structure. Leaving this thread open for your ack.
Address human review on PR #13929: - Remove the NativeClipboard trait, RealNativeClipboard, and copy_with indirection that existed only to fake the native backend in tests; the native write is now a concrete cfg-gated set_native_text function. - Drop the ClipboardCopy enum: copy_to_clipboard returns Result<()> and the UI shows a single "copied to clipboard" success message for both the native and OSC 52 paths (the transport is an implementation detail). - Remove the inline mod native_arboard from the shared clipboard.rs; the arboard ClipboardLease logic is inlined into set_native_text. - Trim clipboard_tests.rs to the pure OSC 52 encoding tests that no longer depend on a faked backend. Co-Authored-By: Oz <oz-agent@warp.dev>
There was a problem hiding this comment.
Overview
Third review — human-reviewer rework cycle 2 (commit 7d5dbc2). This iteration is a code-structure simplification that resolves every one of @harryalbert's six comments from the prior human review: the test-only NativeClipboard trait + RealNativeClipboard are gone (native write is now a concrete cfg-gated set_native_text), the oddly-named copy_with seam is inlined into copy_to_clipboard, the inline mod native_arboard is removed (ClipboardLease logic folded into set_native_text), the ClipboardCopy enum and the "copied via terminal" hint are dropped in favour of a single "copied to clipboard" message, and the per-target arboard dependency split was confirmed as an established workspace pattern with examples. Net −205/+45 in the clipboard path. The core fix is intact: local sessions write natively via arboard (fixing the reported local-copy-in-Warp bug), SSH/remote sessions use OSC 52, and a displayless local run falls back to OSC 52.
Verification
Checked out 7d5dbc2 and ran the target-package gate: cargo nextest run -p warp_tui — all four clipboard tests pass (osc52_encodes_utf8_for_clipboard_and_primary, tmux_passthrough_wraps_and_doubles_escape_bytes, clipboard_writer_emits_exact_markdown_payload, clipboard_writer_propagates_output_errors); cargo fmt -p warp_tui -- --check clean; cargo clippy -p warp_tui --all-targets --tests -- -D warnings clean. CI on the PR is healthy — 0 failing (9 successful, remaining clippy/test jobs pending). The code is correct and idiomatic (imports at top / inside the cfg-guarded fn per AGENTS.md, inline format args, poison-tolerant lock, exhaustive Ok/Err handling). All five action-oriented review threads plus the Wayland thread are already resolved; the Cargo.toml "is this a common pattern?" question thread is correctly left open for the human reviewer's acknowledgement.
Concerns
Non-blocking, but should be tidied before merge. First, the PR description's Verification section is now stale: it still describes the removed enum-based API and tests (local_copy_reports_copied, ssh_session_reports_sent_to_terminal, native_failure_falls_back_to_osc52, hard_failure_reports_err, "returns Ok(ClipboardCopy::SentToTerminal)", "All 8 warp_tui clipboard tests pass"). The shipped code returns anyhow::Result<()> and there are four encoding-only tests — please rewrite the Verification section to match the current state so a reviewer isn't misled about what tests exist. Second, the committed spec agents/specs/APP-4878 - fix TUI copy to use native clipboard.md has drifted from the implementation: it still specifies the three-way ClipboardCopy outcome, distinct honest per-transport feedback (behaviours 3/4/7), and the injectable-backend test design that the human review intentionally removed — the spec should be updated to reflect the shipped single-message design so the checked-in spec and code agree. Third, a behavioural consequence of collapsing the feedback (per @harryalbert's explicit direction, so accepted): the SSH/remote OSC 52 path now returns Ok(()) and shows "copied to clipboard" even though OSC 52 acceptance is unobservable — this is the reviewer's accepted tradeoff, noted only so it's on the record. Finally, the hands-on copy→paste check (spec validation #1, on a real display) remains a manual step for the merger, since it can't be exercised in this headless sandbox and isn't meaningfully demonstrable via TUI render snapshots.
Verdict
Found: 0 critical, 0 important (blocking), 3 non-blocking notes.
Approve (posted as a COMMENT review because the PR author and this review account are the same GitHub integration, which blocks a formal approval; the accepted verdict is recorded durably via the review-done label on CODE-1883).
Summary
Fixes CODE-1883 — copying a selection or running
/export-to-clipboardin the headless Warp TUI (crates/warp_tui) failed consistently in the common case (running./script/run-tuiinside Warp itself), yet the TUI still reported "copied to clipboard".Root cause: the copy path used OSC 52 exclusively, and Warp's own terminal denies programmatic clipboard writes by default (
terminal.osc52_clipboard_access = Deny).copy_to_clipboardreturnedOk(())as soon as the bytes were flushed to stdout — regardless of whether the host honoured them — so the caller always showed the success hint against an empty clipboard.Fix: select the transport up front from the environment instead of relying on an unobservable OSC 52 result.
SSH_CONNECTION/SSH_TTY) use OSC 52 (the local OS clipboard isn't reachable).arboard(already a workspace dep), falling back to OSC 52 only when the native backend errors (e.g. headless Linux with no display).copy_to_clipboardnow returns aClipboardCopyoutcome (CopiedvsSentToTerminal) so feedback is truthful: the confirmed-copy hint shows only on a real native write, best-effort OSC 52 sends show a distinct "copied via terminal" hint, and genuine failures show the existing failure hint.On Linux (X11/Wayland) the clipboard is served by the owning process, so the
arboardhandle is retained for the TUI's lifetime (the ClipboardLease pattern, via a process-lifetimeOnceLock<Mutex<Option<Clipboard>>>) so copied text survives while the TUI runs. On Linux/FreeBSD thearboarddependency now enables thewayland-data-controlfeature so native copies work on Wayland sessions (the workspace dep isdefault-features = false, which otherwise leaves only the X11 backend).Scope is confined to
crates/warp_tui; the security-sensitive hostterminal.osc52_clipboard_accessdefault is unchanged.Verification
Added transport-decision regression tests in
crates/warp_tui/src/clipboard_tests.rsthat fail against the oldio::Result<()>API and pass after the refactor:local_copy_reports_copied,ssh_session_reports_sent_to_terminal,native_failure_falls_back_to_osc52,hard_failure_reports_err. Existing OSC 52 encoding tests preserved. All 8warp_tuiclipboard tests pass (cargo nextest run -p warp_tui).cargo fmt -- --checkandcargo clippy -p warp_tui --all-targets --tests -- -D warningsare clean.This is the headless TUI (not the GUI), so verification is by the deterministic unit tests above plus
./script/presubmit— no GUI computer-use path. Note: the full workspace./script/presubmitbuild was OOM-killed in the CI sandbox during the workspace-widecargo test --no-run; CI's per-platform builds gate the merge.Originating thread: https://warpdev.slack.com/archives/C0BA99TSDB2/p1784335796549789
This PR was generated with Oz.