Skip to content

feat(mobile): Android terminal improvements — input, selection, sessions, Nerd Font#3775

Merged
juliusmarminge merged 16 commits into
pingdotgg:android-dev-pr-3514from
PixPMusic:android-terminal-improvements
Jul 12, 2026
Merged

feat(mobile): Android terminal improvements — input, selection, sessions, Nerd Font#3775
juliusmarminge merged 16 commits into
pingdotgg:android-dev-pr-3514from
PixPMusic:android-terminal-improvements

Conversation

@PixPMusic

@PixPMusic PixPMusic commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Five terminal improvements for the Android renderer introduced in #3579, one per commit:

  • Enter sends CR: raw-mode TUIs (Claude Code, vim) treat LF as Ctrl+J, so Enter never submitted. Terminals send CR for Return; the PTY's icrnl translates for canonical-mode programs.
  • Fling momentum scrolling: scrollback stopped the moment the finger lifted; an OverScroller now drives rows on animation frames, cancelled on touch-down.
  • Text selection: long-press starts a word-snapped selection via ghostty's native selection API (five new JNI functions over the already-exported libghostty-vt symbols), drag extends it, teardrop handles adjust either end, and a floating toolbar offers Copy/Select all. Copy unwraps soft-wrapped lines. Rendering reuses the existing FLAG_SELECTED path.
  • Session lifecycle: 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. Reopening a dead session issues an explicit terminal.open instead 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's onSessionExited flow.
  • MesloLGS NF bundled: the default monospace has no Nerd Font glyphs, so powerline prompts and file icons rendered as tofu. Regular/Bold bundled (Apache 2.0, third-party notice added), italics synthesized, monospace fallback if the asset fails to load.

Validation

  • pnpm exec tsc --noEmit; terminal unit tests pass (45, including 4 new for the exit-fallback picker)
  • Release build installed and exercised on a Pixel 7 Pro: p10k prompt renders Nerd Font glyphs, Enter submits in Claude Code's TUI, session menu lists/switches/opens terminals

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 previousLiveTerminalId or leave the screen; reopening a dead attach stream triggers an explicit terminal.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

  • Implements long-press word selection, drag-to-extend with handles, and a floating Copy/Select All toolbar in TerminalCanvasView.kt via new JNI selection methods in t3_terminal_jni.cpp
  • Bundles MesloLGS NF Regular and Bold fonts for Android terminal rendering, with fallback to system monospace
  • Adds OverScroller-based inertial fling scrolling to the terminal canvas
  • Tracks terminal session lifecycle in ThreadTerminalRouteScreen.tsx and ThreadTerminalPanel.tsx: auto-dismisses or navigates away when a session exits, and respawns on stale attach using a previousLiveTerminalId fallback
  • Behavioral Change: Enter key now sends CR (\r) instead of LF (\n); Android composer collapsed height reduced to match other platforms and expands only on focus

Macroscope summarized 3614f1f.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0ed34aa2-7c04-4310-be41-a3eebd4365d0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Jul 7, 2026
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx Outdated
Comment thread apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx Outdated
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
@macroscopeapp

macroscopeapp Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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.

@PixPMusic PixPMusic force-pushed the android-terminal-improvements branch from 41ed0d8 to 040b6fb Compare July 7, 2026 18:54
@PixPMusic

Copy link
Copy Markdown
Contributor Author

Force-pushed to fix my git identity, sorry! My Github isn't linked to my work email :)

Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
Comment thread apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx
Comment thread apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
@PixPMusic PixPMusic force-pushed the android-terminal-improvements branch from f6fbc81 to ba16828 Compare July 7, 2026 22:46
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx Outdated
Comment thread apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx
@PixPMusic

Copy link
Copy Markdown
Contributor Author
image I love when my agent follows my exact instructions to the letter and does nothing more than what I told it to do

but hey, that triggered a cursor review :)

Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
Comment thread apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx
@juliusmarminge juliusmarminge force-pushed the android-terminal-improvements branch from 67a4458 to 47dc9fb Compare July 8, 2026 11:14
Comment thread apps/mobile/src/features/terminal/ThreadTerminalPanel.tsx
@juliusmarminge juliusmarminge force-pushed the android-terminal-improvements branch from ea78bd0 to e3efc51 Compare July 9, 2026 09:32
@juliusmarminge juliusmarminge force-pushed the android-terminal-improvements branch 3 times, most recently from f647223 to 54c1e08 Compare July 9, 2026 13:54
@juliusmarminge juliusmarminge force-pushed the android-terminal-improvements branch from 54c1e08 to a102676 Compare July 9, 2026 14:03

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a102676. Configure here.

PixPMusic and others added 14 commits July 9, 2026 20:07
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.
@juliusmarminge juliusmarminge force-pushed the android-terminal-improvements branch from a102676 to 1262f17 Compare July 9, 2026 18:07
juliusmarminge and others added 2 commits July 9, 2026 20:41
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Jul 9, 2026
@juliusmarminge juliusmarminge merged commit 80cf687 into pingdotgg:android-dev-pr-3514 Jul 12, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ 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.

2 participants