fix(mobile): Stabilize thread feed layout and Markdown rendering - #4036

Open
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout
Open

fix(mobile): Stabilize thread feed layout and Markdown rendering#4036
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Jul 16, 2026

Copy link
Copy Markdown
Member

Extracted from #3910.

Fixes native selectable Markdown width and stale-content behavior, keeps screenshots interactive, and stabilizes floating-composer spacing, short-content positioning, and hydration layout in the thread feed. Includes the required LegendList patch and regenerated lockfile.

Validation inherited from #3910: vp check and mobile native lint passed before the split. The full typecheck is currently blocked in this workspace by unavailable mobile dependencies (expo-blur, expo-quick-actions, and @tabler/icons-react-native).

Note

Stabilize thread feed scroll layout and fix Markdown rendering in iOS

  • Fixes thread feed scroll position when content is shorter than the viewport by anchoring content under translucent headers and scrolling to end when content grows to fill the viewport.
  • Adds a fillWidth prop to SelectableMarkdownText so assistant markdown messages stretch to fill the full row width.
  • Refactors T3MarkdownTextShadowNode to derive content via buildContent and gate state updates with equality checks, preventing redundant re-renders.
  • Patches @legendapp/list to improve end-scroll maintenance, initial scroll readiness, and keyboard-aware inset handling near translucent headers.
  • Adds measured composer overlay height tracking in ThreadDetailScreen to produce a stable resting inset above the composer.
  • Behavioral Change: maintainScrollAtEnd is disabled while content underflows the viewport; scroll position is instead managed by a requestAnimationFrame loop.

Macroscope summarized 1cd97c7.


Note

Medium Risk
Large changes to vendored list scroll math and native Fabric text state on a core chat screen; regressions would show as wrong scroll position, jank, or missing markdown rather than data/security issues.

Overview
Thread feed and composer insets — The detail screen now tracks measured floating-composer overlay height, adds a fixed gap above the chrome, and passes that through keyboard/LegendList inset hooks (heightAdjustment, contentInsetEndEstimate). ThreadFeed detects when content is shorter than the viewport, pins scroll under the translucent header, turns off maintainScrollAtEnd in that case, and shows a loading overlay while hydrating without remounting the list on draft→thread transitions.

Native selectable markdown — iOS T3MarkdownTextShadowNode stops caching measure-time text in mutable fields; it rebuilds content from children in layout() and only updates native state when attributed text/ranges actually change, fixing dropped/stale text when Yoga layouts without remeasuring. A new optional fillWidth prop stretches assistant markdown rows so they reflow correctly in shrink-to-fit layouts.

LegendList patch — Extends the vendored @legendapp/list keyboard integration for leading inset (transparent header), safe-area end-inset compensation, short-content scroll clamping, and more reliable initial/end scroll behavior; lockfile patch hash updated.

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

juliusmarmingeand others added 2 commits July 16, 2026 10:54
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitaiBot commented Jul 16, 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

Run ID: 7bb6af15-1007-4f8d-9a16-772c36b7da18

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jul 16, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review July 16, 2026 09:25
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@macroscopeapp

macroscopeappBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This bug fix touches native iOS C++ layout code and includes substantial patches to the @legendapp/list third-party library affecting scroll behavior calculations. While the author is the primary maintainer of these files, native code changes and third-party patches to rendering infrastructure warrant human review.

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

juliusmarmingeand others added 2 commits July 16, 2026 11:36
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx Outdated
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
@juliusmarmingejuliusmarminge added the 🚀 Mobile Continuous Deployment Trigger Expo preview build label Jul 20, 2026

@cursorcursorBot 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 ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

// Reset their larger measurement to this thread's known baseline; its own
// overlay layout will immediately replace it if it contains extra cards.
setComposerOverlayHeight(estimatedOverlayHeightRef.current);
}, [selectedThreadKey]);

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.

Thread switch inset desync

Medium Severity

On selectedThreadKey change, composerOverlayHeight resets to the new thread’s baseline, but contentInsetEndAdjustment (the keyboard hook SharedValue) is not updated. Until the new composer lays out, contentInsetEndEstimate reflects the new thread while list end-inset math and the remount reportContentInset call still use the previous thread’s larger value.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 20, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Refresh against the approved codex-turn-mapping tip. CTM's LegendList 3.3.3
patch threads contentInsetStartAdjustment through the list's own scroll math,
and pingdotgg#4867/pingdotgg#4874 carry the gated feed-item layout transition, so this merge
keeps CTM's versions of both. The keyboard-side wiring is not upstream: the
3.3.3 patch never passes contentInsetStartCompensation into
KeyboardChatScrollView, whose clamp floor defaults to 0, so the pingdotgg#4036 wiring
is re-ported onto the 3.3.3 patch. The pingdotgg#4036-unique short-content underflow
correction, Markdown rendering, and thread-detail changes are retained.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 5, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Mobile Continuous DeploymentTrigger Expo preview buildsize:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(mobile): Stabilize thread feed layout and Markdown rendering - #4036

Open
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout
Open

fix(mobile): Stabilize thread feed layout and Markdown rendering#4036
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Jul 16, 2026

Copy link
Copy Markdown
Member

Extracted from #3910.

