Merge pull request #6203 from Louis454545/fix/scroll-jump-during-streaming-5939
fix: prevent scroll position jump when AI completes response while user reading
This commit is contained in:
commit
c3b043eafa
@ -37,6 +37,8 @@ function ThreadDetail() {
|
|||||||
const [isAtBottom, setIsAtBottom] = useState(true)
|
const [isAtBottom, setIsAtBottom] = useState(true)
|
||||||
const [hasScrollbar, setHasScrollbar] = useState(false)
|
const [hasScrollbar, setHasScrollbar] = useState(false)
|
||||||
const lastScrollTopRef = useRef(0)
|
const lastScrollTopRef = useRef(0)
|
||||||
|
const userIntendedPositionRef = useRef<number | null>(null)
|
||||||
|
const wasStreamingRef = useRef(false)
|
||||||
const { currentThreadId, setCurrentThreadId } = useThreads()
|
const { currentThreadId, setCurrentThreadId } = useThreads()
|
||||||
const { setCurrentAssistant, assistants } = useAssistant()
|
const { setCurrentAssistant, assistants } = useAssistant()
|
||||||
const { setMessages, deleteMessage } = useMessages()
|
const { setMessages, deleteMessage } = useMessages()
|
||||||
@ -112,6 +114,8 @@ function ThreadDetail() {
|
|||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
setIsAtBottom(true)
|
setIsAtBottom(true)
|
||||||
setIsUserScrolling(false)
|
setIsUserScrolling(false)
|
||||||
|
userIntendedPositionRef.current = null
|
||||||
|
wasStreamingRef.current = false
|
||||||
checkScrollState()
|
checkScrollState()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -123,11 +127,39 @@ function ThreadDetail() {
|
|||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
setIsAtBottom(true)
|
setIsAtBottom(true)
|
||||||
setIsUserScrolling(false)
|
setIsUserScrolling(false)
|
||||||
|
userIntendedPositionRef.current = null
|
||||||
|
wasStreamingRef.current = false
|
||||||
checkScrollState()
|
checkScrollState()
|
||||||
}, [threadId])
|
}, [threadId])
|
||||||
|
|
||||||
// Single useEffect for all auto-scrolling logic
|
// Single useEffect for all auto-scrolling logic
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Track streaming state changes
|
||||||
|
const isCurrentlyStreaming = !!streamingContent
|
||||||
|
const justFinishedStreaming = wasStreamingRef.current && !isCurrentlyStreaming
|
||||||
|
wasStreamingRef.current = isCurrentlyStreaming
|
||||||
|
|
||||||
|
// If streaming just finished and user had an intended position, restore it
|
||||||
|
if (justFinishedStreaming && userIntendedPositionRef.current !== null) {
|
||||||
|
// Small delay to ensure DOM has updated
|
||||||
|
setTimeout(() => {
|
||||||
|
if (scrollContainerRef.current && userIntendedPositionRef.current !== null) {
|
||||||
|
scrollContainerRef.current.scrollTo({
|
||||||
|
top: userIntendedPositionRef.current,
|
||||||
|
behavior: 'smooth'
|
||||||
|
})
|
||||||
|
userIntendedPositionRef.current = null
|
||||||
|
setIsUserScrolling(false)
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear intended position when streaming starts fresh
|
||||||
|
if (isCurrentlyStreaming && !wasStreamingRef.current) {
|
||||||
|
userIntendedPositionRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
// Only auto-scroll when the user is not actively scrolling
|
// Only auto-scroll when the user is not actively scrolling
|
||||||
// AND either at the bottom OR there's streaming content
|
// AND either at the bottom OR there's streaming content
|
||||||
if (!isUserScrolling && (streamingContent || isAtBottom) && messagesCount) {
|
if (!isUserScrolling && (streamingContent || isAtBottom) && messagesCount) {
|
||||||
@ -163,6 +195,11 @@ function ThreadDetail() {
|
|||||||
// Detect if this is a user-initiated scroll
|
// Detect if this is a user-initiated scroll
|
||||||
if (Math.abs(scrollTop - lastScrollTopRef.current) > 10) {
|
if (Math.abs(scrollTop - lastScrollTopRef.current) > 10) {
|
||||||
setIsUserScrolling(!isBottom)
|
setIsUserScrolling(!isBottom)
|
||||||
|
|
||||||
|
// If user scrolls during streaming and moves away from bottom, record their intended position
|
||||||
|
if (streamingContent && !isBottom) {
|
||||||
|
userIntendedPositionRef.current = scrollTop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setIsAtBottom(isBottom)
|
setIsAtBottom(isBottom)
|
||||||
setHasScrollbar(hasScroll)
|
setHasScrollbar(hasScroll)
|
||||||
@ -180,6 +217,11 @@ function ThreadDetail() {
|
|||||||
// Detect if this is a user-initiated scroll
|
// Detect if this is a user-initiated scroll
|
||||||
if (Math.abs(scrollTop - lastScrollTopRef.current) > 10) {
|
if (Math.abs(scrollTop - lastScrollTopRef.current) > 10) {
|
||||||
setIsUserScrolling(!isBottom)
|
setIsUserScrolling(!isBottom)
|
||||||
|
|
||||||
|
// If user scrolls during streaming and moves away from bottom, record their intended position
|
||||||
|
if (streamingContent && !isBottom) {
|
||||||
|
userIntendedPositionRef.current = scrollTop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setIsAtBottom(isBottom)
|
setIsAtBottom(isBottom)
|
||||||
setHasScrollbar(hasScroll)
|
setHasScrollbar(hasScroll)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user