feat(mobile): tap links, native text selection, and a scrollbar in the terminal

Merged
SA
3mo ago
+721-28

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 via Linking.openURL over 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" HeaderNotice in 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.send framing (#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 emits onCopied to show a “Copied” header notice. TerminalWebView exposes copySelection(). 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→page copySelection. TerminalWebView adds optional onSelectChange and onCopied.
  • Shared UI: HeaderNotice moved to workspace/[id]/components and 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.