File size: 2,855 Bytes
6e29063
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { CodeHighlight } from "@mantine/code-highlight";
import { Box, Button, Code } from "@mantine/core";
import React, { lazy } from "react";
import rehypeExternalLinks from "rehype-external-links";
import remarkGfm from "remark-gfm";

const Markdown = lazy(() => import("react-markdown"));
interface MarkdownRendererProps {
  content: string;
  enableCopy?: boolean;
  className?: string;
}

export default function MarkdownRenderer({
  content,
  enableCopy = true,
  className = "",
}: MarkdownRendererProps) {
  if (!content) {
    return null;
  }

  return (
    <Box className={className}>
      <Markdown
        remarkPlugins={[remarkGfm]}
        rehypePlugins={[
          [
            rehypeExternalLinks,
            { target: "_blank", rel: ["nofollow", "noopener", "noreferrer"] },
          ],
        ]}
        components={{
          a(props) {
            const { href, children } = props;
            return (
              <Button
                component="a"
                href={href}
                target="_blank"
                rel="nofollow noopener noreferrer"
                variant="light"
                color="gray"
                size="compact-xs"
                radius="xl"
                style={{
                  textDecoration: "none",
                  transform: "translateY(-2px)",
                }}
              >
                {children}
              </Button>
            );
          },
          li(props) {
            const { children } = props;
            const processedChildren = React.Children.map(children, (child) => {
              if (React.isValidElement(child) && child.type === "p") {
                return (child.props as { children: React.ReactNode }).children;
              }
              return child;
            });
            return <li>{processedChildren}</li>;
          },
          pre(props) {
            return <>{props.children}</>;
          },
          code(props) {
            const { children, className, node } = props;
            const codeContent = children?.toString().replace(/\n$/, "") ?? "";
            let language = "text";

            if (className) {
              const languageMatch = /language-(\w+)/.exec(className);
              if (languageMatch) language = languageMatch[1];
            }

            if (
              language === "text" &&
              node?.position?.end.line === node?.position?.start.line
            ) {
              return <Code>{codeContent}</Code>;
            }

            return (
              <CodeHighlight
                code={codeContent}
                language={language}
                radius="md"
                withCopyButton={enableCopy}
                mb="xs"
              />
            );
          },
        }}
      >
        {content}
      </Markdown>
    </Box>
  );
}