Fixes native selectable Markdown width and stale-content behavior, keeps screenshots interactive, and stabilizes floating-composer spacing, short-content positioning, and hydration layout in the thread feed. Includes the required LegendList patch and regenerated lockfile.

Validation inherited from #3910: vp check and mobile native lint passed before the split. The full typecheck is currently blocked in this workspace by unavailable mobile dependencies (expo-blur, expo-quick-actions, and @tabler/icons-react-native).

Note

Stabilize thread feed scroll layout and fix Markdown rendering in iOS

  • Fixes thread feed scroll position when content is shorter than the viewport by anchoring content under translucent headers and scrolling to end when content grows to fill the viewport.
  • Adds a fillWidth prop to SelectableMarkdownText so assistant markdown messages stretch to fill the full row width.
  • Refactors T3MarkdownTextShadowNode to derive content via buildContent and gate state updates with equality checks, preventing redundant re-renders.
  • Patches @legendapp/list to improve end-scroll maintenance, initial scroll readiness, and keyboard-aware inset handling near translucent headers.
  • Adds measured composer overlay height tracking in ThreadDetailScreen to produce a stable resting inset above the composer.
  • Behavioral Change: maintainScrollAtEnd is disabled while content underflows the viewport; scroll position is instead managed by a requestAnimationFrame loop.

Macroscope summarized 1cd97c7.


Note

Medium Risk
Large changes to vendored list scroll math and native Fabric text state on a core chat screen; regressions would show as wrong scroll position, jank, or missing markdown rather than data/security issues.

Overview
Thread feed and composer insets — The detail screen now tracks measured floating-composer overlay height, adds a fixed gap above the chrome, and passes that through keyboard/LegendList inset hooks (heightAdjustment, contentInsetEndEstimate). ThreadFeed detects when content is shorter than the viewport, pins scroll under the translucent header, turns off maintainScrollAtEnd in that case, and shows a loading overlay while hydrating without remounting the list on draft→thread transitions.

Native selectable markdown — iOS T3MarkdownTextShadowNode stops caching measure-time text in mutable fields; it rebuilds content from children in layout() and only updates native state when attributed text/ranges actually change, fixing dropped/stale text when Yoga layouts without remeasuring. A new optional fillWidth prop stretches assistant markdown rows so they reflow correctly in shrink-to-fit layouts.

LegendList patch — Extends the vendored @legendapp/list keyboard integration for leading inset (transparent header), safe-area end-inset compensation, short-content scroll clamping, and more reliable initial/end scroll behavior; lockfile patch hash updated.

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

juliusmarmingeand others added 2 commits July 16, 2026 10:54
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitaiBot commented Jul 16, 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

Run ID: 7bb6af15-1007-4f8d-9a16-772c36b7da18

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jul 16, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review July 16, 2026 09:25
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@macroscopeapp

macroscopeappBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This bug fix touches native iOS C++ layout code and includes substantial patches to the @legendapp/list third-party library affecting scroll behavior calculations. While the author is the primary maintainer of these files, native code changes and third-party patches to rendering infrastructure warrant human review.

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

juliusmarmingeand others added 2 commits July 16, 2026 11:36
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx Outdated
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
@juliusmarmingejuliusmarminge added the 🚀 Mobile Continuous Deployment Trigger Expo preview build label Jul 20, 2026

@cursorcursorBot 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 ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

// Reset their larger measurement to this thread's known baseline; its own
// overlay layout will immediately replace it if it contains extra cards.
setComposerOverlayHeight(estimatedOverlayHeightRef.current);
}, [selectedThreadKey]);

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.

Thread switch inset desync

Medium Severity

On selectedThreadKey change, composerOverlayHeight resets to the new thread’s baseline, but contentInsetEndAdjustment (the keyboard hook SharedValue) is not updated. Until the new composer lays out, contentInsetEndEstimate reflects the new thread while list end-inset math and the remount reportContentInset call still use the previous thread’s larger value.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 20, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Refresh against the approved codex-turn-mapping tip. CTM's LegendList 3.3.3
patch threads contentInsetStartAdjustment through the list's own scroll math,
and pingdotgg#4867/pingdotgg#4874 carry the gated feed-item layout transition, so this merge
keeps CTM's versions of both. The keyboard-side wiring is not upstream: the
3.3.3 patch never passes contentInsetStartCompensation into
KeyboardChatScrollView, whose clamp floor defaults to 0, so the pingdotgg#4036 wiring
is re-ported onto the 3.3.3 patch. The pingdotgg#4036-unique short-content underflow
correction, Markdown rendering, and thread-detail changes are retained.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 5, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Mobile Continuous DeploymentTrigger Expo preview buildsize:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mobile): Stabilize thread feed layout and Markdown rendering - #4036

Open
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout
Open

fix(mobile): Stabilize thread feed layout and Markdown rendering#4036
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Jul 16, 2026

Copy link
Copy Markdown
Member

Extracted from #3910.

Fixes native selectable Markdown width and stale-content behavior, keeps screenshots interactive, and stabilizes floating-composer spacing, short-content positioning, and hydration layout in the thread feed. Includes the required LegendList patch and regenerated lockfile.

