"use client"; import type { ChatStatus } from "ai"; import { CheckIcon, GraduationCapIcon, LightbulbIcon, PaperclipIcon, PlusIcon, ZapIcon, } from "lucide-react"; import { useSearchParams } from "next/navigation"; import { useCallback, useMemo, useState, type ComponentProps } from "react"; import { PromptInput, PromptInputActionMenu, PromptInputActionMenuContent, PromptInputActionMenuItem, PromptInputActionMenuTrigger, PromptInputAttachment, PromptInputAttachments, PromptInputBody, PromptInputButton, PromptInputFooter, PromptInputSubmit, PromptInputTextarea, PromptInputTools, usePromptInputAttachments, usePromptInputController, type PromptInputMessage, } from "@/components/ai-elements/prompt-input"; import { DropdownMenuGroup, DropdownMenuLabel, DropdownMenuSeparator, } from "@/components/ui/dropdown-menu"; import { useI18n } from "@/core/i18n/hooks"; import { useModels } from "@/core/models/hooks"; import type { AgentThreadContext } from "@/core/threads"; import { cn } from "@/lib/utils"; import { ModelSelector, ModelSelectorContent, ModelSelectorInput, ModelSelectorItem, ModelSelectorList, ModelSelectorName, ModelSelectorTrigger, } from "../ai-elements/model-selector"; import { Suggestion, Suggestions } from "../ai-elements/suggestion"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "../ui/dropdown-menu"; import { Tooltip } from "./tooltip"; export function InputBox({ className, disabled, autoFocus, status = "ready", context, extraHeader, isNewThread, initialValue, onContextChange, onSubmit, onStop, ...props }: Omit, "onSubmit"> & { assistantId?: string | null; status?: ChatStatus; disabled?: boolean; context: Omit< AgentThreadContext, "thread_id" | "is_plan_mode" | "thinking_enabled" > & { mode: "flash" | "thinking" | "pro" | undefined; }; extraHeader?: React.ReactNode; isNewThread?: boolean; initialValue?: string; onContextChange?: ( context: Omit< AgentThreadContext, "thread_id" | "is_plan_mode" | "thinking_enabled" > & { mode: "flash" | "thinking" | "pro" | undefined; }, ) => void; onSubmit?: (message: PromptInputMessage) => void; onStop?: () => void; }) { const { t } = useI18n(); const searchParams = useSearchParams(); const [modelDialogOpen, setModelDialogOpen] = useState(false); const { models } = useModels(); const selectedModel = useMemo(() => { if (!context.model_name && models.length > 0) { const model = models[0]!; setTimeout(() => { onContextChange?.({ ...context, model_name: model.name, mode: model.supports_thinking ? "pro" : "flash", }); }, 0); return model; } return models.find((m) => m.name === context.model_name); }, [context, models, onContextChange]); const supportThinking = useMemo( () => selectedModel?.supports_thinking ?? false, [selectedModel], ); const handleModelSelect = useCallback( (model_name: string) => { onContextChange?.({ ...context, model_name, }); setModelDialogOpen(false); }, [onContextChange, context], ); const handleModeSelect = useCallback( (mode: "flash" | "thinking" | "pro") => { onContextChange?.({ ...context, mode, }); }, [onContextChange, context], ); const handleSubmit = useCallback( async (message: PromptInputMessage) => { if (status === "streaming") { onStop?.(); return; } if (!message.text) { return; } onSubmit?.(message); }, [onSubmit, onStop, status], ); return ( {extraHeader && (
{extraHeader}
)} {(attachment) => } {/* TODO: Add more connectors here */}
{context.mode === "flash" && } {context.mode === "thinking" && ( )} {context.mode === "pro" && ( )}
{(context.mode === "flash" && t.inputBox.flashMode) || (context.mode === "thinking" && t.inputBox.reasoningMode) || (context.mode === "pro" && t.inputBox.proMode)}
{t.inputBox.mode} handleModeSelect("flash")} >
{t.inputBox.flashMode}
{t.inputBox.flashModeDescription}
{context.mode === "flash" ? ( ) : (
)} {supportThinking && ( handleModeSelect("thinking")} >
{t.inputBox.reasoningMode}
{t.inputBox.reasoningModeDescription}
{context.mode === "thinking" ? ( ) : (
)} )} handleModeSelect("pro")} >
{t.inputBox.proMode}
{t.inputBox.proModeDescription}
{context.mode === "pro" ? ( ) : (
)} {selectedModel?.display_name} {models.map((m) => ( handleModelSelect(m.name)} > {m.display_name} {m.name === context.model_name ? ( ) : (
)} ))} {isNewThread && searchParams.get("mode") !== "skill" && (
)} {!isNewThread && (
)} ); } function SuggestionList() { const { t } = useI18n(); const { textInput } = usePromptInputController(); const handleSuggestionClick = useCallback( (prompt: string | undefined) => { if (!prompt) return; textInput.setInput(prompt); setTimeout(() => { const textarea = document.querySelector( "textarea[name='message']", ); if (textarea) { const selStart = prompt.indexOf("["); const selEnd = prompt.indexOf("]"); if (selStart !== -1 && selEnd !== -1) { textarea.setSelectionRange(selStart, selEnd + 1); textarea.focus(); } } }, 500); }, [textInput], ); return ( {t.inputBox.suggestions.map((suggestion) => ( handleSuggestionClick(suggestion.prompt)} /> ))} {t.inputBox.suggestionsCreate.map((suggestion, index) => "type" in suggestion && suggestion.type === "separator" ? ( ) : ( !("type" in suggestion) && ( handleSuggestionClick(suggestion.prompt)} > {suggestion.icon && } {suggestion.suggestion} ) ), )} ); } function AddAttachmentsButton({ className }: { className?: string }) { const { t } = useI18n(); const attachments = usePromptInputAttachments(); return ( attachments.openFileDialog()} > ); }