Skip to content

Commit 7ee85af

Browse files
author
Dylan Huang
committed
copy button for message bubbles
1 parent 88ac21a commit 7ee85af

1 file changed

Lines changed: 40 additions & 2 deletions

File tree

vite-app/src/components/MessageBubble.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import type { Message } from "../types/eval-protocol";
22
import { useState } from "react";
3+
import Button from "./Button";
4+
import { Tooltip } from "./Tooltip";
35

46
export const MessageBubble = ({ message }: { message: Message }) => {
57
const [isExpanded, setIsExpanded] = useState(false);
8+
const [copySuccess, setCopySuccess] = useState(false);
69
const isUser = message.role === "user";
710
const isSystem = message.role === "system";
811
const isTool = message.role === "tool";
@@ -31,10 +34,20 @@ export const MessageBubble = ({ message }: { message: Message }) => {
3134
? messageContent.substring(0, 200) + "..."
3235
: messageContent;
3336

37+
const handleCopy = async () => {
38+
try {
39+
await navigator.clipboard.writeText(messageContent);
40+
setCopySuccess(true);
41+
setTimeout(() => setCopySuccess(false), 2000);
42+
} catch (err) {
43+
console.error("Failed to copy message:", err);
44+
}
45+
};
46+
3447
return (
3548
<div className={`flex ${isUser ? "justify-end" : "justify-start"} mb-1`}>
3649
<div
37-
className={`max-w-sm lg:max-w-md xl:max-w-lg px-2 py-1 border text-xs ${
50+
className={`max-w-sm lg:max-w-md xl:max-w-lg px-2 py-1 border text-xs relative ${
3851
isUser
3952
? "bg-blue-50 border-blue-200 text-blue-900"
4053
: isSystem
@@ -44,7 +57,32 @@ export const MessageBubble = ({ message }: { message: Message }) => {
4457
: "bg-yellow-50 border-yellow-200 text-yellow-900"
4558
}`}
4659
>
47-
<div className="font-semibold text-xs mb-0.5 capitalize">
60+
{/* Copy button positioned in top-right corner */}
61+
<div className="absolute top-1 right-1">
62+
<Tooltip
63+
content={copySuccess ? "Copied!" : "Copy message to clipboard"}
64+
position="top"
65+
>
66+
<Button
67+
onClick={handleCopy}
68+
size="sm"
69+
variant="secondary"
70+
className={`p-0.5 h-5 text-[10px] opacity-60 hover:opacity-100 transition-opacity cursor-pointer ${
71+
isUser
72+
? "text-blue-600 hover:bg-blue-100"
73+
: isSystem
74+
? "text-gray-600 hover:bg-gray-100"
75+
: isTool
76+
? "text-green-600 hover:bg-green-100"
77+
: "text-yellow-600 hover:bg-yellow-100"
78+
}`}
79+
>
80+
Copy
81+
</Button>
82+
</Tooltip>
83+
</div>
84+
85+
<div className="font-semibold text-xs mb-0.5 capitalize pr-8">
4886
{message.role}
4987
</div>
5088
<div className="whitespace-pre-wrap break-words overflow-hidden text-xs">

0 commit comments

Comments
 (0)