Spaces:
Runtime error
Runtime error
import clsx from "clsx"; | |
import { useTranslation } from "next-i18next"; | |
import type { ReactNode } from "react"; | |
import React, { useEffect, useRef, useState } from "react"; | |
import { FaArrowCircleDown, FaCommentDots } from "react-icons/fa"; | |
import { ImSpinner2 } from "react-icons/im"; | |
import type { HeaderProps } from "./MacWindowHeader"; | |
import { MacWindowHeader, messageListId } from "./MacWindowHeader"; | |
import { useAgentStore } from "../../stores"; | |
import Button from "../Button"; | |
import Input from "../Input"; | |
import HideShow from "../motions/HideShow"; | |
interface ChatControls { | |
value: string; | |
onChange: (string) => void; | |
handleChat: () => Promise<void>; | |
loading?: boolean; | |
} | |
interface ChatWindowProps extends HeaderProps { | |
children?: ReactNode; | |
setAgentRun?: (name: string, goal: string) => void; | |
visibleOnMobile?: boolean; | |
chatControls?: ChatControls; | |
} | |
const ChatWindow = ({ messages, children, title, chatControls }: ChatWindowProps) => { | |
const [t] = useTranslation(); | |
const [hasUserScrolled, setHasUserScrolled] = useState(false); | |
const isThinking = useAgentStore.use.isAgentThinking(); | |
const isStopped = useAgentStore.use.lifecycle() === "stopped"; | |
const scrollRef = useRef<HTMLDivElement>(null); | |
const handleScroll = (event: React.UIEvent<HTMLDivElement>) => { | |
const { scrollTop, scrollHeight, clientHeight } = event.currentTarget; | |
// Use has scrolled if we have scrolled up at all from the bottom | |
const hasUserScrolled = scrollTop < scrollHeight - clientHeight - 10; | |
setHasUserScrolled(hasUserScrolled); | |
}; | |
const handleScrollToBottom = (behaviour: "instant" | "smooth") => { | |
if (!scrollRef || !scrollRef.current) return; | |
scrollRef.current.scrollTo({ | |
top: scrollRef.current.scrollHeight, | |
behavior: behaviour, | |
}); | |
}; | |
useEffect(() => { | |
if (!hasUserScrolled) { | |
handleScrollToBottom("instant"); | |
} | |
}); | |
return ( | |
<div | |
className={clsx( | |
"flex h-full w-full max-w-[inherit] flex-1 flex-col overflow-auto text-slate-12 transition-all duration-500" | |
)} | |
> | |
<HideShow | |
showComponent={hasUserScrolled} | |
className="absolute bottom-11 right-6 z-40 cursor-pointer sm:bottom-14" | |
> | |
<FaArrowCircleDown | |
onClick={() => handleScrollToBottom("smooth")} | |
className="h-6 w-6 animate-bounce md:h-7 md:w-7" | |
/> | |
</HideShow> | |
<MacWindowHeader title={title} messages={messages} /> | |
<div | |
className="mb-2 mr-2 flex-1 overflow-auto transition-all duration-500" | |
ref={scrollRef} | |
onScroll={handleScroll} | |
id={messageListId} | |
> | |
{children} | |
<div | |
className={clsx( | |
isThinking && !isStopped ? "opacity-100" : "opacity-0", | |
"mr-2 flex flex-row items-center gap-2 p-2 transition duration-300 sm:mr-4", | |
"text-xs sm:text-base" | |
)} | |
> | |
<p>🧠 Thinking</p> | |
<ImSpinner2 className="animate-spin" /> | |
</div> | |
</div> | |
{chatControls && ( | |
<div className="mt-auto flex flex-row gap-2 p-2 pt-0 sm:p-4"> | |
<Input | |
small | |
placeholder="Chat with your agent..." | |
value={chatControls.value} | |
onChange={(e) => chatControls?.onChange(e.target.value)} | |
/> | |
<Button | |
className="px-1 py-1 sm:px-3 md:py-1" | |
onClick={chatControls?.handleChat} | |
disabled={chatControls.loading} | |
> | |
<FaCommentDots /> | |
</Button> | |
</div> | |
)} | |
</div> | |
); | |
}; | |
export default ChatWindow; | |