feat(message-input): turn plus button into menu with attach files and quick messages
This commit is contained in:
@@ -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<InputAttachment[]>([])
|
||||
const [isDragActive, setIsDragActive] = useState(false)
|
||||
const [quickMessages, setQuickMessages] = useState<QuickMessage[]>([])
|
||||
const [quickMessagesLoading, setQuickMessagesLoading] = useState(false)
|
||||
const [previewAttachmentId, setPreviewAttachmentId] = useState<string | null>(
|
||||
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({
|
||||
/>
|
||||
<div className="@container flex shrink-0 items-end justify-between gap-2 px-2 pb-2">
|
||||
<div className="flex min-w-0 items-end gap-2">
|
||||
<DropdownMenu onOpenChange={handleAddMenuOpenChange}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
onClick={handlePickFiles}
|
||||
disabled={disabled}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-6 w-6 shrink-0 bg-transparent"
|
||||
title={t("attachFiles")}
|
||||
title={t("addActions")}
|
||||
aria-label={t("addActions")}
|
||||
>
|
||||
<Plus className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
side="top"
|
||||
align="start"
|
||||
className="min-w-48"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
handlePickFiles().catch((error) => {
|
||||
console.error(
|
||||
"[MessageInput] pick files from menu failed:",
|
||||
error
|
||||
)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Paperclip className="size-4" />
|
||||
{t("attachFiles")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<MessageSquareText className="size-4" />
|
||||
{t("quickMessages")}
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent
|
||||
className="min-w-40 overflow-y-auto"
|
||||
style={{
|
||||
maxHeight:
|
||||
"min(32rem, var(--radix-dropdown-menu-content-available-height))",
|
||||
}}
|
||||
>
|
||||
{quickMessagesLoading && quickMessages.length === 0 ? (
|
||||
<div className="px-3 py-4 text-center text-xs text-muted-foreground">
|
||||
{t("quickMessagesLoading")}
|
||||
</div>
|
||||
) : quickMessages.length === 0 ? (
|
||||
<div className="px-3 py-4 text-center text-xs text-muted-foreground">
|
||||
{t("quickMessagesEmpty")}
|
||||
</div>
|
||||
) : (
|
||||
quickMessages.map((message) => (
|
||||
<DropdownMenuItem
|
||||
key={message.id}
|
||||
onClick={() => handleQuickMessageSelect(message)}
|
||||
>
|
||||
<span className="truncate">
|
||||
{message.title || (
|
||||
<span className="italic text-muted-foreground">
|
||||
{t("quickMessageUntitled")}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
)}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "اسأل أي شيء...",
|
||||
"removeAttachmentAria": "إزالة {name}",
|
||||
"attachFiles": "إرفاق ملفات",
|
||||
"addActions": "إضافة",
|
||||
"quickMessages": "الرسائل السريعة",
|
||||
"quickMessagesEmpty": "لا توجد رسائل سريعة بعد",
|
||||
"quickMessagesLoading": "جارٍ التحميل...",
|
||||
"quickMessageUntitled": "بدون عنوان",
|
||||
"dropFilesToAttach": "أسقط الملفات لإرفاقها",
|
||||
"loadingSettings": "جارٍ تحميل الإعدادات...",
|
||||
"loadingMode": "جارٍ تحميل الوضع...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "Fragen Sie alles...",
|
||||
"removeAttachmentAria": "{name} entfernen",
|
||||
"attachFiles": "Dateien anhängen",
|
||||
"addActions": "Hinzufügen",
|
||||
"quickMessages": "Schnellnachrichten",
|
||||
"quickMessagesEmpty": "Noch keine Schnellnachrichten",
|
||||
"quickMessagesLoading": "Wird geladen...",
|
||||
"quickMessageUntitled": "Ohne Titel",
|
||||
"dropFilesToAttach": "Dateien zum Anhängen ablegen",
|
||||
"loadingSettings": "Einstellungen werden geladen...",
|
||||
"loadingMode": "Modus wird geladen...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "Ask anything...",
|
||||
"removeAttachmentAria": "Remove {name}",
|
||||
"attachFiles": "Attach files",
|
||||
"addActions": "Add",
|
||||
"quickMessages": "Quick messages",
|
||||
"quickMessagesEmpty": "No quick messages yet",
|
||||
"quickMessagesLoading": "Loading...",
|
||||
"quickMessageUntitled": "Untitled",
|
||||
"dropFilesToAttach": "Drop files to attach",
|
||||
"loadingSettings": "Loading settings...",
|
||||
"loadingMode": "Loading mode...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "Pregunta lo que sea...",
|
||||
"removeAttachmentAria": "Quitar {name}",
|
||||
"attachFiles": "Adjuntar archivos",
|
||||
"addActions": "Añadir",
|
||||
"quickMessages": "Mensajes rápidos",
|
||||
"quickMessagesEmpty": "Aún no hay mensajes rápidos",
|
||||
"quickMessagesLoading": "Cargando...",
|
||||
"quickMessageUntitled": "Sin título",
|
||||
"dropFilesToAttach": "Suelta archivos para adjuntar",
|
||||
"loadingSettings": "Cargando ajustes...",
|
||||
"loadingMode": "Cargando modo...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "Posez n'importe quelle question...",
|
||||
"removeAttachmentAria": "Retirer {name}",
|
||||
"attachFiles": "Joindre des fichiers",
|
||||
"addActions": "Ajouter",
|
||||
"quickMessages": "Messages rapides",
|
||||
"quickMessagesEmpty": "Aucun message rapide pour l'instant",
|
||||
"quickMessagesLoading": "Chargement...",
|
||||
"quickMessageUntitled": "Sans titre",
|
||||
"dropFilesToAttach": "Déposez des fichiers à joindre",
|
||||
"loadingSettings": "Chargement des paramètres...",
|
||||
"loadingMode": "Chargement du mode...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "何でも質問してください...",
|
||||
"removeAttachmentAria": "{name} を削除",
|
||||
"attachFiles": "ファイルを添付",
|
||||
"addActions": "追加",
|
||||
"quickMessages": "クイックメッセージ",
|
||||
"quickMessagesEmpty": "クイックメッセージはありません",
|
||||
"quickMessagesLoading": "読み込み中...",
|
||||
"quickMessageUntitled": "無題",
|
||||
"dropFilesToAttach": "ファイルをドロップして添付",
|
||||
"loadingSettings": "設定を読み込み中...",
|
||||
"loadingMode": "モードを読み込み中...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "무엇이든 물어보세요...",
|
||||
"removeAttachmentAria": "{name} 제거",
|
||||
"attachFiles": "파일 첨부",
|
||||
"addActions": "추가",
|
||||
"quickMessages": "빠른 메시지",
|
||||
"quickMessagesEmpty": "빠른 메시지가 없습니다",
|
||||
"quickMessagesLoading": "불러오는 중...",
|
||||
"quickMessageUntitled": "제목 없음",
|
||||
"dropFilesToAttach": "파일을 놓아 첨부",
|
||||
"loadingSettings": "설정 불러오는 중...",
|
||||
"loadingMode": "모드 불러오는 중...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "Pergunte qualquer coisa...",
|
||||
"removeAttachmentAria": "Remover {name}",
|
||||
"attachFiles": "Anexar arquivos",
|
||||
"addActions": "Adicionar",
|
||||
"quickMessages": "Mensagens rápidas",
|
||||
"quickMessagesEmpty": "Ainda não há mensagens rápidas",
|
||||
"quickMessagesLoading": "Carregando...",
|
||||
"quickMessageUntitled": "Sem título",
|
||||
"dropFilesToAttach": "Solte arquivos para anexar",
|
||||
"loadingSettings": "Carregando configurações...",
|
||||
"loadingMode": "Carregando modo...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "请开始输入...",
|
||||
"removeAttachmentAria": "移除 {name}",
|
||||
"attachFiles": "附加文件",
|
||||
"addActions": "添加",
|
||||
"quickMessages": "快捷消息",
|
||||
"quickMessagesEmpty": "暂无快捷消息",
|
||||
"quickMessagesLoading": "加载中...",
|
||||
"quickMessageUntitled": "未命名",
|
||||
"dropFilesToAttach": "拖拽文件到此处附加",
|
||||
"loadingSettings": "正在加载设置...",
|
||||
"loadingMode": "正在加载模式...",
|
||||
|
||||
@@ -1569,6 +1569,11 @@
|
||||
"askAnything": "請開始輸入...",
|
||||
"removeAttachmentAria": "移除 {name}",
|
||||
"attachFiles": "附加檔案",
|
||||
"addActions": "新增",
|
||||
"quickMessages": "快捷訊息",
|
||||
"quickMessagesEmpty": "尚無快捷訊息",
|
||||
"quickMessagesLoading": "載入中...",
|
||||
"quickMessageUntitled": "未命名",
|
||||
"dropFilesToAttach": "拖曳檔案到此處附加",
|
||||
"loadingSettings": "正在載入設定...",
|
||||
"loadingMode": "正在載入模式...",
|
||||
|
||||
Reference in New Issue
Block a user