Validation inherited from #3910: vp check and mobile native lint passed before the split. The full typecheck is currently blocked in this workspace by unavailable mobile dependencies (expo-blur, expo-quick-actions, and @tabler/icons-react-native).

Note

Stabilize thread feed scroll layout and fix Markdown rendering in iOS

  • Fixes thread feed scroll position when content is shorter than the viewport by anchoring content under translucent headers and scrolling to end when content grows to fill the viewport.
  • Adds a fillWidth prop to SelectableMarkdownText so assistant markdown messages stretch to fill the full row width.
  • Refactors T3MarkdownTextShadowNode to derive content via buildContent and gate state updates with equality checks, preventing redundant re-renders.
  • Patches @legendapp/list to improve end-scroll maintenance, initial scroll readiness, and keyboard-aware inset handling near translucent headers.
  • Adds measured composer overlay height tracking in ThreadDetailScreen to produce a stable resting inset above the composer.
  • Behavioral Change: maintainScrollAtEnd is disabled while content underflows the viewport; scroll position is instead managed by a requestAnimationFrame loop.

Macroscope summarized 1cd97c7.


Note

Medium Risk
Large changes to vendored list scroll math and native Fabric text state on a core chat screen; regressions would show as wrong scroll position, jank, or missing markdown rather than data/security issues.

Overview
Thread feed and composer insets — The detail screen now tracks measured floating-composer overlay height, adds a fixed gap above the chrome, and passes that through keyboard/LegendList inset hooks (heightAdjustment, contentInsetEndEstimate). ThreadFeed detects when content is shorter than the viewport, pins scroll under the translucent header, turns off maintainScrollAtEnd in that case, and shows a loading overlay while hydrating without remounting the list on draft→thread transitions.

Native selectable markdown — iOS T3MarkdownTextShadowNode stops caching measure-time text in mutable fields; it rebuilds content from children in layout() and only updates native state when attributed text/ranges actually change, fixing dropped/stale text when Yoga layouts without remeasuring. A new optional fillWidth prop stretches assistant markdown rows so they reflow correctly in shrink-to-fit layouts.

LegendList patch — Extends the vendored @legendapp/list keyboard integration for leading inset (transparent header), safe-area end-inset compensation, short-content scroll clamping, and more reliable initial/end scroll behavior; lockfile patch hash updated.

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

juliusmarmingeand others added 2 commits July 16, 2026 10:54
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitaiBot commented Jul 16, 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

Run ID: 7bb6af15-1007-4f8d-9a16-772c36b7da18

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jul 16, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review July 16, 2026 09:25
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@macroscopeapp

macroscopeappBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This bug fix touches native iOS C++ layout code and includes substantial patches to the @legendapp/list third-party library affecting scroll behavior calculations. While the author is the primary maintainer of these files, native code changes and third-party patches to rendering infrastructure warrant human review.

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

juliusmarmingeand others added 2 commits July 16, 2026 11:36
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx Outdated
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
@juliusmarmingejuliusmarminge added the 🚀 Mobile Continuous Deployment Trigger Expo preview build label Jul 20, 2026

@cursorcursorBot 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 ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

// Reset their larger measurement to this thread's known baseline; its own
// overlay layout will immediately replace it if it contains extra cards.
setComposerOverlayHeight(estimatedOverlayHeightRef.current);
}, [selectedThreadKey]);

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.

Thread switch inset desync

Medium Severity

On selectedThreadKey change, composerOverlayHeight resets to the new thread’s baseline, but contentInsetEndAdjustment (the keyboard hook SharedValue) is not updated. Until the new composer lays out, contentInsetEndEstimate reflects the new thread while list end-inset math and the remount reportContentInset call still use the previous thread’s larger value.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 20, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Refresh against the approved codex-turn-mapping tip. CTM's LegendList 3.3.3
patch threads contentInsetStartAdjustment through the list's own scroll math,
and pingdotgg#4867/pingdotgg#4874 carry the gated feed-item layout transition, so this merge
keeps CTM's versions of both. The keyboard-side wiring is not upstream: the
3.3.3 patch never passes contentInsetStartCompensation into
KeyboardChatScrollView, whose clamp floor defaults to 0, so the pingdotgg#4036 wiring
is re-ported onto the 3.3.3 patch. The pingdotgg#4036-unique short-content underflow
correction, Markdown rendering, and thread-detail changes are retained.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 5, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Mobile Continuous DeploymentTrigger Expo preview buildsize:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mobile): Stabilize thread feed layout and Markdown rendering - #4036

Open
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout
Open

fix(mobile): Stabilize thread feed layout and Markdown rendering#4036
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Jul 16, 2026

Copy link
Copy Markdown
Member

Extracted from #3910.

Fixes native selectable Markdown width and stale-content behavior, keeps screenshots interactive, and stabilizes floating-composer spacing, short-content positioning, and hydration layout in the thread feed. Includes the required LegendList patch and regenerated lockfile.

Validation inherited from #3910: vp check and mobile native lint passed before the split. The full typecheck is currently blocked in this workspace by unavailable mobile dependencies (expo-blur, expo-quick-actions, and @tabler/icons-react-native).

Note

