import { useState, useRef, useEffect, useMemo } from "react" import { ChevronDown } from "lucide-react" const AVAILABLE_MODELS = [ { id: "deepseek/deepseek-chat-v3-0324", name: "DeepSeek V3", provider: "DeepSeek", }, { id: "google/gemini-2.0-flash-001", name: "Gemini 2.0 Flash", provider: "Google", }, { id: "anthropic/claude-sonnet-4", name: "Claude Sonnet 4", provider: "Anthropic", }, { id: "openai/gpt-4o", name: "GPT-4o", provider: "OpenAI" }, ] as const export type ModelId = (typeof AVAILABLE_MODELS)[number]["id"] function ModelSparkle() { return ( ) } interface ModelSelectProps { selectedModel: ModelId onChange: (model: ModelId) => void } function ModelSelect({ selectedModel, onChange }: ModelSelectProps) { const [open, setOpen] = useState(false) const containerRef = useRef(null) const selected = useMemo( () => AVAILABLE_MODELS.find((m) => m.id === selectedModel), [selectedModel], ) useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if ( containerRef.current && !containerRef.current.contains(e.target as Node) ) { setOpen(false) } } const handleEscape = (e: KeyboardEvent) => { if (e.key === "Escape") setOpen(false) } window.addEventListener("mousedown", handleClickOutside) window.addEventListener("keydown", handleEscape) return () => { window.removeEventListener("mousedown", handleClickOutside) window.removeEventListener("keydown", handleEscape) } }, []) return ( setOpen((prev) => !prev)} className="flex items-center gap-2 rounded-xl border border-white/8 bg-linear-to-b from-[#2d2e39] via-[#1e1f28] to-[#1a1b24] px-3 py-1.5 text-left shadow-inner shadow-black/40 hover:border-white/14 transition-colors min-w-[170px]" > {selected?.name ?? "Choose model"} {open && ( Models {AVAILABLE_MODELS.map((model) => { const isActive = model.id === selectedModel return ( { onChange(model.id) setOpen(false) }} className={`flex items-center hover:bg-white/2 rounded-lg gap-3 px-3 py-1 text-sm transition-colors ${isActive ? "text-white" : "text-white/65 hover:text-white"}`} > {model.name} ) })} )} ) } interface ChatInputProps { onSubmit: (message: string) => void isLoading: boolean selectedModel: ModelId onModelChange: (model: ModelId) => void limitReached?: boolean remainingRequests?: number } export function ChatInput({ onSubmit, isLoading, selectedModel, onModelChange, limitReached = false, remainingRequests, }: ChatInputProps) { const [message, setMessage] = useState("") const textareaRef = useRef(null) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (message.trim() && !isLoading && !limitReached) { onSubmit(message) setMessage("") textareaRef.current?.focus() } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault() handleSubmit(e) } } const isDisabled = isLoading || limitReached const sendDisabled = !message.trim() || isDisabled return ( {limitReached && ( Sign in to continue chatting Get more requests with a free account Sign in )} setMessage(e.target.value)} onKeyDown={handleKeyDown} placeholder="Ask anything..." className="w-full max-h-32 min-h-[24px] resize-none overflow-y-auto bg-transparent text-[15px] text-neutral-100 placeholder-neutral-500 focus:outline-none disabled:opacity-60 scrollbar-hide [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]" rows={3} disabled={isDisabled} /> {limitReached && ( )} {typeof remainingRequests === "number" && ( {remainingRequests} requests remaining )} ) } export { AVAILABLE_MODELS }