Skip to content

fix(mobile): reserve the bottom safe area on Android surfaces - #6003

Open
PollyGlot wants to merge 2 commits into
pingdotgg:mainfrom
PollyGlot:fix/mobile-bottom-safe-area-rule
Open

fix(mobile): reserve the bottom safe area on Android surfaces#6003
PollyGlot wants to merge 2 commits into
pingdotgg:mainfrom
PollyGlot:fix/mobile-bottom-safe-area-rule

Conversation

@PollyGlot

@PollyGlot PollyGlot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What Changed

Four Android surfaces that anchor content to the bottom edge now reserve the safe-area inset, and a new t3code/require-bottom-safe-area-inset lint rule keeps the next one from shipping without it.

Surface Before After
Terminal floating keyboard button bottom: 16 Math.max(insets.bottom, 16) + 16 (same as AndroidHomeFab)
Terminal surface, keyboard down paddingBottom: 0 insets.bottom
File tree list paddingBottom: 8 Math.max(insets.bottom, 8) + 8
Archived threads list paddingBottom: 32 Math.max(insets.bottom, 16) + 16
AndroidAnchoredMenu usable height ignored the gesture bar subtracts max(keyboard, insets.bottom)

Why

Follow-up to the chat composer fix (#5988), which was one instance of a pattern. contentInsetAdjustmentBehavior="automatic" only pads the safe area on iOS, so Android lists that relied on it ended flush against the gesture bar; the terminal button and the anchored menu never accounted for it at all.

The lint rule fires only on React Native files that pin content to the bottom edge (position: "absolute" with a fixed non-zero bottom, or a literal paddingBottom in contentContainerStyle/contentInset) and never read insets.bottom. bottom: 0 is exempt: that is how a keyboard-synced overlay attaches to the edge while its child owns the padding. Across the whole repo it flagged exactly the three code sites fixed here and nothing else.

UI Changes

Pixel emulator, API 35, gesture navigation, dev client against a local backend.

File tree — beforeFile tree — after
the gesture bar crosses through README.md the last row clears the gesture bar
Terminal button — beforeTerminal button — after
the keyboard button sits on the gesture bar the keyboard button clears the gesture bar

Verified on device: file tree, terminal button, and the anchored menu (no placement regression). The archived-threads padding is a code-level change I did not reproduce on screen; its previous 32 happened to exceed this device's 24dp inset, so it was fragile rather than visibly broken.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • New lint rule ships with tests (oxlint-plugin-t3code/rules/require-bottom-safe-area-inset.test.ts)

🤖 Generated with Claude Code


Note

Low Risk
Mobile UI and layout-only changes with platform guards; no auth, data, or backend impact.

Overview
Android bottom chrome now respects the gesture bar and home indicator by wiring useSafeAreaInsets() into lists, the terminal, anchored menus, and floating controls. iOS paths are mostly unchanged because automatic content insets already handle the bottom edge.

Anchored menus treat usable height below the anchor as overlay.height - max(keyboard, insets.bottom) so dropdowns do not open into the IME or under the gesture bar.

Scroll surfaces (ArchivedThreadsScreen, FileTreeBrowser, ReviewSheet file list) use Android-only paddingBottom: Math.max(insets.bottom, N) + N instead of fixed padding, since contentInsetAdjustmentBehavior does not pad the bottom on Android.

Terminal applies insets.bottom to the surface when the keyboard is hidden, and positions the show-keyboard FAB with Math.max(insets.bottom, 16) + 16.

Lint adds enforced t3code/require-bottom-safe-area-inset to flag React Native files that pin bottom content with literal offsets or list paddingBottom without reading insets.bottom (with exemptions for bottom: 0 edge overlays and bottomInset props).

Reviewed by Cursor Bugbot for commit d1525ff. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Reserve bottom safe area insets on Android across mobile surfaces

  • Fixes multiple Android screens where content or floating buttons overlapped the gesture bar/home indicator by accounting for insets.bottom from useSafeAreaInsets.
  • Affected surfaces: anchored menus, archived threads list, file tree browser, review sheet file list, and the terminal screen with its floating keyboard button.
  • Adds a new require-bottom-safe-area-inset oxlint rule (enforced as error) that detects React Native components anchoring content to the bottom without reserving safe-area insets, preventing regressions.

Macroscope summarized d1525ff.

@coderabbitai

coderabbitai Bot commented Aug 10, 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 Plus

Run ID: bd27b12b-800e-465f-a262-049a6b0f68ce

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

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:L 100-499 changed lines (additions + deletions). labels Aug 10, 2026
Comment thread oxlint-plugin-t3code/rules/require-bottom-safe-area-inset.ts
@PollyGlot
PollyGlot force-pushed the fix/mobile-bottom-safe-area-rule branch from 897f11f to 30dbe7d Compare August 10, 2026 13:12
Comment thread apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx
Comment thread oxlint-plugin-t3code/rules/require-bottom-safe-area-inset.ts
@macroscopeapp

macroscopeapp Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR modifies runtime UI layout behavior across multiple mobile screens and introduces a new lint rule. A Medium-severity finding identifies a potential regression where pre-glass iOS devices may lose bottom safe area padding in FileTreeBrowser. The behavioral changes and potential regression warrant human review.

You can customize Macroscope's approvability policy. Learn more.

@PollyGlot

Copy link
Copy Markdown
Contributor Author

Thanks — all three findings were real. Fixed in cf1ca8c.

iOS doubled bottom spacing (archived threads + file tree). Confirmed, and this repo already documents the mechanism in ThreadDetailScreen.tsx:208-216: with contentInsetAdjustmentBehavior="automatic", UIKit adds the safe-area bottom to the content inset, so adding it again in contentContainerStyle leaves a dead strip. Concretely on a 34pt home indicator, archived threads went from 32 + 34 to 50 + 34, and the file tree from 8 + 34 to 42 + 34 on liquid-glass devices.

Both lists now apply the manual inset on Android only, so iOS spacing is byte-for-byte what it was before this PR:

paddingBottom: Platform.OS === "android" ? Math.max(insets.bottom, 16) + 16 : 32

The two other changes in this PR are unaffected: the terminal's floating button is absolutely positioned (no content inset involved) and AndroidAnchoredMenu is Android-only.

Worth noting for a follow-up, out of scope here: on pre-liquid-glass iOS the file tree uses contentInsetAdjustmentBehavior="never", so its last row genuinely does sit under the home indicator. That is pre-existing, not something this PR introduces, and fixing it needs its own before/after on a simulator.

Rule: file-level inset flag. Fixed — inset reads are now tracked per top-level function instead of per file, so a header component that reads the inset no longer excuses a sibling list that ignores it. Nested functions (renderItem, callbacks) still inherit their component's id, since the hook is called once in the component body and closed over.

This immediately paid off: the stricter rule flagged ReviewSheet.tsx:276, a file navigator with a hardcoded paddingBottom: 8 in a component whose sibling reads insets.bottom. Exactly the class of miss you described, fixed in the same commit.

Rule: style arrays. Fixed — getObjectProperties now flattens ArrayExpression elements, so contentContainerStyle={[styles.base, { paddingBottom: 8 }]} is inspected. Non-literal elements (StyleSheet references) contribute nothing and are skipped.

Test coverage went from 6 to 9 cases, one per finding. Repo-wide the rule now reports zero violations.

@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.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cf1ca8c. Configure here.

Comment thread apps/mobile/src/features/files/FileTreeBrowser.tsx
@PollyGlot
PollyGlot force-pushed the fix/mobile-bottom-safe-area-rule branch from cf1ca8c to ac333f9 Compare August 16, 2026 12:24
PollyGlot and others added 2 commits August 18, 2026 13:01
The terminal's floating keyboard button sat at a raw bottom: 16, the file
tree and archived-threads lists ended in fixed padding, and the anchored
menu treated the gesture bar as usable space. All four render under the
Android gesture bar (and the iOS home indicator).

Pad them with the house Math.max(insets.bottom, N) convention, and add a
t3code/require-bottom-safe-area-inset lint rule so the next bottom-anchored
surface cannot ship without it. The rule only fires on React Native files
that pin content to the bottom edge and never read insets.bottom.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Macroscope was right about the iOS regression: where a list sets
contentInsetAdjustmentBehavior="automatic", UIKit already adds the safe
area to the content inset, so adding it again in contentContainerStyle
left a dead strip below the last row. The archived-threads and file-tree
lists now apply the manual inset on Android only, restoring the exact
pre-PR iOS spacing.

The rule itself had two gaps, both now covered by tests:
- inset reads were tracked per file, so a header component reading the
  inset excused a sibling list that ignored it. They are now tracked per
  top-level function, which immediately surfaced the same bug in
  ReviewSheet's file navigator (fixed here).
- style arrays (contentContainerStyle={[base, { paddingBottom: 8 }]})
  were skipped entirely; array elements are now flattened.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@PollyGlot
PollyGlot force-pushed the fix/mobile-bottom-safe-area-rule branch from ac333f9 to d1525ff Compare August 18, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 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.

1 participant