diff --git a/src/components/features/ChatInput.tsx b/src/components/features/ChatInput.tsx index 6c5e6b5..86c706f 100644 --- a/src/components/features/ChatInput.tsx +++ b/src/components/features/ChatInput.tsx @@ -5,11 +5,14 @@ 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 } from '@/types'; +import type { Model, Tool, QuickPhrase } from '@/types'; import type { UploadFile } from '@/types/file-upload'; interface ChatInputProps { @@ -36,11 +39,22 @@ export function ChatInput({ 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(); @@ -49,6 +63,39 @@ export function ChatInput({ } }, [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, @@ -97,9 +144,36 @@ export function ChatInput({ 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 && (
@@ -153,7 +240,7 @@ export function ChatInput({
); }