* fix: remove DefaultTeam (#4037) * fix :Get application bound knowledge base information logical rewrite (#4057) * fix :Get application bound knowledge base information logical rewrite * fix :Get application bound knowledge base information logical rewrite * fix :Get application bound knowledge base information logical rewrite * fix :Get application bound knowledge base information logical rewrite * update package * fix: import dataset step error;perf: ai proxy avatar (#4074) * perf: pg config params * perf: ai proxy avatar * fix: import dataset step error * feat: data input ux * perf: app dataset rewite * fix: 文本提取不支持arrayString,arrayNumber等jsonSchema (#4079) * update doc ;perf: model test (#4098) * perf: extract array * update doc * perf: model test * perf: model test * perf: think tag parse (#4102) * chat quote reader (#3912) * init chat quote full text reader * linked structure * dataset data linked * optimize code * fix ts build * test finish * delete log * fix * fix ts * fix ts * remove nextId * initial scroll * fix * fix * perf: chunk read (#4109) * package * perf: chunk read * feat: api dataset support pdf parse;fix: chunk reader auth (#4117) * feat: api dataset support pdf parse * fix: chunk reader auth * feat: invitation link (#3979) * feat: invitation link schema and apis * feat: add invitation link * feat: member status: active, leave, forbidden * fix: expires show hours and minutes * feat: invalid invitation link hint * fix: typo * chore: fix typo & i18n * fix * pref: fe * feat: add ttl index for 30-day-clean-up * perf: invite member code (#4118) * perf: invite member code * fix: ts * fix: model test channel id;fix: quote reader (#4123) * fix: model test channel id * fix: quote reader * fix chat quote reader (#4125) * perf: model test;perf: sidebar trigger (#4127) * fix: import dataset step error;perf: ai proxy avatar (#4074) * perf: pg config params * perf: ai proxy avatar * fix: import dataset step error * feat: data input ux * perf: app dataset rewite * perf: model test * perf: sidebar trigger * lock * update nanoid version * fix: select component ux * fix: ts * fix: vitest * remove test * fix: prompt toolcall ui (#4139) * load log error adapt * fix: prompt toolcall ui * perf: commercial function tip * update package * pref: copy link (#4147) * fix(i18n): namespace (#4143) * hiden dataset source (#4152) * hiden dataset source * perf: reader * chore: move all tests into a single folder (#4160) * fix modal close scroll (#4162) * fix modal close scroll * update refresh * feat: rerank modal select and weight (#4164) * fix loadInitData refresh (#4169) * fix * fix * form input number default & api dataset max token * feat: mix search weight (#4170) * feat: mix search weight * feat: svg render * fix: avatar error remove (#4173) * fix: avatar error remove * fix: index * fix: guide * fix: auth * update package;fix: input data model ui (#4181) * update package * fix: ts * update config * update jieba package * add type sign * fix: input data ui * fix: page title refresh (#4186) * fix: ts * update jieba package * fix: page title refresh * fix: remove member length check when opening invite create modal (#4193) * add env to check internal ip (#4187) * fix: ts * update jieba package * add env to check internal ip * package * fix: jieba * reset package * update config * fix: jieba package * init shell * init version * change team reload * update jieba package (#4200) * update jieba package * package * update package * remove invalid code * action * package (#4201) * package * update package * remove invalid code * package * remove i18n tip (#4202) * doc (#4205) * fix: i18n (#4208) * fix: next config (#4207) * reset package * i18n * update config * i18n * remove log --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: gggaaallleee <91131304+gggaaallleee@users.noreply.github.com> Co-authored-by: shilin <39396378+shilin66@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io>
147 lines
4.7 KiB
TypeScript
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}</>;
|
|
}
|