Skip to content

Commit 4e76d44

Browse files
authored
Implemented smart auto-scroll in chat (#143)
1 parent 3c4929d commit 4e76d44

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

plexe/ui/index.html

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,51 +270,74 @@
270270
const [messages, setMessages] = useState([]);
271271
const [input, setInput] = useState('');
272272
const [isLoading, setIsLoading] = useState(false);
273+
const [userScrolledUp, setUserScrolledUp] = useState(false);
274+
const [showScrollButton, setShowScrollButton] = useState(false);
273275
const messagesEndRef = useRef(null);
276+
const messagesContainerRef = useRef(null);
274277

275278
const scrollToBottom = () => {
276279
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
280+
setShowScrollButton(false);
281+
};
282+
283+
const handleScroll = (e) => {
284+
const { scrollTop, scrollHeight, clientHeight } = e.target;
285+
const isAtBottom = scrollHeight - scrollTop <= clientHeight + 10;
286+
setUserScrolledUp(!isAtBottom);
277287
};
278288

279289
useEffect(() => {
280-
scrollToBottom();
281-
}, [messages]);
290+
if (messages.length > 0) {
291+
const lastMessage = messages[messages.length - 1];
292+
if (lastMessage.role === 'user') {
293+
// User sent message - always scroll to bottom
294+
scrollToBottom();
295+
setShowScrollButton(false);
296+
} else {
297+
// AI sent message - check if user scrolled up
298+
if (userScrolledUp) {
299+
setShowScrollButton(true);
300+
} else {
301+
scrollToBottom();
302+
}
303+
}
304+
}
305+
}, [messages, userScrolledUp]);
282306

283307
useEffect(() => {
284308
const unsubscribe = wsRuntime.subscribe((data) => {
285309
if (data.type === 'status') return;
286-
287310
setMessages(prev => [...prev, {
288311
id: data.id || Date.now().toString(),
289312
role: data.role || 'assistant',
290313
content: data.content
291314
}]);
292315
setIsLoading(false);
293316
});
294-
295317
return unsubscribe;
296318
}, [wsRuntime]);
297319

298320
const handleSubmit = (e) => {
299321
e.preventDefault();
300322
if (!input.trim() || isLoading) return;
301-
302323
const userMessage = {
303324
id: Date.now().toString(),
304325
role: 'user',
305326
content: input.trim()
306327
};
307-
308328
setMessages(prev => [...prev, userMessage]);
309329
setInput('');
310330
setIsLoading(true);
311-
312331
wsRuntime.send(userMessage.content);
313332
};
314333

315334
return React.createElement('div', { className: 'flex flex-col h-full' },
316335
// Messages area
317-
React.createElement('div', { className: 'flex-1 overflow-y-auto p-4' },
336+
React.createElement('div', {
337+
className: 'flex-1 overflow-y-auto p-4 relative',
338+
onScroll: handleScroll,
339+
ref: messagesContainerRef
340+
},
318341
messages.length === 0 && React.createElement('div', { className: 'text-center text-gray-500 mt-8' },
319342
React.createElement('p', { className: 'text-lg mb-2' }, '👋 Hello! I\'m your Plexe Assistant.'),
320343
React.createElement('p', { className: 'text-sm' }, 'I help you build machine learning models through natural conversation.'),
@@ -332,6 +355,11 @@
332355
),
333356
React.createElement('div', { ref: messagesEndRef })
334357
),
358+
// Scroll to bottom button
359+
showScrollButton && React.createElement('button', {
360+
onClick: scrollToBottom,
361+
className: 'fixed bottom-20 right-4 bg-blue-500 text-white rounded-full p-3 shadow-lg hover:bg-blue-600 transition-colors z-10'
362+
}, '↓'),
335363
// Input area
336364
React.createElement('form', { onSubmit: handleSubmit, className: 'border-t p-4' },
337365
React.createElement('div', { className: 'flex space-x-2' },

0 commit comments

Comments
 (0)