Skip to content

fix(mobile): long-pressing the iOS terminal offers Paste - #25619

Closed
whoisaldo wants to merge 3 commits into
pingdotgg:mainfrom
whoisaldo:fix/mobile-terminal-paste-menu
Closed

whoisaldo wants to merge 3 commits into
pingdotgg:mainfrom
whoisaldo:fix/mobile-terminal-paste-menu

Conversation

@whoisaldo

@whoisaldo whoisaldo commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #25618

Problem

The iOS terminal has no edit menu, so long-press offers no Paste. Hardware ⌘V and the iPad shortcut bar paste through the hidden text field as typed input. Newlines become spaces, and the text skips the paste encoding and write chunking that the toolbar paste button uses.

Fix

  • A long-press on the terminal shows the system edit menu with only Paste, when the clipboard has text and the terminal is writable. Tap and drag behave as before.
  • Paste from that menu, ⌘V, and the iPad shortcut bar now read the clipboard natively, inside the user's paste action, so iOS shows no "Allow Paste" prompt. The text goes to JS and through the same paste session as the toolbar button.
  • A read-only terminal shows no menu, and ⌘V writes nothing.

The toolbar layout is untouched. #25388 moves the toolbar paste button for #24694; the two branches merge without conflicts. The native module's hardwareKeyRevision goes from 3 to 4, so the change needs a rebuilt iOS client.

Android is unchanged. Long-press there selects a word and offers Copy and Select All.

Known limit: Paste appears whenever iOS reports text on the clipboard. An empty copied string still shows Paste, and choosing it does nothing. Checking for an empty string would require reading the clipboard on long-press, which may trigger the iOS paste-permission prompt.

Before and after

Long-press, iPhone 17 Pro. Left: main, no menu after a 2 s press. Middle: this branch, Paste. Right: after choosing Paste, echo one ran and echo two waits at the prompt.

iPhone terminal: no menu on main; Paste menu on this branch; after Paste, echo one ran and echo two waits

Long-press, iPad mini. Left: main. Right: this branch.

iPad terminal: no menu on main; Paste menu on this branch

Hardware ⌘V, iPhone 17 Pro. Top row: main, after ⌘V and after Return. Bottom row: this branch, after ⌘V and after Return.

iPhone terminal ⌘V: main joins the lines into "echo one echo two"; this branch runs echo one and leaves echo two at the prompt

Recording of long-press, Paste and the result on iPhone (9 s): iphone-longpress-paste-flow.mp4

Verification

iOS Simulator with synthetic data. Clipboard set to echo one, newline, echo two.

  • Long-press shows one menu with only Paste. Paste runs echo one, leaves echo two at the prompt, writes once, and shows no "Allow Paste" alert. iPhone 17 Pro and iPad mini, iOS 26.3.
  • Tap focuses with no menu. Drag scrolls with no menu. A long-press while scrolled keeps the scroll position and shows one menu. iPhone and iPad.
  • ⌘V keeps the newline and writes once. iPhone, iPad, and iPhone Duo folded on iOS 27.1.
  • A 70,000-byte paste arrives whole and once. The shell's shasum of the input matched the host's. iPhone.
  • With a credential that has terminal:read but not terminal:operate, long-press shows no menu, and ⌘V and typing write nothing. iPhone.
  • The accessibility tree lists the Paste item while the menu is open. iPhone and iPad. VoiceOver was not run.
  • vp test run apps/mobile/src/features/terminal passes (26 tests). No unit tests were added; the native paste behavior was checked in the simulator.

Model: Claude Opus 5.5 via Claude Code, in T3 Code; reviewed by GPT-6.1-Sol (Codex).

The iOS terminal had no edit menu, so on a phone the only paste was a toolbar button that scrolls off screen. Hardware ⌘V and the iPad shortcut bar pasted through the hidden text field as typed input, so a newline arrived as a space, control bytes were not stripped, and large pastes were not chunked.

A long press on the terminal now shows the system edit menu with Paste when the clipboard has text. That action, ⌘V, and the shortcut bar read the clipboard natively and send it to JS, which writes it through the same paste session as the toolbar button.
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 26, 2026
@whoisaldo
whoisaldo marked this pull request as ready for review October 26, 2026 21:15
@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

Only developers with an assigned seat can use this organization's usage-based review budget, and seats here are assigned manually. Ask an admin to assign a seat, or change the review continuation mode in Billing.

Next included review available in 33 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 0bfa784 and 0406ad1.

Files selected for processing: 6
  • apps/mobile/modules/t3-terminal/ios/T3TerminalModule.swift
  • apps/mobile/modules/t3-terminal/ios/T3TerminalView.swift
  • apps/mobile/src/features/terminal/NativeTerminalSurface.test.tsx
  • apps/mobile/src/features/terminal/NativeTerminalSurface.tsx
  • apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
  • apps/mobile/src/features/terminal/nativeTerminalModule.ts

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between dd5b540 and 0bfa784.

