feat: add copy-to-clipboard button to code blocks
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { createFileRoute, Link } from '@tanstack/react-router'
|
||||
import { useState } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter'
|
||||
@@ -51,34 +52,67 @@ function CodeBlock({
|
||||
readonly className?: string
|
||||
readonly children?: React.ReactNode
|
||||
}) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const match = /language-(\w+)/.exec(className ?? '')
|
||||
|
||||
if (!match) {
|
||||
return <code className={className}>{children}</code>
|
||||
}
|
||||
|
||||
const handleCopy = () => {
|
||||
void navigator.clipboard.writeText(String(children).replace(/\n$/, ''))
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
<SyntaxHighlighter
|
||||
style={atomOneDark}
|
||||
language={match[1]}
|
||||
lineNumberStyle={{ color: 'var(--sea-ink-soft)' }}
|
||||
wrapLongLines
|
||||
showLineNumbers
|
||||
customStyle={{
|
||||
width: '100%',
|
||||
background: 'var(--surface)',
|
||||
border: '1px solid var(--line)',
|
||||
borderRadius: '0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '1.7',
|
||||
padding: '1.25rem 1.5rem',
|
||||
}}
|
||||
codeTagProps={{
|
||||
style: { fontFamily: 'ui-monospace, monospace' },
|
||||
}}
|
||||
wrapLines
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</SyntaxHighlighter>
|
||||
<div style={{ position: 'relative', margin: '1.5rem 0' }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopy}
|
||||
aria-label="Copy code"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '0.6rem',
|
||||
right: '0.6rem',
|
||||
zIndex: 10,
|
||||
background: 'var(--surface-strong)',
|
||||
border: '1px solid var(--line)',
|
||||
borderRadius: '0.4rem',
|
||||
color: copied ? 'var(--lagoon)' : 'var(--sea-ink-soft)',
|
||||
cursor: 'pointer',
|
||||
fontSize: '0.7rem',
|
||||
fontFamily: 'Manrope, sans-serif',
|
||||
letterSpacing: '0.05em',
|
||||
padding: '0.25rem 0.6rem',
|
||||
transition: 'color 0.2s',
|
||||
}}
|
||||
>
|
||||
{copied ? 'Copied!' : 'Copy'}
|
||||
</button>
|
||||
<SyntaxHighlighter
|
||||
style={atomOneDark}
|
||||
language={match[1]}
|
||||
lineNumberStyle={{ color: 'var(--sea-ink-soft)' }}
|
||||
wrapLongLines
|
||||
showLineNumbers
|
||||
customStyle={{
|
||||
width: '100%',
|
||||
background: 'var(--surface)',
|
||||
border: '1px solid var(--line)',
|
||||
borderRadius: '0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
lineHeight: '1.7',
|
||||
padding: '1.25rem 1.5rem',
|
||||
}}
|
||||
codeTagProps={{
|
||||
style: { fontFamily: 'ui-monospace, monospace' },
|
||||
}}
|
||||
wrapLines
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user