[Bug]: Android terminal keeps old pixel dimensions after a font change that preserves the shared grid #36008
Send to my agentSummary#
Changing the Android terminal's text size can leave the terminal engine using the previous character-cell pixel dimensions when another viewer keeps the shared rows and columns unchanged. Applications using the engine's supported in-band resize reporting then receive no pixel-size update for that font change. The Android adapter failure was established from source; a deterministic probe of the bundled engine confirmed that a resize with unchanged rows and columns updates pixel dimensions and emits the expected report when the new cell sizes reach it.
Before submitting#
- Related issues: searched open and closed issues and PRs; related shared-grid work is listed under Additional context, with different closure criteria.
- Removed credentials, tokens, private paths, and sensitive data.
Area#
apps/mobile
Actual behavior#
Source-only Android finding: the supported fontSize prop updates TerminalCanvasView.fontSizeSp, which recalculates the actual cell width and height and calls emitResize(). emitResize() always reaches applyGrid(), but applyGrid() returns when the effective columns and rows equal the previous grid. That return does not compare the new cell pixel dimensions. The subsequent GhosttyBridge.nativeResize() call is the path that would pass the new cell sizes into the existing engine and forward its response bytes, so it is skipped for this valid font-change case. Sources: T3TerminalView.kt#L99-L105, TerminalCanvasView.kt#L150-L155, TerminalCanvasView.kt#L285-L294, and T3TerminalView.kt#L335-L381.
The source path is reachable through the terminal menu's Text size actions, which update the appearance terminal font size passed to the native surface: ThreadTerminalRouteScreen.tsx#L126-L145, ThreadTerminalRouteScreen.tsx#L720-L725, and NativeTerminalSurface.tsx#L285-L288.
Steps to reproduce#
- Inspected the supported Android font-size prop, canvas metric update, shared-grid calculation, native resize, and reply-forwarding path at the commit under Version.
- Ran the actual web
GhosttyTerminalCoreandGhosttyRuntimeagainst both vendored WASM artifacts with a data-only PTY writer. Created a 12-column, 3-row terminal, enabled mode 2048 withESC [ ? 2048 h, and resized it without changing its rows or columns, supplying new 12-by-24-pixel cell dimensions. The callback receivedESC [ 48 ; 3 ; 12 ; 72 ; 144 t. A subsequent 13-by-4-cell resize producedESC [ 48 ; 4 ; 13 ; 96 ; 156 t, confirming that the setup and response callback worked. - Traced the Android font change with a shared 80-by-24 grid and an Android local fit larger than that grid before and after the font change. These are source-proof inputs, not measured device sizes: a local fit changing from 100-by-40 to 90-by-36 still yields the same 80-by-24 effective grid on both calls, so the early return skips
nativeResize()despite the new cell dimensions.
The engine control was deterministic. The Android behavior is static proof, not a device or emulator reproduction. NOT_ENOUGH_INFO: an Android capture with a program recording mode-2048 reports before and after the text-size change would verify the complete UI-to-PTY path and provide measured cell sizes.
Reproduction script: NOT_ENOUGH_INFO — no script was run that reproduces the Android adapter failure end to end; the executed WASM probe establishes the engine's same-grid pixel-update behavior, while the Android early return and supported font-change path establish the failure statically. An Android integration harness driving the native font-size prop with a fixed shared grid would make a red/green adapter reproduction possible.
Expected behavior#
The terminal engine's cell pixel dimensions must follow the canvas's actual cell metrics even when the shared cell grid remains unchanged, and an application that enabled in-band resize reports must receive the changed pixel dimensions. The pinned C API explicitly uses cell_width_px and cell_height_px to update pixel dimensions and sends a report when mode 2048 is enabled: terminal.h#L1042-L1067. The bundled-engine observation above confirms this behavior at the actual pin; it does not rely on the newer libghostty API generation. Android already supplies these cell metrics and forwards replies whenever it does call resize: t3_terminal_jni.cpp#L251-L267.
Impact#
Minor bug or occasional failure.
Impact: P2 - The adapter leaves supported terminal pixel-size state stale and omits an opted-in size notification for a bounded class of font changes. The affected condition is an existing Android engine whose effective rows and columns stay unchanged while its integer cell pixel dimensions change, especially when another smaller viewer constrains the shared grid.
The screen continues drawing with the new font, while terminal programs using mode-2048 pixel dimensions retain the old geometry until a later engine resize. No Android rendering failure, named application failure, or data loss was observed. NOT_ENOUGH_INFO: a user-level workaround was not exercised on Android; a later change of effective rows or columns reaches the resize path in source, but that is not a verified workaround.
Version or commit#
T3 Code commit 59e64eef881632a23f89c4138bcf37f23d92bf93; vendored web/Android libghostty-vt pin 9f62873bf195e4d8a762d768a1405a5f2f7b1697. The current default-branch T3TerminalView.kt was also read before drafting and retains the same early-return condition; the Android runtime was not run at either revision.
Environment#
Executed engine probe: Node v26.12.1, macOS arm64, actual vendored WASM and PTY trampoline. Unused browser keyboard helpers were isolated by the Node import harness; the runtime, core resize method, engine artifacts, and reply callback were executed unchanged. Android source was inspected; NOT_ENOUGH_INFO: no Android OS, device, packaged client version, or native runtime measurement is claimed.
Interface#
UI: Android terminal Text size menu; native terminal font-size prop and JNI resize bridge.
Earliest failure stage#
Integration or transport
Evidence#
Executed engine observations, with response bytes escaped as JSON:
Node v26.12.1 darwin arm64
Ghostty pin 9f62873bf195e4d8a762d768a1405a5f2f7b1697
sameGridNewCellPixels ["\u001b[48;3;12;72;144t"]
changedGridNewCellPixels ["\u001b[48;4;13;96;156t"]
The Android failure is the unconditional return before the updated cell metrics are submitted:
if (nextCols == cols && nextRows == rows && terminalHandle != 0L) return
Expected source: pinned terminal resize contract and actual bundled-engine output above. Failure source: the supported font-size-to-applyGrid() call chain above. Evidence provenance: static for the Android defect; observed for the engine control. Local verification: static-proof. Reproduction completeness: incomplete. Missing fact: Android runtime observation of the omitted report after a real font change.
Restoration check#
With another viewer holding the shared grid constant, changing Android Text size enough to alter integer cell metrics should leave the effective rows and columns unchanged, update the native engine's pixel width and height to match the new canvas metrics, and send a mode-2048 report with those dimensions to an opted-in program. The existing different-grid resize and normal live replies must keep working. This is the restoration check; it has not been run on Android.
References#
- https://github.com/pingdotgg/t3code/pull/24352 — establishes shared cell-grid negotiation and local-fit clamping; this report concerns cell pixel metrics changing while that negotiated grid stays constant, rather than the already-covered cell-grid clipping defect.
tanstack-source added labelsbug
github-actions[bot](GitHub profile, opens in a new tab) added labelsvouch:denounced
juliusmarminge closed this
Sign in to join this conversation.