chat interface

This commit is contained in:
2026-04-21 14:19:50 +02:00
parent caa9604704
commit 30e3dbb42b
11 changed files with 173 additions and 97 deletions

View File

@@ -9,9 +9,8 @@ import {
} from '~/app/_providers/GsapProvicer';
import Messages from './Messages'
import { DeleteIcon } from 'lucide-react';
import { trpc } from '~/app/_trpc/Client'
import { Spinner } from '~/components/ui/spinner';
import { Skeleton } from '~/components/ui/skeleton';
import { useMessages } from '~/app/_providers/MessagesProvider';
interface DBMessage {
id: string
role: 'user' | 'assistant'
@@ -19,8 +18,8 @@ interface DBMessage {
}
interface ChatInterfaceProps {
sessionId?: string
// initialMessages: DBMessage[]
sessionId: string,
dbMessages: DBMessage[],
}
function toUIMessages(dbMessages: DBMessage[]): UIMessage[] {
@@ -30,28 +29,10 @@ function toUIMessages(dbMessages: DBMessage[]): UIMessage[] {
parts: [{ type: 'text' as const, text: m.content }],
}))
}
export default function ChatInterface({ sessionId }: ChatInterfaceProps) {
if (!sessionId) {
return (
<div className="flex flex-col h-full">
<Skeleton className="w-full"/>
<Skeleton className="w-[20%]"/>
<Skeleton className='w-[45%]'/>
</div>
)
}
const utils = trpc.useUtils();
const { data: dbMessages, refetch: refetchMessages } = trpc.chat.getMessages.useQuery(sessionId)
const [messages, setMessages] = useState<UIMessage[]>([]);
function addMessage(newMessage: UIMessage) {
setMessages(prev => [...prev, newMessage]);
}
useEffect(() => {
setMessages(toUIMessages(dbMessages ?? []));
}, [dbMessages]);
if (messages.at(0)?.id != 'init') {
messages.unshift({
function addInitMessage(messageArray: UIMessage[]) {
if (messageArray.at(0)?.id != 'init') {
messageArray.unshift({
id: "init",
role: 'assistant',
parts: [{
@@ -60,46 +41,31 @@ export default function ChatInterface({ sessionId }: ChatInterfaceProps) {
}],
})
}
}
export default function ChatInterface({ dbMessages, sessionId }: ChatInterfaceProps) {
const [input, setInput] = useState('')
const { sendMessage, status, error, clearError } = useChat({
const { clearingChat, clearChat, refetchMessages } = useMessages();
const initialMessages = toUIMessages(dbMessages)
addInitMessage(initialMessages)
const { messages, sendMessage, status, error, clearError, setMessages } = useChat({
transport: new DefaultChatTransport({
api: '/api/chat', body: { sessionId },
}),
messages: messages,
messages: initialMessages,
})
const handleSend = () => {
const text = input.trim()
if (!text || status != 'ready' || sessionId == undefined) return
setInput('')
sendMessage({ text })
addMessage({
id: "", role: "user", parts: [
{ type: 'text', text }
]
})
addMessage({
id: "", role: "assistant", parts: [
{ type: 'text', text: "Thinking..." }
]
})
}
const clearChatMutation = trpc.chat.clearChat.useMutation()
const handleClear = () => {
clearChatMutation.mutate(undefined, {
onSuccess: () => {
utils.chat.getMessages.invalidate()
refetchMessages()
}
})
}
const gsapContext = useGsapContext()
useEffect(() => {
console.log(status)
if (status == 'ready') {
utils.chat.getMessages.invalidate();
return () => {
refetchMessages()
}
}, [status])
}, [])
const handleSend = () => {
const text = input.trim()
if (!text || status != 'ready' || clearingChat) return
setInput('')
sendMessage({ text })
}
const gsapContext = useGsapContext()
useEffect(() => {
let scroller = gsapContext?.getScroller()
if (scroller instanceof Window) {
@@ -111,9 +77,8 @@ export default function ChatInterface({ sessionId }: ChatInterfaceProps) {
return (
<div className="flex flex-col h-full">
{messages &&
<Messages messages={messages} />
<Messages status={status} messages={messages} />
}
{error && (
<div className="mx-4 mb-2 flex items-start gap-2 rounded-lg border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive">
<span className="flex-1">
@@ -156,10 +121,16 @@ export default function ChatInterface({ sessionId }: ChatInterfaceProps) {
</Button>
<Button
variant='destructive'
onClick={handleClear}
disabled={status != "ready" || clearChatMutation.isPending}
onClick={() => {
clearChat(() => {
let messages: UIMessage[] = [];
addInitMessage(messages);
setMessages(messages)
})
}}
disabled={status != "ready" || clearingChat}
>
{clearChatMutation.isPending ?
{clearingChat ?
<Spinner /> :
"Clear Chat"
}