Skip to content

Commit a0169fa

Browse files
fix the input scroll
1 parent 4f918f1 commit a0169fa

File tree

1 file changed

+62
-3
lines changed

1 file changed

+62
-3
lines changed

src/components/Chat.tsx

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,57 @@ function Chat() {
7878
const [isLoading, setIsLoading] = useState(false);
7979
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
8080
const messagesEndRef = useRef<HTMLDivElement>(null);
81+
const chatContainerRef = useRef<HTMLDivElement>(null);
82+
const [autoScroll, setAutoScroll] = useState(true);
83+
const isStreamingRef = useRef(false);
84+
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
8185

8286
// Add effect to log model changes
8387
useEffect(() => {
8488
console.log("Model changed to:", selectedModel);
8589
}, [selectedModel]);
8690

91+
// Improved scrollToBottom with debouncing during streaming
8792
const scrollToBottom = () => {
88-
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
93+
if (!autoScroll) return;
94+
95+
// Clear any pending scroll timeout
96+
if (scrollTimeoutRef.current) {
97+
clearTimeout(scrollTimeoutRef.current);
98+
}
99+
100+
// If streaming, debounce the scroll to avoid too many scroll events
101+
if (isStreamingRef.current) {
102+
scrollTimeoutRef.current = setTimeout(() => {
103+
if (messagesEndRef.current) {
104+
messagesEndRef.current.scrollIntoView({ behavior: "auto" });
105+
}
106+
}, 300); // Adjust this timeout as needed
107+
} else {
108+
// For non-streaming updates, scroll immediately
109+
if (messagesEndRef.current) {
110+
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
111+
}
112+
}
89113
};
90114

115+
useEffect(() => {
116+
const chatContainer = chatContainerRef.current;
117+
118+
const handleScroll = () => {
119+
if (!chatContainer) return;
120+
121+
const { scrollTop, scrollHeight, clientHeight } = chatContainer;
122+
// If user has scrolled up more than 100px from bottom, disable auto-scroll
123+
// When they scroll back to bottom, re-enable it
124+
const isNearBottom = scrollHeight - scrollTop - clientHeight < 100;
125+
setAutoScroll(isNearBottom);
126+
};
127+
128+
chatContainer?.addEventListener("scroll", handleScroll);
129+
return () => chatContainer?.removeEventListener("scroll", handleScroll);
130+
}, []);
131+
91132
useEffect(() => {
92133
scrollToBottom();
93134
}, [chatHistory]);
@@ -137,9 +178,17 @@ function Chat() {
137178

138179
setChatHistory((prev) => [...prev, assistantMessage]);
139180

181+
// Set streaming flag to true
182+
isStreamingRef.current = true;
183+
140184
while (true) {
141185
const { done, value } = await reader.read();
142-
if (done) break;
186+
if (done) {
187+
// Final scroll when streaming is complete
188+
isStreamingRef.current = false;
189+
setTimeout(scrollToBottom, 100);
190+
break;
191+
}
143192

144193
const chunk = decoder.decode(value);
145194
assistantMessage.content += chunk;
@@ -149,15 +198,22 @@ function Chat() {
149198
newHistory[newHistory.length - 1] = { ...assistantMessage };
150199
return newHistory;
151200
});
201+
202+
// Only scroll occasionally during streaming, not on every chunk
203+
if (chunk.length > 50) {
204+
scrollToBottom();
205+
}
152206
}
153207
} catch (error) {
154208
console.error("Error:", error);
155209
setChatHistory((prev) => [
156210
...prev,
157211
{ role: "assistant", content: "Error: Could not connect to server" },
158212
]);
213+
isStreamingRef.current = false;
159214
} finally {
160215
setIsLoading(false);
216+
isStreamingRef.current = false;
161217
}
162218
};
163219

@@ -252,7 +308,10 @@ function Chat() {
252308
</button>
253309
</div>
254310

255-
<div className="flex-1 overflow-y-auto scrollbar-custom p-6 space-y-8">
311+
<div
312+
ref={chatContainerRef}
313+
className="flex-1 overflow-y-auto scrollbar-custom p-6 space-y-8"
314+
>
256315
{chatHistory.length === 0 ? (
257316
<div className="text-center text-gray-500 mt-20">
258317
<Welcome />

0 commit comments

Comments
 (0)