"use client"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Mic, Send, Square } from "lucide-react"; import { MarkdownPreview } from "@/components/markdown/markdown-widgets"; export interface ChatBubbleMessage { role: "user" | "assistant"; content: string; } /** * Shared message-bubble list for the two plain-text chat surfaces (the * global Chat page and the group Status Update follow-up Q&A) -- both * just render a back-and-forth conversation with no ask/finalize review * step, unlike GroupAiDialog/TodoAiDialog (which inline their own nearly * identical list because they also render an editable "pending" state * this shared component doesn't need to know about). */ export function ChatMessageList({ messages, sending, error, emptyHint, colorMode, }: { messages: ChatBubbleMessage[]; sending: boolean; error: string | null; emptyHint: string; colorMode?: "light" | "dark"; }) { return (
{messages.length === 0 ? (

{emptyHint}

) : ( messages.map((message, index) => (
{message.role === "assistant" ? ( ) : ( message.content )}
)) )} {sending && (
Thinking…
)} {error &&

{error}

}
); } /** * Shared textarea + mic + send composer, matching the input row already * used by GroupAiDialog/TodoAiDialog (voice input via * useSpeechRecognition, Enter-to-send, Shift+Enter for a newline). */ export function ChatComposer({ value, onChange, onSend, sending, listening, micSupported, onStartListening, onStopListening, placeholder, }: { value: string; onChange: (value: string) => void; onSend: () => void; sending: boolean; listening: boolean; micSupported: boolean; onStartListening: () => void; onStopListening: () => void; placeholder?: string; }) { return (