From 1f623c0d6e0790bce81376be6e9dd7b3c0c63ed0 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Thu, 12 Mar 2026 23:19:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=BC=9A=E8=AF=9D=E5=93=8D?= =?UTF-8?q?=E5=BA=94=E6=97=B6=E6=B7=BB=E5=8A=A0=E9=98=9F=E5=88=97=E6=B6=88?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/chat/chat-input.tsx | 42 ++++- src/components/chat/conversation-shell.tsx | 31 ++++ src/components/chat/message-input.tsx | 163 ++++++++++++++---- src/components/chat/message-queue-display.tsx | 121 +++++++++++++ .../conversation-detail-panel.tsx | 81 +++++++++ src/hooks/use-message-queue.ts | 89 ++++++++++ src/i18n/messages/ar.json | 7 + src/i18n/messages/de.json | 7 + src/i18n/messages/en.json | 7 + src/i18n/messages/es.json | 7 + src/i18n/messages/fr.json | 7 + src/i18n/messages/ja.json | 7 + src/i18n/messages/ko.json | 7 + src/i18n/messages/pt.json | 7 + src/i18n/messages/zh-CN.json | 7 + src/i18n/messages/zh-TW.json | 7 + 16 files changed, 566 insertions(+), 31 deletions(-) create mode 100644 src/components/chat/message-queue-display.tsx create mode 100644 src/hooks/use-message-queue.ts diff --git a/src/components/chat/chat-input.tsx b/src/components/chat/chat-input.tsx index 9b6edc8..d0fd62a 100644 --- a/src/components/chat/chat-input.tsx +++ b/src/components/chat/chat-input.tsx @@ -9,7 +9,9 @@ import type { SessionModeInfo, AvailableCommandInfo, } from "@/lib/types" +import type { QueuedMessage } from "@/hooks/use-message-queue" import { MessageInput } from "@/components/chat/message-input" +import { MessageQueueDisplay } from "@/components/chat/message-queue-display" interface ChatInputProps { status: ConnectionStatus | null @@ -29,6 +31,16 @@ interface ChatInputProps { attachmentTabId?: string | null draftStorageKey?: string | null isActive?: boolean + queue?: QueuedMessage[] + onEnqueue?: (draft: PromptDraft, modeId: string | null) => void + onQueueReorder?: (items: QueuedMessage[]) => void + onQueueEdit?: (id: string) => void + onQueueDelete?: (id: string) => void + editingItemId?: string | null + editingDraftText?: string | null + isEditingQueueItem?: boolean + onSaveQueueEdit?: (draft: PromptDraft) => void + onCancelQueueEdit?: () => void } export function ChatInput({ @@ -49,6 +61,16 @@ export function ChatInput({ attachmentTabId, draftStorageKey, isActive, + queue, + onEnqueue, + onQueueReorder, + onQueueEdit, + onQueueDelete, + editingItemId, + editingDraftText, + isEditingQueueItem, + onSaveQueueEdit, + onCancelQueueEdit, }: ChatInputProps) { const t = useTranslations("Folder.chat.chatInput") const isConnected = status === "connected" @@ -57,12 +79,25 @@ export function ChatInput({ return (
+ {queue && + queue.length > 0 && + onQueueReorder && + onQueueEdit && + onQueueDelete && ( + + )} void + onQueueReorder?: (items: QueuedMessage[]) => void + onQueueEdit?: (id: string) => void + onQueueDelete?: (id: string) => void + editingItemId?: string | null + editingDraftText?: string | null + isEditingQueueItem?: boolean + onSaveQueueEdit?: (draft: PromptDraft) => void + onCancelQueueEdit?: () => void } export function ConversationShell({ @@ -67,6 +78,16 @@ export function ConversationShell({ draftStorageKey, hideInput = false, isActive, + queue, + onEnqueue, + onQueueReorder, + onQueueEdit, + onQueueDelete, + editingItemId, + editingDraftText, + isEditingQueueItem, + onSaveQueueEdit, + onCancelQueueEdit, }: ConversationShellProps) { return (
@@ -98,6 +119,16 @@ export function ConversationShell({ attachmentTabId={attachmentTabId} draftStorageKey={draftStorageKey} isActive={isActive} + queue={queue} + onEnqueue={onEnqueue} + onQueueReorder={onQueueReorder} + onQueueEdit={onQueueEdit} + onQueueDelete={onQueueDelete} + editingItemId={editingItemId} + editingDraftText={editingDraftText} + isEditingQueueItem={isEditingQueueItem} + onSaveQueueEdit={onSaveQueueEdit} + onCancelQueueEdit={onCancelQueueEdit} /> )} diff --git a/src/components/chat/message-input.tsx b/src/components/chat/message-input.tsx index 32d2047..01540b9 100644 --- a/src/components/chat/message-input.tsx +++ b/src/components/chat/message-input.tsx @@ -13,7 +13,16 @@ import { PopoverTrigger, } from "@/components/ui/popover" import { Textarea } from "@/components/ui/textarea" -import { Ellipsis, FileSearch, Plus, Send, Square, X } from "lucide-react" +import { + Check, + Ellipsis, + FileSearch, + ListPlus, + Plus, + Send, + Square, + X, +} from "lucide-react" import { cn } from "@/lib/utils" import { matchShortcutEvent } from "@/lib/keyboard-shortcuts" import { useShortcutSettings } from "@/hooks/use-shortcut-settings" @@ -62,6 +71,11 @@ interface MessageInputProps { attachmentTabId?: string | null draftStorageKey?: string | null isActive?: boolean + onEnqueue?: (draft: PromptDraft, modeId: string | null) => void + editingDraftText?: string | null + isEditingQueueItem?: boolean + onSaveQueueEdit?: (draft: PromptDraft) => void + onCancelQueueEdit?: () => void } interface ResourceInputAttachment { @@ -261,8 +275,14 @@ export function MessageInput({ attachmentTabId, draftStorageKey, isActive = false, + onEnqueue, + editingDraftText, + isEditingQueueItem = false, + onSaveQueueEdit, + onCancelQueueEdit, }: MessageInputProps) { const t = useTranslations("Folder.chat.messageInput") + const tQueue = useTranslations("Folder.chat.messageQueue") const { shortcuts } = useShortcutSettings() const effectiveDraftStorageKey = draftStorageKey ?? attachmentTabId ?? null const resolvedPlaceholder = placeholder ?? t("askAnything") @@ -302,6 +322,24 @@ export function MessageInput({ isPromptingRef.current = isPrompting }, [isPrompting]) + // Load external draft text when editing a queue item + const prevEditingDraftRef = useRef(null) + useEffect(() => { + if ( + isEditingQueueItem && + editingDraftText != null && + editingDraftText !== prevEditingDraftRef.current + ) { + prevEditingDraftRef.current = editingDraftText + setText(editingDraftText) + requestAnimationFrame(() => { + textareaRef.current?.focus() + }) + } else if (!isEditingQueueItem) { + prevEditingDraftRef.current = null + } + }, [isEditingQueueItem, editingDraftText]) + const setDragActiveIfChanged = useCallback((next: boolean) => { if (dragActiveRef.current === next) return dragActiveRef.current = next @@ -309,9 +347,9 @@ export function MessageInput({ }, []) useEffect(() => { - if (!effectiveDraftStorageKey) return + if (!effectiveDraftStorageKey || isEditingQueueItem) return saveMessageInputDraft(effectiveDraftStorageKey, text) - }, [effectiveDraftStorageKey, text]) + }, [effectiveDraftStorageKey, text, isEditingQueueItem]) const availableModes = useMemo(() => modes ?? [], [modes]) const availableConfigOptions = useMemo( @@ -653,7 +691,7 @@ export function MessageInput({ const handlePaste = useCallback( (event: React.ClipboardEvent) => { - if (disabled || isPrompting) return + if (disabled) return const files = Array.from(event.clipboardData?.files ?? []) if (files.length === 0) return event.preventDefault() @@ -661,7 +699,7 @@ export function MessageInput({ console.error("[MessageInput] paste files failed:", error) }) }, - [appendFilesFromInput, disabled, isPrompting] + [appendFilesFromInput, disabled] ) useEffect(() => { @@ -763,15 +801,13 @@ export function MessageInput({ if (payload.type === "drop") { setDragActiveIfChanged(false) if (Date.now() - lastDomDropAtRef.current < 250) return - if (!inside || disabledRef.current || isPromptingRef.current) return + if (!inside || disabledRef.current) return void appendPathsFromDropRef.current(payload.paths).catch((error) => { console.error("[MessageInput] drag drop paths failed:", error) }) return } - setDragActiveIfChanged( - inside && !disabledRef.current && !isPromptingRef.current - ) + setDragActiveIfChanged(inside && !disabledRef.current) } const setup = async () => { @@ -843,9 +879,9 @@ export function MessageInput({ setAttachments((prev) => prev.filter((item) => item.id !== id)) }, []) - const handleSend = useCallback(() => { + const buildDraft = useCallback((): PromptDraft | null => { const trimmed = textRef.current.trim() - if (!trimmed && attachments.length === 0) return + if (!trimmed && attachments.length === 0) return null const blocks: PromptInputBlock[] = [] if (trimmed) { @@ -883,14 +919,41 @@ export function MessageInput({ const displayText = trimmed || `Attached ${attachments.length} attachment${attachments.length > 1 ? "s" : ""}` - onSend({ blocks, displayText }, showModeSelector ? effectiveModeId : null) + return { blocks, displayText } + }, [attachments]) + + const handleSend = useCallback(() => { + const draft = buildDraft() + if (!draft) return + + // Edit mode: save back to queue item + if (isEditingQueueItem && onSaveQueueEdit) { + onSaveQueueEdit(draft) + setText("") + setAttachments([]) + return + } + + // Prompting mode: enqueue instead of sending + if (isPrompting && onEnqueue) { + onEnqueue(draft, showModeSelector ? effectiveModeId : null) + setText("") + setAttachments([]) + return + } + + onSend(draft, showModeSelector ? effectiveModeId : null) if (effectiveDraftStorageKey) { clearMessageInputDraft(effectiveDraftStorageKey) } setText("") setAttachments([]) }, [ - attachments, + buildDraft, + isEditingQueueItem, + isPrompting, + onSaveQueueEdit, + onEnqueue, onSend, effectiveModeId, showModeSelector, @@ -935,9 +998,15 @@ export function MessageInput({ } } + if (isEditingQueueItem && e.key === "Escape") { + e.preventDefault() + onCancelQueueEdit?.() + return + } + if (matchShortcutEvent(e, shortcuts.send_message)) { e.preventDefault() - if (!disabled) handleSend() + if (!disabled || isPrompting || isEditingQueueItem) handleSend() } else if (matchShortcutEvent(e, shortcuts.newline_in_message)) { e.preventDefault() const textarea = e.currentTarget as HTMLTextAreaElement @@ -953,6 +1022,9 @@ export function MessageInput({ }, [ disabled, + isPrompting, + isEditingQueueItem, + onCancelQueueEdit, handleSend, shortcuts, slashMenuOpen, @@ -966,11 +1038,11 @@ export function MessageInput({ (event: React.DragEvent) => { if (!hasDragFiles(event.dataTransfer)) return event.preventDefault() - if (!disabled && !isPrompting) { + if (!disabled) { setDragActiveIfChanged(true) } }, - [disabled, isPrompting, setDragActiveIfChanged] + [disabled, setDragActiveIfChanged] ) const handleContainerDragLeave = useCallback( @@ -994,7 +1066,7 @@ export function MessageInput({ event.preventDefault() lastDomDropAtRef.current = Date.now() setDragActiveIfChanged(false) - if (disabled || isPrompting) return + if (disabled) return const files = Array.from(event.dataTransfer.files ?? []) if (files.length > 0) { void appendFilesFromInput(files).catch((error) => { @@ -1002,7 +1074,7 @@ export function MessageInput({ }) } }, - [appendFilesFromInput, disabled, isPrompting, setDragActiveIfChanged] + [appendFilesFromInput, disabled, setDragActiveIfChanged] ) const hasImageAttachments = imageAttachments.length > 0 @@ -1016,7 +1088,7 @@ export function MessageInput({ ? "pt-10" : "pt-3" const bottomPaddingClass = "pb-10" - const showDragActive = isDragActive && !disabled && !isPrompting + const showDragActive = isDragActive && !disabled const selectorItems = ( <> @@ -1143,7 +1215,7 @@ export function MessageInput({
- {isPrompting && onCancel ? ( - + {isEditingQueueItem ? ( +
+ + +
+ ) : isPrompting && onCancel ? ( +
+ + +
) : ( + + #{index + 1} + + + {item.draft.displayText} + + + + + ) +} + +export function MessageQueueDisplay({ + queue, + onReorder, + onEdit, + onDelete, + editingItemId, +}: MessageQueueDisplayProps) { + if (queue.length === 0) return null + + return ( +
+ + {queue.map((item, index) => ( + + ))} + +
+ ) +} diff --git a/src/components/conversations/conversation-detail-panel.tsx b/src/components/conversations/conversation-detail-panel.tsx index 61eabcc..cea7a4f 100644 --- a/src/components/conversations/conversation-detail-panel.tsx +++ b/src/components/conversations/conversation-detail-panel.tsx @@ -18,6 +18,7 @@ import { useTabContext } from "@/contexts/tab-context" import { useSessionStats } from "@/contexts/session-stats-context" import { cn } from "@/lib/utils" import { useConnectionLifecycle } from "@/hooks/use-connection-lifecycle" +import { useMessageQueue, type QueuedMessage } from "@/hooks/use-message-queue" import { MessageListView } from "@/components/message/message-list-view" import { ConversationShell } from "@/components/chat/conversation-shell" import { AgentSelector } from "@/components/chat/agent-selector" @@ -254,6 +255,18 @@ const ConversationTabView = memo(function ConversationTabView({ disconnect: connDisconnect, sessionId: connSessionId, } = conn + const messageQueue = useMessageQueue() + const { + queue: msgQueue, + enqueue: mqEnqueue, + dequeue: mqDequeue, + remove: mqRemove, + reorder: mqReorder, + updateItem: mqUpdateItem, + editingItemId: mqEditingItemId, + startEditing: mqStartEditing, + cancelEditing: mqCancelEditing, + } = messageQueue const connStatusRef = useRef(connStatus) useEffect(() => { connStatusRef.current = connStatus @@ -329,6 +342,32 @@ const ConversationTabView = memo(function ConversationTabView({ syncTurnMetadata, ]) + // Auto-send queued messages when agent finishes responding. + // Refs are synced via useEffect; the auto-send effect is declared + // AFTER completeTurn so React runs it second. + const autoSendQueueRef = useRef<() => QueuedMessage | undefined>(mqDequeue) + useEffect(() => { + autoSendQueueRef.current = mqDequeue + }, [mqDequeue]) + const handleSendRef = useRef< + (draft: PromptDraft, modeId?: string | null) => void + >(() => {}) + + const prevAutoSendStatusRef = useRef(connStatus) + useEffect(() => { + const wasPrompting = prevAutoSendStatusRef.current === "prompting" + prevAutoSendStatusRef.current = connStatus + if (!wasPrompting || connStatus !== "connected") return + + // Use queueMicrotask to ensure completeTurn effect has fully committed + queueMicrotask(() => { + const next = autoSendQueueRef.current() + if (next) { + handleSendRef.current(next.draft, next.modeId) + } + }) + }, [connStatus]) + useEffect(() => { // Only sync non-null liveMessage updates to state. When conn.liveMessage // goes null (agent finished streaming), don't clear state.liveMessage — @@ -545,6 +584,11 @@ const ConversationTabView = memo(function ConversationTabView({ ] ) + // Sync handleSend ref for auto-send effect (declared before handleSend) + useEffect(() => { + handleSendRef.current = handleSend + }, [handleSend]) + const handleOpenAgentsSettings = useCallback(() => { openSettingsWindow("agents", { agentType: selectedAgent }).catch((err) => { console.error( @@ -615,6 +659,33 @@ const ConversationTabView = memo(function ConversationTabView({ ] ) + // Queue edit flow: derive editing draft text from queue state + const editingQueueDraftText = useMemo(() => { + if (!mqEditingItemId) return null + const item = msgQueue.find((m) => m.id === mqEditingItemId) + return item?.draft.displayText ?? null + }, [mqEditingItemId, msgQueue]) + + const handleQueueEdit = useCallback( + (id: string) => { + mqStartEditing(id) + }, + [mqStartEditing] + ) + + const handleQueueCancelEdit = useCallback(() => { + mqCancelEditing() + }, [mqCancelEditing]) + + const handleSaveQueueEdit = useCallback( + (draft: PromptDraft) => { + if (mqEditingItemId) { + mqUpdateItem(mqEditingItemId, draft) + } + }, + [mqEditingItemId, mqUpdateItem] + ) + const showDraftHeader = !hasPersistedConversation && !hasSentMessage const isWelcomeMode = showDraftHeader @@ -657,6 +728,16 @@ const ConversationTabView = memo(function ConversationTabView({ draftStorageKey={draftStorageKey} hideInput={isWelcomeMode} isActive={isActive} + queue={msgQueue} + onEnqueue={mqEnqueue} + onQueueReorder={mqReorder} + onQueueEdit={handleQueueEdit} + onQueueDelete={mqRemove} + editingItemId={mqEditingItemId} + editingDraftText={editingQueueDraftText} + isEditingQueueItem={mqEditingItemId != null} + onSaveQueueEdit={handleSaveQueueEdit} + onCancelQueueEdit={handleQueueCancelEdit} > {isWelcomeMode ? (
diff --git a/src/hooks/use-message-queue.ts b/src/hooks/use-message-queue.ts new file mode 100644 index 0000000..948b539 --- /dev/null +++ b/src/hooks/use-message-queue.ts @@ -0,0 +1,89 @@ +"use client" + +import { useCallback, useEffect, useRef, useState } from "react" +import type { PromptDraft } from "@/lib/types" + +export interface QueuedMessage { + id: string + draft: PromptDraft + modeId: string | null +} + +export interface UseMessageQueueReturn { + queue: QueuedMessage[] + enqueue: (draft: PromptDraft, modeId: string | null) => void + dequeue: () => QueuedMessage | undefined + remove: (id: string) => void + reorder: (items: QueuedMessage[]) => void + updateItem: (id: string, draft: PromptDraft) => void + editingItemId: string | null + startEditing: (id: string) => void + cancelEditing: () => void +} + +export function useMessageQueue(): UseMessageQueueReturn { + const [queue, setQueue] = useState([]) + const [editingItemId, setEditingItemId] = useState(null) + const queueRef = useRef(queue) + useEffect(() => { + queueRef.current = queue + }, [queue]) + + const enqueue = useCallback((draft: PromptDraft, modeId: string | null) => { + const item: QueuedMessage = { + id: crypto.randomUUID(), + draft, + modeId, + } + setQueue((prev) => [...prev, item]) + }, []) + + const dequeue = useCallback((): QueuedMessage | undefined => { + const current = queueRef.current + if (current.length === 0) return undefined + const first = current[0] + setQueue((prev) => prev.slice(1)) + return first + }, []) + + const remove = useCallback( + (id: string) => { + if (editingItemId === id) { + setEditingItemId(null) + } + setQueue((prev) => prev.filter((item) => item.id !== id)) + }, + [editingItemId] + ) + + const reorder = useCallback((items: QueuedMessage[]) => { + setQueue(items) + }, []) + + const updateItem = useCallback((id: string, draft: PromptDraft) => { + setQueue((prev) => + prev.map((item) => (item.id === id ? { ...item, draft } : item)) + ) + setEditingItemId(null) + }, []) + + const startEditing = useCallback((id: string) => { + setEditingItemId(id) + }, []) + + const cancelEditing = useCallback(() => { + setEditingItemId(null) + }, []) + + return { + queue, + enqueue, + dequeue, + remove, + reorder, + updateItem, + editingItemId, + startEditing, + cancelEditing, + } +} diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 9729a07..fb651cb 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -1145,6 +1145,13 @@ "cancel": "إلغاء", "send": "إرسال" }, + "messageQueue": { + "addToQueue": "إضافة للقائمة", + "saveEdit": "حفظ", + "cancelEdit": "إلغاء التعديل", + "editItem": "تعديل", + "deleteItem": "حذف" + }, "welcomeInputPanel": { "agentsSettingsPath": "الإعدادات > الوكلاء", "autoConnectFallback": "انقر لفتح {path} وإدارة التثبيت.", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 52bd16c..4402d85 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -1145,6 +1145,13 @@ "cancel": "Abbrechen", "send": "Senden" }, + "messageQueue": { + "addToQueue": "Zur Warteschlange", + "saveEdit": "Speichern", + "cancelEdit": "Bearbeitung abbrechen", + "editItem": "Bearbeiten", + "deleteItem": "Entfernen" + }, "welcomeInputPanel": { "agentsSettingsPath": "Einstellungen > Agenten", "autoConnectFallback": "Klicken Sie, um {path} zu öffnen und die Installation zu verwalten.", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 43d959f..f1e80c1 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -1145,6 +1145,13 @@ "cancel": "Cancel", "send": "Send" }, + "messageQueue": { + "addToQueue": "Queue message", + "saveEdit": "Save", + "cancelEdit": "Cancel edit", + "editItem": "Edit", + "deleteItem": "Remove" + }, "welcomeInputPanel": { "agentsSettingsPath": "Settings > Agents", "autoConnectFallback": "Click to open {path} and manage installation.", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index f86127d..96380f9 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -1145,6 +1145,13 @@ "cancel": "Cancelar", "send": "Enviar" }, + "messageQueue": { + "addToQueue": "Agregar a la cola", + "saveEdit": "Guardar", + "cancelEdit": "Cancelar edición", + "editItem": "Editar", + "deleteItem": "Eliminar" + }, "welcomeInputPanel": { "agentsSettingsPath": "Ajustes > Agentes", "autoConnectFallback": "Haz clic para abrir {path} y gestionar la instalación.", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 30e856a..8188c08 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -1145,6 +1145,13 @@ "cancel": "Annuler", "send": "Envoyer" }, + "messageQueue": { + "addToQueue": "Mettre en file", + "saveEdit": "Enregistrer", + "cancelEdit": "Annuler la modification", + "editItem": "Modifier", + "deleteItem": "Supprimer" + }, "welcomeInputPanel": { "agentsSettingsPath": "Paramètres > Agents", "autoConnectFallback": "Cliquez pour ouvrir {path} et gérer l'installation.", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index d41b1dc..211572c 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -1145,6 +1145,13 @@ "cancel": "キャンセル", "send": "送信" }, + "messageQueue": { + "addToQueue": "キューに追加", + "saveEdit": "保存", + "cancelEdit": "編集をキャンセル", + "editItem": "編集", + "deleteItem": "削除" + }, "welcomeInputPanel": { "agentsSettingsPath": "設定 > エージェント", "autoConnectFallback": "{path} を開いてインストールを管理してください。", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index bb8e6b8..b58fa62 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -1145,6 +1145,13 @@ "cancel": "취소", "send": "보내기" }, + "messageQueue": { + "addToQueue": "대기열에 추가", + "saveEdit": "저장", + "cancelEdit": "편집 취소", + "editItem": "편집", + "deleteItem": "삭제" + }, "welcomeInputPanel": { "agentsSettingsPath": "설정 > 에이전트", "autoConnectFallback": "{path}을(를) 열어 설치를 관리하세요.", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 0db30af..1ce9763 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -1145,6 +1145,13 @@ "cancel": "Cancelar", "send": "Enviar" }, + "messageQueue": { + "addToQueue": "Adicionar à fila", + "saveEdit": "Salvar", + "cancelEdit": "Cancelar edição", + "editItem": "Editar", + "deleteItem": "Remover" + }, "welcomeInputPanel": { "agentsSettingsPath": "Configurações > Agentes", "autoConnectFallback": "Clique para abrir {path} e gerenciar a instalação.", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index aa3cb30..88afb2c 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -1145,6 +1145,13 @@ "cancel": "取消", "send": "发送" }, + "messageQueue": { + "addToQueue": "加入队列", + "saveEdit": "保存", + "cancelEdit": "取消编辑", + "editItem": "编辑", + "deleteItem": "删除" + }, "welcomeInputPanel": { "agentsSettingsPath": "设置 > Agents", "autoConnectFallback": "点击前往 {path} 管理安装。", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index de959ac..f29dcb6 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -1145,6 +1145,13 @@ "cancel": "取消", "send": "傳送" }, + "messageQueue": { + "addToQueue": "加入佇列", + "saveEdit": "儲存", + "cancelEdit": "取消編輯", + "editItem": "編輯", + "deleteItem": "刪除" + }, "welcomeInputPanel": { "agentsSettingsPath": "設定 > Agents", "autoConnectFallback": "點擊前往 {path} 管理安裝。",