feat: refresh queued message controls
Queued messages now match the reverted message dock style Added per-message edit and send actions Sending one queued message no longer sends the full queue
This commit is contained in:
@@ -1332,6 +1332,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
);
|
);
|
||||||
const addToQueue = useMessageQueueStore((state) => state.addToQueue);
|
const addToQueue = useMessageQueueStore((state) => state.addToQueue);
|
||||||
const clearQueue = useMessageQueueStore((state) => state.clearQueue);
|
const clearQueue = useMessageQueueStore((state) => state.clearQueue);
|
||||||
|
const removeFromQueue = useMessageQueueStore((state) => state.removeFromQueue);
|
||||||
|
|
||||||
// Inline comment drafts
|
// Inline comment drafts
|
||||||
const draftCount = useInlineCommentDraftStore(
|
const draftCount = useInlineCommentDraftStore(
|
||||||
@@ -1579,6 +1580,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
// Keep a ref to handleSubmit so callbacks don't depend on it.
|
// Keep a ref to handleSubmit so callbacks don't depend on it.
|
||||||
type SubmitOptions = {
|
type SubmitOptions = {
|
||||||
queuedOnly?: boolean;
|
queuedOnly?: boolean;
|
||||||
|
queuedMessageId?: string;
|
||||||
};
|
};
|
||||||
const handleSubmitRef = React.useRef<(options?: SubmitOptions) => Promise<void>>(async () => {});
|
const handleSubmitRef = React.useRef<(options?: SubmitOptions) => Promise<void>>(async () => {});
|
||||||
|
|
||||||
@@ -1627,6 +1629,10 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
}, 0);
|
}, 0);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleQueuedMessageSend = React.useCallback((messageId: string) => {
|
||||||
|
void handleSubmitRef.current({ queuedOnly: true, queuedMessageId: messageId });
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleOpenAgentPanel = React.useCallback(() => {
|
const handleOpenAgentPanel = React.useCallback(() => {
|
||||||
setMobileControlsPanel('agent');
|
setMobileControlsPanel('agent');
|
||||||
}, []);
|
}, []);
|
||||||
@@ -1645,15 +1651,25 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
|
|
||||||
const handleSubmit = async (options?: SubmitOptions) => {
|
const handleSubmit = async (options?: SubmitOptions) => {
|
||||||
const queuedOnly = options?.queuedOnly ?? false;
|
const queuedOnly = options?.queuedOnly ?? false;
|
||||||
|
const queuedMessageId = options?.queuedMessageId;
|
||||||
const inputSnapshot = getCurrentInputSnapshot();
|
const inputSnapshot = getCurrentInputSnapshot();
|
||||||
|
const queuedMessagesToSend = queuedMessageId
|
||||||
|
? queuedMessages.filter((message) => message.id === queuedMessageId)
|
||||||
|
: queuedMessages;
|
||||||
|
|
||||||
if (queuedOnly) {
|
if (queuedOnly) {
|
||||||
if (!hasQueuedMessages || !currentSessionId) return;
|
if (queuedMessagesToSend.length === 0 || !currentSessionId) return;
|
||||||
} else if ((!inputSnapshot.hasContent && !hasQueuedMessages) || (!currentSessionId && !newSessionDraftOpen)) {
|
} else if ((!inputSnapshot.hasContent && !hasQueuedMessages) || (!currentSessionId && !newSessionDraftOpen)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentProviderId || !currentModelId) {
|
const capturedSendConfig = queuedOnly ? queuedMessagesToSend[0]?.sendConfig : undefined;
|
||||||
|
const providerIdToSend = capturedSendConfig?.providerID ?? currentProviderId;
|
||||||
|
const modelIdToSend = capturedSendConfig?.modelID ?? currentModelId;
|
||||||
|
const agentNameToSend = capturedSendConfig?.agent ?? currentAgentName;
|
||||||
|
const variantToSend = capturedSendConfig?.variant ?? currentVariant;
|
||||||
|
|
||||||
|
if (!providerIdToSend || !modelIdToSend) {
|
||||||
console.warn('Cannot send message: provider or model not selected');
|
console.warn('Cannot send message: provider or model not selected');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1675,8 +1691,8 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
const syntheticParts = consumePendingSyntheticParts();
|
const syntheticParts = consumePendingSyntheticParts();
|
||||||
|
|
||||||
// Process queued messages first
|
// Process queued messages first
|
||||||
for (let i = 0; i < queuedMessages.length; i++) {
|
for (let i = 0; i < queuedMessagesToSend.length; i++) {
|
||||||
const queuedMsg = queuedMessages[i];
|
const queuedMsg = queuedMessagesToSend[i];
|
||||||
const { sanitizedText, mention } = parseAgentMentions(queuedMsg.content, agents);
|
const { sanitizedText, mention } = parseAgentMentions(queuedMsg.content, agents);
|
||||||
const { sanitizedText: queuedText, attachments: mentionAttachments } = extractInlineFileMentions(sanitizedText);
|
const { sanitizedText: queuedText, attachments: mentionAttachments } = extractInlineFileMentions(sanitizedText);
|
||||||
addMentionedSkills(queuedText);
|
addMentionedSkills(queuedText);
|
||||||
@@ -1715,7 +1731,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
agentMentionName = mention.name;
|
agentMentionName = mention.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queuedMessages.length === 0) {
|
if (queuedMessagesToSend.length === 0) {
|
||||||
// No queue - current input is primary
|
// No queue - current input is primary
|
||||||
primaryText = messageText;
|
primaryText = messageText;
|
||||||
primaryAttachments = [...attachmentsToSend, ...mentionAttachments];
|
primaryAttachments = [...attachmentsToSend, ...mentionAttachments];
|
||||||
@@ -1735,7 +1751,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (drafts.length > 0) {
|
if (drafts.length > 0) {
|
||||||
if (queuedMessages.length === 0) {
|
if (queuedMessagesToSend.length === 0) {
|
||||||
primaryText = appendInlineComments(primaryText, drafts);
|
primaryText = appendInlineComments(primaryText, drafts);
|
||||||
} else if (additionalParts.length > 0) {
|
} else if (additionalParts.length > 0) {
|
||||||
const lastPart = additionalParts[additionalParts.length - 1];
|
const lastPart = additionalParts[additionalParts.length - 1];
|
||||||
@@ -1786,7 +1802,9 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
if (!primaryText && primaryAttachments.length === 0 && additionalParts.length === 0) return;
|
if (!primaryText && primaryAttachments.length === 0 && additionalParts.length === 0) return;
|
||||||
|
|
||||||
// Clear queue and input
|
// Clear queue and input
|
||||||
if (currentSessionId && hasQueuedMessages) {
|
if (currentSessionId && queuedMessageId) {
|
||||||
|
removeFromQueue(currentSessionId, queuedMessageId);
|
||||||
|
} else if (currentSessionId && hasQueuedMessages) {
|
||||||
clearQueue(currentSessionId);
|
clearQueue(currentSessionId);
|
||||||
}
|
}
|
||||||
if (!queuedOnly) {
|
if (!queuedOnly) {
|
||||||
@@ -1862,13 +1880,13 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
const instructionsText = await renderMagicPrompt('session.summary.instructions', { topic_block: topicBlock });
|
const instructionsText = await renderMagicPrompt('session.summary.instructions', { topic_block: topicBlock });
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
visibleText,
|
visibleText,
|
||||||
currentProviderId,
|
providerIdToSend,
|
||||||
currentModelId,
|
modelIdToSend,
|
||||||
currentAgentName,
|
agentNameToSend,
|
||||||
[],
|
[],
|
||||||
agentMentionName,
|
agentMentionName,
|
||||||
[{ text: instructionsText, synthetic: true }],
|
[{ text: instructionsText, synthetic: true }],
|
||||||
currentVariant,
|
variantToSend,
|
||||||
inputMode,
|
inputMode,
|
||||||
);
|
);
|
||||||
scrollToBottom?.();
|
scrollToBottom?.();
|
||||||
@@ -1884,13 +1902,13 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
const instructionsText = await renderMagicPrompt('session.review.instructions');
|
const instructionsText = await renderMagicPrompt('session.review.instructions');
|
||||||
await sendMessage(
|
await sendMessage(
|
||||||
visibleText,
|
visibleText,
|
||||||
currentProviderId,
|
providerIdToSend,
|
||||||
currentModelId,
|
modelIdToSend,
|
||||||
currentAgentName,
|
agentNameToSend,
|
||||||
[],
|
[],
|
||||||
agentMentionName,
|
agentMentionName,
|
||||||
[{ text: instructionsText, synthetic: true }],
|
[{ text: instructionsText, synthetic: true }],
|
||||||
currentVariant,
|
variantToSend,
|
||||||
inputMode,
|
inputMode,
|
||||||
);
|
);
|
||||||
scrollToBottom?.();
|
scrollToBottom?.();
|
||||||
@@ -1933,13 +1951,13 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
|
|
||||||
const sendPromise = sendMessage(
|
const sendPromise = sendMessage(
|
||||||
primaryText,
|
primaryText,
|
||||||
currentProviderId,
|
providerIdToSend,
|
||||||
currentModelId,
|
modelIdToSend,
|
||||||
currentAgentName,
|
agentNameToSend,
|
||||||
primaryAttachments,
|
primaryAttachments,
|
||||||
agentMentionName,
|
agentMentionName,
|
||||||
additionalParts.length > 0 ? additionalParts : undefined,
|
additionalParts.length > 0 ? additionalParts : undefined,
|
||||||
currentVariant,
|
variantToSend,
|
||||||
inputMode
|
inputMode
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -3772,6 +3790,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
<AttachedFilesList onShowPopup={handleShowAttachmentPreview} />
|
<AttachedFilesList onShowPopup={handleShowAttachmentPreview} />
|
||||||
<QueuedMessageChips
|
<QueuedMessageChips
|
||||||
onEditMessage={handleQueuedMessageEdit}
|
onEditMessage={handleQueuedMessageEdit}
|
||||||
|
onSendMessage={handleQueuedMessageSend}
|
||||||
/>
|
/>
|
||||||
{hasDrafts && (
|
{hasDrafts && (
|
||||||
<div className="flex flex-wrap items-center gap-2 pb-2">
|
<div className="flex flex-wrap items-center gap-2 pb-2">
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ import { useSessionUIStore } from '@/sync/session-ui-store';
|
|||||||
import { useInputStore } from '@/sync/input-store';
|
import { useInputStore } from '@/sync/input-store';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
import { Icon } from "@/components/icon/Icon";
|
import { Icon } from "@/components/icon/Icon";
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
interface QueuedMessageChipProps {
|
interface QueuedMessageChipProps {
|
||||||
message: QueuedMessage;
|
message: QueuedMessage;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
onEdit: (message: QueuedMessage) => void;
|
onEdit: (message: QueuedMessage) => void;
|
||||||
|
onSend: (message: QueuedMessage) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QueuedMessageChip = memo(({ message, sessionId, onEdit }: QueuedMessageChipProps) => {
|
const QueuedMessageChip = memo(({ message, sessionId, onEdit, onSend }: QueuedMessageChipProps) => {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const removeFromQueue = useMessageQueueStore((state) => state.removeFromQueue);
|
const removeFromQueue = useMessageQueueStore((state) => state.removeFromQueue);
|
||||||
|
|
||||||
@@ -29,24 +31,31 @@ const QueuedMessageChip = memo(({ message, sessionId, onEdit }: QueuedMessageChi
|
|||||||
const attachmentCount = message.attachments?.length ?? 0;
|
const attachmentCount = message.attachments?.length ?? 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full items-center gap-1.5 text-sm h-5 px-1">
|
<div className="flex min-w-0 items-center gap-2 py-1">
|
||||||
<button
|
<span className="min-w-0 flex-1 truncate typography-ui-label text-foreground">
|
||||||
|
{firstLine || t('chat.queuedMessage.empty')}
|
||||||
|
{attachmentCount > 0 && (
|
||||||
|
<span className="ml-1 text-muted-foreground">{t('chat.queuedMessage.attachments', { count: attachmentCount })}</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
size="xs"
|
||||||
onClick={() => onEdit(message)}
|
onClick={() => onEdit(message)}
|
||||||
className="flex min-w-0 flex-1 items-center gap-1.5 text-left hover:opacity-80 transition-opacity"
|
|
||||||
>
|
>
|
||||||
<Icon name="message-2" className="h-4 w-4 flex-shrink-0 text-muted-foreground"
|
<Icon name="edit" className="h-3 w-3" aria-hidden="true" />
|
||||||
/>
|
{t('chat.queuedMessage.edit')}
|
||||||
<span className="text-muted-foreground flex-shrink-0">
|
</Button>
|
||||||
Queued
|
<Button
|
||||||
{attachmentCount > 0 && (
|
type="button"
|
||||||
<span className="ml-1">{t('chat.queuedMessage.attachments', { count: attachmentCount })}</span>
|
variant="secondary"
|
||||||
)}
|
size="xs"
|
||||||
</span>
|
onClick={() => onSend(message)}
|
||||||
<span className="text-foreground truncate">
|
>
|
||||||
{firstLine || t('chat.queuedMessage.empty')}
|
<Icon name="send-plane" className="h-3 w-3" aria-hidden="true" />
|
||||||
</span>
|
{t('chat.queuedMessage.send')}
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => removeFromQueue(sessionId, message.id)}
|
onClick={() => removeFromQueue(sessionId, message.id)}
|
||||||
@@ -63,11 +72,13 @@ QueuedMessageChip.displayName = 'QueuedMessageChip';
|
|||||||
|
|
||||||
interface QueuedMessageChipsProps {
|
interface QueuedMessageChipsProps {
|
||||||
onEditMessage: (content: string, attachments?: QueuedMessage['attachments']) => void;
|
onEditMessage: (content: string, attachments?: QueuedMessage['attachments']) => void;
|
||||||
|
onSendMessage: (messageId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY_QUEUE: QueuedMessage[] = [];
|
const EMPTY_QUEUE: QueuedMessage[] = [];
|
||||||
|
|
||||||
export const QueuedMessageChips = memo(({ onEditMessage }: QueuedMessageChipsProps) => {
|
export const QueuedMessageChips = memo(({ onEditMessage, onSendMessage }: QueuedMessageChipsProps) => {
|
||||||
|
const { t } = useI18n();
|
||||||
const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
|
const currentSessionId = useSessionUIStore((state) => state.currentSessionId);
|
||||||
const queuedMessages = useMessageQueueStore(
|
const queuedMessages = useMessageQueueStore(
|
||||||
React.useCallback(
|
React.useCallback(
|
||||||
@@ -93,20 +104,35 @@ export const QueuedMessageChips = memo(({ onEditMessage }: QueuedMessageChipsPro
|
|||||||
}
|
}
|
||||||
}, [currentSessionId, popToInput, onEditMessage]);
|
}, [currentSessionId, popToInput, onEditMessage]);
|
||||||
|
|
||||||
|
const handleSend = React.useCallback((message: QueuedMessage) => {
|
||||||
|
onSendMessage(message.id);
|
||||||
|
}, [onSendMessage]);
|
||||||
|
|
||||||
if (queuedMessages.length === 0 || !currentSessionId) {
|
if (queuedMessages.length === 0 || !currentSessionId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pb-2 w-full px-1 space-y-1">
|
<div className="pb-2 w-full px-1">
|
||||||
{queuedMessages.map((message) => (
|
<div className="rounded-xl border border-border/60 bg-[var(--surface-elevated)] text-[var(--surface-elevated-foreground)] shadow-sm overflow-hidden">
|
||||||
<QueuedMessageChip
|
<div className="flex w-full items-center gap-2 px-3 py-2 text-left">
|
||||||
key={message.id}
|
<span className="typography-ui-label font-medium text-foreground flex-shrink-0">
|
||||||
message={message}
|
{t('chat.queuedMessage.title')} {queuedMessages.length}
|
||||||
sessionId={currentSessionId}
|
</span>
|
||||||
onEdit={handleEdit}
|
<Icon name="time" className="ml-auto h-4 w-4 text-muted-foreground" aria-hidden="true" />
|
||||||
/>
|
</div>
|
||||||
))}
|
<div className="px-3 pb-3 flex flex-col gap-1.5 max-h-[10.5rem] overflow-y-auto">
|
||||||
|
{queuedMessages.map((message) => (
|
||||||
|
<QueuedMessageChip
|
||||||
|
key={message.id}
|
||||||
|
message={message}
|
||||||
|
sessionId={currentSessionId}
|
||||||
|
onEdit={handleEdit}
|
||||||
|
onSend={handleSend}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1519,6 +1519,9 @@ export const dict = {
|
|||||||
'chat.fileMentionAutocomplete.empty': 'No matches found',
|
'chat.fileMentionAutocomplete.empty': 'No matches found',
|
||||||
'chat.queuedMessage.attachments': '+{count} file(s)',
|
'chat.queuedMessage.attachments': '+{count} file(s)',
|
||||||
'chat.queuedMessage.empty': '(empty)',
|
'chat.queuedMessage.empty': '(empty)',
|
||||||
|
'chat.queuedMessage.title': 'Queued messages',
|
||||||
|
'chat.queuedMessage.edit': 'edit',
|
||||||
|
'chat.queuedMessage.send': 'send',
|
||||||
'chat.queuedMessage.removeAria': 'Remove from queue',
|
'chat.queuedMessage.removeAria': 'Remove from queue',
|
||||||
'chat.container.returnToParent.aria': 'Return to parent session',
|
'chat.container.returnToParent.aria': 'Return to parent session',
|
||||||
'chat.container.returnToParent.titleNamed': 'Return to: {title}',
|
'chat.container.returnToParent.titleNamed': 'Return to: {title}',
|
||||||
|
|||||||
@@ -1485,6 +1485,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"chat.fileMentionAutocomplete.empty": "No se encontraron coincidencias",
|
"chat.fileMentionAutocomplete.empty": "No se encontraron coincidencias",
|
||||||
"chat.queuedMessage.attachments": "+{count} archivo(s)",
|
"chat.queuedMessage.attachments": "+{count} archivo(s)",
|
||||||
"chat.queuedMessage.empty": "(vacío)",
|
"chat.queuedMessage.empty": "(vacío)",
|
||||||
|
"chat.queuedMessage.title": "Queued messages",
|
||||||
|
"chat.queuedMessage.edit": "edit",
|
||||||
|
"chat.queuedMessage.send": "send",
|
||||||
"chat.queuedMessage.removeAria": "Eliminar de la cola",
|
"chat.queuedMessage.removeAria": "Eliminar de la cola",
|
||||||
"chat.container.returnToParent.aria": "Volver a la sesión principal",
|
"chat.container.returnToParent.aria": "Volver a la sesión principal",
|
||||||
"chat.container.returnToParent.titleNamed": "Volver a: {title}",
|
"chat.container.returnToParent.titleNamed": "Volver a: {title}",
|
||||||
|
|||||||
@@ -1521,6 +1521,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.fileMentionAutocomplete.empty': '일치하는 항목 없음',
|
'chat.fileMentionAutocomplete.empty': '일치하는 항목 없음',
|
||||||
'chat.queuedMessage.attachments': '+{count} 파일(s)',
|
'chat.queuedMessage.attachments': '+{count} 파일(s)',
|
||||||
'chat.queuedMessage.empty': '(비어 있음)',
|
'chat.queuedMessage.empty': '(비어 있음)',
|
||||||
|
'chat.queuedMessage.title': 'Queued messages',
|
||||||
|
'chat.queuedMessage.edit': 'edit',
|
||||||
|
'chat.queuedMessage.send': 'send',
|
||||||
'chat.queuedMessage.removeAria': '큐에서 제거',
|
'chat.queuedMessage.removeAria': '큐에서 제거',
|
||||||
'chat.container.returnToParent.aria': '상위 세션으로 돌아가기',
|
'chat.container.returnToParent.aria': '상위 세션으로 돌아가기',
|
||||||
'chat.container.returnToParent.titleNamed': '돌아가기: {title}',
|
'chat.container.returnToParent.titleNamed': '돌아가기: {title}',
|
||||||
|
|||||||
@@ -512,6 +512,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.fileMentionAutocomplete.empty': 'Nie znaleziono pasujących',
|
'chat.fileMentionAutocomplete.empty': 'Nie znaleziono pasujących',
|
||||||
'chat.queuedMessage.attachments': '+{count} plik(i)',
|
'chat.queuedMessage.attachments': '+{count} plik(i)',
|
||||||
'chat.queuedMessage.empty': '(puste)',
|
'chat.queuedMessage.empty': '(puste)',
|
||||||
|
'chat.queuedMessage.title': 'Queued messages',
|
||||||
|
'chat.queuedMessage.edit': 'edit',
|
||||||
|
'chat.queuedMessage.send': 'send',
|
||||||
'chat.queuedMessage.removeAria': 'Usuń z kolejki',
|
'chat.queuedMessage.removeAria': 'Usuń z kolejki',
|
||||||
'chat.container.returnToParent.aria': 'Powrót do sesji nadrzędnej',
|
'chat.container.returnToParent.aria': 'Powrót do sesji nadrzędnej',
|
||||||
'chat.container.returnToParent.titleNamed': 'Powrót do: {title}',
|
'chat.container.returnToParent.titleNamed': 'Powrót do: {title}',
|
||||||
|
|||||||
@@ -1485,6 +1485,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"chat.fileMentionAutocomplete.empty": "Nenhuma correspondência encontrada",
|
"chat.fileMentionAutocomplete.empty": "Nenhuma correspondência encontrada",
|
||||||
"chat.queuedMessage.attachments": "+{count} arquivo(s)",
|
"chat.queuedMessage.attachments": "+{count} arquivo(s)",
|
||||||
"chat.queuedMessage.empty": "(vazio)",
|
"chat.queuedMessage.empty": "(vazio)",
|
||||||
|
"chat.queuedMessage.title": "Queued messages",
|
||||||
|
"chat.queuedMessage.edit": "edit",
|
||||||
|
"chat.queuedMessage.send": "send",
|
||||||
"chat.queuedMessage.removeAria": "Excluir da fila",
|
"chat.queuedMessage.removeAria": "Excluir da fila",
|
||||||
"chat.container.returnToParent.aria": "Voltar para a sessão principal",
|
"chat.container.returnToParent.aria": "Voltar para a sessão principal",
|
||||||
"chat.container.returnToParent.titleNamed": "Voltar para: {title}",
|
"chat.container.returnToParent.titleNamed": "Voltar para: {title}",
|
||||||
|
|||||||
@@ -1485,6 +1485,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"chat.fileMentionAutocomplete.empty": "Збігів не знайдено",
|
"chat.fileMentionAutocomplete.empty": "Збігів не знайдено",
|
||||||
"chat.queuedMessage.attachments": "+{count} файл(и)",
|
"chat.queuedMessage.attachments": "+{count} файл(и)",
|
||||||
"chat.queuedMessage.empty": "(порожній)",
|
"chat.queuedMessage.empty": "(порожній)",
|
||||||
|
"chat.queuedMessage.title": "Повідомлення в черзі",
|
||||||
|
"chat.queuedMessage.edit": "edit",
|
||||||
|
"chat.queuedMessage.send": "send",
|
||||||
"chat.queuedMessage.removeAria": "Видалити з черги",
|
"chat.queuedMessage.removeAria": "Видалити з черги",
|
||||||
"chat.container.returnToParent.aria": "Повернутися до батьківської сесії",
|
"chat.container.returnToParent.aria": "Повернутися до батьківської сесії",
|
||||||
"chat.container.returnToParent.titleNamed": "Повернутися до: {title}",
|
"chat.container.returnToParent.titleNamed": "Повернутися до: {title}",
|
||||||
|
|||||||
@@ -1485,6 +1485,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.fileMentionAutocomplete.empty': '未找到匹配项',
|
'chat.fileMentionAutocomplete.empty': '未找到匹配项',
|
||||||
'chat.queuedMessage.attachments': '+{count} 个文件',
|
'chat.queuedMessage.attachments': '+{count} 个文件',
|
||||||
'chat.queuedMessage.empty': '(空)',
|
'chat.queuedMessage.empty': '(空)',
|
||||||
|
'chat.queuedMessage.title': 'Queued messages',
|
||||||
|
'chat.queuedMessage.edit': 'edit',
|
||||||
|
'chat.queuedMessage.send': 'send',
|
||||||
'chat.queuedMessage.removeAria': '从队列移除',
|
'chat.queuedMessage.removeAria': '从队列移除',
|
||||||
'chat.container.returnToParent.aria': '返回父会话',
|
'chat.container.returnToParent.aria': '返回父会话',
|
||||||
'chat.container.returnToParent.titleNamed': '返回到:{title}',
|
'chat.container.returnToParent.titleNamed': '返回到:{title}',
|
||||||
|
|||||||
@@ -1482,6 +1482,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.fileMentionAutocomplete.empty': '找不到符合項目',
|
'chat.fileMentionAutocomplete.empty': '找不到符合項目',
|
||||||
'chat.queuedMessage.attachments': '+{count} 個檔案',
|
'chat.queuedMessage.attachments': '+{count} 個檔案',
|
||||||
'chat.queuedMessage.empty': '(空)',
|
'chat.queuedMessage.empty': '(空)',
|
||||||
|
'chat.queuedMessage.title': 'Queued messages',
|
||||||
|
'chat.queuedMessage.edit': 'edit',
|
||||||
|
'chat.queuedMessage.send': 'send',
|
||||||
'chat.queuedMessage.removeAria': '從佇列移除',
|
'chat.queuedMessage.removeAria': '從佇列移除',
|
||||||
'chat.container.returnToParent.aria': '返回父會話',
|
'chat.container.returnToParent.aria': '返回父會話',
|
||||||
'chat.container.returnToParent.titleNamed': '返回到:{title}',
|
'chat.container.returnToParent.titleNamed': '返回到:{title}',
|
||||||
|
|||||||
Reference in New Issue
Block a user