添加mermaid图表接口 (#85)

* 添加mermaid图表接口

* 添加类型文件

* Update package.json

* Create next-env.d.ts
This commit is contained in:
stakeswky
2023-06-10 23:27:11 +08:00
committed by GitHub
parent 9a831b5b8b
commit d057d20c17
5 changed files with 117 additions and 23 deletions

View File

@@ -0,0 +1,63 @@
import React, { FC, useEffect, useState, useRef } from 'react';
import mermaid from 'mermaid';
import { Spinner } from '@chakra-ui/react';
interface MermaidCodeBlockProps {
code: string;
}
const MermaidCodeBlock: FC<MermaidCodeBlockProps> = ({ code }) => {
const [svg, setSvg] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const codeTimeoutIdRef = useRef<number | null>(null);
useEffect(() => {
if (codeTimeoutIdRef.current) {
clearTimeout(codeTimeoutIdRef.current);
}
codeTimeoutIdRef.current = window.setTimeout(() => {
setLoading(true);
const mermaidAPI = (mermaid as any).mermaidAPI as any;
mermaidAPI.initialize({ startOnLoad: false, theme: 'forest' });
try {
mermaidAPI.parse(code);
mermaidAPI.render('mermaid-svg', code, (svgCode: string) => {
setSvg(svgCode);
setLoading(false);
});
} catch (error) {
console.error('Error parsing Mermaid code:', '\n', error, '\n', 'Code:', code);
setLoading(false);
return;
}
}, 1000);
}, [code]);
useEffect(() => {
return () => {
if (codeTimeoutIdRef.current) {
clearTimeout(codeTimeoutIdRef.current);
}
};
}, []);
return (
<>
{loading ? (
<div className="loading">
<img src="/imgs/loading.gif" alt="Loading..." />
</div>
) : (
<div
className="mermaid-wrapper"
dangerouslySetInnerHTML={svg ? { __html: svg } : undefined}
/>
)}
</>
);
};
export default MermaidCodeBlock;

View File

@@ -1,4 +1,4 @@
import React, { memo, useMemo } from 'react';
import React, { memo, useMemo, useEffect } from 'react';
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
@@ -7,6 +7,7 @@ import Icon from '@/components/Icon';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
import MermaidCodeBlock from './MermaidCodeBlock';
import 'katex/dist/katex.min.css';
import styles from './index.module.scss';
@@ -29,49 +30,54 @@ const Markdown = ({
return (
<ReactMarkdown
className={`markdown ${styles.markdown} ${
isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''
}`}
className={`markdown ${styles.markdown}
${
isChatting
? source === ""
? styles.waitingAnimation
: styles.animation
: ""
}
`}
remarkPlugins={[remarkMath]}
rehypePlugins={[remarkGfm, rehypeKatex]}
components={{
pre: 'div',
pre: "div",
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '');
const match = /language-(\w+)/.exec(className ||'');
const code = String(children);
return !inline || match ? (
<Box my={3} borderRadius={'md'} overflow={'overlay'} backgroundColor={'#222'}>
if (match && match[1] === "mermaid") {
return <MermaidCodeBlock code={code} />;
}
return !inline && match ? (
<Box my={3} borderRadius={"md"} overflow={"overlay"} backgroundColor={"#222"}>
<Flex
className="code-header"
py={2}
px={5}
backgroundColor={useColorModeValue('#323641', 'gray.600')}
color={'#fff'}
fontSize={'sm'}
userSelect={'none'}
backgroundColor={useColorModeValue("#323641", "gray.600")}
color={"#fff"}
fontSize={"sm"}
userSelect={"none"}
>
<Box flex={1}>{match?.[1]}</Box>
<Flex cursor={'pointer'} onClick={() => copyData(code)} alignItems={'center'}>
<Icon name={'copy'} width={15} height={15} fill={'#fff'}></Icon>
<Flex cursor={"pointer"} onClick={() => copyData(code)} alignItems={"center"}>
<Icon name={"copy"} width={15} height={15} fill={"#fff"}></Icon>
<Box ml={1}></Box>
</Flex>
</Flex>
<SyntaxHighlighter
style={codeLight as any}
language={match?.[1]}
PreTag="pre"
{...props}
>
<SyntaxHighlighter style={codeLight as any} language={match?.[1]} PreTag="pre" {...props}>
{code}
</SyntaxHighlighter>
</Box>
) : (
<code className={className} {...props}>
{code}
{children}
</code>
);
}
},
}}
linkTarget="_blank"
>