4.6.4-alpha (#582)
This commit is contained in:
@@ -315,7 +315,7 @@ const CodeLight = ({
|
||||
</Flex>
|
||||
</Flex>
|
||||
<SyntaxHighlighter style={codeLight as any} language={match?.[1]} PreTag="pre">
|
||||
{String(children)}
|
||||
{String(children).replace(/ /g, ' ')}
|
||||
</SyntaxHighlighter>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ import RemarkGfm from 'remark-gfm';
|
||||
import RemarkMath from 'remark-math';
|
||||
import RehypeKatex from 'rehype-katex';
|
||||
import RemarkBreaks from 'remark-breaks';
|
||||
import { eventBus } from '@/web/common/utils/eventbus';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
|
||||
import 'katex/dist/katex.min.css';
|
||||
import styles from '../index.module.scss';
|
||||
@@ -27,7 +27,7 @@ function MyLink(e: any) {
|
||||
textDecoration={'underline'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
eventBus.emit('guideClick', { text });
|
||||
eventBus.emit(EventNameEnum.sendQuestion, { text });
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
|
||||
92
projects/app/src/components/Markdown/chat/QuestionGuide.tsx
Normal file
92
projects/app/src/components/Markdown/chat/QuestionGuide.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, Flex, useTheme } from '@chakra-ui/react';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import ChatBoxDivider from '@/components/core/chat/Divider';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import MyIcon from '@/components/Icon';
|
||||
|
||||
const QuestionGuide = ({ text }: { text: string }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const questionGuides = useMemo(() => {
|
||||
try {
|
||||
const json = JSON.parse(text);
|
||||
if (Array.isArray(json) && !json.find((item) => typeof item !== 'string')) {
|
||||
return json as string[];
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
}, [text]);
|
||||
|
||||
return questionGuides.length > 0 ? (
|
||||
<Box mt={2}>
|
||||
<ChatBoxDivider icon="core/chat/QGFill" text={t('chat.Question Guide Tips')} />
|
||||
<Flex alignItems={'center'} flexWrap={'wrap'} gap={2}>
|
||||
{questionGuides.map((text) => (
|
||||
<Flex
|
||||
key={text}
|
||||
alignItems={'center'}
|
||||
flexWrap={'wrap'}
|
||||
fontSize={'sm'}
|
||||
border={theme.borders.sm}
|
||||
py={'1px'}
|
||||
px={3}
|
||||
borderRadius={'md'}
|
||||
_hover={{
|
||||
'.controller': {
|
||||
display: 'flex'
|
||||
}
|
||||
}}
|
||||
overflow={'hidden'}
|
||||
position={'relative'}
|
||||
>
|
||||
<Box className="textEllipsis" flex={'1 0 0'}>
|
||||
{text}
|
||||
</Box>
|
||||
<Box
|
||||
className="controller"
|
||||
display={['flex', 'none']}
|
||||
pr={2}
|
||||
position={'absolute'}
|
||||
right={0}
|
||||
left={0}
|
||||
justifyContent={'flex-end'}
|
||||
alignItems={'center'}
|
||||
h={'100%'}
|
||||
lineHeight={0}
|
||||
bg={`linear-gradient(to left, white,white min(60px,100%),rgba(255,255,255,0) 80%)`}
|
||||
>
|
||||
<MyTooltip label={t('core.chat.markdown.Edit Question')}>
|
||||
<MyIcon
|
||||
name={'edit'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
color: 'green.600'
|
||||
}}
|
||||
onClick={() => eventBus.emit(EventNameEnum.editQuestion, { text })}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<MyTooltip label={t('core.chat.markdown.Send Question')}>
|
||||
<MyIcon
|
||||
ml={4}
|
||||
name={'core/chat/sendLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'myBlue.600' }}
|
||||
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text })}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
</Box>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default React.memo(QuestionGuide);
|
||||
@@ -1,64 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, useTheme } from '@chakra-ui/react';
|
||||
import { getFileAndOpen } from '@/web/core/dataset/utils';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
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;
|
||||
@@ -46,7 +46,7 @@ const MdImage = ({ src }: { src?: string }) => {
|
||||
/>
|
||||
<Modal isOpen={isOpen} onClose={onClose} isCentered>
|
||||
<ModalOverlay />
|
||||
<ModalContent maxW={'auto'} w="auto" bg={'transparent'}>
|
||||
<ModalContent boxShadow={'none'} maxW={'auto'} w="auto" bg={'transparent'}>
|
||||
<Image
|
||||
borderRadius={'md'}
|
||||
src={src}
|
||||
|
||||
@@ -9,17 +9,26 @@ import 'katex/dist/katex.min.css';
|
||||
import styles from './index.module.scss';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import CodeLight from './CodeLight';
|
||||
import { Link, Button } from '@chakra-ui/react';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import MyIcon from '../Icon';
|
||||
import { getFileAndOpen } from '@/web/core/dataset/utils';
|
||||
import { MARKDOWN_QUOTE_SIGN } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
const CodeLight = dynamic(() => import('./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'));
|
||||
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'));
|
||||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'));
|
||||
const ImageBlock = dynamic(() => import('./chat/Image'));
|
||||
|
||||
export enum CodeClassName {
|
||||
guide = 'guide',
|
||||
questionGuide = 'questionGuide',
|
||||
mermaid = 'mermaid',
|
||||
echarts = 'echarts',
|
||||
quote = 'quote',
|
||||
@@ -37,12 +46,12 @@ function Code({ inline, className, children }: any) {
|
||||
if (codeType === CodeClassName.guide) {
|
||||
return <ChatGuide text={String(children)} />;
|
||||
}
|
||||
if (codeType === CodeClassName.questionGuide) {
|
||||
return <QuestionGuide text={String(children)} />;
|
||||
}
|
||||
if (codeType === CodeClassName.echarts) {
|
||||
return <EChartsCodeBlock code={String(children)} />;
|
||||
}
|
||||
if (codeType === CodeClassName.quote) {
|
||||
return <QuoteBlock code={String(children)} />;
|
||||
}
|
||||
if (codeType === CodeClassName.img) {
|
||||
return <ImageBlock images={String(children)} />;
|
||||
}
|
||||
@@ -55,6 +64,52 @@ function Code({ inline, className, children }: any) {
|
||||
function Image({ src }: { src?: string }) {
|
||||
return <MdImage src={src} />;
|
||||
}
|
||||
function A({ children, ...props }: any) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// empty href link
|
||||
if (!props.href && typeof children?.[0] === 'string') {
|
||||
const text = useMemo(() => String(children), [children]);
|
||||
|
||||
return (
|
||||
<MyTooltip label={t('core.chat.markdown.Quick Question')}>
|
||||
<Button
|
||||
variant={'base'}
|
||||
size={'xs'}
|
||||
borderRadius={'md'}
|
||||
my={1}
|
||||
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text })}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
// quote link
|
||||
if (children?.length === 1 && typeof children?.[0] === 'string') {
|
||||
const text = String(children);
|
||||
if (text === MARKDOWN_QUOTE_SIGN && props.href) {
|
||||
return (
|
||||
<MyTooltip label={props.href}>
|
||||
<MyIcon
|
||||
name={'core/chat/quoteSign'}
|
||||
transform={'translateY(-2px)'}
|
||||
w={'18px'}
|
||||
color={'myBlue.600'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
color: 'myBlue.800'
|
||||
}}
|
||||
onClick={() => getFileAndOpen(props.href)}
|
||||
/>
|
||||
</MyTooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return <Link {...props}>{children}</Link>;
|
||||
}
|
||||
|
||||
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
|
||||
const components = useMemo(
|
||||
@@ -62,14 +117,16 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
|
||||
img: Image,
|
||||
pre: 'div',
|
||||
p: 'div',
|
||||
code: Code
|
||||
code: Code,
|
||||
a: A
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const formatSource = source
|
||||
.replace(/\\n/g, '\n ')
|
||||
.replace(/(http[s]?:\/\/[^\s,。]+)([。,])/g, '$1 $2');
|
||||
.replace(/(http[s]?:\/\/[^\s,。]+)([。,])/g, '$1 $2')
|
||||
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1');
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
|
||||
Reference in New Issue
Block a user