'use client'; import { useState, useRef, useEffect } from 'react'; import { Paperclip, ArrowUp, Upload } from 'lucide-react'; import { ModelSelector } from './ModelSelector'; import { ToolsDropdown } from './ToolsDropdown'; import { FilePreviewList } from './FilePreviewList'; import { QuickPhrasesTrigger, QuickPhrasesPopover } from './QuickPhrasesPopover'; import { QuickPhrasesModal } from './QuickPhrasesModal'; import { Tooltip } from '@/components/ui/Tooltip'; import { useFileUpload } from '@/hooks/useFileUpload'; import { usePromptOptimizer } from '@/providers/PromptOptimizerProvider'; import { useQuickPhrases } from '@/hooks/useQuickPhrases'; import { cn } from '@/lib/utils'; import type { Model, Tool, QuickPhrase } from '@/types'; import type { UploadFile } from '@/types/file-upload'; interface ChatInputProps { models: Model[]; selectedModel: Model; onModelSelect: (model: Model) => void; tools: Tool[]; onToolToggle: (toolId: string) => void; onEnableAllTools: (enabled: boolean) => void; onSend: (message: string, files?: UploadFile[]) => void; placeholder?: string; className?: string; } export function ChatInput({ models, selectedModel, onModelSelect, tools, onToolToggle, onEnableAllTools, onSend, placeholder = 'How can I help you today?', className, }: ChatInputProps) { const [message, setMessage] = useState(''); const [isManageModalOpen, setIsManageModalOpen] = useState(false); const [isPhrasesOpen, setIsPhrasesOpen] = useState(false); const fileInputRef = useRef(null); const phrasesContainerRef = useRef(null); // 使用提示词优化 Hook const { consumeOptimizedPrompt } = usePromptOptimizer(); // 使用快捷短语 Hook const { phrases, addPhrase, updatePhrase, deletePhrase, } = useQuickPhrases(); // 监听优化后的提示词并填入输入框 useEffect(() => { const prompt = consumeOptimizedPrompt(); if (prompt) { setMessage(prompt); } }, [consumeOptimizedPrompt]); // 点击外部关闭快捷短语弹出层 useEffect(() => { function handleClickOutside(event: MouseEvent) { if ( phrasesContainerRef.current && !phrasesContainerRef.current.contains(event.target as Node) ) { setIsPhrasesOpen(false); } } if (isPhrasesOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => document.removeEventListener('mousedown', handleClickOutside); }, [isPhrasesOpen]); // ESC键关闭快捷短语弹出层 useEffect(() => { function handleEscKey(event: KeyboardEvent) { if (event.key === 'Escape' && isPhrasesOpen) { setIsPhrasesOpen(false); } } if (isPhrasesOpen) { document.addEventListener('keydown', handleEscKey); } return () => document.removeEventListener('keydown', handleEscKey); }, [isPhrasesOpen]); // 使用文件上传 Hook const { files, isDragging, addFiles, removeFile, clearFiles, handleDragEnter, handleDragLeave, handleDragOver, handleDrop, handlePaste, } = useFileUpload(); const handleSend = () => { if (message.trim() || files.length > 0) { onSend(message, files.length > 0 ? files : undefined); setMessage(''); clearFiles(); } }; const handleKeyDown = (e: React.KeyboardEvent) => { // 检查是否正在使用输入法组合(如中文输入法选词时按回车) // isComposing 为 true 时,表示用户正在用输入法选词,此时不应发送消息 if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { e.preventDefault(); handleSend(); } }; // 处理文件选择 const handleFileSelect = (e: React.ChangeEvent) => { const selectedFiles = e.target.files; if (selectedFiles && selectedFiles.length > 0) { addFiles(selectedFiles); } // 重置 input 以允许重复选择同一文件 if (fileInputRef.current) { fileInputRef.current.value = ''; } }; // 打开文件选择对话框 const openFileDialog = () => { fileInputRef.current?.click(); }; // 插入快捷短语内容 const handleInsertPhrase = (content: string) => { setMessage((prev) => { // 如果已有内容,在末尾添加空格 if (prev.trim()) { return prev + ' ' + content; } return content; }); }; // 打开管理模态框 const handleOpenManageModal = () => { setIsManageModalOpen(true); }; // 关闭管理模态框 const handleCloseManageModal = () => { setIsManageModalOpen(false); }; // 处理添加短语(从管理模态框) const handleAddFromModal = () => { handleOpenManageModal(); }; return (
{/* 快捷短语弹出层 - 横跨在输入框顶部 */} { setIsManageModalOpen(true); }} onDelete={deletePhrase} onManage={handleOpenManageModal} onAdd={handleAddFromModal} onClose={() => setIsPhrasesOpen(false)} /> {/* 拖拽覆盖层 */} {isDragging && (

释放以添加文件

支持图片、PDF、文档等格式

)} {/* 文件预览区域 */} {files.length > 0 && (
)} {/* 第一行:输入区域 */}
setMessage(e.target.value)} onKeyDown={handleKeyDown} onPaste={handlePaste} placeholder={files.length > 0 ? '添加描述(可选)...' : placeholder} className="w-full border-none outline-none text-[var(--color-text-primary)] bg-transparent py-2 placeholder:text-[var(--color-text-placeholder)]" />
{/* 第二行:功能按钮区域 */}
{/* 左侧按钮 */}
{/* 添加附件 */} {/* 隐藏的文件输入 */} {/* 快捷短语触发按钮 */} setIsPhrasesOpen(!isPhrasesOpen)} /> {/* 工具下拉 */}
{/* 右侧按钮 */}
{/* 模型选择器 */} {/* 发送按钮 */}
{/* 提示文字 */}

可拖拽文件到此处或使用 Ctrl+V 粘贴图片

{/* 快捷短语管理模态框 */}
); }