fixed scroll bug

This commit is contained in:
Steve Androulakis
2025-01-05 09:44:12 -08:00
parent bde21f2a3a
commit 1e9f3b4111
2 changed files with 58 additions and 55 deletions

View File

@@ -35,11 +35,8 @@ export default function LLMResponse({ data, onConfirm, isLastMessage }) {
{!requiresConfirm && data.tool && data.next === "confirm" && ( {!requiresConfirm && data.tool && data.next === "confirm" && (
<div className="text-sm text-center text-green-600 dark:text-green-400"> <div className="text-sm text-center text-green-600 dark:text-green-400">
<div> <div>
Agent ran tool: <strong>{data.tool ?? "Unknown"}</strong> Agent chose tool: <strong>{data.tool ?? "Unknown"}</strong>
</div> </div>
{/* <div>
{JSON.stringify(data, null, 2)}
</div> */}
</div> </div>
)} )}
</div> </div>

View File

@@ -8,9 +8,10 @@ export default function App() {
const containerRef = useRef(null); const containerRef = useRef(null);
const inputRef = useRef(null); const inputRef = useRef(null);
const [conversation, setConversation] = useState([]); const [conversation, setConversation] = useState([]);
const [lastMessage, setLastMessage] = useState(null); // New state for tracking the last message
const [userInput, setUserInput] = useState(""); const [userInput, setUserInput] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [done, setDone] = useState(true); // New `done` state const [done, setDone] = useState(true);
useEffect(() => { useEffect(() => {
// Poll /get-conversation-history every 0.5 seconds // Poll /get-conversation-history every 0.5 seconds
@@ -19,16 +20,22 @@ export default function App() {
const res = await fetch("http://127.0.0.1:8000/get-conversation-history"); const res = await fetch("http://127.0.0.1:8000/get-conversation-history");
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
// Update conversation const newConversation = data.messages || [];
setConversation(data.messages || []); setConversation(newConversation);
if (data.messages && data.messages.length > 0) { if (newConversation.length > 0) {
const lastMessage = data.messages[data.messages.length - 1]; const lastMsg = newConversation[newConversation.length - 1];
setLoading(lastMessage.actor !== "agent"); setLoading(lastMsg.actor !== "agent");
setDone(lastMessage.response.next === "done"); setDone(lastMsg.response.next === "done");
// Only scroll if the last message changes
if (!lastMessage || lastMsg.response.response !== lastMessage.response.response) {
setLastMessage(lastMsg); // Update the last message
}
} else { } else {
setLoading(false); setLoading(false);
setDone(true); // Default to `done` if no messages setDone(true);
setLastMessage(null); // Clear last message if no messages
} }
} }
} catch (err) { } catch (err) {
@@ -37,12 +44,24 @@ export default function App() {
}, POLL_INTERVAL); }, POLL_INTERVAL);
return () => clearInterval(intervalId); return () => clearInterval(intervalId);
}, []); }, [lastMessage]);
useEffect(() => {
if (containerRef.current && lastMessage) {
containerRef.current.scrollTop = containerRef.current.scrollHeight;
}
}, [lastMessage]); // Scroll only when the last message changes
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus(); // Ensure the input box retains focus
}
}, [userInput, loading, done]);
const handleSendMessage = async () => { const handleSendMessage = async () => {
if (!userInput.trim()) return; if (!userInput.trim()) return;
try { try {
setLoading(true); // Mark as loading setLoading(true);
await fetch( await fetch(
`http://127.0.0.1:8000/send-prompt?prompt=${encodeURIComponent(userInput)}`, `http://127.0.0.1:8000/send-prompt?prompt=${encodeURIComponent(userInput)}`,
{ method: "POST" } { method: "POST" }
@@ -71,6 +90,7 @@ export default function App() {
{ method: "POST" } { method: "POST" }
); );
setConversation([]); // Clear local state setConversation([]); // Clear local state
setLastMessage(null); // Reset last message
} catch (err) { } catch (err) {
console.error("Error ending chat:", err); console.error("Error ending chat:", err);
} }
@@ -82,58 +102,44 @@ export default function App() {
} }
}; };
useEffect(() => {
if (containerRef.current) {
containerRef.current.scrollTop = containerRef.current.scrollHeight;
}
}, [conversation, loading, done]);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus(); // Ensure the input box retains focus
}
}, [userInput, loading, done]); // Add other dependencies if necessary
return ( return (
<div className="flex flex-col h-screen"> <div className="flex flex-col h-screen">
<NavBar title="Temporal AI Agent" /> <NavBar title="Temporal AI Agent" />
{/* Centered content, but no manual bottom margin */} {/* Centered content, but no manual bottom margin */}
<div className="flex-grow flex justify-center px-4 py-2 overflow-hidden"> <div className="flex-grow flex justify-center px-4 py-2 overflow-hidden">
<div className="w-full max-w-lg bg-white dark:bg-gray-900 p-8 px-3 rounded shadow-md <div className="w-full max-w-lg bg-white dark:bg-gray-900 p-8 px-3 rounded shadow-md
flex flex-col overflow-hidden"> flex flex-col overflow-hidden">
{/* Scrollable chat area */} {/* Scrollable chat area */}
<div ref={containerRef} className="flex-grow overflow-y-auto pb-20 pt-10"> <div ref={containerRef} className="flex-grow overflow-y-auto pb-20 pt-10">
<ChatWindow <ChatWindow
conversation={conversation} conversation={conversation}
loading={loading} loading={loading}
onConfirm={handleConfirm} onConfirm={handleConfirm}
/> />
{done && ( {done && (
<div className="text-center text-sm text-gray-500 dark:text-gray-400 mt-4"> <div className="text-center text-sm text-gray-500 dark:text-gray-400 mt-4">
Chat ended Chat ended
</div>
)}
</div>
</div>
</div> </div>
)}
</div>
</div>
</div>
{/* Floating Input Section */} {/* Fixed bottom input */}
{/* Fixed bottom input */} <div
<div className="fixed bottom-0 left-1/2 transform -translate-x-1/2
className="fixed bottom-0 left-1/2 transform -translate-x-1/2 w-full max-w-lg bg-white dark:bg-gray-900 p-4
w-full max-w-lg bg-white dark:bg-gray-900 p-4 border-t border-gray-300 dark:border-gray-700"
border-t border-gray-300 dark:border-gray-700" style={{ zIndex: 10 }}
style={{ zIndex: 10 }} >
>
<div className="flex items-center"> <div className="flex items-center">
<input <input
ref={inputRef} ref={inputRef}
type="text" type="text"
className={`flex-grow rounded-l px-3 py-2 border border-gray-300 className={`flex-grow rounded-l px-3 py-2 border border-gray-300
dark:bg-gray-700 dark:border-gray-600 focus:outline-none dark:bg-gray-700 dark:border-gray-600 focus:outline-none
${loading || done ? "opacity-50 cursor-not-allowed" : ""}`} ${loading || done ? "opacity-50 cursor-not-allowed" : ""}`}
placeholder="Type your message..." placeholder="Type your message..."
value={userInput} value={userInput}
onChange={(e) => setUserInput(e.target.value)} onChange={(e) => setUserInput(e.target.value)}
@@ -143,7 +149,7 @@ export default function App() {
<button <button
onClick={handleSendMessage} onClick={handleSendMessage}
className={`bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-r className={`bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-r
${loading || done ? "opacity-50 cursor-not-allowed" : ""}`} ${loading || done ? "opacity-50 cursor-not-allowed" : ""}`}
disabled={loading || done} disabled={loading || done}
> >
Send Send
@@ -153,7 +159,7 @@ export default function App() {
<button <button
onClick={handleStartNewChat} onClick={handleStartNewChat}
className={`text-sm underline text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 className={`text-sm underline text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200
${!done ? "opacity-0 cursor-not-allowed" : ""}`} ${!done ? "opacity-0 cursor-not-allowed" : ""}`}
disabled={!done} disabled={!done}
> >
Start New Chat Start New Chat
@@ -162,4 +168,4 @@ export default function App() {
</div> </div>
</div> </div>
); );
} }