From b359c515277b4266fde2fb9a29b5356949c07c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?X=C3=AC=20G=C3=A0?= Date: Fri, 27 Feb 2026 22:28:10 +0700 Subject: [PATCH] feat: add copy icon for user messages (#449) * feat: add copy icon for user messages Expose a copy control on user chat bubbles so previous content can be reused quickly. * fix: Copy control is effectively hidden on touch devices * fix: copyTextToClipboard doesn't need timer --------- Co-authored-by: dev Co-authored-by: Haileyesus <118998054+blackmammoth@users.noreply.github.com> --- .../view/subcomponents/MessageComponent.tsx | 45 +++++++++++++++++-- src/i18n/locales/en/chat.json | 4 ++ src/i18n/locales/ja/chat.json | 4 ++ src/i18n/locales/ko/chat.json | 4 ++ src/i18n/locales/zh-CN/chat.json | 4 ++ 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/components/chat/view/subcomponents/MessageComponent.tsx b/src/components/chat/view/subcomponents/MessageComponent.tsx index 2abc170..fe12f42 100644 --- a/src/components/chat/view/subcomponents/MessageComponent.tsx +++ b/src/components/chat/view/subcomponents/MessageComponent.tsx @@ -10,6 +10,7 @@ import type { import { Markdown } from './Markdown'; import { formatUsageLimitText } from '../../utils/chatFormatting'; import { getClaudePermissionSuggestion } from '../../utils/chatPermissions'; +import { copyTextToClipboard } from '../../../../utils/clipboard'; import type { Project } from '../../../../types/app'; import { ToolRenderer, shouldHideToolResult } from '../../tools'; @@ -53,6 +54,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile const [isExpanded, setIsExpanded] = React.useState(false); const permissionSuggestion = getClaudePermissionSuggestion(message, provider); const [permissionGrantState, setPermissionGrantState] = React.useState('idle'); + const [messageCopied, setMessageCopied] = React.useState(false); React.useEffect(() => { @@ -100,7 +102,7 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile {message.type === 'user' ? ( /* User message bubble on the right */
-
+
{message.content}
@@ -117,8 +119,45 @@ const MessageComponent = memo(({ message, index, prevMessage, createDiff, onFile ))}
)} -
- {formattedTime} +
+ + {formattedTime}
{!isGrouped && ( diff --git a/src/i18n/locales/en/chat.json b/src/i18n/locales/en/chat.json index b907f18..dc2b310 100644 --- a/src/i18n/locales/en/chat.json +++ b/src/i18n/locales/en/chat.json @@ -4,6 +4,10 @@ "copied": "Copied", "copyCode": "Copy code" }, + "copyMessage": { + "copy": "Copy message", + "copied": "Message copied" + }, "messageTypes": { "user": "U", "error": "Error", diff --git a/src/i18n/locales/ja/chat.json b/src/i18n/locales/ja/chat.json index 0d81a1a..2c3dda4 100644 --- a/src/i18n/locales/ja/chat.json +++ b/src/i18n/locales/ja/chat.json @@ -4,6 +4,10 @@ "copied": "コピーしました", "copyCode": "コードをコピー" }, + "copyMessage": { + "copy": "メッセージをコピー", + "copied": "メッセージをコピーしました" + }, "messageTypes": { "user": "U", "error": "エラー", diff --git a/src/i18n/locales/ko/chat.json b/src/i18n/locales/ko/chat.json index cddc89a..ca66e3a 100644 --- a/src/i18n/locales/ko/chat.json +++ b/src/i18n/locales/ko/chat.json @@ -4,6 +4,10 @@ "copied": "복사됨", "copyCode": "코드 복사" }, + "copyMessage": { + "copy": "메시지 복사", + "copied": "메시지 복사됨" + }, "messageTypes": { "user": "U", "error": "오류", diff --git a/src/i18n/locales/zh-CN/chat.json b/src/i18n/locales/zh-CN/chat.json index 678ea17..1fdbc46 100644 --- a/src/i18n/locales/zh-CN/chat.json +++ b/src/i18n/locales/zh-CN/chat.json @@ -4,6 +4,10 @@ "copied": "已复制", "copyCode": "复制代码" }, + "copyMessage": { + "copy": "复制消息", + "copied": "消息已复制" + }, "messageTypes": { "user": "U", "error": "错误",