Stabilize thread feed scroll layout and fix Markdown rendering in iOS

  • Fixes thread feed scroll position when content is shorter than the viewport by anchoring content under translucent headers and scrolling to end when content grows to fill the viewport.
  • Adds a fillWidth prop to SelectableMarkdownText so assistant markdown messages stretch to fill the full row width.
  • Refactors T3MarkdownTextShadowNode to derive content via buildContent and gate state updates with equality checks, preventing redundant re-renders.
  • Patches @legendapp/list to improve end-scroll maintenance, initial scroll readiness, and keyboard-aware inset handling near translucent headers.
  • Adds measured composer overlay height tracking in ThreadDetailScreen to produce a stable resting inset above the composer.
  • Behavioral Change: maintainScrollAtEnd is disabled while content underflows the viewport; scroll position is instead managed by a requestAnimationFrame loop.

Macroscope summarized 1cd97c7.


Note

Medium Risk
Large changes to vendored list scroll math and native Fabric text state on a core chat screen; regressions would show as wrong scroll position, jank, or missing markdown rather than data/security issues.

Overview
Thread feed and composer insets — The detail screen now tracks measured floating-composer overlay height, adds a fixed gap above the chrome, and passes that through keyboard/LegendList inset hooks (heightAdjustment, contentInsetEndEstimate). ThreadFeed detects when content is shorter than the viewport, pins scroll under the translucent header, turns off maintainScrollAtEnd in that case, and shows a loading overlay while hydrating without remounting the list on draft→thread transitions.

Native selectable markdown — iOS T3MarkdownTextShadowNode stops caching measure-time text in mutable fields; it rebuilds content from children in layout() and only updates native state when attributed text/ranges actually change, fixing dropped/stale text when Yoga layouts without remeasuring. A new optional fillWidth prop stretches assistant markdown rows so they reflow correctly in shrink-to-fit layouts.

LegendList patch — Extends the vendored @legendapp/list keyboard integration for leading inset (transparent header), safe-area end-inset compensation, short-content scroll clamping, and more reliable initial/end scroll behavior; lockfile patch hash updated.

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

juliusmarmingeand others added 2 commits July 16, 2026 10:54
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitaiBot commented Jul 16, 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

Run ID: 7bb6af15-1007-4f8d-9a16-772c36b7da18

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jul 16, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review July 16, 2026 09:25
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@macroscopeapp

macroscopeappBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This bug fix touches native iOS C++ layout code and includes substantial patches to the @legendapp/list third-party library affecting scroll behavior calculations. While the author is the primary maintainer of these files, native code changes and third-party patches to rendering infrastructure warrant human review.

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

juliusmarmingeand others added 2 commits July 16, 2026 11:36
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx Outdated
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
@juliusmarmingejuliusmarminge added the 🚀 Mobile Continuous Deployment Trigger Expo preview build label Jul 20, 2026

@cursorcursorBot 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 ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

// Reset their larger measurement to this thread's known baseline; its own
// overlay layout will immediately replace it if it contains extra cards.
setComposerOverlayHeight(estimatedOverlayHeightRef.current);
}, [selectedThreadKey]);

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.

Thread switch inset desync

Medium Severity

On selectedThreadKey change, composerOverlayHeight resets to the new thread’s baseline, but contentInsetEndAdjustment (the keyboard hook SharedValue) is not updated. Until the new composer lays out, contentInsetEndEstimate reflects the new thread while list end-inset math and the remount reportContentInset call still use the previous thread’s larger value.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 20, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Refresh against the approved codex-turn-mapping tip. CTM's LegendList 3.3.3
patch threads contentInsetStartAdjustment through the list's own scroll math,
and pingdotgg#4867/pingdotgg#4874 carry the gated feed-item layout transition, so this merge
keeps CTM's versions of both. The keyboard-side wiring is not upstream: the
3.3.3 patch never passes contentInsetStartCompensation into
KeyboardChatScrollView, whose clamp floor defaults to 0, so the pingdotgg#4036 wiring
is re-ported onto the 3.3.3 patch. The pingdotgg#4036-unique short-content underflow
correction, Markdown rendering, and thread-detail changes are retained.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 5, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Mobile Continuous DeploymentTrigger Expo preview buildsize:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(mobile): Stabilize thread feed layout and Markdown rendering - #4036

Open
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout
Open

fix(mobile): Stabilize thread feed layout and Markdown rendering#4036
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Jul 16, 2026

Copy link
Copy Markdown
Member

Extracted from #3910.

Fixes native selectable Markdown width and stale-content behavior, keeps screenshots interactive, and stabilizes floating-composer spacing, short-content positioning, and hydration layout in the thread feed. Includes the required LegendList patch and regenerated lockfile.

Validation inherited from #3910: vp check and mobile native lint passed before the split. The full typecheck is currently blocked in this workspace by unavailable mobile dependencies (expo-blur, expo-quick-actions, and @tabler/icons-react-native).

Note

