Skip to content

Commit 86d0b38

Browse files
committed
add copy button to activities
1 parent 67bceb8 commit 86d0b38

File tree

1 file changed

+41
-2
lines changed

1 file changed

+41
-2
lines changed

components/conversation/activity.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { ReactNode, useEffect, useMemo, useState } from 'react';
44
import dayjs from 'dayjs';
55
import timezone from 'dayjs/plugin/timezone';
66
import utc from 'dayjs/plugin/utc';
7-
import { Ban as Error, CircleCheck, TriangleAlert, ChevronRight } from 'lucide-react';
7+
import { Ban as Error, CircleCheck, TriangleAlert, ChevronRight, Copy, Check } from 'lucide-react';
88
import { LuRefreshCw as AutorenewOutlined, LuInfo as Info, LuPencil as Pencil } from 'react-icons/lu';
99
import { FaRunning } from 'react-icons/fa';
1010
import { TfiThought } from 'react-icons/tfi';
@@ -45,6 +45,26 @@ function ModernSubactivity({
4545
completedSubactivities: number;
4646
}) {
4747
const [isExpanded, setIsExpanded] = useState(false);
48+
const [copiedIndex, setCopiedIndex] = useState<number | null>(null);
49+
50+
// Function to copy text to clipboard
51+
const copyToClipboard = async (text: string, index: number) => {
52+
try {
53+
await navigator.clipboard.writeText(text);
54+
setCopiedIndex(index);
55+
setTimeout(() => setCopiedIndex(null), 2000); // Reset after 2 seconds
56+
} catch (err) {
57+
console.error('Failed to copy text: ', err);
58+
}
59+
};
60+
61+
// Function to clean the message content
62+
const getCleanContent = (message: string): string => {
63+
// Remove the [SUBACTIVITY] prefix and any bracketed metadata
64+
const messageBody = message.substring(message.indexOf(' '));
65+
// Remove any remaining [TYPE] brackets at the start
66+
return messageBody.replace(/^\[[^\]]+]\s*/, '').trim();
67+
};
4868

4969
return (
5070
<div className='w-full bg-gradient-to-br from-background/60 to-muted/30 border border-border/30 rounded-2xl p-4 backdrop-blur-md shadow-sm my-2'>
@@ -93,7 +113,7 @@ function ModernSubactivity({
93113
<div
94114
key={`${child.timestamp}-${child.message.slice(0, 20)}`}
95115
className={cn(
96-
'flex items-start gap-3 opacity-0 animate-slide-in-from-left rounded-xl p-3 transition-all duration-200',
116+
'group flex items-start gap-3 opacity-0 animate-slide-in-from-left rounded-xl p-3 transition-all duration-200 hover:bg-gradient-to-r hover:from-muted/10 hover:to-muted/5',
97117
getAnimationDelay(index),
98118
isCurrentlyRunning
99119
? 'bg-gradient-to-r from-primary/5 to-primary/10 border-l-2 border-primary/30'
@@ -121,6 +141,25 @@ function ModernSubactivity({
121141
<div className='text-xs leading-relaxed text-foreground/85 flex-1'>
122142
<MarkdownBlock content={messageBody.trim()} />
123143
</div>
144+
<Tooltip>
145+
<TooltipTrigger asChild>
146+
<Button
147+
variant='ghost'
148+
size='sm'
149+
onClick={() => copyToClipboard(getCleanContent(child.message), index)}
150+
className='opacity-0 group-hover:opacity-100 transition-opacity duration-200 h-6 w-6 p-0 hover:bg-muted/50 rounded-md'
151+
>
152+
{copiedIndex === index ? (
153+
<Check className='w-3 h-3 text-green-600' />
154+
) : (
155+
<Copy className='w-3 h-3 text-muted-foreground' />
156+
)}
157+
</Button>
158+
</TooltipTrigger>
159+
<TooltipContent side='top' className='text-xs'>
160+
{copiedIndex === index ? 'Copied!' : 'Copy content'}
161+
</TooltipContent>
162+
</Tooltip>
124163
</div>
125164
);
126165
})}

0 commit comments

Comments
 (0)