fix: scroll bottom issue (#4308)

This commit is contained in:
Faisal Amir 2024-12-20 23:39:08 +08:00 committed by GitHub
parent 4e43f9798d
commit af84a3a8d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 47 additions and 27 deletions

View File

@ -378,14 +378,14 @@ const ModelDropdown = ({
!selectedModel && 'text-[hsla(var(--text-tertiary))]' !selectedModel && 'text-[hsla(var(--text-tertiary))]'
)} )}
> >
{selectedModel?.name || 'Select Model'} {selectedModel?.name || 'Select a model'}
</span> </span>
</Badge> </Badge>
) : ( ) : (
<Input <Input
value={selectedModel?.name || ''} value={selectedModel?.name || ''}
className="cursor-pointer" className="cursor-pointer"
placeholder="Select Model" placeholder="Select a model"
disabled={disabled} disabled={disabled}
readOnly readOnly
suffixIcon={ suffixIcon={

View File

@ -23,9 +23,7 @@ const EmptyThread = () => {
<LogoMark className="mx-auto mb-2 animate-wave" width={32} height={32} /> <LogoMark className="mx-auto mb-2 animate-wave" width={32} height={32} />
{showOnboardingStep ? ( {showOnboardingStep ? (
<> <>
<p className="mt-1 font-medium"> <p className="mt-1 font-medium">{`You don't have any model`}</p>
{`You don't have a local model yet.`}
</p>
<Button <Button
onClick={() => setMainViewState(MainViewState.Hub)} onClick={() => setMainViewState(MainViewState.Hub)}
variant="soft" variant="soft"

View File

@ -14,7 +14,11 @@ import LoadModelError from '../LoadModelError'
import EmptyThread from './EmptyThread' import EmptyThread from './EmptyThread'
import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'
import { activeThreadAtom } from '@/helpers/atoms/Thread.atom' import {
activeThreadAtom,
isGeneratingResponseAtom,
threadStatesAtom,
} from '@/helpers/atoms/Thread.atom'
const ChatConfigurator = memo(() => { const ChatConfigurator = memo(() => {
const messages = useAtomValue(getCurrentChatMessagesAtom) const messages = useAtomValue(getCurrentChatMessagesAtom)
@ -61,6 +65,12 @@ const ChatBody = memo(
const prevScrollTop = useRef(0) const prevScrollTop = useRef(0)
const isUserManuallyScrollingUp = useRef(false) const isUserManuallyScrollingUp = useRef(false)
const currentThread = useAtomValue(activeThreadAtom) const currentThread = useAtomValue(activeThreadAtom)
const threadStates = useAtomValue(threadStatesAtom)
const isGeneratingResponse = useAtomValue(isGeneratingResponseAtom)
const isStreamingResponse = Object.values(threadStates).some(
(threadState) => threadState.waitingForResponse
)
const count = useMemo( const count = useMemo(
() => (messages?.length ?? 0) + (loadModelError ? 1 : 0), () => (messages?.length ?? 0) + (loadModelError ? 1 : 0),
@ -76,15 +86,23 @@ const ChatBody = memo(
}) })
useEffect(() => { useEffect(() => {
// Delay the scroll until the DOM is updated if (parentRef.current && isGeneratingResponse) {
requestAnimationFrame(() => {
if (parentRef.current) { if (parentRef.current) {
parentRef.current.scrollTo({ top: parentRef.current.scrollHeight })
}
})
}
}, [count, virtualizer, isGeneratingResponse])
useEffect(() => {
isUserManuallyScrollingUp.current = false
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (parentRef.current) { if (parentRef.current) {
parentRef.current.scrollTo({ top: parentRef.current.scrollHeight }) parentRef.current.scrollTo({ top: parentRef.current.scrollHeight })
virtualizer.scrollToIndex(count - 1) virtualizer.scrollToIndex(count - 1)
} }
}) })
}
}, [count, currentThread?.id, virtualizer]) }, [count, currentThread?.id, virtualizer])
const items = virtualizer.getVirtualItems() const items = virtualizer.getVirtualItems()
@ -94,17 +112,19 @@ const ChatBody = memo(
_, _,
instance instance
) => { ) => {
if (isUserManuallyScrollingUp.current === true) return false if (isUserManuallyScrollingUp.current === true && isStreamingResponse)
return false
return ( return (
// item.start < (instance.scrollOffset ?? 0) && // item.start < (instance.scrollOffset ?? 0) &&
instance.scrollDirection !== 'backward' instance.scrollDirection !== 'backward'
) )
} }
const handleScroll = useCallback((event: React.UIEvent<HTMLElement>) => { const handleScroll = useCallback(
(event: React.UIEvent<HTMLElement>) => {
const currentScrollTop = event.currentTarget.scrollTop const currentScrollTop = event.currentTarget.scrollTop
if (prevScrollTop.current > currentScrollTop) { if (prevScrollTop.current > currentScrollTop && isStreamingResponse) {
isUserManuallyScrollingUp.current = true isUserManuallyScrollingUp.current = true
} else { } else {
const currentScrollTop = event.currentTarget.scrollTop const currentScrollTop = event.currentTarget.scrollTop
@ -121,7 +141,9 @@ const ChatBody = memo(
event.stopPropagation() event.stopPropagation()
} }
prevScrollTop.current = currentScrollTop prevScrollTop.current = currentScrollTop
}, []) },
[isStreamingResponse]
)
return ( return (
<div className="flex h-full w-full flex-col overflow-x-hidden"> <div className="flex h-full w-full flex-col overflow-x-hidden">