Stabilize thread feed scroll layout and fix Markdown rendering in iOS

  • Fixes thread feed scroll position when content is shorter than the viewport by anchoring content under translucent headers and scrolling to end when content grows to fill the viewport.
  • Adds a fillWidth prop to SelectableMarkdownText so assistant markdown messages stretch to fill the full row width.
  • Refactors T3MarkdownTextShadowNode to derive content via buildContent and gate state updates with equality checks, preventing redundant re-renders.
  • Patches @legendapp/list to improve end-scroll maintenance, initial scroll readiness, and keyboard-aware inset handling near translucent headers.
  • Adds measured composer overlay height tracking in ThreadDetailScreen to produce a stable resting inset above the composer.
  • Behavioral Change: maintainScrollAtEnd is disabled while content underflows the viewport; scroll position is instead managed by a requestAnimationFrame loop.

Macroscope summarized 1cd97c7.


Note

Medium Risk
Large changes to vendored list scroll math and native Fabric text state on a core chat screen; regressions would show as wrong scroll position, jank, or missing markdown rather than data/security issues.

Overview
Thread feed and composer insets — The detail screen now tracks measured floating-composer overlay height, adds a fixed gap above the chrome, and passes that through keyboard/LegendList inset hooks (heightAdjustment, contentInsetEndEstimate). ThreadFeed detects when content is shorter than the viewport, pins scroll under the translucent header, turns off maintainScrollAtEnd in that case, and shows a loading overlay while hydrating without remounting the list on draft→thread transitions.

Native selectable markdown — iOS T3MarkdownTextShadowNode stops caching measure-time text in mutable fields; it rebuilds content from children in layout() and only updates native state when attributed text/ranges actually change, fixing dropped/stale text when Yoga layouts without remeasuring. A new optional fillWidth prop stretches assistant markdown rows so they reflow correctly in shrink-to-fit layouts.

LegendList patch — Extends the vendored @legendapp/list keyboard integration for leading inset (transparent header), safe-area end-inset compensation, short-content scroll clamping, and more reliable initial/end scroll behavior; lockfile patch hash updated.

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

juliusmarmingeand others added 2 commits July 16, 2026 10:54
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitaiBot commented Jul 16, 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

Run ID: 7bb6af15-1007-4f8d-9a16-772c36b7da18

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jul 16, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review July 16, 2026 09:25
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@macroscopeapp

macroscopeappBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This bug fix touches native iOS C++ layout code and includes substantial patches to the @legendapp/list third-party library affecting scroll behavior calculations. While the author is the primary maintainer of these files, native code changes and third-party patches to rendering infrastructure warrant human review.

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

juliusmarmingeand others added 2 commits July 16, 2026 11:36
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx Outdated
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
@juliusmarmingejuliusmarminge added the 🚀 Mobile Continuous Deployment Trigger Expo preview build label Jul 20, 2026

@cursorcursorBot 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 ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

// Reset their larger measurement to this thread's known baseline; its own
// overlay layout will immediately replace it if it contains extra cards.
setComposerOverlayHeight(estimatedOverlayHeightRef.current);
}, [selectedThreadKey]);

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.

Thread switch inset desync

Medium Severity

On selectedThreadKey change, composerOverlayHeight resets to the new thread’s baseline, but contentInsetEndAdjustment (the keyboard hook SharedValue) is not updated. Until the new composer lays out, contentInsetEndEstimate reflects the new thread while list end-inset math and the remount reportContentInset call still use the previous thread’s larger value.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 20, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Refresh against the approved codex-turn-mapping tip. CTM's LegendList 3.3.3
patch threads contentInsetStartAdjustment through the list's own scroll math,
and pingdotgg#4867/pingdotgg#4874 carry the gated feed-item layout transition, so this merge
keeps CTM's versions of both. The keyboard-side wiring is not upstream: the
3.3.3 patch never passes contentInsetStartCompensation into
KeyboardChatScrollView, whose clamp floor defaults to 0, so the pingdotgg#4036 wiring
is re-ported onto the 3.3.3 patch. The pingdotgg#4036-unique short-content underflow
correction, Markdown rendering, and thread-detail changes are retained.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 5, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Mobile Continuous DeploymentTrigger Expo preview buildsize:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mobile): Stabilize thread feed layout and Markdown rendering - #4036

Open
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout
Open

fix(mobile): Stabilize thread feed layout and Markdown rendering#4036
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Jul 16, 2026

Copy link
Copy Markdown
Member

Extracted from #3910.

Fixes native selectable Markdown width and stale-content behavior, keeps screenshots interactive, and stabilizes floating-composer spacing, short-content positioning, and hydration layout in the thread feed. Includes the required LegendList patch and regenerated lockfile.

Validation inherited from #3910: vp check and mobile native lint passed before the split. The full typecheck is currently blocked in this workspace by unavailable mobile dependencies (expo-blur, expo-quick-actions, and @tabler/icons-react-native).

Note

Stabilize thread feed scroll layout and fix Markdown rendering in iOS

  • Fixes thread feed scroll position when content is shorter than the viewport by anchoring content under translucent headers and scrolling to end when content grows to fill the viewport.
  • Adds a fillWidth prop to SelectableMarkdownText so assistant markdown messages stretch to fill the full row width.
  • Refactors T3MarkdownTextShadowNode to derive content via buildContent and gate state updates with equality checks, preventing redundant re-renders.
  • Patches @legendapp/list to improve end-scroll maintenance, initial scroll readiness, and keyboard-aware inset handling near translucent headers.
  • Adds measured composer overlay height tracking in ThreadDetailScreen to produce a stable resting inset above the composer.
  • Behavioral Change: maintainScrollAtEnd is disabled while content underflows the viewport; scroll position is instead managed by a requestAnimationFrame loop.

