|
import ReactMarkdown from 'react-markdown' |
|
import remarkGfm from 'remark-gfm' |
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' |
|
import { oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism' |
|
|
|
interface MarkdownRendererProps { |
|
content: string |
|
} |
|
|
|
const MarkdownRenderer = ({ content }: MarkdownRendererProps) => { |
|
const cleanContent = content |
|
.replace(/^---\s*\n.*?\n---\s*\n/s, '') |
|
.replace(/<[^>]*>/g, '') |
|
|
|
return ( |
|
<ReactMarkdown |
|
remarkPlugins={[remarkGfm]} |
|
disallowedElements={['script', 'style', 'iframe', 'object', 'embed']} |
|
unwrapDisallowed={true} |
|
components={{ |
|
code: ({ className, children, ...props }: any) => { |
|
const match = /language-(\w+)/.exec(className || '') |
|
const isInline = !match |
|
return !isInline ? ( |
|
<SyntaxHighlighter |
|
style={oneLight} |
|
language={match[1]} |
|
PreTag="div" |
|
className="rounded-md my-4 border text-sm" |
|
{...props} |
|
> |
|
{String(children).replace(/\n$/, '')} |
|
</SyntaxHighlighter> |
|
) : ( |
|
<code |
|
className="bg-gray-100 px-1 py-0.5 rounded-sm text-sm font-mono" |
|
{...props} |
|
> |
|
{children} |
|
</code> |
|
) |
|
}, |
|
a: ({ children, href }) => ( |
|
<a |
|
href={href} |
|
className="text-blue-600 hover:text-blue-800 underline" |
|
target="_blank" |
|
rel="noopener noreferrer" |
|
> |
|
{children} |
|
</a> |
|
), |
|
table: ({ children }) => ( |
|
<div className="overflow-x-auto"> |
|
<table className="min-w-full divide-y divide-gray-200"> |
|
{children} |
|
</table> |
|
</div> |
|
), |
|
thead: ({ children }) => ( |
|
<thead className="bg-gray-50">{children}</thead> |
|
), |
|
tbody: ({ children }) => ( |
|
<tbody className="bg-white divide-y divide-gray-200"> |
|
{children} |
|
</tbody> |
|
), |
|
th: ({ children }) => ( |
|
<th className="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider border border-gray-300"> |
|
{children} |
|
</th> |
|
), |
|
td: ({ children }) => ( |
|
<td className="px-3 py-2 whitespace-nowrap text-sm text-gray-900 border border-gray-300"> |
|
{children} |
|
</td> |
|
), |
|
ul: ({ children }) => ( |
|
<ul className="list-disc list-inside space-y-1 ml-4 mb-4"> |
|
{children} |
|
</ul> |
|
), |
|
ol: ({ children }) => ( |
|
<ol className="list-decimal list-inside space-y-1 ml-4 mb-4"> |
|
{children} |
|
</ol> |
|
), |
|
li: ({ children }) => ( |
|
<li className="text-sm text-gray-900">{children}</li> |
|
), |
|
h1: ({ children }) => ( |
|
<h1 className="text-xl font-semibold text-gray-900 mb-3 mt-4"> |
|
{children} |
|
</h1> |
|
), |
|
h2: ({ children }) => ( |
|
<h2 className="text-lg font-semibold text-gray-900 mb-2 mt-3"> |
|
{children} |
|
</h2> |
|
), |
|
h3: ({ children }) => ( |
|
<h3 className="text-base font-semibold text-gray-900 mb-2 mt-3"> |
|
{children} |
|
</h3> |
|
) |
|
}} |
|
> |
|
{cleanContent} |
|
</ReactMarkdown> |
|
) |
|
} |
|
|
|
export default MarkdownRenderer |
|
|