feat: add retry/resend button on user messages
Hover over any user message to reveal a retry button (↻) that resends the message text. Disabled while a response is generating. Includes EN/FR i18n strings.
This commit is contained in:
@@ -112,7 +112,7 @@ export function Chat({ messages, isGenerating, status, onSend, onAbort }: Props)
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{messages.filter(hasVisibleContent).map(msg => (
|
{messages.filter(hasVisibleContent).map(msg => (
|
||||||
<ChatMessageComponent key={msg.id} message={msg} />
|
<ChatMessageComponent key={msg.id} message={msg} onRetry={!isGenerating ? handleSend : undefined} />
|
||||||
))}
|
))}
|
||||||
{showTyping && <TypingIndicator />}
|
{showTyping && <TypingIndicator />}
|
||||||
<div ref={bottomRef} />
|
<div ref={bottomRef} />
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { ThinkingBlock } from './ThinkingBlock';
|
|||||||
import { CodeBlock } from './CodeBlock';
|
import { CodeBlock } from './CodeBlock';
|
||||||
import { ToolCall } from './ToolCall';
|
import { ToolCall } from './ToolCall';
|
||||||
import { ImageBlock, buildImageSrc } from './ImageBlock';
|
import { ImageBlock, buildImageSrc } from './ImageBlock';
|
||||||
import { Bot, User, Wrench, Copy, Check } from 'lucide-react';
|
import { Bot, User, Wrench, Copy, Check, RefreshCw } from 'lucide-react';
|
||||||
import { t, getLocale } from '../lib/i18n';
|
import { t, getLocale } from '../lib/i18n';
|
||||||
import { useLocale } from '../hooks/useLocale';
|
import { useLocale } from '../hooks/useLocale';
|
||||||
// ChevronDown, ChevronRight, Wrench still used by InternalOnlyMessage
|
// ChevronDown, ChevronRight, Wrench still used by InternalOnlyMessage
|
||||||
@@ -243,7 +243,7 @@ function getPlainText(message: ChatMessageType): string {
|
|||||||
return message.content;
|
return message.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatMessageComponent({ message }: { message: ChatMessageType }) {
|
export function ChatMessageComponent({ message, onRetry }: { message: ChatMessageType; onRetry?: (text: string) => void }) {
|
||||||
useLocale(); // re-render on locale change
|
useLocale(); // re-render on locale change
|
||||||
const isUser = message.role === 'user';
|
const isUser = message.role === 'user';
|
||||||
|
|
||||||
@@ -278,6 +278,17 @@ export function ChatMessageComponent({ message }: { message: ChatMessageType })
|
|||||||
{!isUser && !message.isStreaming && getPlainText(message).trim() && (
|
{!isUser && !message.isStreaming && getPlainText(message).trim() && (
|
||||||
<CopyButton text={getPlainText(message)} />
|
<CopyButton text={getPlainText(message)} />
|
||||||
)}
|
)}
|
||||||
|
{/* Retry button (user messages only) */}
|
||||||
|
{isUser && onRetry && (
|
||||||
|
<button
|
||||||
|
onClick={() => onRetry(getPlainText(message))}
|
||||||
|
className="absolute top-2 right-2 h-7 w-7 rounded-lg border border-white/8 bg-zinc-800/80 backdrop-blur-sm flex items-center justify-center text-zinc-400 hover:text-cyan-300 hover:border-cyan-400/30 transition-all opacity-0 group-hover:opacity-100"
|
||||||
|
title={t('message.retry')}
|
||||||
|
aria-label={t('message.retry')}
|
||||||
|
>
|
||||||
|
<RefreshCw size={13} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{/* User-visible text */}
|
{/* User-visible text */}
|
||||||
{message.blocks.length > 0 ? renderTextBlocks(message.blocks) : (
|
{message.blocks.length > 0 ? renderTextBlocks(message.blocks) : (
|
||||||
<div className="markdown-body">
|
<div className="markdown-body">
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const en = {
|
|||||||
// Message actions
|
// Message actions
|
||||||
'message.copy': 'Copy message',
|
'message.copy': 'Copy message',
|
||||||
'message.copied': 'Copied!',
|
'message.copied': 'Copied!',
|
||||||
|
'message.retry': 'Resend message',
|
||||||
|
|
||||||
// Timestamps
|
// Timestamps
|
||||||
'time.yesterday': 'Yesterday',
|
'time.yesterday': 'Yesterday',
|
||||||
@@ -127,6 +128,7 @@ const fr: Record<keyof typeof en, string> = {
|
|||||||
|
|
||||||
'message.copy': 'Copier le message',
|
'message.copy': 'Copier le message',
|
||||||
'message.copied': 'Copié !',
|
'message.copied': 'Copié !',
|
||||||
|
'message.retry': 'Renvoyer le message',
|
||||||
|
|
||||||
'time.yesterday': 'Hier',
|
'time.yesterday': 'Hier',
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user