Macroscope summarized 1cd97c7.


Note

Medium Risk
Large changes to vendored list scroll math and native Fabric text state on a core chat screen; regressions would show as wrong scroll position, jank, or missing markdown rather than data/security issues.

Overview
Thread feed and composer insets — The detail screen now tracks measured floating-composer overlay height, adds a fixed gap above the chrome, and passes that through keyboard/LegendList inset hooks (heightAdjustment, contentInsetEndEstimate). ThreadFeed detects when content is shorter than the viewport, pins scroll under the translucent header, turns off maintainScrollAtEnd in that case, and shows a loading overlay while hydrating without remounting the list on draft→thread transitions.

Native selectable markdown — iOS T3MarkdownTextShadowNode stops caching measure-time text in mutable fields; it rebuilds content from children in layout() and only updates native state when attributed text/ranges actually change, fixing dropped/stale text when Yoga layouts without remeasuring. A new optional fillWidth prop stretches assistant markdown rows so they reflow correctly in shrink-to-fit layouts.

LegendList patch — Extends the vendored @legendapp/list keyboard integration for leading inset (transparent header), safe-area end-inset compensation, short-content scroll clamping, and more reliable initial/end scroll behavior; lockfile patch hash updated.

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

juliusmarmingeand others added 2 commits July 16, 2026 10:54
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitaiBot commented Jul 16, 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

Run ID: 7bb6af15-1007-4f8d-9a16-772c36b7da18

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jul 16, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review July 16, 2026 09:25
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@macroscopeapp

macroscopeappBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This bug fix touches native iOS C++ layout code and includes substantial patches to the @legendapp/list third-party library affecting scroll behavior calculations. While the author is the primary maintainer of these files, native code changes and third-party patches to rendering infrastructure warrant human review.

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

juliusmarmingeand others added 2 commits July 16, 2026 11:36
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx Outdated
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
@juliusmarmingejuliusmarminge added the 🚀 Mobile Continuous Deployment Trigger Expo preview build label Jul 20, 2026

@cursorcursorBot 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 ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

// Reset their larger measurement to this thread's known baseline; its own
// overlay layout will immediately replace it if it contains extra cards.
setComposerOverlayHeight(estimatedOverlayHeightRef.current);
}, [selectedThreadKey]);

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.

Thread switch inset desync

Medium Severity

On selectedThreadKey change, composerOverlayHeight resets to the new thread’s baseline, but contentInsetEndAdjustment (the keyboard hook SharedValue) is not updated. Until the new composer lays out, contentInsetEndEstimate reflects the new thread while list end-inset math and the remount reportContentInset call still use the previous thread’s larger value.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 20, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Refresh against the approved codex-turn-mapping tip. CTM's LegendList 3.3.3
patch threads contentInsetStartAdjustment through the list's own scroll math,
and pingdotgg#4867/pingdotgg#4874 carry the gated feed-item layout transition, so this merge
keeps CTM's versions of both. The keyboard-side wiring is not upstream: the
3.3.3 patch never passes contentInsetStartCompensation into
KeyboardChatScrollView, whose clamp floor defaults to 0, so the pingdotgg#4036 wiring
is re-ported onto the 3.3.3 patch. The pingdotgg#4036-unique short-content underflow
correction, Markdown rendering, and thread-detail changes are retained.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 5, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Mobile Continuous DeploymentTrigger Expo preview buildsize:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mobile): Stabilize thread feed layout and Markdown rendering - #4036

Open
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout
Open

fix(mobile): Stabilize thread feed layout and Markdown rendering#4036
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Jul 16, 2026

Copy link
Copy Markdown
Member

Extracted from #3910.

Fixes native selectable Markdown width and stale-content behavior, keeps screenshots interactive, and stabilizes floating-composer spacing, short-content positioning, and hydration layout in the thread feed. Includes the required LegendList patch and regenerated lockfile.

Validation inherited from #3910: vp check and mobile native lint passed before the split. The full typecheck is currently blocked in this workspace by unavailable mobile dependencies (expo-blur, expo-quick-actions, and @tabler/icons-react-native).

Note

Stabilize thread feed scroll layout and fix Markdown rendering in iOS

  • Fixes thread feed scroll position when content is shorter than the viewport by anchoring content under translucent headers and scrolling to end when content grows to fill the viewport.
  • Adds a fillWidth prop to SelectableMarkdownText so assistant markdown messages stretch to fill the full row width.
  • Refactors T3MarkdownTextShadowNode to derive content via buildContent and gate state updates with equality checks, preventing redundant re-renders.
  • Patches @legendapp/list to improve end-scroll maintenance, initial scroll readiness, and keyboard-aware inset handling near translucent headers.
  • Adds measured composer overlay height tracking in ThreadDetailScreen to produce a stable resting inset above the composer.
  • Behavioral Change: maintainScrollAtEnd is disabled while content underflows the viewport; scroll position is instead managed by a requestAnimationFrame loop.

Macroscope summarized 1cd97c7.


Note

