Skip to content

Use Ghostty for native mobile terminals - #2656

Open
colonelpanic8 wants to merge 1 commit into
getpaseo:mainfrom
colonelpanic8:libghostty-integration
Open

Use Ghostty for native mobile terminals#2656
colonelpanic8 wants to merge 1 commit into
getpaseo:mainfrom
colonelpanic8:libghostty-integration

Conversation

@colonelpanic8

Copy link
Copy Markdown
Contributor

Summary

  • adapt T3 Code's MIT-licensed native terminal module for Paseo
  • use custom-I/O GhosttyKit on iOS and libghostty-vt plus a Canvas/JNI renderer on Android
  • keep the existing xterm WebView renderer as an automatic fallback when the native module is unavailable
  • preserve snapshot restore, input-mode tracking, hardware/software keyboard input, resize/focus handling, scrolling, Android selection/copy, and terminal panel swipe gestures
  • discard renderer-generated terminal query replies because Paseo's daemon already owns those responses
  • feed live output directly and retain only the latest snapshot payload, avoiding T3's unbounded replay-buffer growth

Packaging and compatibility

  • vendors pinned Ghostty artifacts, headers, rebuild scripts, licenses, and T3 Code attribution
  • verifies all Android Ghostty/JNI libraries use 16 KB segment alignment
  • raises the iOS deployment target from 15.1 to 16.1 to match the vendored GhosttyKit build
  • excludes the binary-backed native module from F-Droid autolinking; F-Droid and Expo Go continue to use the WebView fallback

The checked-in native module adds approximately 55 MB to the source tree, mostly the two iOS static-library slices.

Known follow-ups

  • native rendering does not yet activate Paseo's local file links
  • iOS Ghostty selection/clipboard support remains disabled by the upstream custom-I/O integration
  • custom terminal font-family selection is not yet bridged to the native renderers

Verification

  • npm run build:app-deps
  • npm run build:server
  • npm run typecheck
  • npm run lint
  • npm run format:check
  • npx vitest run packages/app/src/terminal/runtime/terminal-snapshot.test.ts --bail=1
  • Expo autolinking discovery for Android and Apple
  • Android :getpaseo-mobile-terminal-native:assembleDebug across arm64-v8a, armeabi-v7a, x86, and x86_64
  • F-Droid Gradle project configuration without the native terminal module
  • readelf -lW alignment check for vendored libghostty-vt.so and generated libpaseoterminal.so across all four ABIs

iOS prebuild and autolinking were verified on Linux; an Xcode compile/runtime pass still needs macOS CI or a device build.

Comment on lines +317 to +332
private fun emitResize() {
if (
width <= 0 ||
height <= 0 ||
terminalCanvas.width <= 0 ||
terminalCanvas.height <= 0 ||
isCleanedUp
) {
return
}
val nextCols = (terminalCanvas.usableWidth() / terminalCanvas.cellWidthPx)
.toInt()
.coerceIn(2, 400)
val nextRows = (terminalCanvas.usableHeight() / terminalCanvas.cellHeightPx)
.toInt()
.coerceIn(2, 200)

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.

P1 Hardware navigation keys are dropped

When the Android Ghostty renderer receives Escape, Tab, arrow, or other terminal navigation keys from a hardware keyboard, this listener handles only Delete and Ctrl+A–Z and the native React adapter does not provide the shared onTerminalKey encoding path, causing those keys not to reach shells or terminal TUIs.

Knowledge Base Used: Paseo Mobile App (packages/app)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +505 to +506
surfaceCreationFailed = true
return

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.

P1 Surface failure disables the terminal

When Ghostty initialization or subsequent app or surface creation fails, this path permanently sets surfaceCreationFailed without reporting readiness failure or switching to the WebView renderer. The view never emits its first resize, so JavaScript keeps terminal operations queued and the terminal remains behind its loading state for the lifetime of the view.

Knowledge Base Used: Paseo Mobile App (packages/app)

@greptile-apps

greptile-apps Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds Ghostty-backed native mobile terminals while retaining the existing WebView implementation as a fallback.

  • Adds an iOS GhosttyKit terminal surface and an Android libghostty-vt/JNI/Canvas renderer.
  • Integrates native output, snapshot restoration, input-mode tracking, resizing, focus, scrolling, selection, and panel gestures.
  • Vendors pinned Ghostty artifacts and build scripts, raises the iOS deployment target to 16.1, and excludes the binary-backed module from F-Droid autolinking.

Confidence Score: 4/5

The native terminal input and iOS creation-failure paths need to be fixed before merging because they can leave hardware navigation keys unusable on Android or the entire terminal unavailable on iOS.

The Android listener emits only Delete and Ctrl-letter hardware input without the generic terminal-key path, while the iOS view permanently stops surface creation after an initialization failure and therefore never releases queued terminal operations.

Files Needing Attention: packages/app/modules/paseo-terminal/android/src/main/java/expo/modules/paseoterminal/PaseoTerminalView.kt, packages/app/modules/paseo-terminal/ios/PaseoTerminalView.swift, and packages/app/src/components/terminal-emulator.native.tsx

Important Files Changed

Filename Overview
packages/app/src/components/terminal-emulator.native.tsx Selects the native renderer when available, queues terminal operations until resize readiness, tracks input modes, and falls back to the extracted WebView renderer.
packages/app/modules/paseo-terminal/android/src/main/java/expo/modules/paseoterminal/PaseoTerminalView.kt Implements Android terminal lifecycle, input, resize, selection, and rendering, but fails to forward common hardware terminal navigation keys.
packages/app/modules/paseo-terminal/android/src/main/cpp/paseo_terminal_jni.cpp Bridges libghostty-vt terminal state, snapshots, scrolling, themes, and selection to the Android view.
packages/app/modules/paseo-terminal/ios/PaseoTerminalView.swift Implements the GhosttyKit iOS surface, but a surface creation failure permanently prevents readiness and leaves terminal operations queued.
packages/app/src/components/terminal-emulator-webview.native.tsx Preserves the existing xterm WebView renderer as the fallback implementation.
packages/app/plugins/with-fdroid-autolinking.js Excludes the binary-backed terminal module from F-Droid native autolinking.
packages/app/app.config.js Raises the generated iOS deployment target to 16.1 for the vendored GhosttyKit build.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Runtime[Terminal stream runtime] --> Detect{Native view manager available?}
  Detect -->|Yes| Native[NativeTerminalEmulator]
  Detect -->|No| WebView[xterm WebView fallback]
  Native -->|iOS| GhosttyKit[GhosttyKit surface]
  Native -->|Android| JNI[libghostty-vt JNI]
  JNI --> Canvas[Android Canvas renderer]
  GhosttyKit --> Resize[Input / resize / focus events]
  Canvas --> Resize
  Resize --> Runtime
Loading

Reviews (1): Last reviewed commit: "feat(app): use Ghostty for native termin..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant