monorepo packages (#344)

This commit is contained in:
Archer
2023-09-24 18:02:09 +08:00
committed by GitHub
parent a4ff5a3f73
commit 3d7178d06f
535 changed files with 12048 additions and 227 deletions

View File

@@ -0,0 +1,327 @@
import React from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
import Icon from '@/components/Icon';
import { useCopyData } from '@/hooks/useCopyData';
const codeLight: { [key: string]: React.CSSProperties } = {
'code[class*=language-]': {
color: '#d4d4d4',
textShadow: 'none',
direction: 'ltr',
textAlign: 'left',
whiteSpace: 'pre',
wordSpacing: 'normal',
wordBreak: 'normal',
lineHeight: '1.5',
MozTabSize: '4',
OTabSize: '4',
tabSize: '4',
WebkitHyphens: 'none',
MozHyphens: 'none',
msHyphens: 'none',
hyphens: 'none'
},
'pre[class*=language-]': {
color: '#d4d4d4',
textShadow: 'none',
direction: 'ltr',
textAlign: 'left',
whiteSpace: 'pre',
wordSpacing: 'normal',
wordBreak: 'normal',
lineHeight: '1.5',
MozTabSize: '4',
OTabSize: '4',
tabSize: '4',
WebkitHyphens: 'none',
MozHyphens: 'none',
msHyphens: 'none',
hyphens: 'none',
padding: '1em',
margin: '.5em 0',
overflow: 'auto',
background: '#1e1e1e'
},
'code[class*=language-] ::selection': {
textShadow: 'none',
background: '#264f78'
},
'code[class*=language-]::selection': {
textShadow: 'none',
background: '#264f78'
},
'pre[class*=language-] ::selection': {
textShadow: 'none',
background: '#264f78'
},
'pre[class*=language-]::selection': {
textShadow: 'none',
background: '#264f78'
},
':not(pre)>code[class*=language-]': {
padding: '.1em .3em',
borderRadius: '.3em',
color: '#db4c69',
background: '#1e1e1e'
},
'.namespace': {
opacity: '0.7'
},
'doctype.doctype-tag': {
color: '#569cd6'
},
'doctype.name': {
color: '#9cdcfe'
},
comment: {
color: '#6a9955'
},
prolog: {
color: '#6a9955'
},
'.language-html .language-css .token.punctuation': {
color: '#d4d4d4'
},
'.language-html .language-javascript .token.punctuation': {
color: '#d4d4d4'
},
punctuation: {
color: '#d4d4d4'
},
boolean: {
color: '#569cd6'
},
constant: {
color: '#9cdcfe'
},
inserted: {
color: '#b5cea8'
},
number: {
color: '#b5cea8'
},
property: {
color: '#9cdcfe'
},
symbol: {
color: '#b5cea8'
},
tag: {
color: '#569cd6'
},
unit: {
color: '#b5cea8'
},
'attr-name': {
color: '#9cdcfe'
},
builtin: {
color: '#ce9178'
},
char: {
color: '#ce9178'
},
deleted: {
color: '#ce9178'
},
selector: {
color: '#d7ba7d'
},
string: {
color: '#ce9178'
},
'.language-css .token.string.url': {
textDecoration: 'underline'
},
entity: {
color: '#569cd6'
},
operator: {
color: '#d4d4d4'
},
'operator.arrow': {
color: '#569cd6'
},
atrule: {
color: '#ce9178'
},
'atrule.rule': {
color: '#c586c0'
},
'atrule.url': {
color: '#9cdcfe'
},
'atrule.url.function': {
color: '#dcdcaa'
},
'atrule.url.punctuation': {
color: '#d4d4d4'
},
keyword: {
color: '#569cd6'
},
'keyword.control-flow': {
color: '#c586c0'
},
'keyword.module': {
color: '#c586c0'
},
function: {
color: '#dcdcaa'
},
'function.maybe-class-name': {
color: '#dcdcaa'
},
regex: {
color: '#d16969'
},
important: {
color: '#569cd6'
},
italic: {
fontStyle: 'italic'
},
'class-name': {
color: '#4ec9b0'
},
'maybe-class-name': {
color: '#4ec9b0'
},
console: {
color: '#9cdcfe'
},
parameter: {
color: '#9cdcfe'
},
interpolation: {
color: '#9cdcfe'
},
'punctuation.interpolation-punctuation': {
color: '#569cd6'
},
'exports.maybe-class-name': {
color: '#9cdcfe'
},
'imports.maybe-class-name': {
color: '#9cdcfe'
},
variable: {
color: '#9cdcfe'
},
escape: {
color: '#d7ba7d'
},
'tag.punctuation': {
color: 'grey'
},
cdata: {
color: 'grey'
},
'attr-value': {
color: '#ce9178'
},
'attr-value.punctuation': {
color: '#ce9178'
},
'attr-value.punctuation.attr-equals': {
color: '#d4d4d4'
},
namespace: {
color: '#4ec9b0'
},
'code[class*=language-javascript]': {
color: '#9cdcfe'
},
'code[class*=language-jsx]': {
color: '#9cdcfe'
},
'code[class*=language-tsx]': {
color: '#9cdcfe'
},
'code[class*=language-typescript]': {
color: '#9cdcfe'
},
'pre[class*=language-javascript]': {
color: '#9cdcfe'
},
'pre[class*=language-jsx]': {
color: '#9cdcfe'
},
'pre[class*=language-tsx]': {
color: '#9cdcfe'
},
'pre[class*=language-typescript]': {
color: '#9cdcfe'
},
'code[class*=language-css]': {
color: '#ce9178'
},
'pre[class*=language-css]': {
color: '#ce9178'
},
'code[class*=language-html]': {
color: '#d4d4d4'
},
'pre[class*=language-html]': {
color: '#d4d4d4'
},
'.language-regex .token.anchor': {
color: '#dcdcaa'
},
'.language-html .token.punctuation': {
color: 'grey'
},
'pre[class*=language-]>code[class*=language-]': {
position: 'relative',
zIndex: '1'
},
'.line-highlight.line-highlight': {
background: '#f7ebc6',
boxShadow: 'inset 5px 0 0 #f7d87c',
zIndex: '0'
}
};
const CodeLight = ({
children,
className,
inline,
match
}: {
children: React.ReactNode & React.ReactNode[];
className?: string;
inline?: boolean;
match: RegExpExecArray | null;
}) => {
const { copyData } = useCopyData();
if (!inline && match) {
return (
<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'}
>
<Box flex={1}>{match?.[1]}</Box>
<Flex cursor={'pointer'} onClick={() => copyData(String(children))} 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">
{String(children)}
</SyntaxHighlighter>
</Box>
);
}
return <code className={className}>{children}</code>;
};
export default React.memo(CodeLight);

View File

@@ -0,0 +1,58 @@
import React, { useMemo } from 'react';
import { Box, Link } from '@chakra-ui/react';
import ReactMarkdown from 'react-markdown';
import RemarkGfm from 'remark-gfm';
import RemarkMath from 'remark-math';
import RehypeKatex from 'rehype-katex';
import { event } from '@/utils/plugin/eventbus';
import 'katex/dist/katex.min.css';
import styles from '../index.module.scss';
import Image from '../img/Image';
function MyLink(e: any) {
const href = e.href;
const text = String(e.children);
return !!href ? (
<Link href={href} target={'_blank'}>
{text}
</Link>
) : (
<Box as={'ul'}>
<Box as={'li'}>
<Box
as={'span'}
color={'blue.600'}
textDecoration={'underline'}
cursor={'pointer'}
onClick={() => {
event.emit('guideClick', { text });
}}
>
{text}
</Box>
</Box>
</Box>
);
}
const Guide = ({ text }: { text: string }) => {
const formatText = useMemo(() => text.replace(/\[(.*?)\]($|\n)/g, '[$1]()\n'), [text]);
return (
<ReactMarkdown
className={`markdown ${styles.markdown}`}
remarkPlugins={[RemarkGfm, RemarkMath]}
rehypePlugins={[RehypeKatex]}
components={{
a: MyLink,
img: Image
}}
>
{formatText}
</ReactMarkdown>
);
};
export default React.memo(Guide);

View File

@@ -0,0 +1,64 @@
import React, { useMemo } from 'react';
import { Box, useTheme } from '@chakra-ui/react';
import { getFileAndOpen } from '@/utils/web/file';
import { useToast } from '@/hooks/useToast';
import { getErrText } from '@/utils/tools';
type QuoteItemType = {
file_id?: string;
filename: string;
};
const QuoteBlock = ({ code }: { code: string }) => {
const theme = useTheme();
const { toast } = useToast();
const quoteList = useMemo(() => {
try {
return JSON.parse(code) as QuoteItemType[];
} catch (error) {
return [];
}
}, [code]);
return (
<Box mt={3} pt={2} borderTop={theme.borders.base}>
{quoteList.length > 0 ? (
<>
<Box>:</Box>
<Box as={'ol'}>
{quoteList.map((item, i) => (
<Box
key={i}
as={'li'}
{...(item.file_id
? {
textDecoration: 'underline',
color: 'myBlue.800',
cursor: 'pointer'
}
: {})}
onClick={async () => {
if (!item.file_id) return;
try {
await getFileAndOpen(item.file_id);
} catch (error) {
toast({
status: 'warning',
title: getErrText(error, '打开文件失败')
});
}
}}
>
{item.filename}
</Box>
))}
</Box>
</>
) : (
<Box></Box>
)}
</Box>
);
};
export default QuoteBlock;

View File

@@ -0,0 +1,64 @@
import React, { useEffect, useRef, useState } from 'react';
import * as echarts from 'echarts';
import type { ECharts } from 'echarts';
import { Box, Skeleton } from '@chakra-ui/react';
const EChartsCodeBlock = ({ code }: { code: string }) => {
const chartRef = useRef<HTMLDivElement>(null);
const eChart = useRef<ECharts>();
const [option, setOption] = useState<any>();
const [width, setWidth] = useState(400);
useEffect(() => {
const clientWidth = document.getElementById('chat-container')?.clientWidth || 500;
setWidth(clientWidth * 0.9);
setTimeout(() => {
eChart.current?.resize();
}, 100);
}, []);
useEffect(() => {
let option;
try {
option = JSON.parse(code.trim());
option = {
...option,
toolbox: {
show: true,
feature: {
saveAsImage: {}
}
}
};
setOption(option);
} catch (error) {}
if (!option) return;
(async () => {
// @ts-ignore
await import('echarts-gl');
})();
if (chartRef.current) {
eChart.current = echarts.init(chartRef.current);
eChart.current.setOption(option);
eChart.current?.resize();
}
return () => {
if (eChart.current) {
eChart.current.dispose();
}
};
}, [code]);
return (
<Box overflowX={'auto'}>
<Box h={'400px'} minW={'400px'} w={`${width}px`} ref={chartRef} />
{!option && <Skeleton isLoaded={true} fadeDuration={2} h={'400px'} w={`400px`}></Skeleton>}
</Box>
);
};
export default EChartsCodeBlock;

View File

@@ -0,0 +1,40 @@
import React, { useState } from 'react';
import { Image, Skeleton } from '@chakra-ui/react';
const MdImage = ({ src }: { src?: string }) => {
const [isLoading, setIsLoading] = useState(true);
const [succeed, setSucceed] = useState(false);
return (
<Skeleton
minH="100px"
isLoaded={!isLoading}
fadeDuration={2}
display={'flex'}
justifyContent={'center'}
my={1}
>
<Image
display={'inline-block'}
borderRadius={'md'}
src={src}
alt={''}
fallbackSrc={'/imgs/errImg.png'}
fallbackStrategy={'onError'}
cursor={succeed ? 'pointer' : 'default'}
loading="eager"
objectFit={'contain'}
onLoad={() => {
setIsLoading(false);
setSucceed(true);
}}
onError={() => setIsLoading(false)}
onClick={() => {
if (!succeed) return;
window.open(src, '_blank');
}}
/>
</Skeleton>
);
};
export default React.memo(MdImage);

View File

@@ -0,0 +1,137 @@
import React, { useEffect, useRef, memo, useCallback, useState, useMemo } from 'react';
import { Box } from '@chakra-ui/react';
// @ts-ignore
import mermaid from 'mermaid';
import MyIcon from '../../Icon';
const mermaidAPI = mermaid.mermaidAPI;
mermaidAPI.initialize({
startOnLoad: true,
theme: 'base',
flowchart: {
useMaxWidth: false
},
themeVariables: {
fontSize: '14px',
primaryColor: '#d6e8ff',
primaryTextColor: '#485058',
primaryBorderColor: '#fff',
lineColor: '#5A646E',
secondaryColor: '#B5E9E5',
tertiaryColor: '#485058'
}
});
const punctuationMap: Record<string, string> = {
'': ',',
'': ';',
'。': '.',
'': ':',
'': '!',
'': '?',
'“': '"',
'”': '"',
'': "'",
'': "'",
'【': '[',
'】': ']',
'': '(',
'': ')',
'《': '<',
'》': '>',
'、': ','
};
const MermaidBlock = ({ code }: { code: string }) => {
const ref = useRef<HTMLDivElement>(null);
const [svg, setSvg] = useState('');
useEffect(() => {
(async () => {
if (!code) return;
try {
const formatCode = code.replace(
new RegExp(`[${Object.keys(punctuationMap).join('')}]`, 'g'),
(match) => punctuationMap[match]
);
const { svg } = await mermaid.render(`mermaid-${Date.now()}`, formatCode);
setSvg(svg);
} catch (e: any) {
// console.log('[Mermaid] ', e?.message);
}
})();
}, [code]);
const onclickExport = useCallback(() => {
const svg = ref.current?.children[0];
if (!svg) return;
const rate = svg.clientHeight / svg.clientWidth;
const w = 3000;
const h = rate * w;
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
if (!ctx) return;
// 绘制白色背景
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, w, h);
const img = new Image();
img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(ref.current?.innerHTML)}`;
img.onload = () => {
ctx.drawImage(img, 0, 0, w, h);
const jpgDataUrl = canvas.toDataURL('image/jpeg', 1);
const a = document.createElement('a');
a.href = jpgDataUrl;
a.download = 'mermaid.jpg';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
img.onerror = (e) => {
console.log(e);
};
}, []);
return (
<Box
position={'relative'}
_hover={{
'& > .export': {
display: 'block'
}
}}
>
<Box
overflowX={'auto'}
ref={ref}
minW={'100px'}
minH={'50px'}
py={4}
dangerouslySetInnerHTML={{ __html: svg }}
/>
<MyIcon
className="export"
display={'none'}
name={'export'}
w={'20px'}
position={'absolute'}
color={'myGray.600'}
_hover={{
color: 'myBlue.700'
}}
right={0}
top={0}
cursor={'pointer'}
onClick={onclickExport}
/>
</Box>
);
};
export default MermaidBlock;

View File

@@ -0,0 +1,420 @@
.waitingAnimation::after {
display: inline-block;
content: '';
width: 4px;
height: 14px;
transform: translate(4px, 2px) scaleY(1.3);
background-color: var(--chakra-colors-chakra-body-text);
animation: blink 0.6s infinite;
}
.animation {
> :last-child::after {
display: inline-block;
content: '';
width: 4px;
height: 14px;
transform: translate(4px, 2px) scaleY(1.3);
background-color: var(--chakra-colors-chakra-body-text);
animation: blink 0.6s infinite;
}
}
@keyframes blink {
from,
to {
opacity: 0;
}
50% {
opacity: 1;
}
}
.markdown > *:first-child {
margin-top: 0 !important;
}
.markdown > *:last-child {
margin-bottom: 0 !important;
}
.markdown a.absent {
color: #cc0000;
}
.markdown a.anchor {
bottom: 0;
cursor: pointer;
display: block;
left: 0;
margin-left: -30px;
padding-left: 30px;
position: absolute;
top: 0;
}
.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
cursor: text;
font-weight: bold;
margin: 10px 0;
padding: 0;
position: relative;
}
.markdown h1 .mini-icon-link,
.markdown h2 .mini-icon-link,
.markdown h3 .mini-icon-link,
.markdown h4 .mini-icon-link,
.markdown h5 .mini-icon-link,
.markdown h6 .mini-icon-link {
display: none;
}
.markdown h1:hover a.anchor,
.markdown h2:hover a.anchor,
.markdown h3:hover a.anchor,
.markdown h4:hover a.anchor,
.markdown h5:hover a.anchor,
.markdown h6:hover a.anchor {
line-height: 1;
margin-left: -22px;
padding-left: 0;
text-decoration: none;
top: 15%;
}
.markdown h1:hover a.anchor .mini-icon-link,
.markdown h2:hover a.anchor .mini-icon-link,
.markdown h3:hover a.anchor .mini-icon-link,
.markdown h4:hover a.anchor .mini-icon-link,
.markdown h5:hover a.anchor .mini-icon-link,
.markdown h6:hover a.anchor .mini-icon-link {
display: inline-block;
}
.markdown h1 tt,
.markdown h1 code,
.markdown h2 tt,
.markdown h2 code,
.markdown h3 tt,
.markdown h3 code,
.markdown h4 tt,
.markdown h4 code,
.markdown h5 tt,
.markdown h5 code,
.markdown h6 tt,
.markdown h6 code {
font-size: inherit;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 24px;
}
.markdown h3 {
font-size: 18px;
}
.markdown h4 {
font-size: 16px;
}
.markdown h5 {
font-size: 14px;
}
.markdown h6 {
font-size: 12px;
}
.markdown p,
.markdown blockquote,
.markdown ul,
.markdown ol,
.markdown dl,
.markdown table,
.markdown pre {
margin: 10px 0;
}
.markdown > h2:first-child,
.markdown > h1:first-child,
.markdown > h1:first-child + h2,
.markdown > h3:first-child,
.markdown > h4:first-child,
.markdown > h5:first-child,
.markdown > h6:first-child {
margin-top: 0;
padding-top: 0;
}
.markdown a:first-child h1,
.markdown a:first-child h2,
.markdown a:first-child h3,
.markdown a:first-child h4,
.markdown a:first-child h5,
.markdown a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
.markdown h1 + p,
.markdown h2 + p,
.markdown h3 + p,
.markdown h4 + p,
.markdown h5 + p,
.markdown h6 + p {
margin-top: 0;
}
.markdown li p.first {
display: inline-block;
}
.markdown ul,
.markdown ol {
padding-left: 2em;
}
.markdown ul.no-list,
.markdown ol.no-list {
list-style-type: none;
padding: 0;
}
.markdown ul li > *:first-child,
.markdown ol li > *:first-child {
margin-top: 0;
}
.markdown ul ul,
.markdown ul ol,
.markdown ol ol,
.markdown ol ul {
margin-bottom: 0;
}
.markdown dl {
padding: 0;
}
.markdown dl dt {
font-size: 14px;
font-style: italic;
font-weight: bold;
margin: 15px 0 5px;
padding: 0;
}
.markdown dl dt:first-child {
padding: 0;
}
.markdown dl dt > *:first-child {
margin-top: 0;
}
.markdown dl dt > *:last-child {
margin-bottom: 0;
}
.markdown dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
.markdown dl dd > *:first-child {
margin-top: 0;
}
.markdown dl dd > *:last-child {
margin-bottom: 0;
}
.markdown blockquote {
border-left: 4px solid #dddddd;
color: #777777;
padding: 0 15px;
}
.markdown blockquote > *:first-child {
margin-top: 0;
}
.markdown blockquote > *:last-child {
margin-bottom: 0;
}
.markdown table th {
font-weight: bold;
}
.markdown table th,
.markdown table td {
padding: 6px 13px;
}
.markdown table tr {
background-color: #ffffff;
}
.markdown table tr:nth-child(2n) {
background-color: #f0f0f0;
}
.markdown img {
max-width: 100%;
}
.markdown span.frame {
display: block;
overflow: hidden;
}
.markdown span.frame > span {
border: 1px solid #dddddd;
display: block;
float: left;
margin: 13px 0 0;
overflow: hidden;
padding: 7px;
width: auto;
}
.markdown span.frame span img {
display: block;
float: left;
}
.markdown span.frame span span {
clear: both;
color: #333333;
display: block;
padding: 5px 0 0;
}
.markdown span.align-center {
clear: both;
display: block;
overflow: hidden;
}
.markdown span.align-center > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: center;
}
.markdown span.align-center span img {
margin: 0 auto;
text-align: center;
}
.markdown span.align-right {
clear: both;
display: block;
overflow: hidden;
}
.markdown span.align-right > span {
display: block;
margin: 13px 0 0;
overflow: hidden;
text-align: right;
}
.markdown span.align-right span img {
margin: 0;
text-align: right;
}
.markdown span.float-left {
display: block;
float: left;
margin-right: 13px;
overflow: hidden;
}
.markdown span.float-left span {
margin: 13px 0 0;
}
.markdown span.float-right {
display: block;
float: right;
margin-left: 13px;
overflow: hidden;
}
.markdown span.float-right > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: right;
}
.markdown code,
.markdown tt {
border: 1px solid #eaeaea;
border-radius: 3px 3px 3px 3px;
margin: 0 2px;
padding: 0 5px;
}
.markdown pre > code {
background: none repeat scroll 0 0 transparent;
border: medium none;
margin: 0;
padding: 0;
}
.markdown .highlight pre,
.markdown pre {
border: 1px solid #cccccc;
border-radius: 3px 3px 3px 3px;
font-size: max(0.9em, 14px);
line-height: 19px;
overflow: auto;
padding: 6px 10px;
}
.markdown pre code,
.markdown pre tt {
background-color: transparent;
border: medium none;
}
.markdown hr {
margin: 10px 0;
}
.markdown {
text-align: justify;
tab-size: 4;
word-spacing: normal;
width: 100%;
* {
word-break: break-all;
}
pre {
display: block;
width: 100%;
padding: 15px;
margin: 0;
border: none;
border-radius: 0;
background-color: #292b33 !important;
overflow-x: auto;
color: #fff;
}
pre code {
background-color: #292b33 !important;
width: 100%;
}
a {
text-decoration: underline;
color: var(--chakra-colors-blue-600);
}
table {
border-collapse: separate;
border-spacing: 0px;
color: var(--chakra-colors-gray-700);
thead tr:first-child th {
border-bottom-width: 1px;
border-left-width: 1px;
border-top-width: 1px;
border-color: #ccc;
background-color: rgba(236, 236, 241, 0.2);
overflow: hidden;
&:first-child {
border-top-left-radius: 0.375rem;
}
&:last-child {
border-right-width: 1px;
border-top-right-radius: 0.375rem;
}
}
td {
border-bottom-width: 1px;
border-left-width: 1px;
border-color: #ccc;
&:last-of-type {
border-right-width: 1px;
}
}
tbody tr:last-child {
overflow: hidden;
td {
&:first-child {
border-bottom-left-radius: 0.375rem;
}
&:last-child {
border-bottom-right-radius: 0.375rem;
}
}
}
}
}
.mermaid {
overflow-x: auto;
}

View File

@@ -0,0 +1,81 @@
import React, { useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
import RemarkGfm from 'remark-gfm';
import RemarkMath from 'remark-math';
import RehypeKatex from 'rehype-katex';
import RemarkBreaks from 'remark-breaks';
import 'katex/dist/katex.min.css';
import styles from './index.module.scss';
import dynamic from 'next/dynamic';
import CodeLight from './CodeLight';
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'));
const MdImage = dynamic(() => import('./img/Image'));
const ChatGuide = dynamic(() => import('./chat/Guide'));
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'));
const QuoteBlock = dynamic(() => import('./chat/Quote'));
export enum CodeClassName {
guide = 'guide',
mermaid = 'mermaid',
echarts = 'echarts',
quote = 'quote'
}
function Code({ inline, className, children }: any) {
const match = /language-(\w+)/.exec(className || '');
const codeType = match?.[1];
if (codeType === CodeClassName.mermaid) {
return <MermaidCodeBlock code={String(children)} />;
}
if (codeType === CodeClassName.guide) {
return <ChatGuide text={String(children)} />;
}
if (codeType === CodeClassName.echarts) {
return <EChartsCodeBlock code={String(children)} />;
}
if (codeType === CodeClassName.quote) {
return <QuoteBlock code={String(children)} />;
}
return (
<CodeLight className={className} inline={inline} match={match}>
{children}
</CodeLight>
);
}
function Image({ src }: { src?: string }) {
return <MdImage src={src} />;
}
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
const components = useMemo(
() => ({
img: Image,
pre: 'div',
p: 'div',
code: Code
}),
[]
);
return (
<ReactMarkdown
className={`markdown ${styles.markdown}
${isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''}
`}
remarkPlugins={[RemarkGfm, RemarkMath, RemarkBreaks]}
rehypePlugins={[RehypeKatex]}
// @ts-ignore
components={components}
linkTarget={'_blank'}
>
{source}
</ReactMarkdown>
);
};
export default Markdown;