Medium Risk
Large changes to vendored list scroll math and native Fabric text state on a core chat screen; regressions would show as wrong scroll position, jank, or missing markdown rather than data/security issues.

Overview
Thread feed and composer insets — The detail screen now tracks measured floating-composer overlay height, adds a fixed gap above the chrome, and passes that through keyboard/LegendList inset hooks (heightAdjustment, contentInsetEndEstimate). ThreadFeed detects when content is shorter than the viewport, pins scroll under the translucent header, turns off maintainScrollAtEnd in that case, and shows a loading overlay while hydrating without remounting the list on draft→thread transitions.

Native selectable markdown — iOS T3MarkdownTextShadowNode stops caching measure-time text in mutable fields; it rebuilds content from children in layout() and only updates native state when attributed text/ranges actually change, fixing dropped/stale text when Yoga layouts without remeasuring. A new optional fillWidth prop stretches assistant markdown rows so they reflow correctly in shrink-to-fit layouts.

LegendList patch — Extends the vendored @legendapp/list keyboard integration for leading inset (transparent header), safe-area end-inset compensation, short-content scroll clamping, and more reliable initial/end scroll behavior; lockfile patch hash updated.

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

juliusmarmingeand others added 2 commits July 16, 2026 10:54
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitaiBot commented Jul 16, 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

Run ID: 7bb6af15-1007-4f8d-9a16-772c36b7da18

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jul 16, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review July 16, 2026 09:25
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@macroscopeapp

macroscopeappBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This bug fix touches native iOS C++ layout code and includes substantial patches to the @legendapp/list third-party library affecting scroll behavior calculations. While the author is the primary maintainer of these files, native code changes and third-party patches to rendering infrastructure warrant human review.

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

juliusmarmingeand others added 2 commits July 16, 2026 11:36
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx Outdated
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
@juliusmarmingejuliusmarminge added the 🚀 Mobile Continuous Deployment Trigger Expo preview build label Jul 20, 2026

@cursorcursorBot 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 ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

// Reset their larger measurement to this thread's known baseline; its own
// overlay layout will immediately replace it if it contains extra cards.
setComposerOverlayHeight(estimatedOverlayHeightRef.current);
}, [selectedThreadKey]);

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.

Thread switch inset desync

Medium Severity

On selectedThreadKey change, composerOverlayHeight resets to the new thread’s baseline, but contentInsetEndAdjustment (the keyboard hook SharedValue) is not updated. Until the new composer lays out, contentInsetEndEstimate reflects the new thread while list end-inset math and the remount reportContentInset call still use the previous thread’s larger value.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 20, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Refresh against the approved codex-turn-mapping tip. CTM's LegendList 3.3.3
patch threads contentInsetStartAdjustment through the list's own scroll math,
and pingdotgg#4867/pingdotgg#4874 carry the gated feed-item layout transition, so this merge
keeps CTM's versions of both. The keyboard-side wiring is not upstream: the
3.3.3 patch never passes contentInsetStartCompensation into
KeyboardChatScrollView, whose clamp floor defaults to 0, so the pingdotgg#4036 wiring
is re-ported onto the 3.3.3 patch. The pingdotgg#4036-unique short-content underflow
correction, Markdown rendering, and thread-detail changes are retained.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 5, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Mobile Continuous DeploymentTrigger Expo preview buildsize:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(mobile): Stabilize thread feed layout and Markdown rendering - #4036

Open
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout
Open

fix(mobile): Stabilize thread feed layout and Markdown rendering#4036
juliusmarminge wants to merge 9 commits into
pingdotgg:mainfrom
juliusmarminge:codex/pr3910-thread-feed-layout

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Jul 16, 2026

Copy link
Copy Markdown
Member

Extracted from #3910.

Fixes native selectable Markdown width and stale-content behavior, keeps screenshots interactive, and stabilizes floating-composer spacing, short-content positioning, and hydration layout in the thread feed. Includes the required LegendList patch and regenerated lockfile.

Validation inherited from #3910: vp check and mobile native lint passed before the split. The full typecheck is currently blocked in this workspace by unavailable mobile dependencies (expo-blur, expo-quick-actions, and @tabler/icons-react-native).

Note

Stabilize thread feed scroll layout and fix Markdown rendering in iOS

  • Fixes thread feed scroll position when content is shorter than the viewport by anchoring content under translucent headers and scrolling to end when content grows to fill the viewport.
  • Adds a fillWidth prop to SelectableMarkdownText so assistant markdown messages stretch to fill the full row width.
  • Refactors T3MarkdownTextShadowNode to derive content via buildContent and gate state updates with equality checks, preventing redundant re-renders.
  • Patches @legendapp/list to improve end-scroll maintenance, initial scroll readiness, and keyboard-aware inset handling near translucent headers.
  • Adds measured composer overlay height tracking in ThreadDetailScreen to produce a stable resting inset above the composer.
  • Behavioral Change: maintainScrollAtEnd is disabled while content underflows the viewport; scroll position is instead managed by a requestAnimationFrame loop.

Macroscope summarized 1cd97c7.


Note

Medium Risk
Large changes to vendored list scroll math and native Fabric text state on a core chat screen; regressions would show as wrong scroll position, jank, or missing markdown rather than data/security issues.

