fix(chat): let the timeline rail reach the last turn while it is unmounted

Both scrollToTurnId and scrollToMessageId hard-returned false when the
target was the trailing (last) turn and its element was not mounted, so
clicking the rail's last item in a long scrolled-up session did nothing.
The trailing entry is a regular list row at the end of the data — scroll
to its index the same way history targets are reached.
This commit is contained in:
Bohdan Triapitsyn
2026-08-25 15:42:02 +03:00
parent 44de220442
commit 92185f3a7b
@@ -1494,8 +1494,11 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
return container.querySelector(`[data-message-id="${messageId}"]`);
}, [resolveScrollContainer]);
// Accepts any index the list renders, the trailing streaming entry
// included — it lives at historyEntries.length and is a legitimate
// navigation target (the timeline rail's last item).
const scrollHistoryIndexIntoView = React.useCallback((index: number) => {
if (index < 0 || index >= historyEntries.length) {
if (index < 0 || index >= allEntries.length) {
return false;
}
@@ -1510,7 +1513,7 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
// path in scrollMessageElementIntoView.
void list.scrollToIndex({ index, animated: false, viewPosition: 0 });
return true;
}, [historyEntries.length]);
}, [allEntries.length]);
const scrollMessageElementIntoView = React.useCallback((messageId: string, behavior: ScrollBehavior = 'auto') => {
const container = resolveScrollContainer();
@@ -1553,10 +1556,6 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
return true;
}
const targetIsTail = trailingStreamingEntry !== undefined && index >= historyEntries.length;
if (targetIsTail) {
return false;
}
return scrollHistoryIndexIntoView(index);
},
@@ -1569,11 +1568,7 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
}
return scrollMessageElementIntoView(messageId, behavior)
|| (
trailingStreamingEntry !== undefined && index >= historyEntries.length
? false
: scrollHistoryIndexIntoView(index)
);
|| scrollHistoryIndexIntoView(index);
},
holdViewportAnchor: (anchor) => {
@@ -1716,7 +1711,7 @@ const MessageList = React.forwardRef<MessageListHandle, MessageListProps>(({
return () => {
objectRef.current = null;
};
}, [findMessageElement, historyEntries.length, messageIndexMap, resolveScrollContainer, scrollHistoryIndexIntoView, scrollMessageElementIntoView, trailingStreamingEntry, turnIndexMap, ref]);
}, [findMessageElement, historyEntries.length, messageIndexMap, resolveScrollContainer, scrollHistoryIndexIntoView, scrollMessageElementIntoView, turnIndexMap, ref]);
const anchoredEndSpace = React.useMemo<TimelineAnchoredEndSpace | undefined>(() => {
const resolved = resolveChatListAnchoredEndSpace(