fix(chat): drive anchored end-follow from the list's total size
Phase 2 of the anchored turn (following the live edge once the reply outgrows the viewport) never ran: the reveal correction was triggered only by entries-array changes, and the streaming tail grows inside a single row without touching the array. The hook now also subscribes to LegendList's totalSize and re-runs the same guarded correction on every content growth.
This commit is contained in:
@@ -56,7 +56,13 @@ export interface AnimationHandlers {
|
||||
// hook stays testable without a renderer and does not hard-depend on the list
|
||||
// implementation.
|
||||
export interface TimelineListHandle {
|
||||
getState: () => TimelineListMeasurementState & { readonly scroll: number };
|
||||
getState: () => TimelineListMeasurementState & {
|
||||
readonly scroll: number;
|
||||
readonly listen?: (
|
||||
listenerType: 'totalSize',
|
||||
callback: (value: number) => void,
|
||||
) => () => void;
|
||||
};
|
||||
getScrollableNode: () => HTMLElement | null;
|
||||
scrollToEnd: (options?: { animated?: boolean }) => unknown;
|
||||
scrollToOffset: (params: { offset: number; animated?: boolean }) => unknown;
|
||||
@@ -555,6 +561,22 @@ export const useChatTimelineScroll = ({
|
||||
});
|
||||
}, [isLiveFollowActive, realContentOverflowsViewport]);
|
||||
|
||||
// The streaming tail grows inside one row without changing the entries
|
||||
// array, so data-change callbacks are silent for the entire stream. The
|
||||
// list's total content size is the authoritative growth signal; every
|
||||
// change re-runs the same guarded correction.
|
||||
const onTimelineDataChangeRef = React.useRef(onTimelineDataChange);
|
||||
onTimelineDataChangeRef.current = onTimelineDataChange;
|
||||
React.useEffect(() => {
|
||||
if (!scrollNode) return;
|
||||
const listen = listRef.current?.getState().listen;
|
||||
if (!listen) return;
|
||||
const unsubscribe = listen('totalSize', () => {
|
||||
onTimelineDataChangeRef.current();
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [scrollNode]);
|
||||
|
||||
// ── gesture opt-out ─────────────────────────────────────────────────────
|
||||
const onManualNavigationRef = React.useRef(onManualNavigation);
|
||||
onManualNavigationRef.current = onManualNavigation;
|
||||
|
||||
Reference in New Issue
Block a user