feat(mobile): tap links, native text selection, and a scrollbar in the terminal
Merged
SA
3mo ago+721+721+012345678901234567890123456789-28-28-01234567890123456789
Summary
The mobile terminal (xterm.js in a WKWebView) previously had no touch affordances beyond scrolling: links weren't tappable, text couldn't be selected or copied, and there was no scroll position indicator. This PR adds all three, plus closes a keyboard hole the work uncovered.
- Tap opens links. xterm's linkifier is hover-driven and its gesture handling cancels touches, so taps never reached it. A tap now replays the hover→click sequence as non-bubbling synthetic events on the screen element. URL detection handles hard-wrapped URLs (joined across rows), trailing punctuation, and wide chars; OSC 8 hyperlinks go through xterm's own provider via
linkHandler. Opens viaLinking.openURLover the bridge. - Long-press selects, natively. The buffer freezes into a selectable
<pre>overlay aligned to the grid, pre-selecting the run under the finger (URLs to their exact extent). Selection is WebKit's own (grabbers, loupe, callout). A centered native SwiftUI Copy Selection button replaces the quick keys row while selecting; copying (button or system callout) joins soft-wrapped rows into one line, writes the clipboard, and shows a "Copied"HeaderNoticein the header. Select mode lives exactly as long as the selection: deselecting auto-exits (debounced past iOS's mid-gesture selection blips), and a long-press that finds nothing never enters. - Overlay scrollbar. Appears only when scrolled up (doubling as the "not at bottom" cue) and is drag-to-scroll. xterm's built-in hover-reveal scrollbar is disabled, returning its 14px gutter to the columns.
- Keyboard can never open from the page. WKWebView synthesizes real clicks from some taps and xterm focuses its hidden textarea on click — previously reachable, letting keystrokes bypass the composer's
terminal.sendframing (#6284). The page now refuses textarea focus unless RN explicitly requests it over the bridge.
HeaderNotice moved from the pull-request screen to workspace/[id]/components (now shared by both screens).
Test plan
- Headless (playwright + CDP touch): tap opens plain/punctuated/wrapped/OSC 8 URLs, non-link taps are no-ops, forced clicks no longer focus the textarea, empty long-press doesn't enter select mode, deselect auto-exits
- iPhone 16e simulator: wrapped-URL tap opens Safari on the exact URL; long-press shows grabbers with word-boundary pre-selection; Copy Selection button and native callout Copy both write the pasteboard (verified via
pbpaste, wrapped URL copies with no embedded newline); "Copied" notice renders in the header; scrollbar appears when scrolled up and drags; deselect returns the quick keys row - Re-smoked on device after rebasing onto main (host-prop dial path, ⏎ quick key)
- Typecheck + biome clean
https://claude.ai/code/session_012tXfKc8hRYnkM5pkvy5xdq
Summary by cubic
Adds tap-to-open links, native long‑press text selection with copy, and a drag scrollbar to the mobile terminal, and blocks the webview from opening the soft keyboard. Previously taps didn’t activate links, text couldn’t be selected, and no scroll position cue was shown.
- Links: replays hover→click on the xterm screen so taps open URLs; handles wrapped URLs, trims trailing punctuation, and supports OSC 8; opens via Linking; disables xterm’s hover scrollbar to reclaim its gutter.
- Selection: long‑press enters a selectable overlay aligned to the grid; copying (native callout or Copy Selection) joins soft‑wrapped rows and writes the clipboard via
expo-clipboard, then emitsonCopiedto show a “Copied” header notice.TerminalWebViewexposescopySelection(). The composer swaps quick keys for a centered Copy Selection when select mode is active. - Scrollbar: overlay shows only when scrolled up (not‑at‑bottom cue) and supports drag‑to‑scroll.
- Keyboard safety: refuses focus of xterm’s hidden textarea unless RN calls
focus, preventing keyboard bypass. - Bridge/API: page→RN messages
openUrl,copy,select; RN→pagecopySelection.TerminalWebViewadds optionalonSelectChangeandonCopied. - Shared UI:
HeaderNoticemoved toworkspace/[id]/componentsand imports updated.
Summary by CodeRabbit
- New Features
- Added text selection, native clipboard support, and a “Copied” confirmation notice to the mobile terminal.
- Added tappable terminal links that open in the device browser.
- Added selection-aware copy and close controls.
- Added an overlay scrollbar with drag scrolling.
- Improvements
- Improved terminal focus handling and cleanup when switching sessions.
- Added local HTTPS reverse-proxy support for development environments.