From 85de3c1d64dca8222b29328f1b5c733674513b7b Mon Sep 17 00:00:00 2001
From: papapatrick <109422393+Patrickill@users.noreply.github.com>
Date: Mon, 22 Jul 2024 18:25:25 +0800
Subject: [PATCH] Chat-perf (#2117)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: 新增对话框底部复制按钮
* fix: 对话框底部复制按钮定位问题
* feat: 过长引用折叠功能
* merge: 合并主仓库代码
* refactor: 删除不必要代码
* doc: 文档补充的部分修改成英文
---
dev.md | 17 ++-
.../web/components/common/Icon/constants.ts | 2 +
.../Icon/icons/core/chat/chevronDown.svg | 3 +
.../common/Icon/icons/core/chat/chevronUp.svg | 3 +
.../ChatBox/components/ChatItem.tsx | 33 +++-
.../ChatBox/components/ResponseTags.tsx | 143 +++++++++++++-----
6 files changed, 157 insertions(+), 44 deletions(-)
create mode 100644 packages/web/components/common/Icon/icons/core/chat/chevronDown.svg
create mode 100644 packages/web/components/common/Icon/icons/core/chat/chevronUp.svg
diff --git a/dev.md b/dev.md
index f6154854e..94cfe5ed0 100644
--- a/dev.md
+++ b/dev.md
@@ -23,12 +23,20 @@ pnpm dev
make dev name=app
```
+Note: If the Node version is >= 20, you need to pass the `--no-node-snapshot` parameter to Node when running `pnpm i`
+
+```sh
+NODE_OPTIONS=--no-node-snapshot pnpm i
+```
+
## I18N
+
### Install i18n-ally Plugin
1. Open the Extensions Marketplace in VSCode, search for and install the `i18n Ally` plugin.
### Code Optimization Examples
+
#### Fetch Specific Namespace Translations in `getServerSideProps`
```typescript
@@ -42,7 +50,9 @@ export async function getServerSideProps(context: any) {
};
}
```
+
#### Use useTranslation Hook in Page
+
```typescript
// pages/yourPage.tsx
import { useTranslation } from 'next-i18next';
@@ -64,7 +74,9 @@ const YourComponent = () => {
export default YourComponent;
```
+
#### Handle Static File Translations
+
```typescript
// utils/i18n.ts
import { i18nT } from '@fastgpt/web/i18n/utils';
@@ -77,12 +89,12 @@ const staticContent = {
export default staticContent;
```
+
### Standardize Translation Format
+
- Use the t(namespace:key) format to ensure consistent naming.
- Translation keys should use lowercase letters and underscores, e.g., common.close.
-
-
## Build
```sh
@@ -96,4 +108,3 @@ docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/f
# Make cmd: Build image with proxy
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao
```
-
diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts
index 14a58fd82..94f9450cf 100644
--- a/packages/web/components/common/Icon/constants.ts
+++ b/packages/web/components/common/Icon/constants.ts
@@ -106,6 +106,8 @@ export const iconPaths = {
'core/chat/fileSelect': () => import('./icons/core/chat/fileSelect.svg'),
'core/chat/finishSpeak': () => import('./icons/core/chat/finishSpeak.svg'),
'core/chat/quoteFill': () => import('./icons/core/chat/quoteFill.svg'),
+ 'core/chat/chevronDown': () => import('./icons/core/chat/chevronDown.svg'),
+ 'core/chat/chevronUp': () => import('./icons/core/chat/chevronUp.svg'),
'core/chat/quoteSign': () => import('./icons/core/chat/quoteSign.svg'),
'core/chat/recordFill': () => import('./icons/core/chat/recordFill.svg'),
'core/chat/sendFill': () => import('./icons/core/chat/sendFill.svg'),
diff --git a/packages/web/components/common/Icon/icons/core/chat/chevronDown.svg b/packages/web/components/common/Icon/icons/core/chat/chevronDown.svg
new file mode 100644
index 000000000..cc8ed229c
--- /dev/null
+++ b/packages/web/components/common/Icon/icons/core/chat/chevronDown.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/web/components/common/Icon/icons/core/chat/chevronUp.svg b/packages/web/components/common/Icon/icons/core/chat/chevronUp.svg
new file mode 100644
index 000000000..bdca3776b
--- /dev/null
+++ b/packages/web/components/common/Icon/icons/core/chat/chevronUp.svg
@@ -0,0 +1,3 @@
+
diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
index de8dd7d32..2cf15aed2 100644
--- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
+++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx
@@ -1,5 +1,5 @@
import { Box, BoxProps, Card, Flex } from '@chakra-ui/react';
-import React, { useMemo } from 'react';
+import React, { useMemo, useTransition } from 'react';
import ChatController, { type ChatControllerProps } from './ChatController';
import ChatAvatar from './ChatAvatar';
import { MessageCardStyle } from '../constants';
@@ -11,7 +11,10 @@ import FilesBlock from './FilesBox';
import { ChatBoxContext } from '../Provider';
import { useContextSelector } from 'use-context-selector';
import AIResponseBox from '../../../components/AIResponseBox';
-
+import { useCopyData } from '@/web/common/hooks/useCopyData';
+import MyIcon from '@fastgpt/web/components/common/Icon';
+import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
+import { useTranslation } from 'next-i18next';
const colorMap = {
[ChatStatusEnum.loading]: {
bg: 'myGray.100',
@@ -62,9 +65,11 @@ const ChatItem = ({
bg: 'myGray.50'
};
+ const { t } = useTranslation();
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
const { chat } = chatControllerProps;
-
+ const { copyData } = useCopyData();
+ const chatText = useMemo(() => formatChatValue2InputType(chat.value).text || '', [chat.value]);
const ContentCard = useMemo(() => {
if (type === 'Human') {
const { text, files = [] } = formatChatValue2InputType(chat.value);
@@ -148,6 +153,28 @@ const ChatItem = ({
>
{ContentCard}
{children}
+ {/* 对话框底部的复制按钮 */}
+ {type == ChatRoleEnum.AI && (!isChatting || (isChatting && !isLastChild)) && (
+
+
+ copyData(chatText)}
+ />
+
+
+ )}
>
diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx
index 4e10637fd..06a33a9f5 100644
--- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx
+++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ResponseTags.tsx
@@ -1,7 +1,7 @@
-import React, { useMemo, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
import { type ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
import { DispatchNodeResponseType } from '@fastgpt/global/core/workflow/runtime/type.d';
-import { Flex, useDisclosure, useTheme, Box } from '@chakra-ui/react';
+import { Flex, useDisclosure, Box, Collapse } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import dynamic from 'next/dynamic';
@@ -13,6 +13,7 @@ import ChatBoxDivider from '@/components/core/chat/Divider';
import { strIsLink } from '@fastgpt/global/common/string/tools';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
+import { useSize } from 'ahooks';
const QuoteModal = dynamic(() => import('./QuoteModal'));
const ContextModal = dynamic(() => import('./ContextModal'));
@@ -28,9 +29,9 @@ const ResponseTags = ({
flowResponses?: ChatHistoryItemResType[];
showDetail: boolean;
}) => {
- const theme = useTheme();
const { isPc } = useSystem();
const { t } = useTranslation();
+ const quoteListRef = React.useRef(null);
const [quoteModalData, setQuoteModalData] = useState<{
rawSearch: SearchDataResponseItemType[];
metadata?: {
@@ -39,6 +40,8 @@ const ResponseTags = ({
sourceName: string;
};
}>();
+ const [isOverflow, setIsOverflow] = useState(true);
+ const [quoteFolded, setQuoteFolded] = useState(true);
const [contextModalData, setContextModalData] =
useState();
const {
@@ -47,6 +50,13 @@ const ResponseTags = ({
onClose: onCloseWholeModal
} = useDisclosure();
+ const quoteListSize = useSize(quoteListRef);
+ useEffect(() => {
+ setIsOverflow(
+ quoteListRef.current ? quoteListRef.current.scrollHeight > (isPc ? 50 : 55) : true
+ );
+ }, [isOverflow, quoteListSize]);
+
const {
llmModuleAccount,
quoteList = [],
@@ -64,7 +74,6 @@ const ResponseTags = ({
.flat();
const chatData = flatResponse.find(isLLMNode);
-
const quoteList = flatResponse
.filter((item) => item.moduleType === FlowNodeTypeEnum.datasetSearchNode)
.map((item) => item.quoteList)
@@ -80,7 +89,6 @@ const ResponseTags = ({
},
{}
);
-
return {
llmModuleAccount: flatResponse.filter(isLLMNode).length,
quoteList,
@@ -102,44 +110,103 @@ const ResponseTags = ({
<>
{sourceList.length > 0 && (
<>
-
-
- {sourceList.map((item) => (
-
+
+
+ {' '}
+
+ {quoteFolded && isOverflow && (
+ setQuoteFolded(!quoteFolded)}
+ />
+ )}
+
+
+
+ {
+
{
- e.stopPropagation();
- setQuoteModalData({
- rawSearch: quoteList,
- metadata: {
- collectionId: item.collectionId,
- sourceId: item.sourceId,
- sourceName: item.sourceName
- }
- });
- }}
+ flexWrap={'wrap'}
+ gap={2}
+ height={quoteFolded && isOverflow ? ['55px', '50px'] : 'auto'}
+ overflow={'hidden'}
+ _after={
+ quoteFolded && isOverflow
+ ? {
+ content: '""',
+ position: 'absolute',
+ zIndex: 2,
+ bottom: 0,
+ left: 0,
+ width: '100%',
+ height: '50%',
+ background:
+ 'linear-gradient(to bottom, rgba(247,247,247,0), rgba(247, 247, 247, 0.91))',
+ pointerEvents: 'none'
+ }
+ : {}
+ }
>
-
-
- {item.sourceName}
-
+ {sourceList.map((item) => {
+ return (
+
+ {
+ e.stopPropagation();
+ setQuoteModalData({
+ rawSearch: quoteList,
+ metadata: {
+ collectionId: item.collectionId,
+ sourceId: item.sourceId,
+ sourceName: item.sourceName
+ }
+ });
+ }}
+ >
+
+
+ {item.sourceName}
+
+
+
+ );
+ })}
+ {isOverflow && !quoteFolded && (
+ setQuoteFolded(!quoteFolded)}
+ />
+ )}
-
- ))}
+
+ }
>
)}