From 92185f3a7bcf526edfbba6a01e5d3a282e974752 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 25 Aug 2026 15:42:02 +0300 Subject: [PATCH] fix(chat): let the timeline rail reach the last turn while it is unmounted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../ui/src/components/chat/MessageList.tsx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/ui/src/components/chat/MessageList.tsx b/packages/ui/src/components/chat/MessageList.tsx index 834bf4f7..f81f0e4e 100644 --- a/packages/ui/src/components/chat/MessageList.tsx +++ b/packages/ui/src/components/chat/MessageList.tsx @@ -1494,8 +1494,11 @@ const MessageList = React.forwardRef(({ 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(({ // 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(({ return true; } - const targetIsTail = trailingStreamingEntry !== undefined && index >= historyEntries.length; - if (targetIsTail) { - return false; - } return scrollHistoryIndexIntoView(index); }, @@ -1569,11 +1568,7 @@ const MessageList = React.forwardRef(({ } return scrollMessageElementIntoView(messageId, behavior) - || ( - trailingStreamingEntry !== undefined && index >= historyEntries.length - ? false - : scrollHistoryIndexIntoView(index) - ); + || scrollHistoryIndexIntoView(index); }, holdViewportAnchor: (anchor) => { @@ -1716,7 +1711,7 @@ const MessageList = React.forwardRef(({ 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(() => { const resolved = resolveChatListAnchoredEndSpace(