Files selected for processing: 5
  • apps/mobile/modules/t3-terminal/ios/T3TerminalModule.swift
  • apps/mobile/modules/t3-terminal/ios/T3TerminalView.swift
  • apps/mobile/src/features/terminal/NativeTerminalSurface.tsx
  • apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
  • apps/mobile/src/features/terminal/nativeTerminalModule.ts

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

Walkthrough

The iOS terminal now exposes paste actions through a native event. The terminal route passes the event text to its existing paste flow, which handles the supplied text instead of reading the clipboard again.

Changes: iOS Terminal Paste Handling

Layer / File(s) Summary
Native paste capture
apps/mobile/modules/t3-terminal/ios/T3TerminalModule.swift, apps/mobile/modules/t3-terminal/ios/T3TerminalView.swift
The native terminal emits clipboard text for input-field paste and edit-menu Paste. A long press presents the menu when Paste is available.
Paste event bridge
apps/mobile/src/features/terminal/nativeTerminalModule.ts, apps/mobile/src/features/terminal/NativeTerminalSurface.tsx
The native event carries a text payload. The terminal surface forwards it only when the terminal is running and writable.
Existing paste flow integration
apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
The route passes native paste text to the existing paste flow and clears any pending modifier.

Priority: Low

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

Change: Bug fix · Severity of issue fixed: Low

Possibly related PRs

  • pingdotgg/t3code#25388: Updates the mobile terminal toolbar Paste action and the paste flow that this change uses.

Suggested reviewers: juliusmarminge, maria-rcks

Merge Risk: Minimal · previous review 0bfa7

No confirmed paste-path issue remains that would block merging. Long-press behavior with the input field unfocused remains worth checking on a device.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly identifies the main change: adding Paste to the iOS terminal long-press menu.
Description check Passed The description covers the problem, implementation, scope, linked issue, verification results, UI evidence, known limitation, and agent attribution. The "Fix" heading serves the template's "Change" se…
Linked Issues check Passed Issue #25618 is directly linked and remains open. The iOS native view adds a long-press edit menu with only the Paste command when the terminal is writable and UIPasteboard.general.hasStrings is tru…
Out of Scope Changes check Passed The changed Swift and TypeScript code supports the requested iOS terminal paste behavior. The hardwareKeyRevision increment supports delivery of the native keyboard change. The event type and callba…
Full details: Description check

Explanation

The description covers the problem, implementation, scope, linked issue, verification results, UI evidence, known limitation, and agent attribution. The "Fix" heading serves the template's "Change" section.

Full details: Linked Issues check

Explanation

Issue #25618 is directly linked and remains open. The iOS native view adds a long-press edit menu with only the Paste command when the terminal is writable and UIPasteboard.general.hasStrings is true. ⌘V, the shortcut bar, and menu Paste route through onPaste. ThreadTerminalRouteScreen.tsx supplies the clipboard text to the existing pasteSession.paste flow, which applies encodeTerminalPaste and chunkTerminalWrite. This preserves multiline input and the toolbar paste behavior. Read-only terminals do not emit paste events. Android is not changed. The existing terminal test command is reported as passing, and the issue does not require new tests.

Full details: Out of Scope Changes check

Explanation

The changed Swift and TypeScript code supports the requested iOS terminal paste behavior. The hardwareKeyRevision increment supports delivery of the native keyboard change. The event type and callback plumbing are required to send native paste text through the existing terminal paste session. No unrelated feature or Android implementation change is shown in the pull-request diff.

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.

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

thanks @whoisaldo. your implementation correctly keeps system paste separate from typed input and reuses the existing paste session. i updated this original branch, preserving your commit and implementation.

  • merged current main without rebasing or force-pushing.
  • added three regression cases in the existing terminal surface test: multiline/control-byte handling and write chunking, read-only terminals, and stopped terminals. the writable-paste case fails with main's surface and passes with this fix.
  • shortened the swift comments. no behavior or feature was dropped.

verification at 0406ad16afe3f16112acaa4c0d85f9ee95a030fb: all 28 ci checks pass; no conflicts. blacksmith passed all 29 terminal tests and vp run check:changed against the pinned main baseline, including format, lint, both knip passes, mobile/scripts typechecks, and the focused test file. two independent static reviews found no actionable code issues. coderabbit's new-head review was rate-limited; its earlier review had no actionable findings.

unverified: fresh ios runtime behavior and before/after media. the native device tool returned "Agent device access is turned off for this environment." this linux vm has no uikit/xcode, and chrome cannot exercise the native terminal. the default dev server was started and stopped; its web client cannot prove this ios change. the original simulator evidence remains in the description as historical contributor evidence, not a reproduction of this head.

