This commit is contained in:
Zacharias-Brohn
2026-01-14 21:05:23 +01:00
parent 85ba44bab5
commit 7c4cf97254
2 changed files with 22 additions and 16 deletions
+4
View File
@@ -10,3 +10,7 @@
.chatBubbleAssistant { .chatBubbleAssistant {
border-top-left-radius: 0; border-top-left-radius: 0;
} }
.chatScrollViewport {
scroll-behavior: smooth;
}
+18 -16
View File
@@ -34,6 +34,7 @@ import { getInstalledModels, type OllamaModel } from '@/app/actions/ollama';
import { useThemeContext } from '@/components/DynamicThemeProvider'; import { useThemeContext } from '@/components/DynamicThemeProvider';
import { SettingsModal } from '@/components/Settings/SettingsModal'; import { SettingsModal } from '@/components/Settings/SettingsModal';
import { MarkdownMessage } from './MarkdownMessage'; import { MarkdownMessage } from './MarkdownMessage';
import classes from './ChatLayout.module.css';
interface Message { interface Message {
id: string; id: string;
@@ -96,30 +97,27 @@ export default function ChatLayout() {
// Scroll state // Scroll state
const scrollViewportRef = useRef<HTMLDivElement>(null); const scrollViewportRef = useRef<HTMLDivElement>(null);
const isUserScrolledUp = useRef(false); const isUserScrolledUp = useRef(false);
const isStreaming = useRef(false);
// Check if user is at bottom of scroll area // Handle scroll events to track if user scrolled up (only when not streaming)
const checkIfAtBottom = () => { const handleScroll = () => {
if (isStreaming.current) {
return; // Ignore scroll position checks during streaming
}
const viewport = scrollViewportRef.current; const viewport = scrollViewportRef.current;
if (!viewport) { if (!viewport) {
return true; return;
} }
const threshold = 50; // pixels from bottom to consider "at bottom" const threshold = 50;
return viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight < threshold; isUserScrolledUp.current =
viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight > threshold;
}; };
// Handle scroll events to track if user scrolled up // Scroll to bottom using CSS scroll-behavior for smooth animation
const handleScroll = () => {
isUserScrolledUp.current = !checkIfAtBottom();
};
// Scroll to bottom smoothly
const scrollToBottom = () => { const scrollToBottom = () => {
const viewport = scrollViewportRef.current; const viewport = scrollViewportRef.current;
if (viewport && !isUserScrolledUp.current) { if (viewport && !isUserScrolledUp.current) {
viewport.scrollTo({ viewport.scrollTop = viewport.scrollHeight;
top: viewport.scrollHeight,
behavior: 'smooth',
});
} }
}; };
@@ -130,11 +128,14 @@ export default function ChatLayout() {
} }
}, [messages, streamingMessageId]); }, [messages, streamingMessageId]);
// Scroll to bottom when user sends a message // Track streaming state and scroll to bottom when streaming starts
useEffect(() => { useEffect(() => {
if (streamingMessageId) { if (streamingMessageId) {
isStreaming.current = true;
isUserScrolledUp.current = false; isUserScrolledUp.current = false;
scrollToBottom(); scrollToBottom();
} else {
isStreaming.current = false;
} }
}, [streamingMessageId]); }, [streamingMessageId]);
@@ -433,6 +434,7 @@ export default function ChatLayout() {
offsetScrollbars offsetScrollbars
viewportRef={scrollViewportRef} viewportRef={scrollViewportRef}
onScrollPositionChange={handleScroll} onScrollPositionChange={handleScroll}
classNames={{ viewport: classes.chatScrollViewport }}
> >
<Stack gap="xl" px="md" py="lg"> <Stack gap="xl" px="md" py="lg">
{messages.map((message) => ( {messages.map((message) => (