fix: scroll issue padding not re render correctly (#6639)
This commit is contained in:
parent
75dee86375
commit
f2a3861410
@ -3,7 +3,8 @@ import { useAppState } from './useAppState'
|
|||||||
import { useMessages } from './useMessages'
|
import { useMessages } from './useMessages'
|
||||||
|
|
||||||
const VIEWPORT_PADDING = 40 // Offset from viewport bottom for user message positioning
|
const VIEWPORT_PADDING = 40 // Offset from viewport bottom for user message positioning
|
||||||
const MAX_DOM_RETRY_ATTEMPTS = 3 // Maximum attempts to find DOM elements before giving up
|
const MAX_DOM_RETRY_ATTEMPTS = 5 // Maximum attempts to find DOM elements before giving up
|
||||||
|
const DOM_RETRY_DELAY = 100 // Delay in ms between DOM element retry attempts
|
||||||
|
|
||||||
export const useThreadScrolling = (
|
export const useThreadScrolling = (
|
||||||
threadId: string,
|
threadId: string,
|
||||||
@ -16,6 +17,7 @@ export const useThreadScrolling = (
|
|||||||
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 lastAssistantMessageRef = useRef<HTMLElement | null>(null)
|
||||||
|
|
||||||
const messageCount = useMessages((state) => state.messages[threadId]?.length ?? 0)
|
const messageCount = useMessages((state) => state.messages[threadId]?.length ?? 0)
|
||||||
const lastMessageRole = useMessages((state) => {
|
const lastMessageRole = useMessages((state) => {
|
||||||
@ -33,13 +35,12 @@ export const useThreadScrolling = (
|
|||||||
|
|
||||||
const userMessages = scrollContainer.querySelectorAll('[data-message-author-role="user"]')
|
const userMessages = scrollContainer.querySelectorAll('[data-message-author-role="user"]')
|
||||||
const assistantMessages = scrollContainer.querySelectorAll('[data-message-author-role="assistant"]')
|
const assistantMessages = scrollContainer.querySelectorAll('[data-message-author-role="assistant"]')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scrollContainer,
|
scrollContainer,
|
||||||
lastUserMessage: userMessages[userMessages.length - 1] as HTMLElement,
|
lastUserMessage: userMessages[userMessages.length - 1] as HTMLElement,
|
||||||
lastAssistantMessage: assistantMessages[assistantMessages.length - 1] as HTMLElement,
|
lastAssistantMessage: assistantMessages[assistantMessages.length - 1] as HTMLElement,
|
||||||
}
|
}
|
||||||
}, [])
|
}, [scrollContainerRef])
|
||||||
|
|
||||||
|
|
||||||
const showScrollToBottomBtn = !isAtBottom && hasScrollbar
|
const showScrollToBottomBtn = !isAtBottom && hasScrollbar
|
||||||
@ -121,6 +122,7 @@ export const useThreadScrolling = (
|
|||||||
setPaddingHeight(calculatedPadding)
|
setPaddingHeight(calculatedPadding)
|
||||||
originalPaddingRef.current = calculatedPadding
|
originalPaddingRef.current = calculatedPadding
|
||||||
|
|
||||||
|
// Scroll after padding is applied to the DOM
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
elements.scrollContainer.scrollTo({
|
elements.scrollContainer.scrollTo({
|
||||||
top: elements.scrollContainer.scrollHeight,
|
top: elements.scrollContainer.scrollHeight,
|
||||||
@ -136,11 +138,11 @@ export const useThreadScrolling = (
|
|||||||
calculatePadding()
|
calculatePadding()
|
||||||
} else if (retryCount < MAX_DOM_RETRY_ATTEMPTS) {
|
} else if (retryCount < MAX_DOM_RETRY_ATTEMPTS) {
|
||||||
retryCount++
|
retryCount++
|
||||||
requestAnimationFrame(tryCalculatePadding)
|
setTimeout(tryCalculatePadding, DOM_RETRY_DELAY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(tryCalculatePadding)
|
tryCalculatePadding()
|
||||||
}
|
}
|
||||||
|
|
||||||
prevCountRef.current = messageCount
|
prevCountRef.current = messageCount
|
||||||
@ -150,23 +152,48 @@ export const useThreadScrolling = (
|
|||||||
const previouslyStreaming = wasStreamingRef.current
|
const previouslyStreaming = wasStreamingRef.current
|
||||||
const currentlyStreaming = !!streamingContent && streamingContent.thread_id === threadId
|
const currentlyStreaming = !!streamingContent && streamingContent.thread_id === threadId
|
||||||
|
|
||||||
|
const streamingStarted = !previouslyStreaming && currentlyStreaming
|
||||||
const streamingEnded = previouslyStreaming && !currentlyStreaming
|
const streamingEnded = previouslyStreaming && !currentlyStreaming
|
||||||
const hasPaddingToAdjust = originalPaddingRef.current > 0
|
const hasPaddingToAdjust = originalPaddingRef.current > 0
|
||||||
|
|
||||||
|
// Store the current assistant message when streaming starts
|
||||||
|
if (streamingStarted) {
|
||||||
|
const elements = getDOMElements()
|
||||||
|
lastAssistantMessageRef.current = elements?.lastAssistantMessage || null
|
||||||
|
}
|
||||||
|
|
||||||
if (streamingEnded && hasPaddingToAdjust) {
|
if (streamingEnded && hasPaddingToAdjust) {
|
||||||
requestAnimationFrame(() => {
|
let retryCount = 0
|
||||||
|
|
||||||
|
const adjustPaddingWhenReady = () => {
|
||||||
const elements = getDOMElements()
|
const elements = getDOMElements()
|
||||||
if (!elements?.lastAssistantMessage || !elements?.lastUserMessage) return
|
const currentAssistantMessage = elements?.lastAssistantMessage
|
||||||
|
|
||||||
const userRect = elements.lastUserMessage.getBoundingClientRect()
|
// Check if a new assistant message has appeared (different from the one before streaming)
|
||||||
const assistantRect = elements.lastAssistantMessage.getBoundingClientRect()
|
const hasNewAssistantMessage = currentAssistantMessage &&
|
||||||
const actualSpacing = assistantRect.top - userRect.bottom
|
currentAssistantMessage !== lastAssistantMessageRef.current
|
||||||
const totalAssistantHeight = elements.lastAssistantMessage.offsetHeight + actualSpacing
|
|
||||||
const newPadding = Math.max(0, originalPaddingRef.current - totalAssistantHeight)
|
|
||||||
|
|
||||||
setPaddingHeight(newPadding)
|
if (hasNewAssistantMessage && elements?.lastUserMessage) {
|
||||||
originalPaddingRef.current = newPadding
|
const userRect = elements.lastUserMessage.getBoundingClientRect()
|
||||||
})
|
const assistantRect = currentAssistantMessage.getBoundingClientRect()
|
||||||
|
const actualSpacing = assistantRect.top - userRect.bottom
|
||||||
|
const totalAssistantHeight = currentAssistantMessage.offsetHeight + actualSpacing
|
||||||
|
const newPadding = Math.max(0, originalPaddingRef.current - totalAssistantHeight)
|
||||||
|
|
||||||
|
setPaddingHeight(newPadding)
|
||||||
|
originalPaddingRef.current = newPadding
|
||||||
|
lastAssistantMessageRef.current = currentAssistantMessage
|
||||||
|
} else if (retryCount < MAX_DOM_RETRY_ATTEMPTS) {
|
||||||
|
retryCount++
|
||||||
|
setTimeout(adjustPaddingWhenReady, DOM_RETRY_DELAY)
|
||||||
|
} else {
|
||||||
|
// Max retries hit - remove padding as fallback
|
||||||
|
setPaddingHeight(0)
|
||||||
|
originalPaddingRef.current = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustPaddingWhenReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
wasStreamingRef.current = currentlyStreaming
|
wasStreamingRef.current = currentlyStreaming
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user