|
1 | | -import { memo } from 'react'; |
| 1 | +'use client'; |
| 2 | + |
| 3 | +import { memo, useState } from 'react'; |
2 | 4 | import { ModelResponse, Model, Message } from '@/types'; |
3 | 5 | import { SynthesizedResponse } from './SynthesizedResponse'; |
4 | 6 | import { ResponsePanel } from './ResponsePanel'; |
5 | | -import { User, Sparkles, Info } from 'lucide-react'; |
| 7 | +import { User, Sparkles, Info, Copy, Check } from 'lucide-react'; |
6 | 8 | import { MarkdownRenderer } from './MarkdownRenderer'; |
7 | 9 |
|
8 | 10 | interface ChatMessageProps { |
@@ -30,9 +32,30 @@ export const ChatMessage = memo(function ChatMessage({ |
30 | 32 | synthesisPromptData |
31 | 33 | }: ChatMessageProps) { |
32 | 34 |
|
| 35 | + const [copiedUser, setCopiedUser] = useState(false); |
| 36 | + |
| 37 | + const handleCopyUser = async () => { |
| 38 | + if (!content) return; |
| 39 | + try { |
| 40 | + await navigator.clipboard.writeText(content); |
| 41 | + setCopiedUser(true); |
| 42 | + setTimeout(() => setCopiedUser(false), 2000); |
| 43 | + } catch (error) { |
| 44 | + console.error('Failed to copy:', error); |
| 45 | + } |
| 46 | + }; |
| 47 | + |
33 | 48 | if (role === 'user') { |
34 | 49 | return ( |
35 | | - <div className="chat-message user-message"> |
| 50 | + <div className="chat-message user-message group"> |
| 51 | + <button |
| 52 | + onClick={handleCopyUser} |
| 53 | + className="opacity-0 group-hover:opacity-100 transition-opacity p-2 text-text-tertiary hover:text-text-primary self-start mt-1 mr-1" |
| 54 | + aria-label="Copy message" |
| 55 | + title="Copy message" |
| 56 | + > |
| 57 | + {copiedUser ? <Check size={16} /> : <Copy size={16} />} |
| 58 | + </button> |
36 | 59 | <div className="message-content user-content"> |
37 | 60 | <MarkdownRenderer content={content} forceNewlines={true} /> |
38 | 61 | </div> |
|
0 commit comments