Commit e7d3a8e2 by archer

perf: http recognition and input textarea

parent 32a8d68c
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
"redis": "^4.6.5", "redis": "^4.6.5",
"rehype-katex": "^6.0.2", "rehype-katex": "^6.0.2",
"rehype-raw": "^6.1.1",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-math": "^5.1.1", "remark-math": "^5.1.1",
"sass": "^1.58.3", "sass": "^1.58.3",
......
import React, { memo } from 'react'; import React, { memo, useMemo } from 'react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { Box, Flex, useColorModeValue } from '@chakra-ui/react'; import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
import { useCopyData } from '@/utils/tools'; import { useCopyData, formatLinkTextToHtml } from '@/utils/tools';
import Icon from '@/components/Icon'; import Icon from '@/components/Icon';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math'; import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex'; import rehypeKatex from 'rehype-katex';
import rehypeRaw from 'rehype-raw';
import 'katex/dist/katex.min.css'; import 'katex/dist/katex.min.css';
import styles from './index.module.scss'; import styles from './index.module.scss';
...@@ -15,13 +16,17 @@ import { codeLight } from './codeLight'; ...@@ -15,13 +16,17 @@ import { codeLight } from './codeLight';
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => { const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
const { copyData } = useCopyData(); const { copyData } = useCopyData();
const formatSource = useMemo(() => {
return formatLinkTextToHtml(source);
}, [source]);
return ( return (
<ReactMarkdown <ReactMarkdown
className={`markdown ${styles.markdown} ${ className={`markdown ${styles.markdown} ${
isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : '' isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''
}`} }`}
remarkPlugins={[remarkMath]} remarkPlugins={[remarkMath]}
rehypePlugins={[remarkGfm, rehypeKatex]} rehypePlugins={[rehypeRaw, remarkGfm, rehypeKatex]}
components={{ components={{
pre: 'div', pre: 'div',
code({ node, inline, className, children, ...props }) { code({ node, inline, className, children, ...props }) {
...@@ -63,7 +68,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: ...@@ -63,7 +68,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
}} }}
linkTarget="_blank" linkTarget="_blank"
> >
{source} {formatSource}
</ReactMarkdown> </ReactMarkdown>
); );
}; };
......
...@@ -171,6 +171,9 @@ export const theme = extendTheme({ ...@@ -171,6 +171,9 @@ export const theme = extendTheme({
fontWeight: 400, fontWeight: 400,
height: '100%', height: '100%',
overflow: 'hidden' overflow: 'hidden'
},
a: {
color: 'myBlue.700'
} }
} }
}, },
......
...@@ -36,7 +36,7 @@ import { useToast } from '@/hooks/useToast'; ...@@ -36,7 +36,7 @@ import { useToast } from '@/hooks/useToast';
import { useScreen } from '@/hooks/useScreen'; import { useScreen } from '@/hooks/useScreen';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useCopyData, voiceBroadcast } from '@/utils/tools'; import { useCopyData, voiceBroadcast, formatLinkTextToHtml } from '@/utils/tools';
import { streamFetch } from '@/api/fetch'; import { streamFetch } from '@/api/fetch';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
...@@ -90,7 +90,6 @@ const Chat = ({ ...@@ -90,7 +90,6 @@ const Chat = ({
const controller = useRef(new AbortController()); const controller = useRef(new AbortController());
const isLeavePage = useRef(false); const isLeavePage = useRef(false);
const [inputVal, setInputVal] = useState(''); // user input prompt
const [showSystemPrompt, setShowSystemPrompt] = useState(''); const [showSystemPrompt, setShowSystemPrompt] = useState('');
const [messageContextMenuData, setMessageContextMenuData] = useState<{ const [messageContextMenuData, setMessageContextMenuData] = useState<{
// message messageContextMenuData // message messageContextMenuData
...@@ -168,7 +167,8 @@ const Chat = ({ ...@@ -168,7 +167,8 @@ const Chat = ({
// 重置输入内容 // 重置输入内容
const resetInputVal = useCallback((val: string) => { const resetInputVal = useCallback((val: string) => {
setInputVal(val); if (!TextareaDom.current) return;
TextareaDom.current.value = val;
setTimeout(() => { setTimeout(() => {
/* 回到最小高度 */ /* 回到最小高度 */
if (TextareaDom.current) { if (TextareaDom.current) {
...@@ -289,6 +289,7 @@ const Chat = ({ ...@@ -289,6 +289,7 @@ const Chat = ({
* 发送一个内容 * 发送一个内容
*/ */
const sendPrompt = useCallback(async () => { const sendPrompt = useCallback(async () => {
// get value
if (isChatting) { if (isChatting) {
toast({ toast({
title: '正在聊天中...请等待结束', title: '正在聊天中...请等待结束',
...@@ -296,9 +297,10 @@ const Chat = ({ ...@@ -296,9 +297,10 @@ const Chat = ({
}); });
return; return;
} }
const storeInput = inputVal;
// 去除空行 // get input value
const val = inputVal.trim().replace(/\n\s*/g, '\n'); const value = TextareaDom.current?.value || '';
const val = value.trim().replace(/\n\s*/g, '\n');
if (!val) { if (!val) {
toast({ toast({
...@@ -346,7 +348,7 @@ const Chat = ({ ...@@ -346,7 +348,7 @@ const Chat = ({
isClosable: true isClosable: true
}); });
resetInputVal(storeInput); resetInputVal(value);
setChatData((state) => ({ setChatData((state) => ({
...state, ...state,
...@@ -355,7 +357,6 @@ const Chat = ({ ...@@ -355,7 +357,6 @@ const Chat = ({
} }
}, [ }, [
isChatting, isChatting,
inputVal,
chatData.history, chatData.history,
setChatData, setChatData,
resetInputVal, resetInputVal,
...@@ -855,7 +856,10 @@ const Chat = ({ ...@@ -855,7 +856,10 @@ const Chat = ({
bg={'myBlue.300'} bg={'myBlue.300'}
onContextMenu={(e) => onclickContextMenu(e, item)} onContextMenu={(e) => onclickContextMenu(e, item)}
> >
<Box as={'p'}>{item.value}</Box> <Box
as={'p'}
dangerouslySetInnerHTML={{ __html: formatLinkTextToHtml(item.value) }}
/>
</Card> </Card>
</Box> </Box>
)} )}
...@@ -888,7 +892,6 @@ const Chat = ({ ...@@ -888,7 +892,6 @@ const Chat = ({
}} }}
placeholder="提问" placeholder="提问"
resize={'none'} resize={'none'}
value={inputVal}
rows={1} rows={1}
height={'22px'} height={'22px'}
lineHeight={'22px'} lineHeight={'22px'}
...@@ -901,7 +904,6 @@ const Chat = ({ ...@@ -901,7 +904,6 @@ const Chat = ({
color={useColorModeValue('blackAlpha.700', 'white')} color={useColorModeValue('blackAlpha.700', 'white')}
onChange={(e) => { onChange={(e) => {
const textarea = e.target; const textarea = e.target;
setInputVal(textarea.value);
textarea.style.height = textareaMinH; textarea.style.height = textareaMinH;
textarea.style.height = `${textarea.scrollHeight}px`; textarea.style.height = `${textarea.scrollHeight}px`;
}} }}
......
...@@ -113,3 +113,9 @@ export const voiceBroadcast = ({ text }: { text: string }) => { ...@@ -113,3 +113,9 @@ export const voiceBroadcast = ({ text }: { text: string }) => {
cancel: () => window.speechSynthesis?.cancel() cancel: () => window.speechSynthesis?.cancel()
}; };
}; };
export const formatLinkTextToHtml = (text: string) => {
const httpReg =
/(?<!\[.*\]\()((http|https|ftp):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)(?![\)])/gi;
return text.replace(httpReg, '<a href="$&" target="_blank">$&</a>');
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment