Skip to content

fix(mobile): stop Android user bubbles with code blocks from overlapping - #5659

Merged
juliusmarminge merged 1 commit into
pingdotgg:mainfrom
Brechard:fix/android-user-bubble-wide-block
Aug 10, 2026
Merged

fix(mobile): stop Android user bubbles with code blocks from overlapping#5659
juliusmarminge merged 1 commit into
pingdotgg:mainfrom
Brechard:fix/android-user-bubble-wide-block

Conversation

@Brechard

@Brechard Brechard commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

The bug

On Android, a user message that contains a fenced code block renders its paragraphs stacked on top of one another inside a hugely over-tall bubble, and the overflow paints over the rows that follow it in the feed. It is fully deterministic — scrolling away and back reproduces it at the same message.

Reproduced on the current Play Store release (1.0.2, versionCode 14) on a Pixel 8 / Android 16, and again from a clean checkout of main.

Before After

Same seeded thread, same entry point, Pixel 8 / Android 16. A user message containing a fenced TypeScript block: before, the paragraphs are drawn on top of each other, the code block sits over the opening paragraph, and the trailing prose overflows the bubble and paints over the assistant reply below. After, the blocks are laid out in order and nothing overlaps.

Root cause

The user bubble sizes itself from its content — maxWidth: userBubbleMaxWidth with no width (ThreadFeed.tsx). MarkdownCodeBlock wraps its body in a horizontal ScrollView, so its intrinsic width is the width of its widest line, which is effectively unbounded. That forces the bubble's width to be clamped.

Android lays the bubble's children out during the unclamped intrinsic pass — where the surrounding paragraphs collapse to a single line — and never repositions them once the width is clamped back to maxWidth.

Actual view bounds from uiautomator dump on the broken bubble:

ViewGroup [191,1570][1038,2400]   <- bubble
  TextView [223,1593][1005,2205]  <- paragraph, 612px tall
  ViewGroup [223,1621][1005,2400] <- code block, placed 28px below the paragraph's TOP

The paragraph is laid out 612px tall, but its next sibling starts 28px after the paragraph's top — a 584px overlap. The bubble still reserves the full stacked height, which is the large empty region.

An assistant message with the identical code block renders correctly, because assistant rows are full width and never hit the clamp:

TextView  [51,487][1028,691]    paragraph
ViewGroup [51,745][1029,1011]   code block   <- starts after the paragraph
TextView  [51,1037][1028,1173]  next paragraph

iOS is unaffected: hasNativeSelectableMarkdownText() is true there, so markdown goes through the native T3SelectableMarkdownText view and there are no nested block children to mis-position. Android has no .android.tsx variant, so it falls back to the JS react-native-nitro-markdown renderer.

The fix

Pin the bubble's width when the message contains a block that renders as a horizontally scrollable View. This removes the intrinsic-width pass entirely, and is the same reason review-comment bubbles already carry an explicit width. The resulting width matches what the clamp would have produced, so nothing looks different other than being laid out correctly.

Covers fenced code blocks and GFM tables (same renderer shape). Bubbles without such a block are untouched and still hug their content.

Verification

A/B on a physical Pixel 8 against a disposable seeded environment with four message shapes:

Message Before After
Plain prose (control) correct correct, still hugs content
User + fenced code block broken correct
User + narrow table correct correct
Assistant + code block (control) correct correct

After the fix, child bounds are strictly sequential (paragraph → code block 298..614 → trailing paragraph 641..981).

Also verified on a real, long production thread where the bug was originally reported: the previously broken message now renders correctly, as do the rows around it.

vp lint, tsc --noEmit, and the mobile suite (618 tests) pass. The helper ships with unit tests.

Known limits

  • The narrow-table case never actually broke at that width, so table detection is preventive — same mechanism, just untriggered until the table is wider than the bubble.
  • Indented (four-space) code blocks are deliberately not detected; the check would fire on ordinary nested list continuations, and they are vanishingly rare in chat input.
  • iOS was not rebuilt. The change is very likely a no-op there (the native view almost certainly already reports a wide intrinsic size, so those bubbles sit at max width already), but I did not confirm it on a simulator.

Note

Low Risk
Narrow mobile UI/layout change with unit-tested detection; no auth, data, or API impact.

Overview
Fixes Android layout where user messages with fenced code blocks (or wide GFM tables) stacked paragraphs on top of each other inside an over-tall bubble.

Adds hasWideMarkdownBlock to detect fenced code and GFM table delimiter rows (not inline code or prose with |). In ThreadFeed, when a user message contains such content, the bubble gets an explicit width: userBubbleMaxWidth—the same approach already used for review-comment bubbles—so Android skips the broken intrinsic-width layout pass. Plain prose bubbles still size from content only.

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

Note

Fix Android user message bubbles with code blocks or tables overlapping

