1diff --git a/react-native.js b/react-native.js
2index 4af685dfb961eb0c8a4bf1ae9132c9304cf4fa4d..df4ead7ae4f03cd0fbcd99a3df7d95e15b1852b1 100644
3--- a/react-native.js
4+++ b/react-native.js
5@@ -1795,7 +1795,35 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
6 }
7 return false;
8 }
9+// T3 patch: while following the end, a size-only change keeps the end in the same pass that
10+// repositions the items. Native end following otherwise runs a frame later, after MVCP has
11+// anchored the top row, so the feed visibly jumps twice.
12+function shouldHoldEndOnSizeChange(ctx, dataChanged) {
13+ const state = ctx.state;
14+ return !dataChanged && !!state.props.maintainScrollAtEnd && !!state.didFinishInitialScroll && !state.endFollowInterrupted && !state.visibleContentAnchor && state.scrollingTo === void 0 && !state.isDragging && state.userScrollContentSize === void 0 && !!peek$(ctx, "isFollowingEnd");
15+}
16 function prepareMVCP(ctx, dataChanged, adjustmentSource) {
17+ const restore = prepareDefaultMVCP(ctx, dataChanged, adjustmentSource);
18+ if (!shouldHoldEndOnSizeChange(ctx, dataChanged)) {
19+ return restore;
20+ }
21+ return () => {
22+ if (!shouldHoldEndOnSizeChange(ctx, dataChanged)) {
23+ restore == null ? void 0 : restore();
24+ return;
25+ }
26+ // Content that shrinks under an offset past its new end pulls the native offset back by itself.
27+ const endOffset = getScrollRangeEnd(ctx);
28+ if (ctx.state.scroll > endOffset) {
29+ ctx.state.scroll = endOffset;
30+ }
31+ const positionDiff = endOffset - ctx.state.scroll;
32+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
33+ requestAdjust(ctx, positionDiff, adjustmentSource);
34+ }
35+ };
36+}
37+function prepareDefaultMVCP(ctx, dataChanged, adjustmentSource) {
38 var _a3, _b, _c;
39 const state = ctx.state;
40 const { idsInView, positions, props } = state;
41diff --git a/react-native.mjs b/react-native.mjs
42index 9ebfddc183a9b202d35c990a387c9b628feeb185..c414a49e866ecab1393645101fb67d55530a7d00 100644
43--- a/react-native.mjs
44+++ b/react-native.mjs
45@@ -1774,7 +1774,35 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
46 }
47 return false;
48 }
49+// T3 patch: while following the end, a size-only change keeps the end in the same pass that
50+// repositions the items. Native end following otherwise runs a frame later, after MVCP has
51+// anchored the top row, so the feed visibly jumps twice.
52+function shouldHoldEndOnSizeChange(ctx, dataChanged) {
53+ const state = ctx.state;
54+ return !dataChanged && !!state.props.maintainScrollAtEnd && !!state.didFinishInitialScroll && !state.endFollowInterrupted && !state.visibleContentAnchor && state.scrollingTo === void 0 && !state.isDragging && state.userScrollContentSize === void 0 && !!peek$(ctx, "isFollowingEnd");
55+}
56 function prepareMVCP(ctx, dataChanged, adjustmentSource) {
57+ const restore = prepareDefaultMVCP(ctx, dataChanged, adjustmentSource);
58+ if (!shouldHoldEndOnSizeChange(ctx, dataChanged)) {
59+ return restore;
60+ }
61+ return () => {
62+ if (!shouldHoldEndOnSizeChange(ctx, dataChanged)) {
63+ restore == null ? void 0 : restore();
64+ return;
65+ }
66+ // Content that shrinks under an offset past its new end pulls the native offset back by itself.
67+ const endOffset = getScrollRangeEnd(ctx);
68+ if (ctx.state.scroll > endOffset) {
69+ ctx.state.scroll = endOffset;
70+ }
71+ const positionDiff = endOffset - ctx.state.scroll;
72+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
73+ requestAdjust(ctx, positionDiff, adjustmentSource);
74+ }
75+ };
76+}
77+function prepareDefaultMVCP(ctx, dataChanged, adjustmentSource) {
78 var _a3, _b, _c;
79 const state = ctx.state;
80 const { idsInView, positions, props } = state;