fix(mobile): size a chat image's frame before its bytes arrive#10199

merged2
media-dims/contract
media-dims/mobile
openedSep 5, 2026byJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 4d ago

Stack 2/3 (#10201). Depends on #10198.

Problem

An assistant image on mobile went through three sizes: full width on first render, the measured width after its own onLayout, then the decoded size (up to 480px tall for a portrait screenshot). Each step changed the row's height after LegendList had already placed the rows below it. That's the "scroll area cut off under the composer" and "tool calls render under the media" reports.

Fix

  • ThreadFeed hands markdown its content width through MarkdownImageAvailableWidthContext, so the placeholder is the right width on the first render instead of after onLayout. The context is an upper bound: a list item or blockquote indents its column, and the measured width takes over once known.
  • ThreadMarkdownImage uses the server's imageDimensions (#10198) to size the frame exactly before the first byte arrives. The decoded size still wins once it exists, so a wrong hint can never stick. An image the server couldn't measure keeps the 16:9 slot.
  • Padding constants use the mobile 14px rem (px-3.5 on the bubble, px-1 on assistant rows).

Evidence

iPhone 17 Pro simulator on macmini, seeded thread with landscape/portrait/wide screenshots, asset responses delayed 2.5 s through a proxy so the loading phase is observable. Frames captured every ~100 ms during thread open.

Before (main): portrait placeholder is 16:9; when the image decodes the row grows ~250px and everything below reflows. After: the frame is portrait from first paint; rows below never move.

iOS before/after

Verification

  • apps/mobile typecheck clean; lint on changed files clean (pre-existing effect warnings unchanged).
  • Simulator pass above.

Model: Claude Fable 5 Ā· Harness: Claude Code in T3 Code

šŸ¤– Generated with Claude Code

Note

Size chat image frames before bytes arrive in mobile thread feed

  • ThreadFeed derives a markdown content width from the centered content width and row padding, then passes it to renderFeedEntry so user and assistant markdown rows have a width estimate before their own onLayout fires.
  • ThreadMarkdownImage combines the feed-provided width with a server-supplied image size to render an aspect-ratio frame before decode; after decode, decoded dimensions take precedence and the measured layout width constrains the estimate.
  • readJpeg now skips later APP1 segments once a rotated EXIF orientation is detected, and IMAGE_DIMENSIONS_HEADER_BYTES is raised from 64 KiB to 256 KiB to reach JPEG frame headers after multiple metadata segments.
  • Risk: in ThreadMarkdownImageView the frame resolves only when both a source size and a positive available width exist, otherwise it falls back to the placeholder frame; rows lacking a width context or unknown image size may keep the placeholder longer.

Macroscope summarized cced7aa.

Synced from GitHub

Thread transfer impact

āœ… Thread transfer remains within every enforced ceiling.

ā„¹ļø No successful main baseline artifact is available yet. This run establishes the initial measurement.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire—13.5 KiB—15.1 KiBāœ…
CodexThread snapshot wire—7.0 KiB—7.3 KiBāœ…
CodexLive turn WebSocket wire—6.5 KiB—7.8 KiBāœ…
CodexLive turn WebSocket decoded—57.0 KiB—66.4 KiBāœ…
CodexLive turn messages—8—21āœ…
ClaudeTotal thread wire—13.6 KiB—15.1 KiBāœ…
ClaudeThread snapshot wire—7.0 KiB—7.3 KiBāœ…
ClaudeLive turn WebSocket wire—6.5 KiB—7.8 KiBāœ…
ClaudeLive turn WebSocket decoded—57.9 KiB—66.4 KiBāœ…
ClaudeLive turn messages—10—21āœ…

Baseline: unavailable Ā· PR result: cced7aa Ā· Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Approvability

Verdict: Approved at a68cf3b

Macroscope's review found this PR approvable — This is a focused mobile layout bug fix that stabilizes existing markdown image frames using the feed width and already-supported image metadata. Its runtime impact is confined to image sizing and row layout, with no new capability, schema change, deployment change, or static-analysis override.

No code changes detected at cced7aa. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

Added 2 commits

  • fix(mobile): size a chat image's frame before its bytes arrive3aad878
  • fix(mobile): treat the feed width as a bound and let the decoded size wincced7aa
Synced from GitHub

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.