88 lines
3.0 KiB
TypeScript
88 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { MessageCircle } from "lucide-react";
|
|
|
|
import { ChatMessageList, ChatComposer, type ChatBubbleMessage } from "@/components/ai/chat-thread";
|
|
import { colorModeFromTheme } from "@/components/theme/use-dark-theme";
|
|
import { useSpeechRecognition } from "@/hooks/use-speech-recognition";
|
|
import { sendChatMessage, type ChatMessage } from "@/lib/actions/chat-ai";
|
|
|
|
export function ChatPageClient({ aiConfigured }: { aiConfigured: boolean }) {
|
|
const colorMode = colorModeFromTheme();
|
|
|
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
|
const [input, setInput] = useState("");
|
|
const [sending, setSending] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const { supported: micSupported, listening, start, stop } = useSpeechRecognition({
|
|
onFinalResult: (transcript) =>
|
|
setInput((prev) => (prev ? `${prev} ${transcript}` : transcript)),
|
|
onError: setError,
|
|
});
|
|
|
|
async function handleSend() {
|
|
const text = input.trim();
|
|
if (!text || sending) return;
|
|
if (listening) stop();
|
|
|
|
const next: ChatMessage[] = [...messages, { role: "user", content: text }];
|
|
setMessages(next);
|
|
setInput("");
|
|
setError(null);
|
|
setSending(true);
|
|
const result = await sendChatMessage(next);
|
|
setSending(false);
|
|
|
|
if ("error" in result) {
|
|
setError(result.error);
|
|
return;
|
|
}
|
|
setMessages([...next, { role: "assistant", content: result.content }]);
|
|
}
|
|
|
|
return (
|
|
<div className="mx-auto flex h-full max-w-2xl flex-col gap-4 p-4">
|
|
<div className="flex items-center gap-3 px-1">
|
|
<span className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary">
|
|
<MessageCircle className="size-5" />
|
|
</span>
|
|
<div className="min-w-0">
|
|
<h1 className="truncate font-heading text-xl font-bold tracking-tight">Chat</h1>
|
|
<p className="truncate text-[13px] text-muted-foreground">
|
|
Ask about anything across your boards — notes, open to-dos, and things you've
|
|
already finished.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{!aiConfigured ? (
|
|
<p className="rounded-md border border-dashed p-8 text-center text-sm text-muted-foreground">
|
|
AI generation isn't configured yet. Ask an administrator to set it up.
|
|
</p>
|
|
) : (
|
|
<div className="flex flex-1 flex-col gap-3 overflow-hidden">
|
|
<ChatMessageList
|
|
messages={messages as ChatBubbleMessage[]}
|
|
sending={sending}
|
|
error={error}
|
|
emptyHint="Ask a question about your projects, groups, notes, or to-dos — done or still open."
|
|
colorMode={colorMode}
|
|
/>
|
|
<ChatComposer
|
|
value={input}
|
|
onChange={setInput}
|
|
onSend={handleSend}
|
|
sending={sending}
|
|
listening={listening}
|
|
micSupported={micSupported}
|
|
onStartListening={start}
|
|
onStopListening={stop}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|