perf: http recognition and input textarea

This commit is contained in:
archer
2023-05-15 13:43:17 +08:00
parent 32a8d68c6c
commit e7d3a8e2e1
6 changed files with 238 additions and 24 deletions

View File

@@ -1,12 +1,13 @@
import React, { memo } from 'react';
import React, { memo, useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
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 remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
import rehypeRaw from 'rehype-raw';
import 'katex/dist/katex.min.css';
import styles from './index.module.scss';
@@ -15,13 +16,17 @@ import { codeLight } from './codeLight';
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
const { copyData } = useCopyData();
const formatSource = useMemo(() => {
return formatLinkTextToHtml(source);
}, [source]);
return (
<ReactMarkdown
className={`markdown ${styles.markdown} ${
isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''
}`}
remarkPlugins={[remarkMath]}
rehypePlugins={[remarkGfm, rehypeKatex]}
rehypePlugins={[rehypeRaw, remarkGfm, rehypeKatex]}
components={{
pre: 'div',
code({ node, inline, className, children, ...props }) {
@@ -63,7 +68,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
}}
linkTarget="_blank"
>
{source}
{formatSource}
</ReactMarkdown>
);
};

View File

@@ -171,6 +171,9 @@ export const theme = extendTheme({
fontWeight: 400,
height: '100%',
overflow: 'hidden'
},
a: {
color: 'myBlue.700'
}
}
},

View File

@@ -36,7 +36,7 @@ import { useToast } from '@/hooks/useToast';
import { useScreen } from '@/hooks/useScreen';
import { useQuery } from '@tanstack/react-query';
import dynamic from 'next/dynamic';
import { useCopyData, voiceBroadcast } from '@/utils/tools';
import { useCopyData, voiceBroadcast, formatLinkTextToHtml } from '@/utils/tools';
import { streamFetch } from '@/api/fetch';
import MyIcon from '@/components/Icon';
import { throttle } from 'lodash';
@@ -90,7 +90,6 @@ const Chat = ({
const controller = useRef(new AbortController());
const isLeavePage = useRef(false);
const [inputVal, setInputVal] = useState(''); // user input prompt
const [showSystemPrompt, setShowSystemPrompt] = useState('');
const [messageContextMenuData, setMessageContextMenuData] = useState<{
// message messageContextMenuData
@@ -168,7 +167,8 @@ const Chat = ({
// 重置输入内容
const resetInputVal = useCallback((val: string) => {
setInputVal(val);
if (!TextareaDom.current) return;
TextareaDom.current.value = val;
setTimeout(() => {
/* 回到最小高度 */
if (TextareaDom.current) {
@@ -289,6 +289,7 @@ const Chat = ({
* 发送一个内容
*/
const sendPrompt = useCallback(async () => {
// get value
if (isChatting) {
toast({
title: '正在聊天中...请等待结束',
@@ -296,9 +297,10 @@ const Chat = ({
});
return;
}
const storeInput = inputVal;
// 去除空行
const val = inputVal.trim().replace(/\n\s*/g, '\n');
// get input value
const value = TextareaDom.current?.value || '';
const val = value.trim().replace(/\n\s*/g, '\n');
if (!val) {
toast({
@@ -346,7 +348,7 @@ const Chat = ({
isClosable: true
});
resetInputVal(storeInput);
resetInputVal(value);
setChatData((state) => ({
...state,
@@ -355,7 +357,6 @@ const Chat = ({
}
}, [
isChatting,
inputVal,
chatData.history,
setChatData,
resetInputVal,
@@ -855,7 +856,10 @@ const Chat = ({
bg={'myBlue.300'}
onContextMenu={(e) => onclickContextMenu(e, item)}
>
<Box as={'p'}>{item.value}</Box>
<Box
as={'p'}
dangerouslySetInnerHTML={{ __html: formatLinkTextToHtml(item.value) }}
/>
</Card>
</Box>
)}
@@ -888,7 +892,6 @@ const Chat = ({
}}
placeholder="提问"
resize={'none'}
value={inputVal}
rows={1}
height={'22px'}
lineHeight={'22px'}
@@ -901,7 +904,6 @@ const Chat = ({
color={useColorModeValue('blackAlpha.700', 'white')}
onChange={(e) => {
const textarea = e.target;
setInputVal(textarea.value);
textarea.style.height = textareaMinH;
textarea.style.height = `${textarea.scrollHeight}px`;
}}

View File

@@ -113,3 +113,9 @@ export const voiceBroadcast = ({ text }: { text: string }) => {
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>');
};