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
Localized mobile layout and lint-only guardrails; iOS paths are explicitly unchanged aside from shared menu height math.

Overview
Fixes Android layouts where lists, menus, and floating controls sat under the gesture bar because contentInsetAdjustmentBehavior only applies on iOS.

Runtime changes: Several screens now use useSafeAreaInsets() and the house pattern Math.max(insets.bottom, N) + N for list paddingBottom on Android only (archived threads, file tree, review file list). The terminal adds insets.bottom to scroll padding when the keyboard is hidden and lifts the “show keyboard” FAB the same way. AndroidAnchoredMenu treats usable height as overlay.height - max(keyboardHeight, insets.bottom) so menus don’t open into the gesture bar when the keyboard is down.

Lint: Adds t3code/require-bottom-safe-area-inset (error in vite.config.ts) for React Native files that pin content with a non-zero bottom or literal paddingBottom on scroll content without reading insets.bottom / bottomInset / SafeAreaView in that component. Includes unit tests; bottom: 0 edge overlays stay exempt.

Reviewed by Cursor Bugbot for commit ac333f9. 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 ac333f9.

@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: f4f2e304-3803-436a-b909-e8867342e4d7

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 and others added 2 commits August 16, 2026 14:21
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 cf1ca8c to ac333f9 Compare August 16, 2026 12:24
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