V4.8.15 feature (#3331)
* feat: add customize toolkit (#3205) * chaoyang * fix-auth * add toolkit * add order * plugin usage * fix * delete console: * Fix: Fix fullscreen preview top positioning and improve Markdown rendering logic (#3247) * 完成任务:修复全屏预览顶部固定问题,优化 Markdown 渲染逻辑 * 有问题修改 * 问题再修改 * 修正问题 * fix: plugin standalone display issue (#3254) * 4.8.15 test (#3246) * o1 config * perf: system plugin code * 调整系统插件代码。增加html 渲染安全配置。 (#3258) * perf: base64 picker * perf: list app or dataset * perf: plugin config code * 小窗适配等问题 (#3257) * 小窗适配等问题 * git问题 * 小窗剩余问题 * feat: system plugin auth and lock version (#3265) * feat: system plugin auth and lock version * update comment * 4.8.15 test (#3267) * tmp log * perf: login direct * perf: iframe html code * remove log * fix: plugin standalone display (#3277) * refactor: 页面拆分&i18n拆分 (#3281) * refactor: account组件拆成独立页面 * script: 新增i18n json文件创建脚本 * refactor: 页面i18n拆分 * i18n: add en&hant * 4.8.15 test (#3285) * tmp log * remove log * fix: watch avatar refresh * perf: i18n code * fix(plugin): use intro instead of userguide (#3290) * Universal SSO (#3292) * tmp log * remove log * feat: common oauth * readme * perf: sso provider * remove sso code * perf: refresh plugins * feat: add api dataset (#3272) * add api-dataset * fix api-dataset * fix api dataset * fix ts * perf: create collection code (#3301) * tmp log * remove log * perf: i18n change * update version doc * feat: question guide from chatId * perf: create collection code * fix: request api * fix: request api * fix: tts auth and response type (#3303) * perf: md splitter * fix: tts auth and response type * fix: api file dataset (#3307) * perf: api dataset init (#3310) * perf: collection schema * perf: api dataset init * refactor: 团队管理独立页面 (#3302) * ui: 团队管理独立页面 * 代码优化 * fix * perf: sync collection and ui check (#3314) * perf: sync collection * remove script * perf: update api server * perf: api dataset parent * perf: team ui * perf: team 18n * update team ui * perf: ui check * perf: i18n * fix: debug variables & cronjob & system plugin callback load (#3315) * fix: debug variables & cronjob & system plugin callback load * fix type * fix * fix * fix: plugin dataset quote;perf: system variables init (#3316) * fix: plugin dataset quote * perf: system variables init * perf: node templates ui;fix: dataset import ui (#3318) * fix: dataset import ui * perf: node templates ui * perf: ui refresh * feat:套餐改名和套餐跳转配置 (#3309) * fixing:except Sidebar * 去除了多余的代码 * 修正了套餐说明的代码 * 修正了误删除的show_git代码 * 修正了名字部分等代码 * 修正了问题,遗留了其他和ui讨论不一致的部分 * 4.8.15 test (#3319) * remove log * pref: bill ui * pref: bill ui * perf: log * html渲染文档 (#3270) * html渲染文档 * 文档有点小问题 * feat: doc (#3322) * 集合重训练 (#3282) * rebaser * 一点补充 * 小问题 * 其他问题修正,删除集合保留文件的参数还没找到... * reTraining * delete uesless * 删除了一行错误代码 * 集合重训练部分 * fixing * 删除console代码 * feat: navbar item config (#3326) * perf: custom navbar code;perf: retraining code;feat: api dataset and dataset api doc (#3329) * feat: api dataset and dataset api doc * perf: retraining code * perf: custom navbar code * fix: ts (#3330) * fix: ts * fix: ts * retraining ui * perf: api collection filter * perf: retrining button --------- Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Jiangween <145003935+Jiangween@users.noreply.github.com> Co-authored-by: papapatrick <109422393+Patrickill@users.noreply.github.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import Icon from '@fastgpt/web/components/common/Icon';
|
||||
import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const codeLight: { [key: string]: React.CSSProperties } = {
|
||||
export const codeLight: { [key: string]: React.CSSProperties } = {
|
||||
'code[class*=language-]': {
|
||||
color: '#d4d4d4',
|
||||
textShadow: 'none',
|
||||
|
||||
@@ -1,25 +1,242 @@
|
||||
import React, { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalBody,
|
||||
ModalHeader,
|
||||
useDisclosure,
|
||||
ModalCloseButton
|
||||
} from '@chakra-ui/react';
|
||||
import Icon from '@fastgpt/web/components/common/Icon';
|
||||
import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useMarkdownWidth } from '../hooks';
|
||||
import type { IconNameType } from '@fastgpt/web/components/common/Icon/type.d';
|
||||
import { codeLight } from '../CodeLight';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
|
||||
const StyledButton = ({
|
||||
label,
|
||||
iconName,
|
||||
onClick,
|
||||
isActive,
|
||||
viewMode,
|
||||
isMobile
|
||||
}: {
|
||||
label: string;
|
||||
iconName: IconNameType;
|
||||
onClick: () => void;
|
||||
isActive?: boolean;
|
||||
viewMode: 'source' | 'iframe';
|
||||
isMobile?: boolean;
|
||||
}) => {
|
||||
const isPreview = viewMode === 'iframe';
|
||||
|
||||
const textColor = isPreview
|
||||
? isActive
|
||||
? 'myGray.900'
|
||||
: 'myGray.500'
|
||||
: isActive
|
||||
? '#FFF'
|
||||
: 'rgba(255, 255, 255, 0.8)';
|
||||
const bg = isPreview ? (isActive ? 'myGray.150' : '') : isActive ? '#333A47' : '';
|
||||
const hoverBg = isPreview ? 'myGray.150' : '#333A47';
|
||||
|
||||
const MermaidBlock = ({ code }: { code: string }) => {
|
||||
const { width, Ref } = useMarkdownWidth();
|
||||
return (
|
||||
<Box w={width} ref={Ref}>
|
||||
<Flex
|
||||
bg={bg}
|
||||
color={textColor}
|
||||
borderRadius="5px"
|
||||
boxShadow="none"
|
||||
fontWeight={isActive ? 500 : 400}
|
||||
_hover={{
|
||||
bg: hoverBg
|
||||
}}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
onClick={onClick}
|
||||
cursor="pointer"
|
||||
px={isMobile ? '6px' : '8px'}
|
||||
h={isMobile ? '24px' : '28px'}
|
||||
>
|
||||
{isMobile ? (
|
||||
<MyTooltip label={label} placement="bottom" hasArrow>
|
||||
<Flex alignItems="center" justifyContent="center">
|
||||
<Icon name={iconName} width="14px" height="14px" />
|
||||
</Flex>
|
||||
</MyTooltip>
|
||||
) : (
|
||||
<Flex alignItems="center" justifyContent="flex-start">
|
||||
<Icon name={iconName} width="14px" height="14px" />
|
||||
<Box ml={2} fontSize="sm">
|
||||
{label}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const IframeHtmlCodeBlock = ({
|
||||
children,
|
||||
className,
|
||||
codeBlock,
|
||||
match
|
||||
}: {
|
||||
children: React.ReactNode & React.ReactNode[];
|
||||
className?: string;
|
||||
codeBlock?: boolean;
|
||||
match: RegExpExecArray | null;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { copyData } = useCopyData();
|
||||
const [viewMode, setViewMode] = useState<'source' | 'iframe'>('source');
|
||||
const isPreview = viewMode === 'iframe';
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const { width, Ref } = useMarkdownWidth();
|
||||
const isMobile = width <= 420;
|
||||
|
||||
const codeBoxName = useMemo(() => {
|
||||
const input = match?.['input'] || '';
|
||||
if (!input) return match?.[1]?.toUpperCase();
|
||||
|
||||
const splitInput = input.split('#');
|
||||
return splitInput[1] || match?.[1]?.toUpperCase();
|
||||
}, [match]);
|
||||
|
||||
const Iframe = useMemo(
|
||||
() => (
|
||||
<iframe
|
||||
src={code}
|
||||
sandbox="allow-scripts allow-forms allow-popups allow-downloads allow-presentation allow-storage-access-by-user-activation"
|
||||
srcDoc={String(children)}
|
||||
sandbox=""
|
||||
referrerPolicy="no-referrer"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
minHeight: '70vh',
|
||||
border: 'none'
|
||||
border: 'none',
|
||||
background: 'white'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
),
|
||||
[children]
|
||||
);
|
||||
|
||||
if (codeBlock) {
|
||||
return (
|
||||
<Box
|
||||
ref={Ref}
|
||||
my={3}
|
||||
borderRadius={'md'}
|
||||
overflow={'hidden'}
|
||||
boxShadow={
|
||||
'0px 1px 2px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)'
|
||||
}
|
||||
>
|
||||
<Flex
|
||||
py={2}
|
||||
px={4}
|
||||
color={'white'}
|
||||
userSelect={'none'}
|
||||
alignItems="center"
|
||||
fontSize={'sm'}
|
||||
gap={1.5}
|
||||
{...(isPreview
|
||||
? {
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'gray.150',
|
||||
bg: 'myGray.25'
|
||||
}
|
||||
: {
|
||||
bg: 'myGray.800'
|
||||
})}
|
||||
>
|
||||
<Box
|
||||
flex={1}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
color={isPreview ? 'myGray.800' : 'rgba(255, 255, 255, 0.9)'}
|
||||
>
|
||||
{codeBoxName}
|
||||
<Flex
|
||||
cursor="pointer"
|
||||
onClick={() => copyData(String(children))}
|
||||
alignItems="center"
|
||||
ml={2}
|
||||
>
|
||||
<Icon name="copy" width="14px" />
|
||||
</Flex>
|
||||
</Box>
|
||||
<StyledButton
|
||||
label={t('common:common.Code')}
|
||||
iconName="code"
|
||||
onClick={() => setViewMode('source')}
|
||||
isActive={viewMode === 'source'}
|
||||
viewMode={viewMode}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
<StyledButton
|
||||
label={t('common:common.Preview')}
|
||||
iconName="preview"
|
||||
onClick={() => setViewMode('iframe')}
|
||||
isActive={viewMode === 'iframe'}
|
||||
viewMode={viewMode}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
<StyledButton
|
||||
label={t('common:common.FullScreen')}
|
||||
iconName="fullScreen"
|
||||
onClick={onOpen}
|
||||
viewMode={viewMode}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
</Flex>
|
||||
{isPreview ? (
|
||||
<Box w={width} h="60vh">
|
||||
{Iframe}
|
||||
</Box>
|
||||
) : (
|
||||
<SyntaxHighlighter style={codeLight as any} language={match?.[1]} PreTag="pre">
|
||||
{String(children).replace(/ /g, ' ')}
|
||||
</SyntaxHighlighter>
|
||||
)}
|
||||
|
||||
{isOpen && (
|
||||
<Modal onClose={onClose} isOpen size={'full'}>
|
||||
<ModalOverlay />
|
||||
<ModalContent h={'100vh'} display={'flex'} flexDirection={'column'}>
|
||||
<ModalHeader
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
p={4}
|
||||
bg="white"
|
||||
borderBottom="1px solid"
|
||||
borderColor="gray.300"
|
||||
height="60px"
|
||||
>
|
||||
<Box fontSize="lg" color="myGray.900">
|
||||
{t('common:common.FullScreenLight')}
|
||||
</Box>
|
||||
<ModalCloseButton zIndex={1} position={'relative'} top={0} right={0} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody p={0} flex="1">
|
||||
{Iframe}
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return <code className={className}>{children}</code>;
|
||||
};
|
||||
|
||||
export default MermaidBlock;
|
||||
export default React.memo(IframeHtmlCodeBlock);
|
||||
|
||||
@@ -23,6 +23,7 @@ const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr:
|
||||
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 ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
|
||||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
|
||||
@@ -127,6 +128,13 @@ function Code(e: any) {
|
||||
if (codeType === CodeClassNameEnum.iframe) {
|
||||
return <IframeCodeBlock code={strChildren} />;
|
||||
}
|
||||
if (codeType && codeType.toLowerCase() === CodeClassNameEnum.html) {
|
||||
return (
|
||||
<IframeHtmlCodeBlock className={className} codeBlock={codeBlock} match={match}>
|
||||
{children}
|
||||
</IframeHtmlCodeBlock>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CodeLight className={className} codeBlock={codeBlock} match={match}>
|
||||
|
||||
@@ -6,7 +6,8 @@ export enum CodeClassNameEnum {
|
||||
quote = 'quote',
|
||||
files = 'files',
|
||||
latex = 'latex',
|
||||
iframe = 'iframe'
|
||||
iframe = 'iframe',
|
||||
html = 'html'
|
||||
}
|
||||
|
||||
function htmlTableToLatex(html: string) {
|
||||
|
||||
Reference in New Issue
Block a user