diff --git a/client/src/api/fetch.ts b/client/src/api/fetch.ts index 3b0c75b44..58f295c66 100644 --- a/client/src/api/fetch.ts +++ b/client/src/api/fetch.ts @@ -89,8 +89,6 @@ export const streamFetch = ({ }); read(); } catch (err: any) { - console.log(111111111111111111); - if (err?.message === 'The user aborted a request.') { return resolve({ responseText, diff --git a/client/src/components/ChatBox/index.tsx b/client/src/components/ChatBox/index.tsx index 686610b14..2c092bc87 100644 --- a/client/src/components/ChatBox/index.tsx +++ b/client/src/components/ChatBox/index.tsx @@ -17,6 +17,7 @@ import { useUserStore } from '@/store/user'; import { Types } from 'mongoose'; import { HUMAN_ICON, quoteLenKey, rawSearchKey } from '@/constants/chat'; import Markdown from '@/components/Markdown'; +import { EventNameEnum } from '../Markdown/constant'; import MyIcon from '@/components/Icon'; import Avatar from '@/components/Avatar'; @@ -236,7 +237,7 @@ const ChatBox = ( * user confirm send prompt */ const sendPrompt = useCallback( - async (data: Record = {}) => { + async (data: Record = {}, inputVal = '') => { if (isChatting) { toast({ title: '正在聊天中...请等待结束', @@ -245,8 +246,7 @@ const ChatBox = ( return; } // get input value - const value = TextareaDom.current?.value || ''; - const val = value.trim().replace(/\n\s*/g, '\n'); + const val = inputVal.trim().replace(/\n\s*/g, '\n'); if (!val) { toast({ @@ -320,7 +320,7 @@ const ChatBox = ( }); if (!err?.responseText) { - resetInputVal(value); + resetInputVal(inputVal); setChatHistory(newChatList.slice(0, newChatList.length - 2)); } @@ -403,11 +403,25 @@ const ChatBox = ( {/* avatar */} {/* message */} - - - {welcomeText} - - + + { + const val = e?.data; + if (e?.event !== EventNameEnum.guideClick || !val) return; + handleSubmit((data) => sendPrompt(data, val))(); + }} + /> + )} {/* variable input */} @@ -416,54 +430,59 @@ const ChatBox = ( {/* avatar */} {/* message */} - - - - {variableModules.map((item) => ( - - {item.label} - {item.type === VariableInputEnum.input && ( - - )} - {item.type === VariableInputEnum.select && ( - ({ - label: item.value, - value: item.value - }))} - value={getValues(item.key)} - onchange={(e) => { - setValue(item.key, e); - setRefresh(!refresh); - }} - /> - )} - - ))} - {!variableIsFinish && ( - + /> + )} + {item.type === VariableInputEnum.select && ( + ({ + label: item.value, + value: item.value + }))} + value={getValues(item.key)} + onchange={(e) => { + setValue(item.key, e); + setRefresh(!refresh); + }} + /> )} - - + ))} + {!variableIsFinish && ( + + )} + )} @@ -650,7 +669,7 @@ const ChatBox = ( onKeyDown={(e) => { // 触发快捷发送 if (e.keyCode === 13 && !e.shiftKey) { - handleSubmit(sendPrompt)(); + handleSubmit((data) => sendPrompt(data, TextareaDom.current?.value))(); e.preventDefault(); } // 全选内容 @@ -685,7 +704,9 @@ const ChatBox = ( height={['18px', '20px']} cursor={'pointer'} color={'gray.500'} - onClick={handleSubmit(sendPrompt)} + onClick={() => { + handleSubmit((data) => sendPrompt(data, TextareaDom.current?.value))(); + }} /> )} diff --git a/client/src/components/Markdown/chat/Guide.tsx b/client/src/components/Markdown/chat/Guide.tsx new file mode 100644 index 000000000..a4288f3d8 --- /dev/null +++ b/client/src/components/Markdown/chat/Guide.tsx @@ -0,0 +1,49 @@ +import React, { useMemo } from 'react'; +import { Box } 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 'katex/dist/katex.min.css'; +import styles from '../index.module.scss'; +import { EventNameEnum } from '../constant'; + +const Guide = ({ text, onClick }: { text: string; onClick?: (e: any) => void }) => { + const formatText = useMemo(() => text.replace(/\[(.*?)\]/g, '[$1]()'), [text]); + + return ( + + { + if (!onClick) return; + onClick({ + event: EventNameEnum.guideClick, + data: String(children) + }); + }} + > + {String(children)} + + + ); + } + }} + > + {formatText} + + ); +}; + +export default React.memo(Guide); diff --git a/client/src/components/Markdown/constant.ts b/client/src/components/Markdown/constant.ts new file mode 100644 index 000000000..3c35344a8 --- /dev/null +++ b/client/src/components/Markdown/constant.ts @@ -0,0 +1,3 @@ +export enum EventNameEnum { + guideClick = 'guideClick' +} diff --git a/client/src/components/Markdown/index.tsx b/client/src/components/Markdown/index.tsx index c8fed70d8..c0616f985 100644 --- a/client/src/components/Markdown/index.tsx +++ b/client/src/components/Markdown/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useRef } from 'react'; import ReactMarkdown from 'react-markdown'; import RemarkGfm from 'remark-gfm'; import RemarkMath from 'remark-math'; @@ -7,19 +7,27 @@ import RemarkBreaks from 'remark-breaks'; import 'katex/dist/katex.min.css'; import styles from './index.module.scss'; +import dynamic from 'next/dynamic'; import Link from './Link'; import CodeLight from './CodeLight'; -import MermaidCodeBlock from './img/MermaidCodeBlock'; -import MdImage from './img/Image'; -function Code({ inline, className, children }: any) { +const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock')); +const MdImage = dynamic(() => import('./img/Image')); +const ChatGuide = dynamic(() => import('./chat/Guide')); + +function Code({ inline, className, children, onClick }: any) { const match = /language-(\w+)/.exec(className || ''); + const codeType = match?.[1]; - if (match?.[1] === 'mermaid') { + if (codeType === 'mermaid') { return ; } + if (codeType === 'guide') { + return ; + } + return ( {children} @@ -31,7 +39,23 @@ function Image({ src }: { src?: string }) { return ; } -const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => { +const Markdown = ({ + source, + isChatting = false, + onClick +}: { + source: string; + isChatting?: boolean; + onClick?: (e: any) => void; +}) => { + const components = useRef({ + a: Link, + img: Image, + pre: 'div', + p: 'div', + code: (props: any) => + }); + return ( {source} diff --git a/client/src/pages/app/detail/components/Settings.tsx b/client/src/pages/app/detail/components/Settings.tsx index 5f30924fe..e55370073 100644 --- a/client/src/pages/app/detail/components/Settings.tsx +++ b/client/src/pages/app/detail/components/Settings.tsx @@ -28,7 +28,7 @@ const Settings = ({ appId }: { appId: string }) => { content: '确认删除该应用?' }); const [settingAppInfo, setSettingAppInfo] = useState(); - const [fullScreen, setFullScreen] = useState(true); + const [fullScreen, setFullScreen] = useState(false); /* 点击删除 */ const handleDelModel = useCallback(async () => { diff --git a/client/src/pages/app/detail/components/edit/components/Nodes/NodeUserGuide.tsx b/client/src/pages/app/detail/components/edit/components/Nodes/NodeUserGuide.tsx index 9fd6d6d9b..3abfe63ac 100644 --- a/client/src/pages/app/detail/components/edit/components/Nodes/NodeUserGuide.tsx +++ b/client/src/pages/app/detail/components/edit/components/Nodes/NodeUserGuide.tsx @@ -12,7 +12,7 @@ const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6); import MyTooltip from '@/components/MyTooltip'; const welcomePlaceholder = - '每次对话开始前,发送一个初始内容。可使用的特殊标记:\n[快捷按键]: 用户点击后可以直接发送该问题'; + '每次对话开始前,发送一个初始内容。支持标准 Markdown 语法,可使用的额外标记:\n[快捷按键]: 用户点击后可以直接发送该问题'; const NodeUserGuide = ({ data: { inputs, outputs, onChangeNode, ...props } diff --git a/client/src/pages/chat/components/ToolMenu.tsx b/client/src/pages/chat/components/ToolMenu.tsx index a2a928484..921878bff 100644 --- a/client/src/pages/chat/components/ToolMenu.tsx +++ b/client/src/pages/chat/components/ToolMenu.tsx @@ -3,15 +3,23 @@ import { useChatBox } from '@/components/ChatBox'; import { ChatItemType } from '@/types/chat'; import { Menu, MenuButton, MenuList, MenuItem, Box } from '@chakra-ui/react'; import MyIcon from '@/components/Icon'; +import { useRouter } from 'next/router'; const ToolMenu = ({ history }: { history: ChatItemType[] }) => { const { onExportChat } = useChatBox(); + const router = useRouter(); const menuList = useRef([ - // { - // icon: 'shareLight', - // label: '分享对话', - // onClick: () => {} - // }, + { + icon: 'chatLight', + label: '新对话', + onClick: () => { + router.push({ + query: { + appId: router.query?.appId + } + }); + } + }, { icon: 'apiLight', label: 'HTML导出',