before merge, an ios pass still needs long-press paste with the keyboard shown and dismissed, hardware command-v, the shortcut bar, and nearby scrolling/typing. the javascript tests simulate the native event and cannot prove uikit delivery. no task processes remain running.

@whoisaldo

Copy link
Copy Markdown
Contributor Author

Thanks @maria-rcks for updating the branch and preserving my original commit!! :)

Fresh native verification at 0406ad16afe3f16112acaa4c0d85f9ee95a030fb is complete using a rebuilt client on iPhone 17 Pro and iPad mini simulators running iOS 26.3, connected to a real terminal PTY.

  • Baseline a2398c2 reproduces the missing long-press menu and Command-V newline loss.
  • Current head: long-press Paste works with the software keyboard shown and explicitly dismissed. Command-V works on both layouts.
  • Multiline input arrives exactly once with the expected terminal newline encoding. A 77,000-byte paste also matches exactly.
  • Scrolling works, opening Paste preserves the scroll position, and nearby software-keyboard typing arrives correctly.
  • iPad system shortcut-bar Paste delivers the correct multiline bytes after clipboard consent.

I also verified this on a physical device aswell! Tried to save u the trouble

@juliusmarminge juliusmarminge added the bugfix Fix PR opened by an issue lead; enters the PR loop label Oct 28, 2026

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified in a real dev environment

No changed runtime behavior could be verified in this Linux Boat VM. This is an unverifiable verification pass, not a successful app verification.

I read the description, discussion, commit authors, and complete six-file diff at 0406ad16afe3f16112acaa4c0d85f9ee95a030fb. The PR is open; its listed commits are by whoisaldo and maria-rcks. The change starts in UIKit: a terminal long-press presents UIEditMenuInteraction, and menu Paste, Command-V, and shortcut-bar Paste emit native clipboard text through onPaste into the mobile paste session. The native module revision becomes 4. No web terminal or provider adapter is changed.

Environment checks this pass:

uname: Linux x86_64
xcodebuild: unavailable
xcrun: unavailable
/usr/bin/google-chrome: present
T3 device_list: Agent device access is turned off for this environment.
cafe pr status: 30 successful checks, 0 failing, 0 pending

CI status is reported only as context. It does not prove UIKit behavior. No screenshots or recordings were captured because the affected client cannot run here. No code, branches, labels, or PR metadata were changed; no dev servers or provider turns were started.

Not verified

The original bug on github/main and the fix on this exact head remain unverified by this pass. Chrome, including mobile viewport emulation, cannot instantiate the native iOS terminal. Starting main and PR web servers or sending Codex turns would not exercise the changed UIKit-to-mobile-client path, so I did not substitute those for the requested reproduction.

A rebuilt iOS client (hardwareKeyRevision 4) connected to a real dev server is still required to independently check long-press Paste with the keyboard shown and dismissed, Command-V, iPad shortcut-bar Paste, multiline/control-byte encoding, large paste chunking and exactly-once delivery. Nearby typing, tap-to-focus, scrolling and scroll-position preservation, plus read-only/stopped terminal guards, also remain unverified here. Android native behavior, VoiceOver, and clipboard consent behavior were not exercised. The contributor reports native verification in the earlier discussion; that evidence was not reproduced in this pass.

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified on Android emulator (API 35) against a real dev server

No runtime verification performed. This heading records the requested verification target, not a passing result.

At head 0406ad16afe3f16112acaa4c0d85f9ee95a030fb, I read the complete six-file diff, commit authors, and discussion. The PR is open, with listed commits by whoisaldo and maria-rcks. The changed behavior is iOS-only: UIKit adds the long-press Paste menu and native clipboard event; the shared TypeScript forwards that iOS event into the existing paste session. No Android native implementation changes. The description explicitly says Android is unchanged.

The verification brief explicitly requires stopping for an iOS-only change. I therefore did not check out the branch, boot the emulator, install a client, start a dev server or Metro, send provider turns, or change code. No screenshots or recordings were captured. T3 device discovery reports Android available, but iOS unavailable: "iOS Simulators need macOS with Xcode."

Reported CI status: 30 successful checks, 0 failing, 0 pending. This is context, not proof of native behavior.

Not verified

iOS cannot run in this Linux VM. Long-press Paste with keyboard shown/dismissed, Command-V, iPad shortcut-bar Paste, multiline and large-paste delivery, read-only/stopped guards, and nearby typing/scrolling were not exercised by this pass. Android behavior and a main-versus-head runtime comparison were also not exercised under the explicit stop rule. The contributor's iOS evidence in the discussion was not independently reproduced.

Copy link
Copy Markdown
Member

Closing in favour of #25828, which makes the same change. Thanks for the work here!

@juliusmarminge juliusmarminge removed the pr:gate-failed A land gate failed label Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mobile iOS terminal has no long-press Paste, and ⌘V joins multi-line text into one line

3 participants