feat(mobile): Android terminal improvements — input, selection, sessions, Nerd Font#3775
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review This PR introduces substantial new Android terminal capabilities (selection, copy, fling scrolling, session management, Nerd Font) with ~1000 lines of new code including native JNI changes. An unresolved review comment also identifies a medium-severity bug in session lifecycle handling. You can customize Macroscope's approvability policy. Learn more. |
41ed0d8 to
040b6fb
Compare
|
Force-pushed to fix my git identity, sorry! My Github isn't linked to my work email :) |
f6fbc81 to
ba16828
Compare
35f5c13 to
319117c
Compare
67a4458 to
47dc9fb
Compare
7e79b0c to
c0ebf71
Compare
ea78bd0 to
e3efc51
Compare
e6c123e to
7d7e76b
Compare
f647223 to
54c1e08
Compare
78d97ed to
8c3e487
Compare
54c1e08 to
a102676
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a102676. Configure here.
| runningTerminalKeyRef.current = null; | ||
| // Mark this key handled so the stale-attach effect doesn't respawn the | ||
| // session the user just ended. | ||
| reopenedStaleTerminalKeyRef.current = terminalKey; |
There was a problem hiding this comment.
Exit guards block session respawn
Medium Severity
After a shell exits, reopenedStaleTerminalKeyRef and pendingExitNavigationRef stay set for that session when the user switches terminals via StackActions.replace on the same screen instance. Choosing that session again from the menu skips the stale openTerminal respawn and can immediately run pending exit navigation, leaving a dead terminal with no new shell.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit a102676. Configure here.
Raw-mode TUIs (Claude Code, vim) treat LF as Ctrl+J, so Enter never submitted. Terminals send CR for the Return key; the PTY's icrnl translates it for canonical-mode programs.
Scrolling stopped the moment the finger lifted. Drive scrollback rows from an OverScroller on animation frames; touch-down cancels the fling.
Long-press starts a word-snapped selection (ghostty word rules via the native selection API), dragging extends it, and teardrop grab handles adjust either end. A floating toolbar offers Copy and Select all; copy unwraps soft-wrapped lines and trims trailing whitespace. The terminal owns selection state, so the existing FLAG_SELECTED render path highlights cells.
Typing exit left a dead terminal on screen: close the session on an observed running->exited transition and switch to the nearest live terminal below the exited id, dismissing only when none remain. Reopen dead sessions with an explicit terminal.open instead of reusing the TTL-cached attach stream, which never respawned the shell. Give Android a header menu (previously iOS-only native toolbar) with the session list, text size controls, and an open-new-terminal action.
The default monospace has no Nerd Font glyphs, so powerline prompts and file icons rendered as tofu. Bundle MesloLGS NF Regular/Bold (Apache 2.0, third-party notice added); italics are synthesized. Falls back to monospace if the asset fails to load.
Ending a session marks its key handled so the stale-attach effect cannot respawn the shell the user just exited. Treat closed like exited when detecting session end, forward env on stale reopen, and forget the panel's running marker while hidden so exit-while-hidden takes the reopen path on the next show.
Extending from the pressed cell shrank the initial word selection when the long-press landed mid-word. Anchor drags to the far word edge for the drag direction, activate the selection before the synchronous word-snap render so handles appear immediately, and anchor the toolbar to the actual selection endpoints.
The session-end guard was never cleared while the panel was hidden, so reopening it showed the dead session and skipped the respawn. When the last terminal exits on a root or deep-linked mount with nothing to pop, replace to the thread screen instead of stranding the user.
A failed terminal.open left the session-end guard set, pinning the screen to a dead session with no retry; release the guard on failure. Android split view had no way to toggle the thread sidebar from the terminal screen — the control lived only in the iOS native toolbar — so add it to the Android header.
A session ending while the screen was unfocused closed the shell but never navigated, stranding the user on a dead, respawn-blocked terminal when they returned; defer the fallback navigation to the next focus. New PTY output now clears an active selection (matching the web drawer) so the copy toolbar can't drift out of sync with the grid.
An environment drop left the running marker set, so a shell that died while unattached read as a locally observed exit after reconnect and navigated away instead of respawning. A pending unfocused-exit navigation also survived a respawn, ejecting a live terminal on the next focus. Clear both markers when the attach input drops and clear the pending navigation whenever the session is running again.
Clearing it when the attach input dropped could strand the user on a dead terminal after an unfocused exit followed by a brief disconnect. The pending navigation now clears only when the session runs again; the detach path resets just the running marker.
A handle grab consumed ACTION_DOWN before the gesture detector could cancel fling momentum, letting the viewport scroll under an active drag. Dismissing the floating toolbar (e.g. via Back) now clears the selection instead of leaving a highlighted grid with no toolbar, except mid-drag where the grab finishes the mode intentionally.
a102676 to
1262f17
Compare
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
80cf687
into
pingdotgg:android-dev-pr-3514



Summary
Five terminal improvements for the Android renderer introduced in #3579, one per commit:
icrnltranslates for canonical-mode programs.OverScrollernow drives rows on animation frames, cancelled on touch-down.libghostty-vtsymbols), drag extends it, teardrop handles adjust either end, and a floating toolbar offers Copy/Select all. Copy unwraps soft-wrapped lines. Rendering reuses the existingFLAG_SELECTEDpath.exitleft a dead terminal on screen. Close the session on an observed running→exited transition and switch to the nearest live terminal below the exited id, dismissing only when none remain. Reopening a dead session issues an explicitterminal.openinstead of reusing the TTL-cached attach stream (which never respawned the shell). Android also gets the header menu that was previously iOS-only native toolbar: session list, text size, and open-new-terminal. Note: the lifecycle change is in shared RN code and applies to iOS too, mirroring the web drawer'sonSessionExitedflow.Validation
pnpm exec tsc --noEmit; terminal unit tests pass (45, including 4 new for the exit-fallback picker)Note
Medium Risk
JNI selection and shared terminal lifecycle/navigation affect core shell UX on Android and iOS; Enter/CR and stale-session respawn logic can change PTY and session behavior in edge cases.
Overview
Android Ghostty terminal gets text selection (JNI → Kotlin bridge), MesloLGS NF for Nerd Font glyphs, fling scroll, and Enter → CR for raw TUIs. New output clears stale selections.
Shared RN terminal UX mirrors the web drawer: on an observed running → exited transition, close the session and navigate to
previousLiveTerminalIdor leave the screen; reopening a dead attach stream triggers an explicitterminal.open. Android adds an in-flow terminal header menu (sessions, font size, new shell).Android composers (
ThreadComposer,NewTaskDraftScreen) expand only on focus (toolbar hidden when collapsed); collapsed chrome height is unified to 60px.Reviewed by Cursor Bugbot for commit 3614f1f. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add text selection, Nerd Font support, inertial scrolling, and session lifecycle handling to the Android terminal
OverScroller-based inertial fling scrolling to the terminal canvaspreviousLiveTerminalIdfallback\r) instead of LF (\n); Android composer collapsed height reduced to match other platforms and expands only on focusMacroscope summarized 3614f1f.