12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- import ReactMarkdown from "react-markdown";
- import remarkGfm from "remark-gfm";
- import rehypeRaw from "rehype-raw";
- import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
- import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
- import { useState } from "react";
- interface MarkdownViewerProps {
- content: string;
- }
- const CodeHeader: React.FC<{
- language: string;
- onCopy: () => void;
- copied: boolean;
- }> = ({ language, onCopy, copied }) => (
- <div
- className="flex justify-between items-center text-white"
- style={{ background: "#afadad", padding: "3px 4px" }}
- >
- <div className="flex items-center">
- <span className="text-xs text-#fff">{language}</span>
- </div>
- <div>
- <span className="cursor-pointer text-xs" onClick={onCopy}>
- {copied ? "已复制!" : "复制代码"}
- </span>
- </div>
- </div>
- );
- const MarkdownViewer: React.FC<MarkdownViewerProps> = ({ content }) => {
- const [copiedIndex, setCopiedIndex] = useState<number | null>(null);
- const handleCopy = (code: string, index: number) => {
- navigator.clipboard.writeText(code);
- setCopiedIndex(index);
- setTimeout(() => setCopiedIndex(null), 2000);
- };
- return (
- <ReactMarkdown
- remarkPlugins={[remarkGfm]}
- rehypePlugins={[rehypeRaw]}
- components={{
- code({ node, className, children, ...props }) {
- const match = /language-(\w+)/.exec(className || "");
- const code = String(children).replace(/\n$/, "");
- const language = match ? match[1] : "";
- if (match) {
- return (
- <div className="rounded-md overflow-hidden mb-4">
- <CodeHeader
- language={language}
- onCopy={() =>
- handleCopy(code, node?.position?.start.line ?? 0)
- }
- copied={copiedIndex === node?.position?.start.line}
- />
- <div className="max-w-full overflow-x-auto">
- <SyntaxHighlighter
- style={vscDarkPlus}
- language={language}
- PreTag="div"
- {...props}
- customStyle={{
- margin: 0,
- borderTopLeftRadius: 0,
- borderTopRightRadius: 0,
- }}
- >
- {code}
- </SyntaxHighlighter>
- </div>
- </div>
- );
- }
- return (
- <code className={className} {...props}>
- {children}
- </code>
- );
- },
- }}
- >
- {content}
- </ReactMarkdown>
- );
- };
- export default MarkdownViewer;
|