chat scroll

This commit is contained in:
Nate Kelley 2025-04-10 12:56:03 -06:00
parent ca90b09186
commit 8e4e423f1a
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 70 additions and 18 deletions

View File

@ -1,11 +1,14 @@
'use client';
import React from 'react';
import React, { useEffect, useRef } from 'react';
import { useGetChat, useGetChatMessage } from '@/api/buster_rest/chats';
import { ReasoningMessageSelector } from './ReasoningMessages';
import { BlackBoxMessage } from './ReasoningMessages/ReasoningBlackBoxMessage';
import { FileIndeterminateLoader } from '@/components/features/FileIndeterminateLoader';
import { ScrollArea } from '@/components/ui/scroll-area';
import { useAutoScroll } from '@/hooks/useAutoScroll';
import isEmpty from 'lodash/isEmpty';
import { ReasoningScrollToBottom } from './ReasoningScrollToBottom';
interface ReasoningControllerProps {
chatId: string;
@ -16,24 +19,43 @@ export const ReasoningController: React.FC<ReasoningControllerProps> = ({ chatId
const { data: hasChat } = useGetChat({ id: chatId || '' }, (x) => !!x.id);
const reasoningMessageIds = useGetChatMessage(messageId, (x) => x?.reasoning_message_ids);
const isCompletedStream = useGetChatMessage(messageId, (x) => x?.isCompletedStream);
const viewportRef = useRef<HTMLDivElement>(null);
const { isAutoScrollEnabled, scrollToBottom, enableAutoScroll } = useAutoScroll(viewportRef, {
observeSubTree: true,
enabled: false
});
useEffect(() => {
if (hasChat && reasoningMessageIds) {
enableAutoScroll();
}
}, [hasChat, isEmpty(reasoningMessageIds)]);
if (!hasChat || !reasoningMessageIds) return <FileIndeterminateLoader />;
return (
<ScrollArea>
<div className="h-full flex-col space-y-2 overflow-y-auto p-5">
{reasoningMessageIds?.map((reasoningMessageId) => (
<ReasoningMessageSelector
key={reasoningMessageId}
reasoningMessageId={reasoningMessageId}
isCompletedStream={isCompletedStream ?? true}
chatId={chatId}
messageId={messageId}
/>
))}
<>
<ScrollArea viewportRef={viewportRef}>
<div className="h-full flex-col space-y-2 overflow-y-auto p-5">
{reasoningMessageIds?.map((reasoningMessageId) => (
<ReasoningMessageSelector
key={reasoningMessageId}
reasoningMessageId={reasoningMessageId}
isCompletedStream={isCompletedStream ?? true}
chatId={chatId}
messageId={messageId}
/>
))}
<BlackBoxMessage messageId={messageId} />
</div>
</ScrollArea>
<BlackBoxMessage messageId={messageId} />
</div>
</ScrollArea>
<ReasoningScrollToBottom
isAutoScrollEnabled={isAutoScrollEnabled}
scrollToBottom={scrollToBottom}
/>
</>
);
};

View File

@ -0,0 +1,31 @@
import { ChevronDown } from '@/components/ui/icons';
import { AppTooltip } from '@/components/ui/tooltip';
import { cn } from '@/lib/classMerge';
import React from 'react';
export const ReasoningScrollToBottom: React.FC<{
isAutoScrollEnabled: boolean;
scrollToBottom: () => void;
}> = React.memo(({ isAutoScrollEnabled, scrollToBottom }) => {
return (
<div
className={cn(
'absolute right-4 bottom-4 z-10 duration-300',
isAutoScrollEnabled
? 'pointer-events-none scale-90 opacity-0'
: 'pointer-events-auto scale-100 cursor-pointer opacity-100'
)}>
<AppTooltip title="Stick to bottom" sideOffset={12} delayDuration={500}>
<button
onClick={scrollToBottom}
className={
'bg-background/90 hover:bg-item-hover/90 cursor-pointer rounded-full border p-2 shadow transition-all duration-300 hover:scale-105 hover:shadow-md'
}>
<ChevronDown />
</button>
</AppTooltip>
</div>
);
});
ReasoningScrollToBottom.displayName = 'ReasoningScrollToBottom';

View File

@ -221,7 +221,6 @@ export const useAutoScroll = (
// Only disable autoscroll if we're not near the bottom.
if (!isAtBottom(container, bottomThreshold)) {
setIsAutoScrollEnabled(false);
console.log('disableAutoScrollHandler', isAutoScrollEnabled, enabled);
// Stop any ongoing animations
if (rAFIdRef.current) {

View File

@ -11,7 +11,7 @@ export const ChatScrollToBottom: React.FC<{
return (
<div
className={cn(
'absolute -top-9 right-3 z-10',
'absolute -top-9 right-3 z-10 transition-all duration-300 hover:scale-105 hover:shadow-md',
isAutoScrollEnabled
? 'pointer-events-none scale-90 opacity-0'
: 'pointer-events-auto scale-100 cursor-pointer opacity-100'
@ -20,7 +20,7 @@ export const ChatScrollToBottom: React.FC<{
<button
onClick={scrollToBottom}
className={
'bg-background/90 hover:bg-item-hover/90 cursor-pointer rounded-full border p-2 shadow transition-all duration-300 hover:scale-105 hover:shadow-md'
'bg-background/90 hover:bg-item-hover/90 cursor-pointer rounded-full border p-2 shadow transition-all duration-300'
}>
<ChevronDown />
</button>