Archer 93f7747904
feat: mix search weight (#4170)
* feat: mix search weight

* feat: svg render
2025-03-14 18:31:37 +08:00

147 lines
4.7 KiB
TypeScript

import React, { useCallback, useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
import 'katex/dist/katex.min.css';
import RemarkMath from 'remark-math'; // Math syntax
import RemarkBreaks from 'remark-breaks'; // Line break
import RehypeKatex from 'rehype-katex'; // Math render
import RemarkGfm from 'remark-gfm'; // Special markdown syntax
import RehypeExternalLinks from 'rehype-external-links';
import styles from './index.module.scss';
import dynamic from 'next/dynamic';
import { Box } from '@chakra-ui/react';
import { CodeClassNameEnum, mdTextFormat } from './utils';
const CodeLight = dynamic(() => import('./codeBlock/CodeLight'), { ssr: false });
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
const MdImage = dynamic(() => import('./img/Image'), { ssr: false });
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'), { ssr: false });
const IframeCodeBlock = dynamic(() => import('./codeBlock/Iframe'), { ssr: false });
const IframeHtmlCodeBlock = dynamic(() => import('./codeBlock/iframe-html'), { ssr: false });
const VideoBlock = dynamic(() => import('./codeBlock/Video'), { ssr: false });
const AudioBlock = dynamic(() => import('./codeBlock/Audio'), { ssr: false });
const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
const A = dynamic(() => import('./A'), { ssr: false });
type Props = {
source?: string;
showAnimation?: boolean;
isDisabled?: boolean;
forbidZhFormat?: boolean;
};
const Markdown = (props: Props) => {
const source = props.source || '';
if (source.length < 200000) {
return <MarkdownRender {...props} />;
}
return <Box whiteSpace={'pre-wrap'}>{source}</Box>;
};
const MarkdownRender = ({ source = '', showAnimation, isDisabled, forbidZhFormat }: Props) => {
const components = useMemo<any>(
() => ({
img: Image,
pre: RewritePre,
code: Code,
a: A
}),
[]
);
const formatSource = useMemo(() => {
if (showAnimation || forbidZhFormat) return source;
return mdTextFormat(source);
}, [forbidZhFormat, showAnimation, source]);
const urlTransform = useCallback((val: string) => {
return val;
}, []);
return (
<Box position={'relative'}>
<ReactMarkdown
className={`markdown ${styles.markdown}
${showAnimation ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''}
`}
remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]}
rehypePlugins={[RehypeKatex, [RehypeExternalLinks, { target: '_blank' }]]}
components={components}
urlTransform={urlTransform}
>
{formatSource}
</ReactMarkdown>
{isDisabled && <Box position={'absolute'} top={0} right={0} left={0} bottom={0} />}
</Box>
);
};
export default React.memo(Markdown);
/* Custom dom */
function Code(e: any) {
const { className, codeBlock, children } = e;
const match = /language-(\w+)/.exec(className || '');
const codeType = match?.[1]?.toLowerCase();
const strChildren = String(children);
const Component = useMemo(() => {
if (codeType === CodeClassNameEnum.mermaid) {
return <MermaidCodeBlock code={strChildren} />;
}
if (codeType === CodeClassNameEnum.guide) {
return <ChatGuide text={strChildren} />;
}
if (codeType === CodeClassNameEnum.questionGuide) {
return <QuestionGuide text={strChildren} />;
}
if (codeType === CodeClassNameEnum.echarts) {
return <EChartsCodeBlock code={strChildren} />;
}
if (codeType === CodeClassNameEnum.iframe) {
return <IframeCodeBlock code={strChildren} />;
}
if (codeType === CodeClassNameEnum.html || codeType === CodeClassNameEnum.svg) {
return (
<IframeHtmlCodeBlock className={className} codeBlock={codeBlock} match={match}>
{children}
</IframeHtmlCodeBlock>
);
}
if (codeType === CodeClassNameEnum.video) {
return <VideoBlock code={strChildren} />;
}
if (codeType === CodeClassNameEnum.audio) {
return <AudioBlock code={strChildren} />;
}
return (
<CodeLight className={className} codeBlock={codeBlock} match={match}>
{children}
</CodeLight>
);
}, [codeType, className, codeBlock, match, children, strChildren]);
return Component;
}
function Image({ src }: { src?: string }) {
return <MdImage src={src} />;
}
function RewritePre({ children }: any) {
const modifiedChildren = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
// @ts-ignore
return React.cloneElement(child, { codeBlock: true });
}
return child;
});
return <>{modifiedChildren}</>;
}