|
1 | 1 | "use client"; |
2 | 2 |
|
| 3 | +import React from "react"; |
3 | 4 | import ReactMarkdown from "react-markdown"; |
4 | 5 | import remarkGfm from "remark-gfm"; |
5 | 6 | import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; |
6 | 7 | import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism"; |
7 | 8 |
|
8 | 9 | export default function MarkdownRenderer({ content }) { |
| 10 | + |
| 11 | + const [copiedCode, setCopiedCode] = React.useState(null); |
| 12 | + const handleCopy = (code) => { |
| 13 | + navigator.clipboard.writeText(code) |
| 14 | + .then(() => { |
| 15 | + setCopiedCode(code); |
| 16 | + setTimeout(() => setCopiedCode(null), 2000); |
| 17 | + }) |
| 18 | + .catch((err) => console.error('Failed to copy code: ', err)); |
| 19 | + }; |
| 20 | + |
9 | 21 | return ( |
10 | 22 | <ReactMarkdown |
11 | 23 | remarkPlugins={[remarkGfm]} |
12 | 24 | components={{ |
13 | 25 | code({ node, inline, className, children, ...props }) { |
14 | 26 | const match = /language-(\w+)/.exec(className || ""); |
| 27 | + const codeString = String(children).replace(/\n$/, ""); |
15 | 28 | return !inline && match ? ( |
16 | | - <SyntaxHighlighter |
17 | | - style={oneDark} |
18 | | - language={match[1]} |
19 | | - PreTag="div" |
20 | | - className="rounded-md" |
21 | | - {...props} |
22 | | - > |
23 | | - {String(children).replace(/\n$/, "")} |
24 | | - </SyntaxHighlighter> |
| 29 | + <div className="relative group"> |
| 30 | + <SyntaxHighlighter |
| 31 | + style={oneDark} |
| 32 | + language={match[1]} |
| 33 | + PreTag="div" |
| 34 | + className="rounded-md" |
| 35 | + {...props} |
| 36 | + > |
| 37 | + {codeString} |
| 38 | + </SyntaxHighlighter> |
| 39 | + <button |
| 40 | + onClick={() => handleCopy(codeString)} |
| 41 | + className="absolute top-2 right-2 bg-gray-700 hover:bg-gray-600 text-white px-2 py-1 text-sm rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200" |
| 42 | + > |
| 43 | + {copiedCode === codeString ? "Copied!" : "Copy"} |
| 44 | + </button> |
| 45 | + </div> |
25 | 46 | ) : ( |
26 | 47 | <code className="bg-muted px-1 py-0.5 rounded text-sm" {...props}> |
27 | 48 | {children} |
|
0 commit comments