"use client" import { BrainIcon, ChevronUpIcon, ZapIcon } from "lucide-react" import NovaOrb from "@/components/nova/nova-orb" import { cn } from "@lib/utils" import { dmSansClassName } from "@/lib/fonts" import { type ReactNode, useEffect, useRef, useState } from "react" import { AnimatePresence, motion } from "motion/react" import { SendButton, StopButton } from "./actions" import { type ModelId, modelNames, type ReasoningEffort } from "@/lib/models" export interface QueuedChatMessagePreview { id: string text: string model: ModelId reasoningEffort: ReasoningEffort } interface ChatInputProps { value: string onChange: (e: React.ChangeEvent) => void onSend: () => void onStop: () => void onKeyDown?: (e: React.KeyboardEvent) => void isResponding?: boolean sendDisabled?: boolean sendDisabledTooltip?: string activeStatus?: string queuedMessages?: QueuedChatMessagePreview[] chainOfThoughtComponent?: React.ReactNode onExpandedChange?: (expanded: boolean) => void /** Model + space controls on one row with send; textarea full-width above */ stackedToolbar?: ReactNode /** Nova status row + chain-of-thought toggle (off for e.g. home composer) */ showStatusStrip?: boolean } export default function ChatInput({ value, onChange, onSend, onStop, onKeyDown, isResponding = false, sendDisabled = false, sendDisabledTooltip, activeStatus, queuedMessages = [], chainOfThoughtComponent, onExpandedChange, stackedToolbar, showStatusStrip = true, }: ChatInputProps) { const [isMultiline, setIsMultiline] = useState(false) const [isExpanded, setIsExpanded] = useState(false) const textareaRef = useRef(null) const isSendDisabled = !value.trim() || sendDisabled const hasQueuedPreview = queuedMessages.length > 0 const resolvedSendDisabledTooltip = sendDisabled && value.trim() ? sendDisabledTooltip : "Type a message to send" useEffect(() => { if (!showStatusStrip && isExpanded) { setIsExpanded(false) onExpandedChange?.(false) } }, [isExpanded, onExpandedChange, showStatusStrip]) const handleChange = (e: React.ChangeEvent) => { onChange(e) const textarea = e.target textarea.style.height = "auto" // Set height based on scrollHeight, with a max of ~96px (4-5 lines) const newHeight = Math.min(textarea.scrollHeight, 96) textarea.style.height = `${newHeight}px` setIsMultiline(textarea.scrollHeight > 52) } return ( {showStatusStrip ? ( <>
{chainOfThoughtComponent}
{hasQueuedPreview && (
{queuedMessages.map((queued) => { const model = modelNames[queued.model] const ReasoningIcon = queued.reasoningEffort === "thinking" ? BrainIcon : ZapIcon return (
{queued.text} {model.name} {model.version} ยท
) })}
)} ) : null} {stackedToolbar ? (