From e801afec43057f0ec5eb095a5abd20196f3c3f2f Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 25 Aug 2026 16:01:22 +0300 Subject: [PATCH] fix(chat): do not snap back to the end after a width resize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A slow edge drag settles the resize detector repeatedly, and every settle replayed the instant return to the live edge — reading as exactly the jumping the resize suspension removes. Stay where the reader is; geometry changed, and the next explicit action or stream correction re-engages the edge as usual. --- packages/ui/src/hooks/useChatTimelineScroll.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/hooks/useChatTimelineScroll.ts b/packages/ui/src/hooks/useChatTimelineScroll.ts index 974698c3..29962ce6 100644 --- a/packages/ui/src/hooks/useChatTimelineScroll.ts +++ b/packages/ui/src/hooks/useChatTimelineScroll.ts @@ -510,9 +510,11 @@ export const useChatTimelineScroll = ({ // While the list width is resizing, every pinning write fights the // per-frame row re-measure and the pinned viewport shakes. Corrections - // stand down for the whole resize (visible content is held by the list's - // size compensation instead), and the live edge is re-asserted once with - // a single instant write after the resize settles. + // stand down for the whole resize and the visible content is held by the + // list's size compensation instead. Deliberately NO snap back to the end + // afterwards: a slow drag settles repeatedly, and each snap reads as the + // very jump this suspension removes — geometry changed, staying where the + // reader is beats re-asserting the edge. const widthResizingRef = React.useRef(false); React.useEffect(() => { if (!scrollNode || typeof ResizeObserver === 'undefined') return; @@ -532,10 +534,6 @@ export const useChatTimelineScroll = ({ quietTimer = setTimeout(() => { quietTimer = null; widthResizingRef.current = false; - if (modeRef.current !== 'following-end' || !isLiveFollowActive()) return; - const node = listRef.current?.getScrollableNode(); - if (!node) return; - node.scrollTop = node.scrollHeight + 4096; }, 350); }); observer.observe(scrollNode); @@ -543,7 +541,7 @@ export const useChatTimelineScroll = ({ observer.disconnect(); if (quietTimer !== null) clearTimeout(quietTimer); }; - }, [isLiveFollowActive, scrollNode]); + }, [scrollNode]); const onTimelineDataChange = React.useCallback(() => { if (widthResizingRef.current) return;