markdown-readonly.tsx•1.78 kB
import React from "react";
import CopyBtn from "@/components/ui/copy-button";
import { cn } from "@/lib/utils";
import Markdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { dracula } from "react-syntax-highlighter/dist/cjs/styles/prism";
import remarkBreaks from "remark-breaks";
import remarkGfm from "remark-gfm";
function PreWithCopyBtn({ className, ...props }: React.ComponentProps<"pre">) {
const ref = React.useRef<HTMLPreElement>(null);
return (
<span className="group relative">
<CopyBtn
className="absolute right-1 top-1 m-1 hidden text-white group-hover:block"
getStringToCopy={() => {
return ref.current?.textContent ?? "";
}}
/>
<pre ref={ref} className={cn(className, "")} {...props} />
</span>
);
}
export function MarkdownReadonly({
children: markdown,
className,
}: {
children: string;
className?: string;
}) {
return (
<Markdown
remarkPlugins={[remarkGfm, remarkBreaks]}
className={cn("prose dark:prose-invert", className)}
components={{
pre({ ...props }) {
return <PreWithCopyBtn {...props} />;
},
code({ className, children, ...props }) {
const match = /language-(\w+)/.exec(className ?? "");
return match ? (
<SyntaxHighlighter
PreTag="div"
language={match[1]}
{...props}
style={dracula}
>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
}}
>
{markdown}
</Markdown>
);
}