import { useCallback, useEffect, useRef, useState } from "react"; import { ArrowUp } from "lucide-react"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; interface ComposerProps { onSend: (content: string) => void; disabled?: boolean; placeholder?: string; /** Visually collapse the outer padding when embedded inside a welcome screen. */ compact?: boolean; } /** * Rounded, shadowed composer with an embedded send button — modeled after the * agent-chat-ui input: a single surface that looks like one interactive unit * rather than a textarea + button pair. */ export function Composer({ onSend, disabled, placeholder = "Type your message…", compact = false, }: ComposerProps) { const [value, setValue] = useState(""); const textareaRef = useRef(null); // Autofocus on mount — coming back to a chat, switching sessions, or // opening the welcome screen should always land the caret in the box. useEffect(() => { if (disabled) return; const el = textareaRef.current; if (!el) return; // Defer so layout settles first (important during enter animations). const id = requestAnimationFrame(() => el.focus()); return () => cancelAnimationFrame(id); }, [disabled]); const submit = useCallback(() => { const trimmed = value.trim(); if (!trimmed || disabled) return; onSend(trimmed); setValue(""); requestAnimationFrame(() => { const el = textareaRef.current; if (el) { el.style.height = "auto"; el.focus(); } }); }, [disabled, onSend, value]); const onKeyDown: React.KeyboardEventHandler = (e) => { if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) { e.preventDefault(); submit(); } }; const onInput: React.FormEventHandler = (e) => { const el = e.currentTarget; el.style.height = "auto"; el.style.height = `${Math.min(el.scrollHeight, 260)}px`; }; return (
{ e.preventDefault(); submit(); }} className={cn( "w-full", compact ? "px-0" : "bg-background/95 px-4 pb-4 pt-2 backdrop-blur", )} >