diff --git a/src/components/chat/message-input.tsx b/src/components/chat/message-input.tsx index beed713..9a75713 100644 --- a/src/components/chat/message-input.tsx +++ b/src/components/chat/message-input.tsx @@ -19,6 +19,8 @@ import { FileSearch, GitFork, ListPlus, + MessageSquareText, + Paperclip, Plus, Search, Send, @@ -33,13 +35,16 @@ import { DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { ImagePreviewDialog } from "@/components/ui/image-preview-dialog" import { cn, randomUUID } from "@/lib/utils" import { matchShortcutEvent } from "@/lib/keyboard-shortcuts" import { useShortcutSettings } from "@/hooks/use-shortcut-settings" -import { readFileBase64 } from "@/lib/api" +import { readFileBase64, quickMessagesList } from "@/lib/api" import { openFileDialog } from "@/lib/platform" import { disposeTauriListener } from "@/lib/tauri-listener" import type { @@ -50,6 +55,7 @@ import type { PromptCapabilitiesInfo, PromptDraft, PromptInputBlock, + QuickMessage, SessionConfigOptionInfo, SessionModeInfo, } from "@/lib/types" @@ -409,6 +415,8 @@ export function MessageInput({ }) const [attachments, setAttachments] = useState([]) const [isDragActive, setIsDragActive] = useState(false) + const [quickMessages, setQuickMessages] = useState([]) + const [quickMessagesLoading, setQuickMessagesLoading] = useState(false) const [previewAttachmentId, setPreviewAttachmentId] = useState( null ) @@ -1299,6 +1307,47 @@ export function MessageInput({ } }, [appendResourceAttachments, defaultPath, disabled]) + const loadQuickMessages = useCallback(async () => { + setQuickMessagesLoading(true) + try { + const list = await quickMessagesList() + setQuickMessages(list) + } catch (error) { + console.error("[MessageInput] load quick messages failed:", error) + } finally { + setQuickMessagesLoading(false) + } + }, []) + + const handleAddMenuOpenChange = useCallback( + (open: boolean) => { + if (!open) return + cursorPosRef.current = textareaRef.current?.selectionStart ?? null + loadQuickMessages().catch((error) => { + console.error("[MessageInput] quick messages refresh failed:", error) + }) + }, + [loadQuickMessages] + ) + + const handleQuickMessageSelect = useCallback((message: QuickMessage) => { + const insertion = message.content + if (!insertion) return + const current = textRef.current + const rawPos = cursorPosRef.current ?? current.length + const pos = Math.max(0, Math.min(rawPos, current.length)) + const before = current.slice(0, pos) + const after = current.slice(pos) + setText(before + insertion + after) + requestAnimationFrame(() => { + const ta = textareaRef.current + if (!ta) return + ta.focus() + const newPos = pos + insertion.length + ta.setSelectionRange(newPos, newPos) + }) + }, []) + useEffect(() => { if (!attachmentTabId) return @@ -1994,16 +2043,77 @@ export function MessageInput({ />
- + + + + + + { + handlePickFiles().catch((error) => { + console.error( + "[MessageInput] pick files from menu failed:", + error + ) + }) + }} + > + + {t("attachFiles")} + + + + + {t("quickMessages")} + + + {quickMessagesLoading && quickMessages.length === 0 ? ( +
+ {t("quickMessagesLoading")} +
+ ) : quickMessages.length === 0 ? ( +
+ {t("quickMessagesEmpty")} +
+ ) : ( + quickMessages.map((message) => ( + handleQuickMessageSelect(message)} + > + + {message.title || ( + + {t("quickMessageUntitled")} + + )} + + + )) + )} +
+
+
+