authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-01 12:18:21-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-01 12:18:21-07:00
log261380f91f764673c7fcefcb6a3f775526dbb776
tree34ffd932a4b0891ef284c6048877ce2b5326b145
parent2d156a83b96ebf2e4a9c6017251baad357ae6ab1
signature Signed by PGP key B5690EEEBB952194

fix(mobile): keep thread scroll bounds current after animations (#9013)

Co-authored-by: Julius Marminge <julius@mac.lan>

7 files changed, 253 insertions(+), 164 deletions(-)

apps/mobile/src/features/threads/ThreadDetailScreen.tsx+1-1
...@@ -688,7 +688,7 @@ export const ThreadDetailScreen = memo(function ThreadDetailScreen(props: Thread...@@ -688,7 +688,7 @@ export const ThreadDetailScreen = memo(function ThreadDetailScreen(props: Thread
688 onTouchCancel={handleFeedTouchCancel}688 onTouchCancel={handleFeedTouchCancel}
689 >689 >
690 <ThreadFeed690 <ThreadFeed
691 key={props.selectedThread.id}691 key={selectedThreadKey}
692 environmentId={props.environmentId}692 environmentId={props.environmentId}
693 threadId={props.selectedThread.id}693 threadId={props.selectedThread.id}
694 workspaceRoot={props.threadCwd}694 workspaceRoot={props.threadCwd}
apps/mobile/src/features/threads/ThreadFeed.tsx+30-50
...@@ -75,7 +75,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";...@@ -75,7 +75,6 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
75import Animated, {75import Animated, {
76 FadeIn,76 FadeIn,
77 FadeInUp,77 FadeInUp,
78 FadeOut,
79 LinearTransition,78 LinearTransition,
80 type SharedValue,79 type SharedValue,
81} from "react-native-reanimated";80} from "react-native-reanimated";
...@@ -137,6 +136,7 @@ import {...@@ -137,6 +136,7 @@ import {
137} from "./thread-feed-live-follow";136} from "./thread-feed-live-follow";
138import {137import {
139 collapsedWorkLogHeight,138 collapsedWorkLogHeight,
139 ThreadDisclosureChevron,
140 ThreadWorkGroupToggle,140 ThreadWorkGroupToggle,
141 ThreadWorkLog,141 ThreadWorkLog,
142 THREAD_DISCLOSURE_TRANSITION_MS,142 THREAD_DISCLOSURE_TRANSITION_MS,
...@@ -171,11 +171,12 @@ function formatMessageTime(input: string): string {...@@ -171,11 +171,12 @@ function formatMessageTime(input: string): string {
171// text-sm line at every supported base font size (26px at the 22pt maximum),171// text-sm line at every supported base font size (26px at the 22pt maximum),
172// so its height is a constant; a drifted value costs one correction on172// so its height is a constant; a drifted value costs one correction on
173// measure, not a persistent offset.173// measure, not a persistent offset.
174const TURN_FOLD_HEIGHT = 56; // min-h-11 (44) + mb-3 (12)174const TURN_FOLD_HEIGHT = 48; // min-h-11 (44) + mb-1 (4)
175const THREAD_FEED_LAYOUT_TRANSITION = LinearTransition.duration(THREAD_DISCLOSURE_TRANSITION_MS);175const THREAD_FEED_LAYOUT_TRANSITION = LinearTransition.duration(THREAD_DISCLOSURE_TRANSITION_MS);
176const THREAD_FEED_DISCLOSURE_ENTER_TRANSITION = FadeIn.duration(140);176// Let neighboring rows move out of the new rows' space before showing their text.
177const THREAD_FEED_DISCLOSURE_EXIT_TRANSITION = FadeOut.duration(120);177const THREAD_FEED_DISCLOSURE_ENTER_TRANSITION = FadeIn.delay(
178const EMPTY_DISCLOSURE_ENTRY_IDS: ReadonlySet<string> = new Set();178 THREAD_DISCLOSURE_TRANSITION_MS,
179).duration(140);
179180
180// Entering animations must only play for rows born just now — LegendList181// Entering animations must only play for rows born just now — LegendList
181// remounts rows when they scroll back into view, and replaying an entrance for182// remounts rows when they scroll back into view, and replaying an entrance for
...@@ -1357,16 +1358,16 @@ function renderFeedEntry(...@@ -1357,16 +1358,16 @@ function renderFeedEntry(
1357 accessibilityState={{ expanded: entry.expanded }}1358 accessibilityState={{ expanded: entry.expanded }}
1358 onPress={() => props.onToggleTurnFold(entry.turnId)}1359 onPress={() => props.onToggleTurnFold(entry.turnId)}
1359 hitSlop={4}1360 hitSlop={4}
1360 className="mb-3 min-h-11 flex-row items-center gap-2 border-b border-adaptive-neutral-200-a80-white-a8 px-2"1361 className="mb-1 min-h-11 flex-row items-center gap-2 border-b border-adaptive-neutral-200-a80-white-a8 px-2"
1361 >1362 >
1362 <Text className="font-t3-medium text-sm tabular-nums text-foreground-muted">1363 <Text className="font-t3-medium text-sm tabular-nums text-foreground-muted">
1363 {entry.label}1364 {entry.label}
1364 </Text>1365 </Text>
1365 <SymbolView1366 <ThreadDisclosureChevron
1366 name={entry.expanded ? "chevron.down" : "chevron.right"}1367 expanded={entry.expanded}
1368 collapsedDirection="right"
1367 size={15}1369 size={15}
1368 tintColorClassName={"accent-icon-subtle"}1370 tintColor={iconSubtleColor}
1369 type="monochrome"
1370 />1371 />
1371 </Pressable>1372 </Pressable>
1372 );1373 );
...@@ -1490,7 +1491,7 @@ function renderFeedEntry(...@@ -1490,7 +1491,7 @@ function renderFeedEntry(
1490 const enterAnimated = isFreshTimestamp(message.createdAt);1491 const enterAnimated = isFreshTimestamp(message.createdAt);
1491 return (1492 return (
1492 <Animated.View1493 <Animated.View
1493 className={cn(showAssistantMeta ? "mb-5 px-1" : "mb-2 px-1")}1494 className={cn(showAssistantMeta ? "mb-5 px-1" : "mb-1 px-1")}
1494 {...(enterAnimated ? { entering: FadeIn.duration(220) } : {})}1495 {...(enterAnimated ? { entering: FadeIn.duration(220) } : {})}
1495 >1496 >
1496 {renderedText.trim().length > 0 ? (1497 {renderedText.trim().length > 0 ? (
...@@ -1832,7 +1833,6 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {...@@ -1832,7 +1833,6 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
1832 const disclosureSettleFrameRef = useRef<number | null>(null);1833 const disclosureSettleFrameRef = useRef<number | null>(null);
1833 const disclosureSettleSecondFrameRef = useRef<number | null>(null);1834 const disclosureSettleSecondFrameRef = useRef<number | null>(null);
1834 const disclosureAnchorKeyRef = useRef<string | null>(null);1835 const disclosureAnchorKeyRef = useRef<string | null>(null);
1835 const previousPresentedFeedRef = useRef<ReadonlyArray<ThreadFeedEntry> | null>(null);
1836 const headerMaterialVisibleRef = useRef(false);1836 const headerMaterialVisibleRef = useRef(false);
1837 const previousLatestTurnRef = useRef(props.latestTurn);1837 const previousLatestTurnRef = useRef(props.latestTurn);
1838 const userScrollSettleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);1838 const userScrollSettleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
...@@ -1845,12 +1845,12 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {...@@ -1845,12 +1845,12 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
1845 // Live-follow latch. LegendList's maintainScrollAtEnd alone re-pins the feed1845 // Live-follow latch. LegendList's maintainScrollAtEnd alone re-pins the feed
1846 // whenever the viewport drifts back inside its geometric threshold, which1846 // whenever the viewport drifts back inside its geometric threshold, which
1847 // yanked users off history they were reading every time a stream chunk grew1847 // yanked users off history they were reading every time a stream chunk grew
1848 // a row. Follow breaks when the user scrolls up and away, and re-arms only1848 // a row. Scrolling away or expanding a disclosure above the end breaks
1849 // when the list actually returns to the end (or on send / thread switch).1849 // follow; reaching the end (or sending / switching threads) re-arms it.
1850 const [endFollowEnabled, setEndFollowEnabled] = useState(true);1850 const [endFollowEnabled, setEndFollowEnabled] = useState(true);
1851 const endFollowEnabledRef = useRef(true);1851 const endFollowEnabledRef = useRef(true);
1852 // A "user scroll session" spans from drag start through the end of its1852 // A "user scroll session" spans from drag start through the end of its
1853 // momentum; only motion inside a session can break follow, so MVCP1853 // momentum; scroll events only break follow inside that session, so MVCP
1854 // compensations and programmatic scrolls never strand a follower.1854 // compensations and programmatic scrolls never strand a follower.
1855 const userScrollSessionRef = useRef(false);1855 const userScrollSessionRef = useRef(false);
1856 const setEndFollow = useCallback(1856 const setEndFollow = useCallback(
...@@ -2187,33 +2187,6 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {...@@ -2187,33 +2187,6 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
2187 props.latestTurn,2187 props.latestTurn,
2188 ],2188 ],
2189 );2189 );
2190 const disclosureEnteringEntryIds = useMemo(() => {
2191 const anchorKey = disclosureAnchorKeyRef.current;
2192 const previousPresentedFeed = previousPresentedFeedRef.current;
2193 if (!disclosureToggleSettling || anchorKey === null || previousPresentedFeed === null) {
2194 return EMPTY_DISCLOSURE_ENTRY_IDS;
2195 }
2196
2197 const previousIds = new Set(previousPresentedFeed.map((entry) => entry.id));
2198 const anchorIndex = presentedFeed.findIndex((entry) => entry.id === anchorKey);
2199 const enteringIds = new Set<string>();
2200 if (anchorIndex < 0) {
2201 return enteringIds;
2202 }
2203 for (let index = anchorIndex + 1; index < presentedFeed.length; index += 1) {
2204 const entryId = presentedFeed[index]!.id;
2205 if (previousIds.has(entryId)) {
2206 break;
2207 }
2208 enteringIds.add(entryId);
2209 }
2210 return enteringIds;
2211 }, [disclosureToggleSettling, presentedFeed]);
2212
2213 useLayoutEffect(() => {
2214 previousPresentedFeedRef.current = presentedFeed;
2215 }, [presentedFeed]);
2216
2217 // The empty↔filled key below remounts the list and resets its imperative2190 // The empty↔filled key below remounts the list and resets its imperative
2218 // content-inset override. Seed the fresh instance synchronously with the2191 // content-inset override. Seed the fresh instance synchronously with the
2219 // current overlay height before the scroll integration's next reaction;2192 // current overlay height before the scroll integration's next reaction;
...@@ -2300,13 +2273,23 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {...@@ -2300,13 +2273,23 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
2300 }2273 }
2301 disclosureSettleFrameRef.current = requestAnimationFrame(() => {2274 disclosureSettleFrameRef.current = requestAnimationFrame(() => {
2302 disclosureSettleSecondFrameRef.current = requestAnimationFrame(() => {2275 disclosureSettleSecondFrameRef.current = requestAnimationFrame(() => {
2276 // A disclosure can leave the reader above the end without a drag.
2277 // Reconcile follow before a later layout or resume can re-pin it.
2278 const listState = props.listRef.current?.getState();
2279 if (listState) {
2280 transitionEndFollow({
2281 type: "disclosure-settled",
2282 isAtEnd: listState.isAtEnd,
2283 userScrollSessionActive: userScrollSessionRef.current,
2284 });
2285 }
2303 disclosureAnchorKeyRef.current = null;2286 disclosureAnchorKeyRef.current = null;
2304 setDisclosureToggleSettling(false);2287 setDisclosureToggleSettling(false);
2305 disclosureSettleFrameRef.current = null;2288 disclosureSettleFrameRef.current = null;
2306 disclosureSettleSecondFrameRef.current = null;2289 disclosureSettleSecondFrameRef.current = null;
2307 });2290 });
2308 });2291 });
2309 }, []);2292 }, [props.listRef, transitionEndFollow]);
23102293
2311 const suspendEndScrollMaintenanceForDisclosure = useCallback((anchorKey: string | null) => {2294 const suspendEndScrollMaintenanceForDisclosure = useCallback((anchorKey: string | null) => {
2312 disclosureAnchorKeyRef.current = anchorKey;2295 disclosureAnchorKeyRef.current = anchorKey;
...@@ -2447,16 +2430,13 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {...@@ -2447,16 +2430,13 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
2447 [expandedWorkRows],2430 [expandedWorkRows],
2448 );2431 );
24492432
2433 // Disclosures can mount existing offscreen rows as well as new work rows.
2434 // Fade those in after movement; never retain removed rows over replacements.
2450 const renderItem = useCallback(2435 const renderItem = useCallback(
2451 (info: { item: ThreadFeedEntry; index: number }) => (2436 (info: { item: ThreadFeedEntry; index: number }) => (
2452 <Animated.View2437 <Animated.View
2453 key={info.item.id}2438 key={info.item.id}
2454 entering={2439 entering={disclosureToggleSettling ? THREAD_FEED_DISCLOSURE_ENTER_TRANSITION : undefined}
2455 disclosureEnteringEntryIds.has(info.item.id)
2456 ? THREAD_FEED_DISCLOSURE_ENTER_TRANSITION
2457 : undefined
2458 }
2459 exiting={THREAD_FEED_DISCLOSURE_EXIT_TRANSITION}
2460 >2440 >
2461 {renderFeedEntry(info, {2441 {renderFeedEntry(info, {
2462 environmentId: props.environmentId,2442 environmentId: props.environmentId,
...@@ -2486,7 +2466,7 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {...@@ -2486,7 +2466,7 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) {
2486 ),2466 ),
2487 [2467 [
2488 copiedRowId,2468 copiedRowId,
2489 disclosureEnteringEntryIds,2469 disclosureToggleSettling,
2490 expandedWorkRows,2470 expandedWorkRows,
2491 terminalAssistantMessageIds,2471 terminalAssistantMessageIds,
2492 unsettledTurnId,2472 unsettledTurnId,
apps/mobile/src/features/threads/thread-feed-live-follow.test.ts+11
...@@ -102,6 +102,17 @@ describe("resolveThreadFeedLiveFollow", () => {...@@ -102,6 +102,17 @@ describe("resolveThreadFeedLiveFollow", () => {
102 ).toBe(false);102 ).toBe(false);
103 });103 });
104104
105 it.each([
106 { isAtEnd: false, userScrollSessionActive: false, expected: false },
107 { isAtEnd: true, userScrollSessionActive: false, expected: true },
108 { isAtEnd: false, userScrollSessionActive: true, expected: false },
109 { isAtEnd: true, userScrollSessionActive: true, expected: false },
110 ])("reconciles follow after a disclosure settles: %j", ({ expected, ...state }) => {
111 expect(resolveThreadFeedLiveFollow(!expected, { type: "disclosure-settled", ...state })).toBe(
112 expected,
113 );
114 });
115
105 it("re-arms at the actual end only after the user scroll session ends", () => {116 it("re-arms at the actual end only after the user scroll session ends", () => {
106 expect(117 expect(
107 resolveThreadFeedLiveFollow(false, {118 resolveThreadFeedLiveFollow(false, {
apps/mobile/src/features/threads/thread-feed-live-follow.ts+3-1
...@@ -7,7 +7,7 @@ export type ThreadFeedLiveFollowEvent =...@@ -7,7 +7,7 @@ export type ThreadFeedLiveFollowEvent =
7 readonly userScrollSessionActive: boolean;7 readonly userScrollSessionActive: boolean;
8 }8 }
9 | {9 | {
10 readonly type: "scroll";10 readonly type: "scroll" | "disclosure-settled";
11 readonly isAtEnd: boolean;11 readonly isAtEnd: boolean;
12 readonly userScrollSessionActive: boolean;12 readonly userScrollSessionActive: boolean;
13 };13 };
...@@ -41,6 +41,8 @@ export function resolveThreadFeedLiveFollow(...@@ -41,6 +41,8 @@ export function resolveThreadFeedLiveFollow(
41 return false;41 return false;
42 case "user-scroll-end":42 case "user-scroll-end":
43 return event.userScrollSessionActive ? event.isAtEnd : current;43 return event.userScrollSessionActive ? event.isAtEnd : current;
44 case "disclosure-settled":
45 return !event.userScrollSessionActive && event.isAtEnd;
44 case "scroll":46 case "scroll":
45 if (event.userScrollSessionActive) {47 if (event.userScrollSessionActive) {
46 return false;48 return false;
apps/mobile/src/features/threads/thread-work-log.tsx+47-17
...@@ -2,7 +2,7 @@ import * as Haptics from "expo-haptics";...@@ -2,7 +2,7 @@ import * as Haptics from "expo-haptics";
2import { type AppSymbolName, SymbolView } from "../../components/AppSymbol";2import { type AppSymbolName, SymbolView } from "../../components/AppSymbol";
3import { MaskedView } from "@expo/ui/community/masked-view";3import { MaskedView } from "@expo/ui/community/masked-view";
4import { useIsFocused } from "@react-navigation/native";4import { useIsFocused } from "@react-navigation/native";
5import { useEffect, useId, useState, type ComponentProps } from "react";5import { useEffect, useId, useLayoutEffect, useState, type ComponentProps } from "react";
6import {6import {
7 AccessibilityInfo,7 AccessibilityInfo,
8 AppState,8 AppState,
...@@ -46,6 +46,44 @@ const WORK_LOG_LAYOUT_TRANSITION = LinearTransition.duration(THREAD_DISCLOSURE_T...@@ -46,6 +46,44 @@ const WORK_LOG_LAYOUT_TRANSITION = LinearTransition.duration(THREAD_DISCLOSURE_T
46const WORK_LOG_DETAIL_ENTER_TRANSITION = FadeIn.duration(140);46const WORK_LOG_DETAIL_ENTER_TRANSITION = FadeIn.duration(140);
47const WORK_LOG_DETAIL_EXIT_TRANSITION = FadeOut.duration(120);47const WORK_LOG_DETAIL_EXIT_TRANSITION = FadeOut.duration(120);
4848
49export function ThreadDisclosureChevron(props: {
50 readonly expanded: boolean;
51 readonly collapsedDirection: "right" | "down";
52 readonly size: number;
53 readonly tintColor: ColorValue;
54}) {
55 const expandedAngle = props.collapsedDirection === "right" ? 90 : 180;
56 const rotation = useSharedValue(props.expanded ? expandedAngle : 0);
57
58 useLayoutEffect(() => {
59 rotation.value = withTiming(props.expanded ? expandedAngle : 0, {
60 duration: THREAD_DISCLOSURE_TRANSITION_MS,
61 reduceMotion: ReduceMotion.System,
62 });
63 }, [expandedAngle, props.expanded, rotation]);
64
65 const rotationStyle = useAnimatedStyle(() => ({
66 transform: [{ rotate: `${rotation.value}deg` }],
67 }));
68
69 return (
70 <Animated.View
71 accessible={false}
72 accessibilityElementsHidden
73 importantForAccessibility="no-hide-descendants"
74 pointerEvents="none"
75 style={[{ width: props.size, height: props.size }, rotationStyle]}
76 >
77 <SymbolView
78 name={props.collapsedDirection === "right" ? "chevron.right" : "chevron.down"}
79 size={props.size}
80 tintColor={props.tintColor}
81 type="monochrome"
82 />
83 </Animated.View>
84 );
85}
86
49function ShimmerWorkContent(props: {87function ShimmerWorkContent(props: {
50 readonly highlighted: boolean;88 readonly highlighted: boolean;
51 readonly icon: AppSymbolName;89 readonly icon: AppSymbolName;
...@@ -264,7 +302,7 @@ const WORK_ROW_HEIGHT = 32; // min-h-8...@@ -264,7 +302,7 @@ const WORK_ROW_HEIGHT = 32; // min-h-8
264const WORK_ROW_GAP = 1; // gap-px302const WORK_ROW_GAP = 1; // gap-px
265const WORK_LOG_BOTTOM_MARGIN = 4; // mb-1303const WORK_LOG_BOTTOM_MARGIN = 4; // mb-1
266304
267export const WORK_GROUP_TOGGLE_HEIGHT = 36; // min-h-8 (32) + mb-1 (4)305export const WORK_GROUP_TOGGLE_HEIGHT = 32; // min-h-8
268306
269export function collapsedWorkLogHeight(activities: ReadonlyArray<ThreadFeedActivity>): number {307export function collapsedWorkLogHeight(activities: ReadonlyArray<ThreadFeedActivity>): number {
270 const rows = activities;308 const rows = activities;
...@@ -376,15 +414,11 @@ export function ThreadWorkLog(props: {...@@ -376,15 +414,11 @@ export function ThreadWorkLog(props: {
376 ) : null}414 ) : null}
377 <View className="h-4 w-4 items-center justify-center">415 <View className="h-4 w-4 items-center justify-center">
378 {canExpand ? (416 {canExpand ? (
379 <SymbolView417 <ThreadDisclosureChevron
380 name={418 expanded={expanded}
381 expanded419 collapsedDirection="down"
382 ? { ios: "chevron.up", android: "keyboard_arrow_up" }
383 : { ios: "chevron.down", android: "keyboard_arrow_down" }
384 }
385 size={11}420 size={11}
386 tintColor={props.iconSubtleColor}421 tintColor={props.iconSubtleColor}
387 type="monochrome"
388 />422 />
389 ) : null}423 ) : null}
390 </View>424 </View>
...@@ -444,7 +478,7 @@ export function ThreadWorkGroupToggle(props: {...@@ -444,7 +478,7 @@ export function ThreadWorkGroupToggle(props: {
444 const icon = toolGroupSummarySymbolName(props.summaryKind);478 const icon = toolGroupSummarySymbolName(props.summaryKind);
445479
446 return (480 return (
447 <View className="-mx-1 mb-1 px-1 py-0">481 <View className="-mx-1 px-1 py-0">
448 <Pressable482 <Pressable
449 accessibilityRole="button"483 accessibilityRole="button"
450 accessibilityState={{ expanded: props.expanded }}484 accessibilityState={{ expanded: props.expanded }}
...@@ -479,15 +513,11 @@ export function ThreadWorkGroupToggle(props: {...@@ -479,15 +513,11 @@ export function ThreadWorkGroupToggle(props: {
479 </Text>513 </Text>
480 </>514 </>
481 )}515 )}
482 <SymbolView516 <ThreadDisclosureChevron
483 name={517 expanded={props.expanded}
484 props.expanded518 collapsedDirection="down"
485 ? { ios: "chevron.up", android: "keyboard_arrow_up" }
486 : { ios: "chevron.down", android: "keyboard_arrow_down" }
487 }
488 size={11}519 size={11}
489 tintColor={props.iconSubtleColor}520 tintColor={props.iconSubtleColor}
490 type="monochrome"
491 />521 />
492 </Pressable>522 </Pressable>
493 </View>523 </View>
patches/@legendapp__list@3.3.5.patch+156-90
...@@ -241,10 +241,35 @@ index ce1fe00001c9e5aee6c6ea8bb2d4757d4586d002..3ccf6f16067152dfcb0c143371e2ec6a...@@ -241,10 +241,35 @@ index ce1fe00001c9e5aee6c6ea8bb2d4757d4586d002..3ccf6f16067152dfcb0c143371e2ec6a
241 * Number of columns to render items in.241 * Number of columns to render items in.
242 * @default 1242 * @default 1
243diff --git a/react-native.js b/react-native.js243diff --git a/react-native.js b/react-native.js
244index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c3ffe83af 100644244index b3c5a306b293f797a8b338adfca3060c0f6db22b..24d0763aef074411eb7d17f0feb8df752a843de0 100644
245--- a/react-native.js245--- a/react-native.js
246+++ b/react-native.js246+++ b/react-native.js
247@@ -954,7 +954,7 @@ function setInitialRenderState(ctx, {247@@ -717,6 +717,15 @@ function hasActiveInitialScroll(state) {
248 return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
249 }
250
251+// Size-only changes may not emit a scroll event to refresh the edge signal.
252+function getIsAtEnd(ctx, contentSize = getContentSize(ctx)) {
253+ const { queuedInitialLayout, scroll, scrollLength } = ctx.state;
254+ if (!(contentSize > 0 && queuedInitialLayout)) {
255+ return peek$(ctx, "isAtEnd");
256+ }
257+ return contentSize < scrollLength || contentSize - scroll - scrollLength - getContentInsetEnd(ctx) <= EDGE_POSITION_EPSILON;
258+}
259+
260 // src/utils/checkAtBottom.ts
261 function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
262 var _a3;
263@@ -737,7 +746,7 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
264 const insetEnd = getContentInsetEnd(ctx);
265 const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
266 const isContentLess = contentSize < scrollLength;
267- set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
268+ set$(ctx, "isAtEnd", getIsAtEnd(ctx, contentSize));
269 set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
270 set$(
271 ctx,
272@@ -954,7 +963,7 @@ function setInitialRenderState(ctx, {
248 if (didInitialScroll) {273 if (didInitialScroll) {
249 state.didFinishInitialScroll = true;274 state.didFinishInitialScroll = true;
250 }275 }
...@@ -253,7 +278,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -253,7 +278,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
253 if (isReadyToRender && !peek$(ctx, "readyToRender")) {278 if (isReadyToRender && !peek$(ctx, "readyToRender")) {
254 set$(ctx, "readyToRender", true);279 set$(ctx, "readyToRender", true);
255 setAdaptiveRender(ctx, "normal", "ready");280 setAdaptiveRender(ctx, "normal", "ready");
256@@ -1090,7 +1090,7 @@ function getRawContentLength(ctx) {281@@ -1090,7 +1099,7 @@ function getRawContentLength(ctx) {
257 function getAlignItemsAtEndPadding(ctx) {282 function getAlignItemsAtEndPadding(ctx) {
258 const { state } = ctx;283 const { state } = ctx;
259 const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;284 const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
...@@ -262,7 +287,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -262,7 +287,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
262 }287 }
263 function updateContentMetricsState(ctx) {288 function updateContentMetricsState(ctx) {
264 const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;289 const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
265@@ -1115,6 +1115,10 @@ function addTotalSize(ctx, key, add, notifyTotalSize = true) {290@@ -1115,6 +1124,10 @@ function addTotalSize(ctx, key, add, notifyTotalSize = true) {
266 totalSize += add;291 totalSize += add;
267 }292 }
268 if (prevTotalSize !== totalSize) {293 if (prevTotalSize !== totalSize) {
...@@ -273,7 +298,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -273,7 +298,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
273 if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {298 if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
274 state.pendingTotalSize = totalSize;299 state.pendingTotalSize = totalSize;
275 } else {300 } else {
276@@ -1304,18 +1308,23 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {301@@ -1304,18 +1317,23 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
277 }302 }
278 303
279 // src/core/clampScrollOffset.ts304 // src/core/clampScrollOffset.ts
...@@ -299,7 +324,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -299,7 +324,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
299 return clampedOffset;324 return clampedOffset;
300 }325 }
301 326
302@@ -1451,10 +1460,10 @@ function checkFinishedScrollFrame(ctx) {327@@ -1451,10 +1469,10 @@ function checkFinishedScrollFrame(ctx) {
303 finishScrollTo(ctx);328 finishScrollTo(ctx);
304 }329 }
305 }330 }
...@@ -312,7 +337,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -312,7 +337,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
312 x: ctx.state.props.horizontal ? offset : 0,337 x: ctx.state.props.horizontal ? offset : 0,
313 y: ctx.state.props.horizontal ? 0 : offset338 y: ctx.state.props.horizontal ? 0 : offset
314 });339 });
315@@ -1503,7 +1512,10 @@ function checkFinishedScrollFallback(ctx) {340@@ -1503,7 +1521,10 @@ function checkFinishedScrollFallback(ctx) {
316 );341 );
317 scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);342 scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
318 } else if (shouldRetryUnalignedEndScroll) {343 } else if (shouldRetryUnalignedEndScroll) {
...@@ -324,7 +349,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -324,7 +349,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
324 scheduleFallbackCheck(100);349 scheduleFallbackCheck(100);
325 } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {350 } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
326 finishScrollTo(ctx);351 finishScrollTo(ctx);
327@@ -1560,15 +1572,28 @@ function doMaintainScrollAtEnd(ctx) {352@@ -1560,15 +1581,28 @@ function doMaintainScrollAtEnd(ctx) {
328 } = state;353 } = state;
329 const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");354 const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
330 const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);355 const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
...@@ -354,7 +379,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -354,7 +379,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
354 }379 }
355 if (!state.maintainingScrollAtEnd) {380 if (!state.maintainingScrollAtEnd) {
356 const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";381 const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
357@@ -1591,9 +1616,18 @@ function doMaintainScrollAtEnd(ctx) {382@@ -1591,9 +1625,18 @@ function doMaintainScrollAtEnd(ctx) {
358 y: 0383 y: 0
359 });384 });
360 } else {385 } else {
...@@ -376,7 +401,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -376,7 +401,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
376 }401 }
377 setTimeout(402 setTimeout(
378 () => {403 () => {
379@@ -1624,6 +1658,10 @@ function doMaintainScrollAtEnd(ctx) {404@@ -1624,6 +1667,10 @@ function doMaintainScrollAtEnd(ctx) {
380 function requestAdjust(ctx, positionDiff, dataChanged) {405 function requestAdjust(ctx, positionDiff, dataChanged) {
381 const state = ctx.state;406 const state = ctx.state;
382 if (Math.abs(positionDiff) > 0.1) {407 if (Math.abs(positionDiff) > 0.1) {
...@@ -387,7 +412,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -387,7 +412,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
387 const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;412 const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
388 const doit = () => {413 const doit = () => {
389 if (needsScrollWorkaround) {414 if (needsScrollWorkaround) {
390@@ -1728,7 +1766,9 @@ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {415@@ -1728,7 +1775,9 @@ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
391 if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {416 if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
392 return 0;417 return 0;
393 }418 }
...@@ -398,7 +423,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -398,7 +423,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
398 const clampDelta = maxScroll - state.scroll;423 const clampDelta = maxScroll - state.scroll;
399 if (unresolvedAmount < 0) {424 if (unresolvedAmount < 0) {
400 return Math.max(unresolvedAmount, Math.min(0, clampDelta));425 return Math.max(unresolvedAmount, Math.min(0, clampDelta));
401@@ -1790,7 +1830,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {426@@ -1790,7 +1839,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
402 settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);427 settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
403 return true;428 return true;
404 }429 }
...@@ -407,7 +432,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -407,7 +432,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
407 const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);432 const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
408 const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;433 const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
409 if (isAtExpectedNativeClamp) {434 if (isAtExpectedNativeClamp) {
410@@ -1923,7 +1963,7 @@ function prepareMVCP(ctx, dataChanged) {435@@ -1923,7 +1972,7 @@ function prepareMVCP(ctx, dataChanged) {
411 if (diff > 0) {436 if (diff > 0) {
412 diff = Math.max(0, totalSize - state.scroll - state.scrollLength);437 diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
413 } else {438 } else {
...@@ -416,7 +441,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -416,7 +441,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
416 state.scroll = maxScroll;441 state.scroll = maxScroll;
417 state.scrollPending = maxScroll;442 state.scrollPending = maxScroll;
418 diff = 0;443 diff = 0;
419@@ -2320,8 +2360,121 @@ function scrollToIndex(ctx, {444@@ -2320,8 +2369,121 @@ function scrollToIndex(ctx, {
420 }445 }
421 446
422 // src/core/initialScroll.ts447 // src/core/initialScroll.ts
...@@ -538,7 +563,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -538,7 +563,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
538 const requestedIndex = target.index;563 const requestedIndex = target.index;
539 const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;564 const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
540 const itemSize = getItemSizeAtIndex(ctx, index);565 const itemSize = getItemSizeAtIndex(ctx, index);
541@@ -2747,7 +2900,9 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {566@@ -2747,7 +2909,9 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
542 return;567 return;
543 }568 }
544 if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {569 if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
...@@ -549,7 +574,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -549,7 +574,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
549 if (!shouldKeepEndTargetAlive) {574 if (!shouldKeepEndTargetAlive) {
550 if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {575 if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
551 clearPendingInitialScrollFooterLayout(ctx, {576 clearPendingInitialScrollFooterLayout(ctx, {
552@@ -4672,7 +4827,8 @@ function maybeUpdateAnchoredEndSpace(ctx) {577@@ -4672,7 +4836,8 @@ function maybeUpdateAnchoredEndSpace(ctx) {
553 contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);578 contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
554 contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);579 contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
555 isReady = !hasUnknownTailSize;580 isReady = !hasUnknownTailSize;
...@@ -559,7 +584,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -559,7 +584,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
559 } else if (anchorIndex >= 0) {584 } else if (anchorIndex >= 0) {
560 isReady = false;585 isReady = false;
561 }586 }
562@@ -4692,6 +4848,12 @@ function maybeUpdateAnchoredEndSpace(ctx) {587@@ -4692,6 +4857,12 @@ function maybeUpdateAnchoredEndSpace(ctx) {
563 updateScroll(ctx, state.scroll, true, { markHasScrolled: false });588 updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
564 }589 }
565 (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });590 (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
...@@ -572,7 +597,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -572,7 +597,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
572 }597 }
573 return nextSize;598 return nextSize;
574 }599 }
575@@ -5715,6 +5877,7 @@ var ContainersLayer = typedMemo(function ContainersLayer2({600@@ -5715,6 +5886,7 @@ var ContainersLayer = typedMemo(function ContainersLayer2({
576 horizontal601 horizontal
577 }) {602 }) {
578 const ctx = useStateContext();603 const ctx = useStateContext();
...@@ -580,7 +605,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -580,7 +605,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
580 const columnWrapperStyle = ctx.columnWrapperStyle;605 const columnWrapperStyle = ctx.columnWrapperStyle;
581 const animSize = useValue$("totalSize");606 const animSize = useValue$("totalSize");
582 const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);607 const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);
583@@ -5725,6 +5888,13 @@ var ContainersLayer = typedMemo(function ContainersLayer2({608@@ -5725,6 +5897,13 @@ var ContainersLayer = typedMemo(function ContainersLayer2({
584 opacity: isVisible ? 1 : 0,609 opacity: isVisible ? 1 : 0,
585 width: animSize610 width: animSize
586 } : { height: animSize, minWidth: otherAxisSize, opacity: isVisible ? 1 : 0 };611 } : { height: animSize, minWidth: otherAxisSize, opacity: isVisible ? 1 : 0 };
...@@ -594,7 +619,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -594,7 +619,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
594 if (columnWrapperStyle) {619 if (columnWrapperStyle) {
595 const { columnGap, rowGap, gap } = columnWrapperStyle;620 const { columnGap, rowGap, gap } = columnWrapperStyle;
596 const gapX = columnGap || gap || 0;621 const gapX = columnGap || gap || 0;
597@@ -5745,7 +5915,8 @@ var ContainersLayer = typedMemo(function ContainersLayer2({622@@ -5745,7 +5924,8 @@ var ContainersLayer = typedMemo(function ContainersLayer2({
598 }623 }
599 }624 }
600 }625 }
...@@ -604,7 +629,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -604,7 +629,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
604 });629 });
605 var Containers = typedMemo(function Containers2({630 var Containers = typedMemo(function Containers2({
606 freshDataTransitionEpoch,631 freshDataTransitionEpoch,
607@@ -5896,7 +6067,12 @@ var StyleSheet = ReactNative.StyleSheet;632@@ -5896,7 +6076,12 @@ var StyleSheet = ReactNative.StyleSheet;
608 633
609 // src/components/ListComponent.tsx634 // src/components/ListComponent.tsx
610 var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {635 var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
...@@ -617,7 +642,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -617,7 +642,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
617 if (alignItemsAtEndPadding <= 0) {642 if (alignItemsAtEndPadding <= 0) {
618 return null;643 return null;
619 }644 }
620@@ -5929,8 +6105,12 @@ var ListComponent = typedMemo(function ListComponent2({645@@ -5929,8 +6114,12 @@ var ListComponent = typedMemo(function ListComponent2({
621 refScrollView,646 refScrollView,
622 renderScrollComponent,647 renderScrollComponent,
623 onLayoutFooter,648 onLayoutFooter,
...@@ -630,7 +655,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -630,7 +655,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
630 scrollAdjustHandler,655 scrollAdjustHandler,
631 snapToIndices,656 snapToIndices,
632 stickyHeaderConfig,657 stickyHeaderConfig,
633@@ -6001,7 +6181,17 @@ var ListComponent = typedMemo(function ListComponent2({658@@ -6001,7 +6190,17 @@ var ListComponent = typedMemo(function ListComponent2({
634 SnapOrScroll,659 SnapOrScroll,
635 {660 {
636 ...rest,661 ...rest,
...@@ -649,7 +674,16 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -649,7 +674,16 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
649 contentContainerStyle: [674 contentContainerStyle: [
650 horizontal ? { height: "100%" } : {},675 horizontal ? { height: "100%" } : {},
651 contentContainerStyle,676 contentContainerStyle,
652@@ -7075,6 +7265,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded677@@ -6751,7 +6950,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
678 endBuffered: state.endBuffered,
679 getAverageItemSizes: () => getAverageItemSizes(state),
680 indexByKey: (key) => state.indexByKey.get(key),
681- isAtEnd: peek$(ctx, "isAtEnd"),
682+ isAtEnd: getIsAtEnd(ctx),
683 isAtStart: peek$(ctx, "isAtStart"),
684 isEndReached: state.isEndReached,
685 isNearEnd: peek$(ctx, "isNearEnd"),
686@@ -7075,6 +7274,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
653 dataVersion,687 dataVersion,
654 drawDistance = 250,688 drawDistance = 250,
655 contentInsetEndAdjustment,689 contentInsetEndAdjustment,
...@@ -657,7 +691,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -657,7 +691,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
657 estimatedItemSize = 100,691 estimatedItemSize = 100,
658 estimatedListSize,692 estimatedListSize,
659 extraData,693 extraData,
660@@ -7132,10 +7323,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded694@@ -7132,10 +7332,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
661 const animatedPropsInternal = props.animatedPropsInternal;695 const animatedPropsInternal = props.animatedPropsInternal;
662 const anchoredEndSpaceOwner = (_a3 = props.anchoredEndSpaceOwnerInternal) != null ? _a3 : "list";696 const anchoredEndSpaceOwner = (_a3 = props.anchoredEndSpaceOwnerInternal) != null ? _a3 : "list";
663 const positionComponentInternal = props.positionComponentInternal;697 const positionComponentInternal = props.positionComponentInternal;
...@@ -670,7 +704,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -670,7 +704,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
670 stickyPositionComponentInternal: _stickyPositionComponentInternal,704 stickyPositionComponentInternal: _stickyPositionComponentInternal,
671 ...restProps705 ...restProps
672 } = rest;706 } = rest;
673@@ -7200,7 +7393,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded707@@ -7200,7 +7402,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
674 const combinedRef = useCombinedRef(refScroller, refScrollView);708 const combinedRef = useCombinedRef(refScroller, refScrollView);
675 const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());709 const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
676 const stickyHeaderIndices = stickyHeaderIndicesProp;710 const stickyHeaderIndices = stickyHeaderIndicesProp;
...@@ -679,7 +713,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -679,7 +713,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
679 const previousContentInsetEndAdjustmentRef = React2.useRef(contentInsetEndAdjustmentResolved);713 const previousContentInsetEndAdjustmentRef = React2.useRef(contentInsetEndAdjustmentResolved);
680 const alwaysRenderIndices = React2.useMemo(() => {714 const alwaysRenderIndices = React2.useMemo(() => {
681 const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);715 const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
682@@ -7341,6 +7534,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded716@@ -7341,6 +7543,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
683 contentContainerAlignItems: contentContainerStyle.alignItems,717 contentContainerAlignItems: contentContainerStyle.alignItems,
684 contentInset,718 contentInset,
685 contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,719 contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
...@@ -687,7 +721,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -687,7 +721,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
687 data: dataProp,721 data: dataProp,
688 dataKey,722 dataKey,
689 dataVersion,723 dataVersion,
690@@ -7372,6 +7566,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded724@@ -7372,6 +7575,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
691 renderItem,725 renderItem,
692 rtl,726 rtl,
693 snapToIndices,727 snapToIndices,
...@@ -695,7 +729,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -695,7 +729,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
695 stickyHeaderIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],729 stickyHeaderIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
696 stickyHeaderIndicesSet: React2.useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),730 stickyHeaderIndicesSet: React2.useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
697 stickyPositionComponentInternal,731 stickyPositionComponentInternal,
698@@ -7423,6 +7618,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded732@@ -7423,6 +7627,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
699 return void 0;733 return void 0;
700 }734 }
701 const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);735 const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
...@@ -709,7 +743,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -709,7 +743,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
709 return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;743 return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;
710 }, [usesBootstrapInitialScroll]);744 }, [usesBootstrapInitialScroll]);
711 React2.useLayoutEffect(() => {745 React2.useLayoutEffect(() => {
712@@ -7547,6 +7749,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded746@@ -7547,6 +7758,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
713 [747 [
714 dataKey,748 dataKey,
715 dataVersion,749 dataVersion,
...@@ -717,7 +751,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -717,7 +751,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
717 memoizedLastItemKeys.join(","),751 memoizedLastItemKeys.join(","),
718 numColumnsProp,752 numColumnsProp,
719 nextScrollAxisGap,753 nextScrollAxisGap,
720@@ -7643,6 +7846,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded754@@ -7643,6 +7855,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
721 () => ({755 () => ({
722 getRenderedItem: (key) => getRenderedItem(ctx, key),756 getRenderedItem: (key) => getRenderedItem(ctx, key),
723 onMomentumScrollEnd: (event) => {757 onMomentumScrollEnd: (event) => {
...@@ -725,7 +759,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -725,7 +759,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
725 checkFinishedScrollFallback(ctx);759 checkFinishedScrollFallback(ctx);
726 if (state.props.onMomentumScrollEnd) {760 if (state.props.onMomentumScrollEnd) {
727 state.props.onMomentumScrollEnd(event);761 state.props.onMomentumScrollEnd(event);
728@@ -7651,6 +7855,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded762@@ -7651,6 +7864,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
729 onScroll: (event) => onScroll(ctx, event),763 onScroll: (event) => onScroll(ctx, event),
730 onScrollBeginDrag: (event) => {764 onScrollBeginDrag: (event) => {
731 var _a4, _b2;765 var _a4, _b2;
...@@ -734,7 +768,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -734,7 +768,7 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
734 prepareReachedEdgeForNextUserScroll(ctx);768 prepareReachedEdgeForNextUserScroll(ctx);
735 (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);769 (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
736 },770 },
737@@ -7676,11 +7882,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded771@@ -7676,11 +7891,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
738 ListFooterComponent,772 ListFooterComponent,
739 ListFooterComponentStyle,773 ListFooterComponentStyle,
740 ListHeaderComponent,774 ListHeaderComponent,
...@@ -754,10 +788,35 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c...@@ -754,10 +788,35 @@ index b3c5a306b293f797a8b338adfca3060c0f6db22b..eb51ff0c375d7e5bd3473fca83995c7c
754 recycleItems,788 recycleItems,
755 refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {789 refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
756diff --git a/react-native.mjs b/react-native.mjs790diff --git a/react-native.mjs b/react-native.mjs
757index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318d57e29dc 100644791index 40e87cda8c9bc79a889e5542f29af429a24b24d4..90e0d1a9dfd07d0212aae308f547b9ff7e3ad022 100644
758--- a/react-native.mjs792--- a/react-native.mjs
759+++ b/react-native.mjs793+++ b/react-native.mjs
760@@ -933,7 +933,7 @@ function setInitialRenderState(ctx, {794@@ -696,6 +696,15 @@ function hasActiveInitialScroll(state) {
795 return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
796 }
797
798+// Size-only changes may not emit a scroll event to refresh the edge signal.
799+function getIsAtEnd(ctx, contentSize = getContentSize(ctx)) {
800+ const { queuedInitialLayout, scroll, scrollLength } = ctx.state;
801+ if (!(contentSize > 0 && queuedInitialLayout)) {
802+ return peek$(ctx, "isAtEnd");
803+ }
804+ return contentSize < scrollLength || contentSize - scroll - scrollLength - getContentInsetEnd(ctx) <= EDGE_POSITION_EPSILON;
805+}
806+
807 // src/utils/checkAtBottom.ts
808 function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
809 var _a3;
810@@ -716,7 +725,7 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
811 const insetEnd = getContentInsetEnd(ctx);
812 const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
813 const isContentLess = contentSize < scrollLength;
814- set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
815+ set$(ctx, "isAtEnd", getIsAtEnd(ctx, contentSize));
816 set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
817 set$(
818 ctx,
819@@ -933,7 +942,7 @@ function setInitialRenderState(ctx, {
761 if (didInitialScroll) {820 if (didInitialScroll) {
762 state.didFinishInitialScroll = true;821 state.didFinishInitialScroll = true;
763 }822 }
...@@ -766,7 +825,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -766,7 +825,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
766 if (isReadyToRender && !peek$(ctx, "readyToRender")) {825 if (isReadyToRender && !peek$(ctx, "readyToRender")) {
767 set$(ctx, "readyToRender", true);826 set$(ctx, "readyToRender", true);
768 setAdaptiveRender(ctx, "normal", "ready");827 setAdaptiveRender(ctx, "normal", "ready");
769@@ -1069,7 +1069,7 @@ function getRawContentLength(ctx) {828@@ -1069,7 +1078,7 @@ function getRawContentLength(ctx) {
770 function getAlignItemsAtEndPadding(ctx) {829 function getAlignItemsAtEndPadding(ctx) {
771 const { state } = ctx;830 const { state } = ctx;
772 const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;831 const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
...@@ -775,7 +834,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -775,7 +834,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
775 }834 }
776 function updateContentMetricsState(ctx) {835 function updateContentMetricsState(ctx) {
777 const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;836 const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
778@@ -1094,6 +1094,10 @@ function addTotalSize(ctx, key, add, notifyTotalSize = true) {837@@ -1094,6 +1103,10 @@ function addTotalSize(ctx, key, add, notifyTotalSize = true) {
779 totalSize += add;838 totalSize += add;
780 }839 }
781 if (prevTotalSize !== totalSize) {840 if (prevTotalSize !== totalSize) {
...@@ -786,7 +845,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -786,7 +845,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
786 if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {845 if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
787 state.pendingTotalSize = totalSize;846 state.pendingTotalSize = totalSize;
788 } else {847 } else {
789@@ -1283,18 +1287,23 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {848@@ -1283,18 +1296,23 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
790 }849 }
791 850
792 // src/core/clampScrollOffset.ts851 // src/core/clampScrollOffset.ts
...@@ -812,7 +871,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -812,7 +871,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
812 return clampedOffset;871 return clampedOffset;
813 }872 }
814 873
815@@ -1430,10 +1439,10 @@ function checkFinishedScrollFrame(ctx) {874@@ -1430,10 +1448,10 @@ function checkFinishedScrollFrame(ctx) {
816 finishScrollTo(ctx);875 finishScrollTo(ctx);
817 }876 }
818 }877 }
...@@ -825,7 +884,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -825,7 +884,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
825 x: ctx.state.props.horizontal ? offset : 0,884 x: ctx.state.props.horizontal ? offset : 0,
826 y: ctx.state.props.horizontal ? 0 : offset885 y: ctx.state.props.horizontal ? 0 : offset
827 });886 });
828@@ -1482,7 +1491,10 @@ function checkFinishedScrollFallback(ctx) {887@@ -1482,7 +1500,10 @@ function checkFinishedScrollFallback(ctx) {
829 );888 );
830 scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);889 scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
831 } else if (shouldRetryUnalignedEndScroll) {890 } else if (shouldRetryUnalignedEndScroll) {
...@@ -837,7 +896,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -837,7 +896,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
837 scheduleFallbackCheck(100);896 scheduleFallbackCheck(100);
838 } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {897 } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
839 finishScrollTo(ctx);898 finishScrollTo(ctx);
840@@ -1539,15 +1551,28 @@ function doMaintainScrollAtEnd(ctx) {899@@ -1539,15 +1560,28 @@ function doMaintainScrollAtEnd(ctx) {
841 } = state;900 } = state;
842 const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");901 const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
843 const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);902 const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
...@@ -867,7 +926,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -867,7 +926,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
867 }926 }
868 if (!state.maintainingScrollAtEnd) {927 if (!state.maintainingScrollAtEnd) {
869 const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";928 const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
870@@ -1570,9 +1595,18 @@ function doMaintainScrollAtEnd(ctx) {929@@ -1570,9 +1604,18 @@ function doMaintainScrollAtEnd(ctx) {
871 y: 0930 y: 0
872 });931 });
873 } else {932 } else {
...@@ -889,7 +948,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -889,7 +948,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
889 }948 }
890 setTimeout(949 setTimeout(
891 () => {950 () => {
892@@ -1603,6 +1637,10 @@ function doMaintainScrollAtEnd(ctx) {951@@ -1603,6 +1646,10 @@ function doMaintainScrollAtEnd(ctx) {
893 function requestAdjust(ctx, positionDiff, dataChanged) {952 function requestAdjust(ctx, positionDiff, dataChanged) {
894 const state = ctx.state;953 const state = ctx.state;
895 if (Math.abs(positionDiff) > 0.1) {954 if (Math.abs(positionDiff) > 0.1) {
...@@ -900,7 +959,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -900,7 +959,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
900 const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;959 const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
901 const doit = () => {960 const doit = () => {
902 if (needsScrollWorkaround) {961 if (needsScrollWorkaround) {
903@@ -1707,7 +1745,9 @@ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {962@@ -1707,7 +1754,9 @@ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
904 if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {963 if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
905 return 0;964 return 0;
906 }965 }
...@@ -911,7 +970,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -911,7 +970,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
911 const clampDelta = maxScroll - state.scroll;970 const clampDelta = maxScroll - state.scroll;
912 if (unresolvedAmount < 0) {971 if (unresolvedAmount < 0) {
913 return Math.max(unresolvedAmount, Math.min(0, clampDelta));972 return Math.max(unresolvedAmount, Math.min(0, clampDelta));
914@@ -1769,7 +1809,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {973@@ -1769,7 +1818,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
915 settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);974 settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
916 return true;975 return true;
917 }976 }
...@@ -920,7 +979,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -920,7 +979,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
920 const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);979 const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
921 const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;980 const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
922 if (isAtExpectedNativeClamp) {981 if (isAtExpectedNativeClamp) {
923@@ -1902,7 +1942,7 @@ function prepareMVCP(ctx, dataChanged) {982@@ -1902,7 +1951,7 @@ function prepareMVCP(ctx, dataChanged) {
924 if (diff > 0) {983 if (diff > 0) {
925 diff = Math.max(0, totalSize - state.scroll - state.scrollLength);984 diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
926 } else {985 } else {
...@@ -929,7 +988,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -929,7 +988,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
929 state.scroll = maxScroll;988 state.scroll = maxScroll;
930 state.scrollPending = maxScroll;989 state.scrollPending = maxScroll;
931 diff = 0;990 diff = 0;
932@@ -2299,8 +2339,121 @@ function scrollToIndex(ctx, {991@@ -2299,8 +2348,121 @@ function scrollToIndex(ctx, {
933 }992 }
934 993
935 // src/core/initialScroll.ts994 // src/core/initialScroll.ts
...@@ -1051,7 +1110,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1051,7 +1110,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1051 const requestedIndex = target.index;1110 const requestedIndex = target.index;
1052 const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;1111 const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
1053 const itemSize = getItemSizeAtIndex(ctx, index);1112 const itemSize = getItemSizeAtIndex(ctx, index);
1054@@ -2726,7 +2879,9 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {1113@@ -2726,7 +2888,9 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
1055 return;1114 return;
1056 }1115 }
1057 if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {1116 if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
...@@ -1062,7 +1121,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1062,7 +1121,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1062 if (!shouldKeepEndTargetAlive) {1121 if (!shouldKeepEndTargetAlive) {
1063 if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {1122 if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
1064 clearPendingInitialScrollFooterLayout(ctx, {1123 clearPendingInitialScrollFooterLayout(ctx, {
1065@@ -4651,7 +4806,8 @@ function maybeUpdateAnchoredEndSpace(ctx) {1124@@ -4651,7 +4815,8 @@ function maybeUpdateAnchoredEndSpace(ctx) {
1066 contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);1125 contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
1067 contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);1126 contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
1068 isReady = !hasUnknownTailSize;1127 isReady = !hasUnknownTailSize;
...@@ -1072,7 +1131,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1072,7 +1131,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1072 } else if (anchorIndex >= 0) {1131 } else if (anchorIndex >= 0) {
1073 isReady = false;1132 isReady = false;
1074 }1133 }
1075@@ -4671,6 +4827,12 @@ function maybeUpdateAnchoredEndSpace(ctx) {1134@@ -4671,6 +4836,12 @@ function maybeUpdateAnchoredEndSpace(ctx) {
1076 updateScroll(ctx, state.scroll, true, { markHasScrolled: false });1135 updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
1077 }1136 }
1078 (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });1137 (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
...@@ -1085,7 +1144,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1085,7 +1144,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1085 }1144 }
1086 return nextSize;1145 return nextSize;
1087 }1146 }
1088@@ -5694,6 +5856,7 @@ var ContainersLayer = typedMemo(function ContainersLayer2({1147@@ -5694,6 +5865,7 @@ var ContainersLayer = typedMemo(function ContainersLayer2({
1089 horizontal1148 horizontal
1090 }) {1149 }) {
1091 const ctx = useStateContext();1150 const ctx = useStateContext();
...@@ -1093,7 +1152,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1093,7 +1152,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1093 const columnWrapperStyle = ctx.columnWrapperStyle;1152 const columnWrapperStyle = ctx.columnWrapperStyle;
1094 const animSize = useValue$("totalSize");1153 const animSize = useValue$("totalSize");
1095 const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);1154 const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);
1096@@ -5704,6 +5867,13 @@ var ContainersLayer = typedMemo(function ContainersLayer2({1155@@ -5704,6 +5876,13 @@ var ContainersLayer = typedMemo(function ContainersLayer2({
1097 opacity: isVisible ? 1 : 0,1156 opacity: isVisible ? 1 : 0,
1098 width: animSize1157 width: animSize
1099 } : { height: animSize, minWidth: otherAxisSize, opacity: isVisible ? 1 : 0 };1158 } : { height: animSize, minWidth: otherAxisSize, opacity: isVisible ? 1 : 0 };
...@@ -1107,7 +1166,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1107,7 +1166,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1107 if (columnWrapperStyle) {1166 if (columnWrapperStyle) {
1108 const { columnGap, rowGap, gap } = columnWrapperStyle;1167 const { columnGap, rowGap, gap } = columnWrapperStyle;
1109 const gapX = columnGap || gap || 0;1168 const gapX = columnGap || gap || 0;
1110@@ -5724,7 +5894,8 @@ var ContainersLayer = typedMemo(function ContainersLayer2({1169@@ -5724,7 +5903,8 @@ var ContainersLayer = typedMemo(function ContainersLayer2({
1111 }1170 }
1112 }1171 }
1113 }1172 }
...@@ -1117,7 +1176,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1117,7 +1176,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1117 });1176 });
1118 var Containers = typedMemo(function Containers2({1177 var Containers = typedMemo(function Containers2({
1119 freshDataTransitionEpoch,1178 freshDataTransitionEpoch,
1120@@ -5875,7 +6046,12 @@ var StyleSheet = StyleSheet$1;1179@@ -5875,7 +6055,12 @@ var StyleSheet = StyleSheet$1;
1121 1180
1122 // src/components/ListComponent.tsx1181 // src/components/ListComponent.tsx
1123 var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {1182 var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
...@@ -1130,7 +1189,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1130,7 +1189,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1130 if (alignItemsAtEndPadding <= 0) {1189 if (alignItemsAtEndPadding <= 0) {
1131 return null;1190 return null;
1132 }1191 }
1133@@ -5908,8 +6084,12 @@ var ListComponent = typedMemo(function ListComponent2({1192@@ -5908,8 +6093,12 @@ var ListComponent = typedMemo(function ListComponent2({
1134 refScrollView,1193 refScrollView,
1135 renderScrollComponent,1194 renderScrollComponent,
1136 onLayoutFooter,1195 onLayoutFooter,
...@@ -1143,7 +1202,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1143,7 +1202,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1143 scrollAdjustHandler,1202 scrollAdjustHandler,
1144 snapToIndices,1203 snapToIndices,
1145 stickyHeaderConfig,1204 stickyHeaderConfig,
1146@@ -5980,7 +6160,17 @@ var ListComponent = typedMemo(function ListComponent2({1205@@ -5980,7 +6169,17 @@ var ListComponent = typedMemo(function ListComponent2({
1147 SnapOrScroll,1206 SnapOrScroll,
1148 {1207 {
1149 ...rest,1208 ...rest,
...@@ -1162,7 +1221,16 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1162,7 +1221,16 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1162 contentContainerStyle: [1221 contentContainerStyle: [
1163 horizontal ? { height: "100%" } : {},1222 horizontal ? { height: "100%" } : {},
1164 contentContainerStyle,1223 contentContainerStyle,
1165@@ -7054,6 +7244,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1224@@ -6730,7 +6929,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
1225 endBuffered: state.endBuffered,
1226 getAverageItemSizes: () => getAverageItemSizes(state),
1227 indexByKey: (key) => state.indexByKey.get(key),
1228- isAtEnd: peek$(ctx, "isAtEnd"),
1229+ isAtEnd: getIsAtEnd(ctx),
1230 isAtStart: peek$(ctx, "isAtStart"),
1231 isEndReached: state.isEndReached,
1232 isNearEnd: peek$(ctx, "isNearEnd"),
1233@@ -7054,6 +7253,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1166 dataVersion,1234 dataVersion,
1167 drawDistance = 250,1235 drawDistance = 250,
1168 contentInsetEndAdjustment,1236 contentInsetEndAdjustment,
...@@ -1170,7 +1238,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1170,7 +1238,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1170 estimatedItemSize = 100,1238 estimatedItemSize = 100,
1171 estimatedListSize,1239 estimatedListSize,
1172 extraData,1240 extraData,
1173@@ -7111,10 +7302,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1241@@ -7111,10 +7311,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1174 const animatedPropsInternal = props.animatedPropsInternal;1242 const animatedPropsInternal = props.animatedPropsInternal;
1175 const anchoredEndSpaceOwner = (_a3 = props.anchoredEndSpaceOwnerInternal) != null ? _a3 : "list";1243 const anchoredEndSpaceOwner = (_a3 = props.anchoredEndSpaceOwnerInternal) != null ? _a3 : "list";
1176 const positionComponentInternal = props.positionComponentInternal;1244 const positionComponentInternal = props.positionComponentInternal;
...@@ -1183,7 +1251,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1183,7 +1251,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1183 stickyPositionComponentInternal: _stickyPositionComponentInternal,1251 stickyPositionComponentInternal: _stickyPositionComponentInternal,
1184 ...restProps1252 ...restProps
1185 } = rest;1253 } = rest;
1186@@ -7179,7 +7372,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1254@@ -7179,7 +7381,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1187 const combinedRef = useCombinedRef(refScroller, refScrollView);1255 const combinedRef = useCombinedRef(refScroller, refScrollView);
1188 const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());1256 const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
1189 const stickyHeaderIndices = stickyHeaderIndicesProp;1257 const stickyHeaderIndices = stickyHeaderIndicesProp;
...@@ -1192,7 +1260,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1192,7 +1260,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1192 const previousContentInsetEndAdjustmentRef = useRef(contentInsetEndAdjustmentResolved);1260 const previousContentInsetEndAdjustmentRef = useRef(contentInsetEndAdjustmentResolved);
1193 const alwaysRenderIndices = useMemo(() => {1261 const alwaysRenderIndices = useMemo(() => {
1194 const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);1262 const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
1195@@ -7320,6 +7513,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1263@@ -7320,6 +7522,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1196 contentContainerAlignItems: contentContainerStyle.alignItems,1264 contentContainerAlignItems: contentContainerStyle.alignItems,
1197 contentInset,1265 contentInset,
1198 contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,1266 contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
...@@ -1200,7 +1268,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1200,7 +1268,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1200 data: dataProp,1268 data: dataProp,
1201 dataKey,1269 dataKey,
1202 dataVersion,1270 dataVersion,
1203@@ -7351,6 +7545,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1271@@ -7351,6 +7554,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1204 renderItem,1272 renderItem,
1205 rtl,1273 rtl,
1206 snapToIndices,1274 snapToIndices,
...@@ -1208,7 +1276,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1208,7 +1276,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1208 stickyHeaderIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],1276 stickyHeaderIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
1209 stickyHeaderIndicesSet: useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),1277 stickyHeaderIndicesSet: useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
1210 stickyPositionComponentInternal,1278 stickyPositionComponentInternal,
1211@@ -7402,6 +7597,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1279@@ -7402,6 +7606,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1212 return void 0;1280 return void 0;
1213 }1281 }
1214 const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);1282 const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
...@@ -1222,7 +1290,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1222,7 +1290,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1222 return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;1290 return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;
1223 }, [usesBootstrapInitialScroll]);1291 }, [usesBootstrapInitialScroll]);
1224 useLayoutEffect(() => {1292 useLayoutEffect(() => {
1225@@ -7526,6 +7728,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1293@@ -7526,6 +7737,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1226 [1294 [
1227 dataKey,1295 dataKey,
1228 dataVersion,1296 dataVersion,
...@@ -1230,7 +1298,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1230,7 +1298,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1230 memoizedLastItemKeys.join(","),1298 memoizedLastItemKeys.join(","),
1231 numColumnsProp,1299 numColumnsProp,
1232 nextScrollAxisGap,1300 nextScrollAxisGap,
1233@@ -7622,6 +7825,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1301@@ -7622,6 +7834,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1234 () => ({1302 () => ({
1235 getRenderedItem: (key) => getRenderedItem(ctx, key),1303 getRenderedItem: (key) => getRenderedItem(ctx, key),
1236 onMomentumScrollEnd: (event) => {1304 onMomentumScrollEnd: (event) => {
...@@ -1238,7 +1306,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1238,7 +1306,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1238 checkFinishedScrollFallback(ctx);1306 checkFinishedScrollFallback(ctx);
1239 if (state.props.onMomentumScrollEnd) {1307 if (state.props.onMomentumScrollEnd) {
1240 state.props.onMomentumScrollEnd(event);1308 state.props.onMomentumScrollEnd(event);
1241@@ -7630,6 +7834,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1309@@ -7630,6 +7843,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1242 onScroll: (event) => onScroll(ctx, event),1310 onScroll: (event) => onScroll(ctx, event),
1243 onScrollBeginDrag: (event) => {1311 onScrollBeginDrag: (event) => {
1244 var _a4, _b2;1312 var _a4, _b2;
...@@ -1247,7 +1315,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318...@@ -1247,7 +1315,7 @@ index 40e87cda8c9bc79a889e5542f29af429a24b24d4..700515b52c27a4ccd5343176121e7318
1247 prepareReachedEdgeForNextUserScroll(ctx);1315 prepareReachedEdgeForNextUserScroll(ctx);
1248 (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);1316 (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
1249 },1317 },
1250@@ -7655,11 +7861,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded1318@@ -7655,11 +7870,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1251 ListFooterComponent,1319 ListFooterComponent,
1252 ListFooterComponentStyle,1320 ListFooterComponentStyle,
1253 ListHeaderComponent,1321 ListHeaderComponent,
...@@ -1358,7 +1426,7 @@ index e5043320700b12f34f4c0babbc341f85ca8135c1..2ce63830a28636b21937a0741fd0e613...@@ -1358,7 +1426,7 @@ index e5043320700b12f34f4c0babbc341f85ca8135c1..2ce63830a28636b21937a0741fd0e613
1358 * Number of columns to render items in.1426 * Number of columns to render items in.
1359 * @default 11427 * @default 1
1360diff --git a/reanimated.js b/reanimated.js1428diff --git a/reanimated.js b/reanimated.js
1361index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71ff8ce9f4d 1006441429index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..fc03be2190c046f743ffde45ed189ee7868d0cec 100644
1362--- a/reanimated.js1430--- a/reanimated.js
1363+++ b/reanimated.js1431+++ b/reanimated.js
1364@@ -115,8 +115,10 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(1432@@ -115,8 +115,10 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
...@@ -1404,7 +1472,7 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f...@@ -1404,7 +1472,7 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f
1404 ref: refView,1472 ref: refView,
1405 style: viewStyle,1473 style: viewStyle,
1406 ...rest1474 ...rest
1407@@ -141,6 +162,96 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(1475@@ -141,6 +162,95 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
1408 children1476 children
1409 );1477 );
1410 });1478 });
...@@ -1418,12 +1486,12 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f...@@ -1418,12 +1486,12 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f
1418+ const previousEpochRef = React__namespace.useRef(ctx.state.contentSizeAnimationEpoch || 0);1486+ const previousEpochRef = React__namespace.useRef(ctx.state.contentSizeAnimationEpoch || 0);
1419+ const animationRunRef = React__namespace.useRef(0);1487+ const animationRunRef = React__namespace.useRef(0);
1420+ const isAnimatingRef = React__namespace.useRef(false);1488+ const isAnimatingRef = React__namespace.useRef(false);
1421+ const [isAnimating, setIsAnimating] = React__namespace.useState(false);
1422+ const transition = props.layoutTransition;1489+ const transition = props.layoutTransition;
1423+ const canAnimate = !!transition && typeof transition.getAnimationAndConfig === "function";1490+ const canAnimate = !!transition && typeof transition.getAnimationAndConfig === "function";
1424+ const renderEpoch = ctx.state.contentSizeAnimationEpoch || 0;1491+ const renderEpoch = ctx.state.contentSizeAnimationEpoch || 0;
1425+ const startsEligibleAnimation = canAnimate && logicalSize !== previousLogicalSizeRef.current && renderEpoch !== previousEpochRef.current && ctx.state.contentSizeAnimationEligible;1492+ // Keep the animated size attached after transitions finish. Reanimated
1426+ const useAnimatedAxis = isAnimating || startsEligibleAnimation;1493+ // retains removed animated props, so switching to a static size leaves
1494+ // native scroll bounds stuck while logical measurements keep changing.
1427+ const animatedStyle = Reanimated.useAnimatedStyle(() => {1495+ const animatedStyle = Reanimated.useAnimatedStyle(() => {
1428+ const size = Math.max(0, baseSize.value + animatedDelta.value);1496+ const size = Math.max(0, baseSize.value + animatedDelta.value);
1429+ return horizontal ? { width: size } : { height: size };1497+ return horizontal ? { width: size } : { height: size };
...@@ -1433,7 +1501,6 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f...@@ -1433,7 +1501,6 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f
1433+ return;1501+ return;
1434+ }1502+ }
1435+ isAnimatingRef.current = false;1503+ isAnimatingRef.current = false;
1436+ setIsAnimating(false);
1437+ const state = ctx.state;1504+ const state = ctx.state;
1438+ if (state.contentSizeAnimationActiveEpoch !== epoch) {1505+ if (state.contentSizeAnimationActiveEpoch !== epoch) {
1439+ return;1506+ return;
...@@ -1450,13 +1517,15 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f...@@ -1450,13 +1517,15 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f
1450+ const previousLogicalSize = previousLogicalSizeRef.current;1517+ const previousLogicalSize = previousLogicalSizeRef.current;
1451+ const delta = logicalSize - previousLogicalSize;1518+ const delta = logicalSize - previousLogicalSize;
1452+ const isNewEpoch = epoch !== previousEpochRef.current;1519+ const isNewEpoch = epoch !== previousEpochRef.current;
1453+ const isNewEligibleEpoch = canAnimate && isNewEpoch && state.contentSizeAnimationEligible;1520+ // Retarget measurements received mid-animation, even in the same epoch.
1521+ // Updating only the base keeps the old delta and can shrink scroll bounds.
1522+ const shouldAnimate = canAnimate && (isAnimatingRef.current || isNewEpoch && state.contentSizeAnimationEligible);
1454+ previousLogicalSizeRef.current = logicalSize;1523+ previousLogicalSizeRef.current = logicalSize;
1455+ previousEpochRef.current = epoch;1524+ previousEpochRef.current = epoch;
1456+ if (!delta) {1525+ if (!delta) {
1457+ return;1526+ return;
1458+ }1527+ }
1459+ if (!isNewEligibleEpoch) {1528+ if (!shouldAnimate) {
1460+ baseSize.value = logicalSize;1529+ baseSize.value = logicalSize;
1461+ if (signalName === "totalSize" && isNewEpoch && state.contentSizeAnimationEligible && !canAnimate) {1530+ if (signalName === "totalSize" && isNewEpoch && state.contentSizeAnimationEligible && !canAnimate) {
1462+ state.contentSizeAnimationEligible = false;1531+ state.contentSizeAnimationEligible = false;
...@@ -1480,7 +1549,6 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f...@@ -1480,7 +1549,6 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f
1480+ state.contentSizeAnimationActiveSignals.add(signalName);1549+ state.contentSizeAnimationActiveSignals.add(signalName);
1481+ const wasAnimating = isAnimatingRef.current;1550+ const wasAnimating = isAnimatingRef.current;
1482+ isAnimatingRef.current = true;1551+ isAnimatingRef.current = true;
1483+ setIsAnimating(true);
1484+ const run = ++animationRunRef.current;1552+ const run = ++animationRunRef.current;
1485+ Reanimated.runOnUI((absoluteSize, previousSize, continuesAnimation, animationRun, animationEpoch) => {1553+ Reanimated.runOnUI((absoluteSize, previousSize, continuesAnimation, animationRun, animationEpoch) => {
1486+ "worklet";1554+ "worklet";
...@@ -1495,13 +1563,12 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f...@@ -1495,13 +1563,12 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f
1495+ }));1563+ }));
1496+ })(logicalSize, previousLogicalSize, wasAnimating, run, epoch);1564+ })(logicalSize, previousLogicalSize, wasAnimating, run, epoch);
1497+ }, [baseSize, animatedDelta, canAnimate, completeAnimation, ctx.state, logicalSize, renderEpoch, signalName, transition]);1565+ }, [baseSize, animatedDelta, canAnimate, completeAnimation, ctx.state, logicalSize, renderEpoch, signalName, transition]);
1498+ const sizeStyle = useAnimatedAxis ? animatedStyle : horizontal ? { width: logicalSize } : { height: logicalSize };1566+ return /* @__PURE__ */ React__namespace.createElement(Reanimated__default.default.View, { ...rest, style: [style, animatedStyle] }, children);
1499+ return /* @__PURE__ */ React__namespace.createElement(Reanimated__default.default.View, { ...rest, style: [style, sizeStyle] }, children);
1500+});1567+});
1501 function setSharedValueValue(sharedValue, value) {1568 function setSharedValueValue(sharedValue, value) {
1502 if (!sharedValue) {1569 if (!sharedValue) {
1503 return;1570 return;
1504@@ -245,10 +356,19 @@ var LegendListForwardedRef = typedMemo(1571@@ -245,10 +355,19 @@ var LegendListForwardedRef = typedMemo(
1505 );1572 );
1506 };1573 };
1507 }, [hasItemLayoutAnimation, recycleItems]);1574 }, [hasItemLayoutAnimation, recycleItems]);
...@@ -1522,7 +1589,7 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f...@@ -1522,7 +1589,7 @@ index f1265fad74189591b5aae86cf2e3a31f9c0fdb02..59e218cb79f56c34995fb86c5de5a71f
1522 renderScrollComponent: renderReanimatedScrollComponent,1589 renderScrollComponent: renderReanimatedScrollComponent,
1523 ...IsNewArchitecture ? { stickyPositionComponentInternal } : {}1590 ...IsNewArchitecture ? { stickyPositionComponentInternal } : {}
1524diff --git a/reanimated.mjs b/reanimated.mjs1591diff --git a/reanimated.mjs b/reanimated.mjs
1525index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a5725398942 1006441592index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..6ab929250c8924e2d9919ee09a4f774238fbb90a 100644
1526--- a/reanimated.mjs1593--- a/reanimated.mjs
1527+++ b/reanimated.mjs1594+++ b/reanimated.mjs
1528@@ -1,7 +1,7 @@1595@@ -1,7 +1,7 @@
...@@ -1577,7 +1644,7 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57...@@ -1577,7 +1644,7 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57
1577 ref: refView,1644 ref: refView,
1578 style: viewStyle,1645 style: viewStyle,
1579 ...rest1646 ...rest
1580@@ -117,6 +138,96 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(1647@@ -117,6 +138,95 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
1581 children1648 children
1582 );1649 );
1583 });1650 });
...@@ -1591,12 +1658,12 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57...@@ -1591,12 +1658,12 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57
1591+ const previousEpochRef = React.useRef(ctx.state.contentSizeAnimationEpoch || 0);1658+ const previousEpochRef = React.useRef(ctx.state.contentSizeAnimationEpoch || 0);
1592+ const animationRunRef = React.useRef(0);1659+ const animationRunRef = React.useRef(0);
1593+ const isAnimatingRef = React.useRef(false);1660+ const isAnimatingRef = React.useRef(false);
1594+ const [isAnimating, setIsAnimating] = React.useState(false);
1595+ const transition = props.layoutTransition;1661+ const transition = props.layoutTransition;
1596+ const canAnimate = !!transition && typeof transition.getAnimationAndConfig === "function";1662+ const canAnimate = !!transition && typeof transition.getAnimationAndConfig === "function";
1597+ const renderEpoch = ctx.state.contentSizeAnimationEpoch || 0;1663+ const renderEpoch = ctx.state.contentSizeAnimationEpoch || 0;
1598+ const startsEligibleAnimation = canAnimate && logicalSize !== previousLogicalSizeRef.current && renderEpoch !== previousEpochRef.current && ctx.state.contentSizeAnimationEligible;1664+ // Keep the animated size attached after transitions finish. Reanimated
1599+ const useAnimatedAxis = isAnimating || startsEligibleAnimation;1665+ // retains removed animated props, so switching to a static size leaves
1666+ // native scroll bounds stuck while logical measurements keep changing.
1600+ const animatedStyle = useAnimatedStyle(() => {1667+ const animatedStyle = useAnimatedStyle(() => {
1601+ const size = Math.max(0, baseSize.value + animatedDelta.value);1668+ const size = Math.max(0, baseSize.value + animatedDelta.value);
1602+ return horizontal ? { width: size } : { height: size };1669+ return horizontal ? { width: size } : { height: size };
...@@ -1606,7 +1673,6 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57...@@ -1606,7 +1673,6 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57
1606+ return;1673+ return;
1607+ }1674+ }
1608+ isAnimatingRef.current = false;1675+ isAnimatingRef.current = false;
1609+ setIsAnimating(false);
1610+ const state = ctx.state;1676+ const state = ctx.state;
1611+ if (state.contentSizeAnimationActiveEpoch !== epoch) {1677+ if (state.contentSizeAnimationActiveEpoch !== epoch) {
1612+ return;1678+ return;
...@@ -1623,13 +1689,15 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57...@@ -1623,13 +1689,15 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57
1623+ const previousLogicalSize = previousLogicalSizeRef.current;1689+ const previousLogicalSize = previousLogicalSizeRef.current;
1624+ const delta = logicalSize - previousLogicalSize;1690+ const delta = logicalSize - previousLogicalSize;
1625+ const isNewEpoch = epoch !== previousEpochRef.current;1691+ const isNewEpoch = epoch !== previousEpochRef.current;
1626+ const isNewEligibleEpoch = canAnimate && isNewEpoch && state.contentSizeAnimationEligible;1692+ // Retarget measurements received mid-animation, even in the same epoch.
1693+ // Updating only the base keeps the old delta and can shrink scroll bounds.
1694+ const shouldAnimate = canAnimate && (isAnimatingRef.current || isNewEpoch && state.contentSizeAnimationEligible);
1627+ previousLogicalSizeRef.current = logicalSize;1695+ previousLogicalSizeRef.current = logicalSize;
1628+ previousEpochRef.current = epoch;1696+ previousEpochRef.current = epoch;
1629+ if (!delta) {1697+ if (!delta) {
1630+ return;1698+ return;
1631+ }1699+ }
1632+ if (!isNewEligibleEpoch) {1700+ if (!shouldAnimate) {
1633+ baseSize.value = logicalSize;1701+ baseSize.value = logicalSize;
1634+ if (signalName === "totalSize" && isNewEpoch && state.contentSizeAnimationEligible && !canAnimate) {1702+ if (signalName === "totalSize" && isNewEpoch && state.contentSizeAnimationEligible && !canAnimate) {
1635+ state.contentSizeAnimationEligible = false;1703+ state.contentSizeAnimationEligible = false;
...@@ -1653,7 +1721,6 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57...@@ -1653,7 +1721,6 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57
1653+ state.contentSizeAnimationActiveSignals.add(signalName);1721+ state.contentSizeAnimationActiveSignals.add(signalName);
1654+ const wasAnimating = isAnimatingRef.current;1722+ const wasAnimating = isAnimatingRef.current;
1655+ isAnimatingRef.current = true;1723+ isAnimatingRef.current = true;
1656+ setIsAnimating(true);
1657+ const run = ++animationRunRef.current;1724+ const run = ++animationRunRef.current;
1658+ runOnUI((absoluteSize, previousSize, continuesAnimation, animationRun, animationEpoch) => {1725+ runOnUI((absoluteSize, previousSize, continuesAnimation, animationRun, animationEpoch) => {
1659+ "worklet";1726+ "worklet";
...@@ -1668,13 +1735,12 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57...@@ -1668,13 +1735,12 @@ index 29a00d5dc084fd408a0e0a0a4d64e856d0ed1525..bd3c680eb4abeca786d538ebf7b23a57
1668+ }));1735+ }));
1669+ })(logicalSize, previousLogicalSize, wasAnimating, run, epoch);1736+ })(logicalSize, previousLogicalSize, wasAnimating, run, epoch);
1670+ }, [baseSize, animatedDelta, canAnimate, completeAnimation, ctx.state, logicalSize, renderEpoch, signalName, transition]);1737+ }, [baseSize, animatedDelta, canAnimate, completeAnimation, ctx.state, logicalSize, renderEpoch, signalName, transition]);
1671+ const sizeStyle = useAnimatedAxis ? animatedStyle : horizontal ? { width: logicalSize } : { height: logicalSize };1738+ return /* @__PURE__ */ React.createElement(Reanimated.View, { ...rest, style: [style, animatedStyle] }, children);
1672+ return /* @__PURE__ */ React.createElement(Reanimated.View, { ...rest, style: [style, sizeStyle] }, children);
1673+});1739+});
1674 function setSharedValueValue(sharedValue, value) {1740 function setSharedValueValue(sharedValue, value) {
1675 if (!sharedValue) {1741 if (!sharedValue) {
1676 return;1742 return;
1677@@ -221,10 +332,19 @@ var LegendListForwardedRef = typedMemo(1743@@ -221,10 +331,19 @@ var LegendListForwardedRef = typedMemo(
1678 );1744 );
1679 };1745 };
1680 }, [hasItemLayoutAnimation, recycleItems]);1746 }, [hasItemLayoutAnimation, recycleItems]);
pnpm-lock.yaml+5-5
...@@ -86,7 +86,7 @@ patchedDependencies:...@@ -86,7 +86,7 @@ patchedDependencies:
86 '@effect/vitest@4.0.0-beta.103': a16b1e870d8c29e4a98b17cc4c638a4ff471753d8ca3487f78a22b759caf951b86 '@effect/vitest@4.0.0-beta.103': a16b1e870d8c29e4a98b17cc4c638a4ff471753d8ca3487f78a22b759caf951b
87 '@expo/metro-config@57.0.12': 96f1a75347e6ea02dc4b7034ace815d8ee39e18b8166ebfb573d9e58328f0dc287 '@expo/metro-config@57.0.12': 96f1a75347e6ea02dc4b7034ace815d8ee39e18b8166ebfb573d9e58328f0dc2
88 '@ff-labs/fff-node@0.9.4': ab9ff544009e1891cfe3930105862d3699007f38922a79f3c98d90018deca36888 '@ff-labs/fff-node@0.9.4': ab9ff544009e1891cfe3930105862d3699007f38922a79f3c98d90018deca368
89 '@legendapp/list@3.3.5': f786e0ada19a32703f71a7230a819697ebff2527873dfb9997c8455947e0060b89 '@legendapp/list@3.3.5': 03ec41339cd915ecb9a774a6b90cc2197c29038f7db67c4d2e55cd3971e5be43
90 '@pierre/diffs@1.3.0-beta.10': 7ef7cb0cbabb17c15cdb137554068b36f15f6f5265e73fb51452aa3380db91aa90 '@pierre/diffs@1.3.0-beta.10': 7ef7cb0cbabb17c15cdb137554068b36f15f6f5265e73fb51452aa3380db91aa
91 '@react-native-ai/apple@0.12.0': 2d09870c2848d185cb05b53ed823a46e12dba519324d8dd8e584e28731990f9d91 '@react-native-ai/apple@0.12.0': 2d09870c2848d185cb05b53ed823a46e12dba519324d8dd8e584e28731990f9d
92 '@react-native-menu/menu@2.0.0': f63d256bf6a97a873b5e628eb595bd6ef0075ddd5bdd890fc920f7a6024290dd92 '@react-native-menu/menu@2.0.0': f63d256bf6a97a873b5e628eb595bd6ef0075ddd5bdd890fc920f7a6024290dd
...@@ -228,7 +228,7 @@ importers:...@@ -228,7 +228,7 @@ importers:
228 version: 57.0.14(@babel/core@7.29.7)(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(expo@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)228 version: 57.0.14(@babel/core@7.29.7)(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(expo@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
229 '@legendapp/list':229 '@legendapp/list':
230 specifier: 'catalog:'230 specifier: 'catalog:'
231 version: 3.3.5(patch_hash=f786e0ada19a32703f71a7230a819697ebff2527873dfb9997c8455947e0060b)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)231 version: 3.3.5(patch_hash=03ec41339cd915ecb9a774a6b90cc2197c29038f7db67c4d2e55cd3971e5be43)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
232 '@noble/curves':232 '@noble/curves':
233 specifier: 'catalog:'233 specifier: 'catalog:'
234 version: 1.9.1234 version: 1.9.1
...@@ -572,7 +572,7 @@ importers:...@@ -572,7 +572,7 @@ importers:
572 version: 0.9.0572 version: 0.9.0
573 '@legendapp/list':573 '@legendapp/list':
574 specifier: 'catalog:'574 specifier: 'catalog:'
575 version: 3.3.5(patch_hash=f786e0ada19a32703f71a7230a819697ebff2527873dfb9997c8455947e0060b)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)575 version: 3.3.5(patch_hash=03ec41339cd915ecb9a774a6b90cc2197c29038f7db67c4d2e55cd3971e5be43)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
576 '@lexical/react':576 '@lexical/react':
577 specifier: ^0.41.0577 specifier: ^0.41.0
578 version: 0.41.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yjs@13.6.31)578 version: 0.41.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yjs@13.6.31)
...@@ -12892,7 +12892,7 @@ snapshots:...@@ -12892,7 +12892,7 @@ snapshots:
12892 dependencies:12892 dependencies:
12893 jsbi: 4.3.212893 jsbi: 4.3.2
1289412894
12895 '@legendapp/list@3.3.5(patch_hash=f786e0ada19a32703f71a7230a819697ebff2527873dfb9997c8455947e0060b)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)':12895 '@legendapp/list@3.3.5(patch_hash=03ec41339cd915ecb9a774a6b90cc2197c29038f7db67c4d2e55cd3971e5be43)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)':
12896 dependencies:12896 dependencies:
12897 react: 19.2.312897 react: 19.2.3
12898 use-sync-external-store: 1.6.0(react@19.2.3)12898 use-sync-external-store: 1.6.0(react@19.2.3)
...@@ -12900,7 +12900,7 @@ snapshots:...@@ -12900,7 +12900,7 @@ snapshots:
12900 react-dom: 19.2.3(react@19.2.3)12900 react-dom: 19.2.3(react@19.2.3)
12901 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)12901 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)
1290212902
12903 '@legendapp/list@3.3.5(patch_hash=f786e0ada19a32703f71a7230a819697ebff2527873dfb9997c8455947e0060b)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':12903 '@legendapp/list@3.3.5(patch_hash=03ec41339cd915ecb9a774a6b90cc2197c29038f7db67c4d2e55cd3971e5be43)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
12904 dependencies:12904 dependencies:
12905 react: 19.2.612905 react: 19.2.6
12906 use-sync-external-store: 1.6.0(react@19.2.6)12906 use-sync-external-store: 1.6.0(react@19.2.6)