On Android, user message bubbles containing fenced code blocks or GFM tables were sizing intrinsically, causing paragraph layout to overlap. The fix introduces hasWideMarkdownBlock, which detects fenced code blocks (``` or ~~~) and GFM table delimiter rows via regex, then applies a fixed `userBubbleMaxWidth` to affected bubbles in `ThreadFeed.tsx`.

Macroscope summarized 5b44d84.

On Android a user message containing a fenced code block draws its
paragraphs on top of each other inside a hugely over-tall bubble, and
the overflow paints over the following rows in the feed.

The bubble sizes itself from its content (`maxWidth`, no `width`), so a
child whose intrinsic width is unbounded — the code block's horizontal
ScrollView — forces a clamp. Android positions the bubble's children
during the unclamped intrinsic pass, where the surrounding paragraphs
collapse to a single line, and never repositions them once the width is
clamped back to `maxWidth`.

Measured on a Pixel 8, the paragraph is laid out 612px tall while the
code block that follows it is placed 28px below the paragraph's *top* —
an overlap of 584px.

Pinning the bubble's width removes the intrinsic pass, which is the same
reason review-comment bubbles already carry an explicit width. Applies
to GFM tables too, which use the same horizontally scrollable renderer.

iOS is unaffected: it renders markdown through the native
SelectableMarkdownText view, which has no nested block children.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

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

Run ID: 98271a39-86a3-465e-8e95-e5e0e2e20240

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:M 30-99 changed lines (additions + deletions). labels Aug 7, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved 5b44d84

Straightforward Android UI bug fix that pins bubble width when code blocks or tables are detected, preventing layout overlap. The changes are self-contained, well-documented, follow existing patterns, and include unit tests.

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

@juliusmarminge
juliusmarminge merged commit d440442 into pingdotgg:main Aug 10, 2026
17 checks passed
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 10, 2026
## What's Changed
* fix(mobile): reconnects no longer shift the thread list by @t3dotgg in pingdotgg/t3code#5372
* feat(web): drag pinned threads into your own order by @t3dotgg in pingdotgg/t3code#5581
* chore(ci): vouch StiensWout by @t3-code[bot] in pingdotgg/t3code#5637
* feat(desktop): remember recently used sites in the Browser panel by @chrisdeeming in pingdotgg/t3code#5270
* chore: vouch chrisdeeming by @t3-code[bot] in pingdotgg/t3code#5641
* feat(web): make sidebar artwork theme-aware by @maria-rcks in pingdotgg/t3code#5636
* fix(web): reconnect the composer seam for remote non-Git projects by @caezium in pingdotgg/t3code#5633
* fix(web): show Stop button while input is pending by @ipanasenko in pingdotgg/t3code#5554
* feat(web): fold plan mode and token-by-token output into Legacy features by @t3dotgg in pingdotgg/t3code#5664
* feat: sidebar v2 is now the default sidebar by @t3dotgg in pingdotgg/t3code#5672
* fix(server): stop PR status lookups amplifying GitHub rate limits by @t3dotgg in pingdotgg/t3code#5673
* fix(web): delay transient reconnect warnings by @t3-code[bot] in pingdotgg/t3code#5670
* fix(web): inherit terminal size in simple typography by @chrisdeeming in pingdotgg/t3code#5628
* fix(server): stop the reaper from silently killing live background subagents by @t3dotgg in pingdotgg/t3code#5677
* fix(desktop): zoom shortcuts no longer die when the preview browser has focus by @t3dotgg in pingdotgg/t3code#5691
* feat(mobile): one sheet for model and thread settings by @t3dotgg in pingdotgg/t3code#5625
* feat(usage): usage page reading provider transcripts across environments by @t3dotgg in pingdotgg/t3code#5684
* fix(web): usage chart no longer makes Claude look like the bigger spender by @t3dotgg in pingdotgg/t3code#5697
* fix(web): persist diff view mode by @leorivastech in pingdotgg/t3code#5731
* feat(web): show how many subagents are running at a glance by @t3dotgg in pingdotgg/t3code#5745
* fix(web): add missing cursor-pointer styling to dropdowns and interactive buttons by @naMqe-h in pingdotgg/t3code#5716
* fix(server): stop Claude resume handshakes from completing turns that never ran by @gfsaaser24 in pingdotgg/t3code#5710
* chore: vouch gfsaaser24 by @t3dotgg in pingdotgg/t3code#5761
* chore: vouch saphid by @t3dotgg in pingdotgg/t3code#5763
* fix(server): stop Codex threads with queued follow-ups by @t3dotgg in pingdotgg/t3code#5762
* fix(web): usage page loses the cost quality panel, gains a back button by @t3dotgg in pingdotgg/t3code#5756
* feat(server): agents can now open the images you paste into chat by @t3dotgg in pingdotgg/t3code#5757
* fix(web): pinned reorder no longer reshuffles while writes land by @t3dotgg in pingdotgg/t3code#5767
* feat(web): overhaul project settings into a real settings page by @t3dotgg in pingdotgg/t3code#5768
* fix(web): usage totals no longer jump while devices report in by @t3dotgg in pingdotgg/t3code#5772
* fix(server): settle no longer leaves monitors and dev servers running by @t3dotgg in pingdotgg/t3code#5774
* feat: pick worktree or current checkout per project by @t3dotgg in pingdotgg/t3code#5766
* fix(web): sidebar rows show the branch again, not a truncated plan step by @t3dotgg in pingdotgg/t3code#5776
* feat(server): vp run migrate-dev-db seeds worktree dev dbs with real data by @t3dotgg in pingdotgg/t3code#5773
* feat(web): keep unsent drafts one click away in the sidebar by @t3dotgg in pingdotgg/t3code#5777
* feat(web): project icons can be chosen manually by @t3dotgg in pingdotgg/t3code#5775
* fix(server): one greedy agent process no longer takes down the whole server by @t3dotgg in pingdotgg/t3code#5788
* ci: label-gated hosted-web preview deploys by @t3dotgg in pingdotgg/t3code#5465
* Add cross-platform mobile usage dashboard by @juliusmarminge in pingdotgg/t3code#5743
* fix(web): preserve desktop route during Clerk auth by @wobsoriano in pingdotgg/t3code#5770
* fix(web): match create theme and import theme buttons to the standard outline style by @UtkarshUsername in pingdotgg/t3code#5860
* fix(server): favicon resolution no longer pins the event loop by @murenovich in pingdotgg/t3code#5538
* fix(shared): bound the file-link label so bracket runs stop rescanning by @tsouth89 in pingdotgg/t3code#5782
* fix(web): thread title button no longer eats the drag area by @nathangerday in pingdotgg/t3code#5857
* fix(web): unify usage page chrome by @t3-code[bot] in pingdotgg/t3code#5823
* fix(shell): add ~/.local/bin to the Windows CLI resolver so native-installed providers are found by @arhxam in pingdotgg/t3code#5074
* fix(web): match settings search shortcut styling to command palette's by @UtkarshUsername in pingdotgg/t3code#5841
* fix(mobile): long-pressing a thread row no longer navigates into the thread by @juliusmarminge in pingdotgg/t3code#5901
* fix(server): usage no longer double-counts forked Codex sessions by @t3dotgg in pingdotgg/t3code#5887
* fix(server): sandbox user-provided SVGs by @t3dotgg in pingdotgg/t3code#5916
* fix(web): match usage titlebar text styling by @t3-code[bot] in pingdotgg/t3code#5897
* Move project settings to contextual project routes by @juliusmarminge in pingdotgg/t3code#5923
* Retain thread sidebar data when navigating to /settings so back navigation is instant by @juliusmarminge in pingdotgg/t3code#5930
* Automate production mobile EAS releases by @juliusmarminge in pingdotgg/t3code#5609
* Add settings and usage breadcrumbs by @juliusmarminge in pingdotgg/t3code#5929
* fix(web): correct model picker trigger padding by @Chrono-byte in pingdotgg/t3code#5935
* fix(web): show worktree icon in sidebar v2 by @tris203 in pingdotgg/t3code#5909
* fix(web): enable restore defaults after theme mix changes by @Lucenx9 in pingdotgg/t3code#5928
* fix(web): trait menu closes after you pick a level by @t3dotgg in pingdotgg/t3code#5879
* fix(web): align project name with headline by @carterwsmith in pingdotgg/t3code#5864
* fix(web): update pills use readable theme foregrounds by @chrisdeeming in pingdotgg/t3code#5938
* fix(web): use themed confirmation dialogs by @StiensWout in pingdotgg/t3code#5624
* fix(web): use import/export-appropriate icons for theme buttons by @UtkarshUsername in pingdotgg/t3code#5964
* fix(mobile): detect PowerShell cmdlet errors in work log rows by @myacoub91 in pingdotgg/t3code#5726
* fix(mobile): stop Android user bubbles with code blocks from overlapping by @Brechard in pingdotgg/t3code#5659
* fix(mobile): parse EAS fingerprint JSON by @juliusmarminge in pingdotgg/t3code#5991

## New Contributors
* @chrisdeeming made their first contribution in pingdotgg/t3code#5270
* @wobsoriano made their first contribution in pingdotgg/t3code#5770
* @murenovich made their first contribution in pingdotgg/t3code#5538
* @tsouth89 made their first contribution in pingdotgg/t3code#5782
* @nathangerday made their first contribution in pingdotgg/t3code#5857
* @carterwsmith made their first contribution in pingdotgg/t3code#5864
* @myacoub91 made their first contribution in pingdotgg/t3code#5726

**Full Changelog**: pingdotgg/t3code@v0.0.32...v0.0.33

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.33
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 10, 2026
## What's Changed
* fix(web): use import/export-appropriate icons for theme buttons by @UtkarshUsername in pingdotgg/t3code#5964
* fix(mobile): detect PowerShell cmdlet errors in work log rows by @myacoub91 in pingdotgg/t3code#5726
* fix(mobile): stop Android user bubbles with code blocks from overlapping by @Brechard in pingdotgg/t3code#5659
* fix(mobile): parse EAS fingerprint JSON by @juliusmarminge in pingdotgg/t3code#5991

## New Contributors
* @myacoub91 made their first contribution in pingdotgg/t3code#5726

**Full Changelog**: pingdotgg/t3code@v0.0.33-nightly.20260810.1056...v0.0.34-nightly.20260810.1059

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.34-nightly.20260810.1059
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 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.

3 participants