Overview
Thread feed and composer insets — The detail screen now tracks measured floating-composer overlay height, adds a fixed gap above the chrome, and passes that through keyboard/LegendList inset hooks (heightAdjustment, contentInsetEndEstimate). ThreadFeed detects when content is shorter than the viewport, pins scroll under the translucent header, turns off maintainScrollAtEnd in that case, and shows a loading overlay while hydrating without remounting the list on draft→thread transitions.

Native selectable markdown — iOS T3MarkdownTextShadowNode stops caching measure-time text in mutable fields; it rebuilds content from children in layout() and only updates native state when attributed text/ranges actually change, fixing dropped/stale text when Yoga layouts without remeasuring. A new optional fillWidth prop stretches assistant markdown rows so they reflow correctly in shrink-to-fit layouts.

LegendList patch — Extends the vendored @legendapp/list keyboard integration for leading inset (transparent header), safe-area end-inset compensation, short-content scroll clamping, and more reliable initial/end scroll behavior; lockfile patch hash updated.

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

juliusmarmingeand others added 2 commits July 16, 2026 10:54
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@coderabbitai

coderabbitaiBot commented Jul 16, 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

Run ID: 7bb6af15-1007-4f8d-9a16-772c36b7da18

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jul 16, 2026
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@juliusmarminge
juliusmarminge marked this pull request as ready for review July 16, 2026 09:25
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx Outdated
@macroscopeapp

macroscopeappBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This bug fix touches native iOS C++ layout code and includes substantial patches to the @legendapp/list third-party library affecting scroll behavior calculations. While the author is the primary maintainer of these files, native code changes and third-party patches to rendering infrastructure warrant human review.

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

juliusmarmingeand others added 2 commits July 16, 2026 11:36
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Co-authored-by: codex <codex@users.noreply.github.com>
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx Outdated
Comment threadapps/mobile/src/features/threads/ThreadDetailScreen.tsx
Comment threadapps/mobile/src/features/threads/ThreadFeed.tsx
@juliusmarmingejuliusmarminge added the 🚀 Mobile Continuous Deployment Trigger Expo preview build label Jul 20, 2026

@cursorcursorBot 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 ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

// Reset their larger measurement to this thread's known baseline; its own
// overlay layout will immediately replace it if it contains extra cards.
setComposerOverlayHeight(estimatedOverlayHeightRef.current);
}, [selectedThreadKey]);

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.

Thread switch inset desync

Medium Severity

On selectedThreadKey change, composerOverlayHeight resets to the new thread’s baseline, but contentInsetEndAdjustment (the keyboard hook SharedValue) is not updated. Until the new composer lays out, contentInsetEndEstimate reflects the new thread while list end-inset math and the remount reportContentInset call still use the previous thread’s larger value.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 1cd97c7. Configure here.

mwolson added a commit to mwolson/t3code that referenced this pull request Jul 20, 2026
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 25, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 28, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 30, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Jul 31, 2026
Refresh against the approved codex-turn-mapping tip. CTM's LegendList 3.3.3
patch threads contentInsetStartAdjustment through the list's own scroll math,
and pingdotgg#4867/pingdotgg#4874 carry the gated feed-item layout transition, so this merge
keeps CTM's versions of both. The keyboard-side wiring is not upstream: the
3.3.3 patch never passes contentInsetStartCompensation into
KeyboardChatScrollView, whose clamp floor defaults to 0, so the pingdotgg#4036 wiring
is re-ported onto the 3.3.3 patch. The pingdotgg#4036-unique short-content underflow
correction, Markdown rendering, and thread-detail changes are retained.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 1, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 2, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 3, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
Squashed replay of our CTM adaptation of pingdotgg#4036 pr3910-thread-feed-layout,
one of Julius's open pingdotgg#3910 ios-fixes-main split PRs, as a post-CTM layer.
Replaces the retired ios-integration merge, so this line keeps main pinned
to exactly the main CTM contains.
Unlike the other two iOS layers this PR does not apply to CTM as-is: CTM
moved the working indicator out of the composer overlay, which the PR's
layout work was built around. The adaptation and its per-hunk reasoning
live on origin/ios/pr3910-thread-feed-layout-ctm, which branches from the
PR head and merges CTM, so Julius's later updates merge in normally. This
commit is that branch's net change over CTM.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 4, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 5, 2026
pingdotgg#4036 pr3910-thread-feed-layout added a full-screen "Loading messages"
placeholder over the feed while a thread hydrates, with a spinner and the
detail line "Catching up this thread before showing the conversation." It
arrived as part of the layout work rather than as a deliberate design change,
and it reads as an awkward interstitial on a transition that is usually
instant.
Removes the overlay, the ThreadFeedPlaceholder `loading` prop it was the only
caller of, and the spinner that prop gated. ActivityIndicator keeps its other
two uses in this file.
Deliberately keeps `listMountState`, which lands in the same PR but is a
distinct hydration fix: it reserves the filled list identity so detail arrival
does not replace the native list mid-transition, and the layout effect below it
re-reports the composer content inset either way. That is correct whether or
not the overlay draws.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 Mobile Continuous DeploymentTrigger Expo preview buildsize:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge