BraisedPork commited on
Commit
bd1a2b6
·
1 Parent(s): e961666

V0.2.0 (#236)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +1 -1
  2. Dockerfile +2 -1
  3. README.md +5 -2
  4. backend_example.py +17 -14
  5. docker/msdl/__main__.py +29 -17
  6. docker/msdl/config.py +1 -0
  7. docker/setup.py +1 -1
  8. frontend/React/.gitignore +5 -0
  9. frontend/React/.prettierignore +1 -1
  10. frontend/React/README.md +134 -79
  11. frontend/React/README_zh-CN.md +135 -0
  12. frontend/React/package.json +6 -0
  13. frontend/React/src/App.module.less +2 -4
  14. frontend/React/src/App.tsx +3 -1
  15. frontend/React/src/components/answer/index.tsx +0 -39
  16. frontend/React/src/components/chat-right/index.tsx +0 -310
  17. frontend/React/src/components/mind-map-item/index.module.less +0 -117
  18. frontend/React/src/components/mind-map-item/index.tsx +0 -50
  19. frontend/React/src/config/cgi.ts +0 -2
  20. frontend/React/src/global.d.ts +1 -1
  21. frontend/React/src/index.less +1 -3
  22. frontend/React/src/pages/mindsearch/assets/bookmark-icon.svg +4 -0
  23. frontend/React/src/pages/mindsearch/assets/empty-chat-right.svg +52 -0
  24. frontend/React/src/pages/mindsearch/assets/fold-icon.svg +3 -0
  25. frontend/React/src/pages/mindsearch/assets/logo.svg +24 -0
  26. frontend/React/src/pages/mindsearch/assets/logo1.svg +32 -0
  27. frontend/React/src/pages/mindsearch/assets/mindsearch-avatar.svg +17 -0
  28. frontend/React/src/pages/mindsearch/assets/pack-up-disabled.svg +3 -0
  29. frontend/React/src/pages/mindsearch/assets/pack-up.svg +5 -0
  30. frontend/React/src/pages/mindsearch/assets/sendIcon.svg +4 -0
  31. frontend/React/src/pages/mindsearch/assets/think-progress-icon.svg +15 -0
  32. frontend/React/src/pages/mindsearch/assets/unflod-icon.svg +3 -0
  33. frontend/React/src/pages/mindsearch/components/answer/index.module.less +110 -0
  34. frontend/React/src/pages/mindsearch/components/answer/index.tsx +108 -0
  35. frontend/React/src/{components/answer → pages/mindsearch/components/answer/loading-animation}/index.module.less +16 -85
  36. frontend/React/src/pages/mindsearch/components/answer/loading-animation/index.tsx +13 -0
  37. frontend/React/src/pages/mindsearch/components/chat-right/components/empty-placeholder/index.module.less +27 -0
  38. frontend/React/src/pages/mindsearch/components/chat-right/components/empty-placeholder/index.tsx +17 -0
  39. frontend/React/src/pages/mindsearch/components/chat-right/components/query-item/index.module.less +30 -0
  40. frontend/React/src/pages/mindsearch/components/chat-right/components/query-item/index.tsx +11 -0
  41. frontend/React/src/pages/mindsearch/components/chat-right/components/search-item/index.module.less +101 -0
  42. frontend/React/src/pages/mindsearch/components/chat-right/components/search-item/index.tsx +30 -0
  43. frontend/React/src/{components → pages/mindsearch/components}/chat-right/index.module.less +92 -95
  44. frontend/React/src/pages/mindsearch/components/chat-right/index.tsx +272 -0
  45. frontend/React/src/pages/mindsearch/components/custom-markdown/index.module.less +168 -0
  46. frontend/React/src/pages/mindsearch/components/custom-markdown/index.tsx +116 -0
  47. frontend/React/src/{components → pages/mindsearch/components}/iconfont/index.tsx +3 -3
  48. frontend/React/src/{components → pages/mindsearch/components}/loading/index.module.less +24 -24
  49. frontend/React/src/{components → pages/mindsearch/components}/loading/index.tsx +2 -2
  50. frontend/React/src/pages/mindsearch/components/mind-map-item/index.module.less +150 -0
.gitignore CHANGED
@@ -1,6 +1,6 @@
1
  # Byte-compiled / optimized / DLL files
2
  __pycache__/
3
- *.py[cod]
4
  *$py.class
5
 
6
  # C extensions
 
1
  # Byte-compiled / optimized / DLL files
2
  __pycache__/
3
+ *.py[ciod]
4
  *$py.class
5
 
6
  # C extensions
Dockerfile CHANGED
@@ -23,4 +23,5 @@ RUN conda create --name fastapi python=3.10 -y && \
23
  EXPOSE 8000
24
 
25
  # 启动 FastAPI 服务
26
- CMD ["conda", "run", "--no-capture-output", "-n", "fastapi", "uvicorn", "mindsearch.app:app", "--host", "0.0.0.0", "--port", "8002"]
 
 
23
  EXPOSE 8000
24
 
25
  # 启动 FastAPI 服务
26
+ ENTRYPOINT ["conda", "run", "--no-capture-output", "-n", "fastapi"]
27
+ CMD ["python3", "-m", "mindsearch.app", "--asy", "--host", "0.0.0.0", "--port", "8002"]
README.md CHANGED
@@ -39,7 +39,7 @@ mv .env.example .env
39
  Setup FastAPI Server.
40
 
41
  ```bash
42
- python -m mindsearch.app --lang en --model_format internlm_server --search_engine DuckDuckGoSearch
43
  ```
44
 
45
  - `--lang`: language of the model, `en` for English and `cn` for Chinese.
@@ -52,8 +52,10 @@ python -m mindsearch.app --lang en --model_format internlm_server --search_engin
52
  - `BingSearch` for Bing search engine.
53
  - `BraveSearch` for Brave search web api engine.
54
  - `GoogleSearch` for Google Serper web search api engine.
 
55
 
56
- Please set your Web Search engine API key as the `WEB_SEARCH_API_KEY` environment variable unless you are using `DuckDuckGo`.
 
57
 
58
  ### Step4: Setup MindSearch Frontend
59
 
@@ -98,6 +100,7 @@ To use a different type of web search API, modify the `searcher_type` attribute
98
  - `DuckDuckGoSearch`
99
  - `BraveSearch`
100
  - `BingSearch`
 
101
 
102
  For example, to change to the Brave Search API, you would configure it as follows:
103
 
 
39
  Setup FastAPI Server.
40
 
41
  ```bash
42
+ python -m mindsearch.app --lang en --model_format internlm_server --search_engine DuckDuckGoSearch --asy
43
  ```
44
 
45
  - `--lang`: language of the model, `en` for English and `cn` for Chinese.
 
52
  - `BingSearch` for Bing search engine.
53
  - `BraveSearch` for Brave search web api engine.
54
  - `GoogleSearch` for Google Serper web search api engine.
55
+ - `TencentSearch` for Tencent search api engine.
56
 
57
+ Please set your Web Search engine API key as the `WEB_SEARCH_API_KEY` environment variable unless you are using `DuckDuckGo`, or `TencentSearch` that requires secret id as `TENCENT_SEARCH_SECRET_ID` and secret key as `TENCENT_SEARCH_SECRET_KEY`.
58
+ - `--asy`: deploy asynchronous agents.
59
 
60
  ### Step4: Setup MindSearch Frontend
61
 
 
100
  - `DuckDuckGoSearch`
101
  - `BraveSearch`
102
  - `BingSearch`
103
+ - `TencentSearch`
104
 
105
  For example, to change to the Brave Search API, you would configure it as follows:
106
 
backend_example.py CHANGED
@@ -1,34 +1,37 @@
1
  import json
 
2
  import requests
3
 
4
  # Define the backend URL
5
- url = 'http://localhost:8002/solve'
6
- headers = {'Content-Type': 'application/json'}
 
7
 
8
  # Function to send a query to the backend and get the response
9
  def get_response(query):
10
  # Prepare the input data
11
- data = {'inputs': [{'role': 'user', 'content': query}]}
12
-
13
  # Send the request to the backend
14
  response = requests.post(url, headers=headers, data=json.dumps(data), timeout=20, stream=True)
15
-
16
  # Process the streaming response
17
- for chunk in response.iter_lines(chunk_size=8192, decode_unicode=False, delimiter=b'\n'):
18
  if chunk:
19
- decoded = chunk.decode('utf-8')
20
- if decoded == '\r':
21
  continue
22
- if decoded[:6] == 'data: ':
23
  decoded = decoded[6:]
24
- elif decoded.startswith(': ping - '):
25
  continue
26
  response_data = json.loads(decoded)
27
- agent_return = response_data['response']
28
- node_name = response_data['current_node']
29
  print(f"Node: {node_name}, Response: {agent_return['response']}")
30
 
 
31
  # Example usage
32
- if __name__ == '__main__':
33
  query = "What is the weather like today in New York?"
34
- get_response(query)
 
1
  import json
2
+
3
  import requests
4
 
5
  # Define the backend URL
6
+ url = "http://localhost:8002/solve"
7
+ headers = {"Content-Type": "application/json"}
8
+
9
 
10
  # Function to send a query to the backend and get the response
11
  def get_response(query):
12
  # Prepare the input data
13
+ data = {"inputs": query}
14
+
15
  # Send the request to the backend
16
  response = requests.post(url, headers=headers, data=json.dumps(data), timeout=20, stream=True)
17
+
18
  # Process the streaming response
19
+ for chunk in response.iter_lines(chunk_size=8192, decode_unicode=False, delimiter=b"\n"):
20
  if chunk:
21
+ decoded = chunk.decode("utf-8")
22
+ if decoded == "\r":
23
  continue
24
+ if decoded[:6] == "data: ":
25
  decoded = decoded[6:]
26
+ elif decoded.startswith(": ping - "):
27
  continue
28
  response_data = json.loads(decoded)
29
+ agent_return = response_data["response"]
30
+ node_name = response_data["current_node"]
31
  print(f"Node: {node_name}, Response: {agent_return['response']}")
32
 
33
+
34
  # Example usage
35
+ if __name__ == "__main__":
36
  query = "What is the weather like today in New York?"
37
+ get_response(query)
docker/msdl/__main__.py CHANGED
@@ -55,8 +55,7 @@ def copy_backend_dockerfile(choice):
55
  "BACKEND_DOCKERFILE_COPIED",
56
  source_path=str(source_path),
57
  dest_path=str(dest_path),
58
- )
59
- )
60
 
61
 
62
  def copy_frontend_dockerfile():
@@ -75,19 +74,30 @@ def copy_frontend_dockerfile():
75
  "FRONTEND_DOCKERFILE_COPIED",
76
  source_path=str(source_path),
77
  dest_path=str(dest_path),
78
- )
79
- )
80
 
81
 
82
  def get_user_choices():
83
  backend_language_choices = [
84
- {"name": t("CHINESE"), "value": "cn"},
85
- {"name": t("ENGLISH"), "value": "en"},
 
 
 
 
 
 
86
  ]
87
 
88
  model_deployment_type = [
89
- {"name": t("CLOUD_MODEL"), "value": CLOUD_LLM_DOCKERFILE},
90
- {"name": t("LOCAL_MODEL"), "value": LOCAL_LLM_DOCKERFILE},
 
 
 
 
 
 
91
  ]
92
 
93
  backend_language = inquirer.select(
@@ -103,7 +113,10 @@ def get_user_choices():
103
  model_formats = get_model_formats(model)
104
  model_format = inquirer.select(
105
  message=t("MODEL_FORMAT_CHOICE"),
106
- choices=[{"name": format, "value": format} for format in model_formats],
 
 
 
107
  ).execute()
108
 
109
  # If the model is cloud_llm, ask for the API key
@@ -118,7 +131,8 @@ def get_user_choices():
118
 
119
  if existing_api_key:
120
  use_existing = inquirer.confirm(
121
- message=t("CONFIRM_USE_EXISTING_API_KEY", ENV_VAR_NAME=env_var_name),
 
122
  default=True,
123
  ).execute()
124
 
@@ -126,17 +140,16 @@ def get_user_choices():
126
  return backend_language, model, model_format
127
  else:
128
  print(
129
- t("CONFIRM_OVERWRITE_EXISTING_API_KEY", ENV_VAR_NAME=env_var_name)
130
- )
131
  else:
132
  print(t("PLEASE_INPUT_NEW_API_KEY", ENV_VAR_NAME=env_var_name))
133
 
134
  while True:
135
  api_key = inquirer.secret(
136
  message=t(
137
- "PLEASE_INPUT_NEW_API_KEY_FROM_ZERO", ENV_VAR_NAME=env_var_name
138
- )
139
- ).execute()
140
  cleaned_api_key = clean_api_key(api_key)
141
 
142
  if validate_api_key(cleaned_api_key, env_var_name, t):
@@ -145,8 +158,7 @@ def get_user_choices():
145
  else:
146
  print(t("INVALID_API_KEY_FORMAT"))
147
  retry = inquirer.confirm(
148
- message=t("RETRY_API_KEY_INPUT"), default=True
149
- ).execute()
150
  if not retry:
151
  print(t("API_KEY_INPUT_CANCELLED"))
152
  sys.exit(1)
 
55
  "BACKEND_DOCKERFILE_COPIED",
56
  source_path=str(source_path),
57
  dest_path=str(dest_path),
58
+ ))
 
59
 
60
 
61
  def copy_frontend_dockerfile():
 
74
  "FRONTEND_DOCKERFILE_COPIED",
75
  source_path=str(source_path),
76
  dest_path=str(dest_path),
77
+ ))
 
78
 
79
 
80
  def get_user_choices():
81
  backend_language_choices = [
82
+ {
83
+ "name": t("CHINESE"),
84
+ "value": "cn"
85
+ },
86
+ {
87
+ "name": t("ENGLISH"),
88
+ "value": "en"
89
+ },
90
  ]
91
 
92
  model_deployment_type = [
93
+ {
94
+ "name": t("CLOUD_MODEL"),
95
+ "value": CLOUD_LLM_DOCKERFILE
96
+ },
97
+ {
98
+ "name": t("LOCAL_MODEL"),
99
+ "value": LOCAL_LLM_DOCKERFILE
100
+ },
101
  ]
102
 
103
  backend_language = inquirer.select(
 
113
  model_formats = get_model_formats(model)
114
  model_format = inquirer.select(
115
  message=t("MODEL_FORMAT_CHOICE"),
116
+ choices=[{
117
+ "name": format,
118
+ "value": format
119
+ } for format in model_formats],
120
  ).execute()
121
 
122
  # If the model is cloud_llm, ask for the API key
 
131
 
132
  if existing_api_key:
133
  use_existing = inquirer.confirm(
134
+ message=t(
135
+ "CONFIRM_USE_EXISTING_API_KEY", ENV_VAR_NAME=env_var_name),
136
  default=True,
137
  ).execute()
138
 
 
140
  return backend_language, model, model_format
141
  else:
142
  print(
143
+ t("CONFIRM_OVERWRITE_EXISTING_API_KEY",
144
+ ENV_VAR_NAME=env_var_name))
145
  else:
146
  print(t("PLEASE_INPUT_NEW_API_KEY", ENV_VAR_NAME=env_var_name))
147
 
148
  while True:
149
  api_key = inquirer.secret(
150
  message=t(
151
+ "PLEASE_INPUT_NEW_API_KEY_FROM_ZERO",
152
+ ENV_VAR_NAME=env_var_name)).execute()
 
153
  cleaned_api_key = clean_api_key(api_key)
154
 
155
  if validate_api_key(cleaned_api_key, env_var_name, t):
 
158
  else:
159
  print(t("INVALID_API_KEY_FORMAT"))
160
  retry = inquirer.confirm(
161
+ message=t("RETRY_API_KEY_INPUT"), default=True).execute()
 
162
  if not retry:
163
  print(t("API_KEY_INPUT_CANCELLED"))
164
  sys.exit(1)
docker/msdl/config.py CHANGED
@@ -4,6 +4,7 @@ from pathlib import Path
4
 
5
 
6
  class FileSystemManager:
 
7
  @staticmethod
8
  def ensure_dir(dir_path):
9
  """Ensure the directory exists, create if it doesn't"""
 
4
 
5
 
6
  class FileSystemManager:
7
+
8
  @staticmethod
9
  def ensure_dir(dir_path):
10
  """Ensure the directory exists, create if it doesn't"""
docker/setup.py CHANGED
@@ -1,4 +1,4 @@
1
- from setuptools import setup, find_packages
2
 
3
  setup(
4
  name="msdl",
 
1
+ from setuptools import find_packages, setup
2
 
3
  setup(
4
  name="msdl",
frontend/React/.gitignore CHANGED
@@ -14,7 +14,12 @@
14
 
15
  # misc
16
  .DS_Store
 
 
 
 
17
 
18
  npm-debug.log*
19
  yarn-debug.log*
20
  yarn-error.log*
 
 
14
 
15
  # misc
16
  .DS_Store
17
+ .env.local
18
+ .env.development.local
19
+ .env.test.local
20
+ .env.production.local
21
 
22
  npm-debug.log*
23
  yarn-debug.log*
24
  yarn-error.log*
25
+
frontend/React/.prettierignore CHANGED
@@ -4,4 +4,4 @@ values
4
  node_modules
5
  .gitignore
6
  .prettierignore
7
- .husky
 
4
  node_modules
5
  .gitignore
6
  .prettierignore
7
+ .husky
frontend/React/README.md CHANGED
@@ -1,123 +1,181 @@
1
  # Notice
2
- 问题回答过程中离开页面后再回到页面,会导致sse重连!
3
- # 开始
4
- ## 请使用大于18.0.0的node版本
5
- ## 准备node.js开发环境
6
- Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,允许你在服务器端运行 JavaScript。以下是在 Windows、Linux 和 macOS 上安装 Node.js 的详细步骤。
7
 
8
- ### Windows 上安装 Node.js
9
- - 步骤 1: 访问 Node.js 官网
10
 
11
- 打开浏览器,访问 [Node.js](https://nodejs.org/zh-cn/download/prebuilt-installer) 官方网站。
 
 
12
 
13
- - 步骤 2: 下载 Node.js 安装包
14
 
15
- 选择你需要的nodejs版本,设备的类型,点击下载,示例如下图:
16
- ![windows install](./windows-.png)
17
 
18
- - 步骤 3: 安装 Node.js
19
 
20
- 双击下载的安装包开始安装。
21
 
22
- 跟随安装向导的指示进行安装。在安装过程中,你可以选择安装位置、是否将 Node.js 添加到系统 PATH 环境变量等选项。推荐选择“添加到 PATH”以便在任何地方都能通过命令行访问 Node.js
23
- 安装完成后,点击“Finish”结束安装。
24
 
25
- - 步骤 4: 验证安装
26
 
27
- 打开命令提示符(CMD)或 PowerShell。
28
- 输入 node -v 并回车,如果系统返回了 Node.js 的版本号,说明安装成功。
29
- 接着,输入 npm -v 并回车,npm 是 Node.js 的包管理器,如果返回了版本号,表示 npm 也已正确安装。
30
 
31
- ### Linux 上安装 Node.js
32
- 注意: 由于 Linux 发行版众多,以下以 Ubuntu 为例说明,其他发行版(如 CentOS、Debian 等)的安装方式可能略有不同,可自行查询对应的安装办法。
33
 
34
- - 步骤 1: 更新你的包管理器
35
 
36
- 打开终端。
37
 
38
- 输入 sudo apt update 并回车,以更新 Ubuntu 的包索引。
39
 
40
- - 步骤 2: 安装 Node.js
41
 
42
- 对于 Ubuntu 18.04 及更高版本,Node.js 可以直接从 Ubuntu 的仓库中安装。
43
- 输入 sudo apt install nodejs npm 并回车。
44
- 对于旧版本的 Ubuntu 或需要安装特定版本的 Node.js,你可能需要使用如 NodeSource 这样的第三方仓库。
 
45
 
46
- - 步骤 3: 验证安装
 
47
 
48
- 在终端中,输入 node -v npm -v 来验证 Node.js 和 npm 是否已正确安装。
49
 
50
- ### macOS 上安装 Node.js
51
 
52
- #### 下载安装
53
- - 步骤 1: 访问 Node.js 官网
54
 
55
- 打开浏览器,访问 Node.js 官方网站。
56
 
57
- - 步骤 2: 下载 Node.js 安装包
 
 
 
58
 
59
- 在首页找到 macOS 对应的安装包(通常是 .pkg 文件),点击下载。
 
 
60
 
61
- - 步骤 3: 安装 Node.js
 
 
62
 
63
- 找到下载的 .pkg 文件,双击打开。
64
- 跟随安装向导的指示进行安装。
65
- 安装完成后,点击“Close”结束安装。
 
66
 
67
- - 步骤 4: 验证安装
 
68
 
69
- 打开终端。
 
 
70
 
71
- 输入 node -v npm -v 来验证 Node.js 和 npm 是否已正确安装。
 
72
 
73
- #### 使用HomeBrew安装
74
- 前提条件:确保你的macOS上已经安装了Homebrew。如果尚未安装,可以通过以下命令进行安装(以终端操作为例):
75
- ```
76
- /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
77
- ```
78
- 按照提示输入密码以确认安装。安装过程中,可能需要你同意许可协议等。
79
 
80
- - 打开终端:
81
- 在macOS上找到并打开“终端”应用程序。
 
 
82
 
83
- - 使用Homebrew安装Node.js:
84
- 在终端中输入以下命令来安装最新版本的Node.js
85
- ```
86
- brew install node
87
- ```
88
- Homebrew会自动下载Node.js的安装包,并处理相关的依赖项和安装过程。你需要等待一段时间,直到安装完成。
89
 
90
- - 验证安装:
91
- 安装完成后,你可以通过输入以下命令来验证Node.js是否成功安装:
92
- ```
93
- node -v
94
- ```
95
- 如果终端输出了Node.js的版本号,那么表示安装成功。同时,你也可以通过输入npm -v来验证npm(Node.js的包管理器)是否也成功安装。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- 完成以上步骤后,你应该能在你的 Windows、Linux macOS 系统上成功安装并运行 Node.js。
98
 
99
- ### 更多
100
- 如需了解更多,可参照:https://nodejs.org/en
 
 
101
 
102
- 如环境已经准备好,跳转下一步
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- ## 安装依赖
105
- 进入前端项目根目录
 
 
 
106
  ```
107
  npm install
108
  ```
109
 
110
- ## 启动
111
  ```
112
  npm start
113
  ```
114
 
115
- 启动成功后,界面将出现可访问的本地url
 
 
 
 
 
116
 
117
- ## 配置
118
- ### 接口请求配置
119
- - 如您需要配置的服务支持跨域,可至/src/config/cgi.ts中修改请求链接,请求链接为http://ip:port/path;
120
- - 如您需要配置的服务不支持跨域,可至vite.config.ts中配置proxy,示例如下:
 
 
 
 
 
 
121
 
122
  ```
123
  server: {
@@ -129,7 +187,4 @@ Homebrew会自动下载Node.js的安装包,并处理相关的依赖项和安
129
  }
130
  }
131
  }
132
- ```
133
-
134
- ## 知悉
135
- - 前端服务基于react开发,如需了解react相关知识,可参考:https://react.dev/
 
1
  # Notice
2
+ - If you leave the page (Make the page invisible) and come back again, it will cause sse to reconnect.
3
+ - the project requires Node.js version >= 18.0.0.
 
 
 
4
 
5
+ # Prepare your dev-environment for frontend
6
+ [Node.js](https://nodejs.org/en)® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.
7
 
8
+ # Node.js Installation Guide (Windows, Linux, macOS)
9
+ ## Windows Installation
10
+ - Step 1: Download Node.js
11
 
12
+ 1. Open your web browser and visit the [Node.js official website](https://nodejs.org/en).
13
 
14
+ 2. Navigate to the "Downloads" section.
 
15
 
16
+ 3. Select the desired version (LTS recommended for long-term stability). As of August 2024, the latest LTS version might be v20.x.x.
17
 
18
+ 4. Click on the "Windows Installer (.msi)" link to download the installation package.
19
 
20
+ - Step 2: Install Node.js
 
21
 
22
+ 1. Double-click the downloaded .msi file to start the installation wizard.
23
 
24
+ 2. Click "Next" to proceed.
 
 
25
 
26
+ 3. Read and accept the license agreement by checking the "I accept the terms in the License Agreement" box.
 
27
 
28
+ 4. Click "Next" again and select the installation directory. It's recommended to change the default location to avoid installing in the C drive.
29
 
30
+ 5. Continue clicking "Next" to use the default settings until you reach the "Install" button.
31
 
32
+ 6. Click "Install" to start the installation process.
33
 
34
+ 7. Wait for the installation to complete and click "Finish" to exit the installation wizard.
35
 
36
+ - Step 3: Verify Installation
37
+ 1. Open the Command Prompt (cmd) by pressing `Win + R`, typing `cmd`, and pressing Enter.
38
+ 2. Type `node -v` and press Enter. You should see the installed Node.js version displayed.
39
+ 3. Type `npm -v` and press Enter to verify the installed npm version. npm is the package manager that comes bundled with Node.js.
40
 
41
+ - Step 4: Configure npm Global Path (Optional)
42
+ If you want to change the default global installation path for npm, follow these steps:
43
 
44
+ 1. Open the Command Prompt (cmd) as an administrator.
45
 
46
+ 2. Navigate to your Node.js installation directory (e.g., C:\Program Files\nodejs).
47
 
48
+ 3. Create two new folders named node_global and node_cache.
 
49
 
50
+ 4. Run the following commands to set the new paths:
51
 
52
+ ```bash
53
+ npm config set prefix "C:\Program Files\nodejs\node_global"
54
+ npm config set cache "C:\Program Files\nodejs\node_cache"
55
+ ```
56
 
57
+ 5. Open the Environment Variables settings in the System Properties.
58
+ 6. Add `C:\Program Files\nodejs\node_global` to the `PATH` variable under User Variables.
59
+ 7. Optionally, create a new system variable named `NODE_PATH` and set its value to ` C:\Program Files\nodejs\node_global\node_modules`.
60
 
61
+ ## Linux Installation
62
+ - Step 1: Update Your System
63
+ Before installing Node.js, ensure your Linux system is up-to-date:
64
 
65
+ ```bash
66
+ sudo apt-get update
67
+ sudo apt-get upgrade
68
+ ```
69
 
70
+ - Step 2: Install Dependencies
71
+ Node.js requires certain dependencies to function properly:
72
 
73
+ ```bash
74
+ sudo apt-get install build-essential libssl-dev
75
+ ```
76
 
77
+ - Step 3: Download and Install Node.js
78
+ You can download the Node.js source code or use a package manager like `curl` or `wget` to download a pre-built binary. For simplicity, this guide assumes you're using a package manager.
79
 
80
+ 1. Navigate to the Node.js download page for package managers.
81
+ Follow the instructions for your Linux distribution. For example, on Ubuntu, you can use:
 
 
 
 
82
 
83
+ ```bash
84
+ curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
85
+ sudo apt-get install -y nodejs
86
+ ```
87
 
88
+ Replace 20.x with the desired version number if you don't want the latest version.
 
 
 
 
 
89
 
90
+ - Step 4: Verify Installation
91
+ 1. Open a terminal.
92
+ 2. Type `node -v` and press Enter to check the Node.js version.
93
+ 3. Type `npm -v` and press Enter to verify the npm version.
94
+
95
+
96
+ ## Installing Node.js on macOS
97
+
98
+ Installing Node.js on macOS is a straightforward process that can be accomplished using the official installer from the Node.js website or through package managers like Homebrew. This guide will cover both methods.
99
+
100
+ ### Method 1: Using the Official Installer
101
+ - Visit the Node.js Website
102
+ - Open your web browser and navigate to https://nodejs.org/.
103
+ - Download the Installer
104
+ - Scroll down to the "Downloads" section.
105
+ - Click on the "macOS Installer" button to download the .pkg file. Ensure you download the latest version, which as of August 2024, might be v20.x.x or higher.
106
+ - Install Node.js
107
+ - Once the download is complete, locate the .pkg file in your Downloads folder.
108
+ - Double-click the file to start the installation process.
109
+ - Follow the on-screen instructions. Typically, you'll need to agree to the license agreement, select an installation location (the default is usually fine), and click "Continue" or "Install" until the installation is complete.
110
+ - Verify the Installation
111
+ - Open the Terminal application by going to "Finder" > "Applications" > "Utilities" > "Terminal" or using Spotlight Search (press `Cmd + Space` and type "Terminal").
112
+ - Type `node -v` and press Enter. This command should display the installed version of Node.js.
113
+ - Type `npm -v` and press Enter to verify that npm, the Node.js package manager, is also installed.
114
+
115
+ ### Method 2: Using Homebrew
116
+ If you prefer to use a package manager, Homebrew is a popular choice for macOS.
117
+
118
+ - Install Homebrew (if not already installed)
119
 
120
+ - Open the Terminal.
121
 
122
+ - Copy and paste the following command into the Terminal and press Enter:
123
+ ```bash
124
+ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
125
+ ```
126
 
127
+ - Follow the on-screen instructions to complete the Homebrew installation.
128
+
129
+ - Install Node.js with Homebrew
130
+ - Once Homebrew is installed, update your package list by running brew update in the Terminal.
131
+ - To install Node.js, run the following command in the Terminal:
132
+ ```bash
133
+ brew install node
134
+ ```
135
+ - Homebrew will download and install the latest version of Node.js and npm.
136
+ - Verify the Installation
137
+ - As with the official installer method, you can verify the installation by typing node -v and npm -v in the Terminal and pressing Enter.
138
+
139
+ ### Additional Configuration (Optional)
140
+ - Configure npm's Global Installation Path (if desired):
141
+ - You may want to change the default location where globally installed npm packages are stored. Follow the steps outlined in the Node.js documentation or search for guides online to configure this.
142
+ - Switch to a Different Node.js Version (if needed):
143
+ - If you need to switch between multiple Node.js versions, consider using a version manager like nvm (Node Version Manager). Follow the instructions on the nvm GitHub page to install and use it.
144
+
145
+
146
+ By following these steps, you should be able to successfully install Node.js on your system. Remember to keep your Node.js and npm versions up-to-date to take advantage of the latest features and security updates.
147
 
148
+ If your env has been prepared, you can
149
+
150
+ # Installation and Setup Instructions
151
+
152
+ ## Installation
153
  ```
154
  npm install
155
  ```
156
 
157
+ ## Start Server
158
  ```
159
  npm start
160
  ```
161
 
162
+ ## Visit Server
163
+ ```
164
+ http://localhost:8080
165
+ ```
166
+
167
+ pay attention to the real port in your terminal.maybe it won`t be 8080.
168
 
169
+ # Config
170
+ ## How to modify the request URL
171
+ - Open the file `/src/config/cgi.ts`
172
+ - Modify the value of `GET_SSE_DATA`, be like:
173
+ ```
174
+ export const GET_SSE_DATA = `http://${ip}:${port}/${path}`
175
+ ```
176
+
177
+ ## you can also modify the proxy
178
+ - Open the file `vite.config.ts`, modify server like:
179
 
180
  ```
181
  server: {
 
187
  }
188
  }
189
  }
190
+ ```
 
 
 
frontend/React/README_zh-CN.md ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Notice
2
+ 问题回答过程中离开页面后再回到页面,会导致sse重连!
3
+ # 开始
4
+ ## 请使用大于18.0.0的node版本
5
+ ## 准备node.js开发环境
6
+ Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,允许你在服务器端运行 JavaScript。以下是在 Windows、Linux 和 macOS 上安装 Node.js 的详细步骤。
7
+
8
+ ### 在 Windows 上安装 Node.js
9
+ - 步骤 1: 访问 Node.js 官网
10
+
11
+ 打开浏览器,访问 [Node.js](https://nodejs.org/zh-cn/download/prebuilt-installer) 官方网站。
12
+
13
+ - 步骤 2: 下载 Node.js 安装包
14
+
15
+ 选择你需要的nodejs版本,设备的类型,点击下载,示例如下图:
16
+ ![windows install](./windows-.png)
17
+
18
+ - 步骤 3: 安装 Node.js
19
+
20
+ 双击下载的安装包开始安装。
21
+
22
+ 跟随安装向导的指示进行安装。在安装过程中,你可以选择安装位置、是否将 Node.js 添加到系统 PATH 环境变量等选项。推荐选择“添加到 PATH”以便在任何地方都能通过命令行访问 Node.js。
23
+ 安装完成后,点击“Finish”结束安装。
24
+
25
+ - 步骤 4: 验证安装
26
+
27
+ 打开命令提示符(CMD)或 PowerShell。
28
+ 输入 node -v 并回车,如果系统返回了 Node.js 的版本号,说明安装成功。
29
+ 接着,输入 npm -v 并回车,npm 是 Node.js 的包管理器,如果返回了版本号,表示 npm 也已正确安装。
30
+
31
+ ### 在 Linux 上安装 Node.js
32
+ 注意: 由于 Linux 发行版众多,以下以 Ubuntu 为例说明,其他发行版(如 CentOS、Debian 等)的安装方式可能略有不同,可自行查询对应的安装办法。
33
+
34
+ - 步骤 1: 更新你的包管理器
35
+
36
+ 打开终端。
37
+
38
+ 输入 sudo apt update 并回车,以更新 Ubuntu 的包索引。
39
+
40
+ - 步骤 2: 安装 Node.js
41
+
42
+ 对于 Ubuntu 18.04 及更高版本,Node.js 可以直接从 Ubuntu 的仓库中安装。
43
+ 输入 sudo apt install nodejs npm 并回车。
44
+ 对于旧版本的 Ubuntu 或需要安装特定版本的 Node.js,你可能需要使用如 NodeSource 这样的第三方仓库。
45
+
46
+ - 步骤 3: 验证安装
47
+
48
+ 在终端中,输入 node -v 和 npm -v 来验证 Node.js 和 npm 是否已正确安装。
49
+
50
+ ### 在 macOS 上安装 Node.js
51
+
52
+ #### 下载安装
53
+ - 步骤 1: 访问 Node.js 官网
54
+
55
+ 打开浏览器,访问 Node.js 官方网站。
56
+
57
+ - 步骤 2: 下载 Node.js 安装包
58
+
59
+ 在首页找到 macOS 对应的安装包(通常是 .pkg 文件),点击下载。
60
+
61
+ - 步骤 3: 安装 Node.js
62
+
63
+ 找到下载的 .pkg 文件,双击打开。
64
+ 跟随安装向导的指示进行安装。
65
+ 安装完成后,点击“Close”结束安装。
66
+
67
+ - 步骤 4: 验证安装
68
+
69
+ 打开终端。
70
+
71
+ 输入 node -v 和 npm -v 来验证 Node.js 和 npm 是否已正确安装。
72
+
73
+ #### 使用HomeBrew安装
74
+ 前提条件:确保你的macOS上已经安装了Homebrew。如果尚未安装,可以通过以下命令进行安装(以终端操作为例):
75
+ ```
76
+ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
77
+ ```
78
+ 按照提示输入密码以确认安装。安装过程中,可能需要你同意许可协议等。
79
+
80
+ - 打开终端:
81
+ 在macOS上找到并打开“终端”应用程序。
82
+
83
+ - 使用Homebrew安装Node.js:
84
+ 在终端中输入以下命令来安装最新版本的Node.js
85
+ ```
86
+ brew install node
87
+ ```
88
+ Homebrew会自动下载Node.js的安装包,并处理相关的依赖项和安装过程。你需要等待一段时间,直到安装完成。
89
+
90
+ - 验证安装:
91
+ 安装完成后,你可以通过输入以下命令来验证Node.js是否成功安装:
92
+ ```
93
+ node -v
94
+ ```
95
+ 如果终端输出了Node.js的版本号,那么表示安装成功。同时,你也可以通过输入npm -v来验证npm(Node.js的包管理器)是否也成功安装。
96
+
97
+ 完成以上步骤后,你应该能在你的 Windows、Linux 或 macOS 系统上成功安装并运行 Node.js。
98
+
99
+ ### 更多
100
+ 如需了解更多,可参照:https://nodejs.org/en
101
+
102
+ 如环境已经准备好,跳转下一步
103
+
104
+ ## 安装依赖
105
+ 进入前端项目根目录
106
+ ```
107
+ npm install
108
+ ```
109
+
110
+ ## 启动
111
+ ```
112
+ npm start
113
+ ```
114
+
115
+ 启动成功后,界面将出现可访问的本地url
116
+
117
+ ## 配置
118
+ ### 接口请求配置
119
+ - 如您需要配置的服务支持跨域,可至/src/config/cgi.ts中修改请求链接,请求链接为http://ip:port/path;
120
+ - 如您需要配置的服务不支持跨域,可至vite.config.ts中配置proxy,示例如下:
121
+
122
+ ```
123
+ server: {
124
+ port: 8080,
125
+ proxy: {
126
+ "/solve": {
127
+ target: "https://example.com",
128
+ changeOrigin: true,
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ ## 知悉
135
+ - 前端服务基于react开发,如需了解react相关知识,可参考:https://react.dev/
frontend/React/package.json CHANGED
@@ -5,7 +5,13 @@
5
  "type": "module",
6
  "scripts": {
7
  "start": "vite --host --mode dev",
 
 
 
8
  "build": "tsc && vite build",
 
 
 
9
  "preview": "vite preview",
10
  "prettier": "prettier --write ."
11
  },
 
5
  "type": "module",
6
  "scripts": {
7
  "start": "vite --host --mode dev",
8
+ "start:dev": "vite --host --mode dev",
9
+ "start:staging": "vite --host --mode staging",
10
+ "start:prod": "vite --host --mode production",
11
  "build": "tsc && vite build",
12
+ "build:dev": "tsc && vite build --mode dev",
13
+ "build:staging": "tsc && vite build --mode staging",
14
+ "build:prod": "tsc && vite build --mode production",
15
  "preview": "vite preview",
16
  "prettier": "prettier --write ."
17
  },
frontend/React/src/App.module.less CHANGED
@@ -8,12 +8,10 @@
8
  }
9
 
10
  .content {
11
- padding-top: 64px;
12
  width: 100%;
13
  height: 100%;
14
  box-sizing: border-box;
15
- // display: flex;
16
- // justify-content: center;
17
  }
18
 
19
  .header {
@@ -51,4 +49,4 @@
51
  display: flex;
52
  align-items: center;
53
  }
54
- }
 
8
  }
9
 
10
  .content {
11
+ padding: 64px 0 16px 0;
12
  width: 100%;
13
  height: 100%;
14
  box-sizing: border-box;
 
 
15
  }
16
 
17
  .header {
 
49
  display: flex;
50
  align-items: center;
51
  }
52
+ }
frontend/React/src/App.tsx CHANGED
@@ -1,7 +1,8 @@
1
  import style from "./App.module.less";
2
- import Logo from "@/assets/logo.svg";
3
  import { BrowserRouter } from "react-router-dom";
4
  import RouterRoutes from "@/routes/routes";
 
5
 
6
  function App() {
7
  return (
@@ -12,6 +13,7 @@ function App() {
12
  <img src={Logo} />
13
  </div>
14
  </div>
 
15
  <div className={style.content}>
16
  <RouterRoutes />
17
  </div>
 
1
  import style from "./App.module.less";
2
+
3
  import { BrowserRouter } from "react-router-dom";
4
  import RouterRoutes from "@/routes/routes";
5
+ import Logo from "@/assets/logo.svg";
6
 
7
  function App() {
8
  return (
 
13
  <img src={Logo} />
14
  </div>
15
  </div>
16
+
17
  <div className={style.content}>
18
  <RouterRoutes />
19
  </div>
frontend/React/src/components/answer/index.tsx DELETED
@@ -1,39 +0,0 @@
1
- import styles from './index.module.less';
2
- import ReactMarkdown from "react-markdown";
3
- import rehypeRaw from 'rehype-raw';
4
- import { replaceStr } from '@/utils/tools';
5
- import MindMapGraph from '../mind-map';
6
-
7
- interface IProps {
8
- renderData: any;
9
- isEnd: boolean;
10
- response: string;
11
- draft: string;
12
- chatIsOver: boolean;
13
- handleHistory: (info: any) => void;
14
- };
15
-
16
- const Answer = ({ renderData, isEnd, chatIsOver, response = '', draft = '', handleHistory }: IProps) => {
17
- return <div className={styles.answer}>
18
- {
19
- renderData?.length > 0 ? <div className={styles.inner}>
20
- <MindMapGraph isEnd={isEnd} renderData={renderData} handleHistory={handleHistory} />
21
- </div> : <></>
22
- }
23
- {
24
- !response && <div className={styles.draft}>
25
- {/* {!draftEnd && draft && <div className={styles.loading}>
26
- <div></div>
27
- <div></div>
28
- <div></div>
29
- </div>} */}
30
- <ReactMarkdown rehypePlugins={[rehypeRaw]}>{replaceStr(draft)}</ReactMarkdown>
31
- </div>
32
- }
33
- {response && <div className={styles.response}>
34
- <ReactMarkdown rehypePlugins={[rehypeRaw]}>{response}</ReactMarkdown>
35
- </div>}
36
- </div>
37
- };
38
-
39
- export default Answer;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/React/src/components/chat-right/index.tsx DELETED
@@ -1,310 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import styles from './index.module.less';
3
- import classNames from 'classnames';
4
- import ReactMarkdown from "react-markdown";
5
- import rehypeRaw from 'rehype-raw';
6
- import IconFont from '@/components/iconfont';
7
- import { replaceStr } from '@/utils/tools';
8
- import PackIcon from '@/assets/pack-up.svg';
9
- import { Tooltip } from 'antd';
10
- import Loading from '../loading';
11
- interface IProps {
12
- nodeInfo: any;
13
- stashInfo?: any;
14
- historyNode?: any;
15
- toggleRight?: () => void;
16
- };
17
-
18
- const ChatRight = ({ nodeInfo, stashInfo = null, historyNode = null, toggleRight }: IProps) => {
19
- const [subQuestion, setSubQuestion] = useState('');
20
- const [queries, setQuries] = useState<any>([]);
21
- const [searchList, setSearchList] = useState<any>([]);
22
- const [thought, setThought] = useState('');
23
- const [isLoading, setIsLoading] = useState(false);
24
- const [selectedIds, setSelectedIds] = useState([]);
25
- const [currentStep, setCurrentStep] = useState(0);
26
- const [conclusion, setConclusion] = useState('');
27
- const [thinkingData, setThinking] = useState<any>(null);
28
- const [readingData, setReading] = useState<any>(null);
29
- // steps展开收起的信息
30
- const [collapseInfo, setCollapseInfo] = useState<boolean[]>([true, true]);
31
- const [currentNode, setCurrentNode] = useState<any>();
32
- // 展开收起
33
- const toggleCard = (index: number) => {
34
- const arr = [...collapseInfo];
35
- arr[index] = !arr[index];
36
- setCollapseInfo(arr);
37
- };
38
- // 高亮searchList
39
- const highLightSearchList = (ids: any) => {
40
- // console.log('high light ids----------', ids);
41
- const timeout = historyNode ? 0 : 3000;
42
- setCurrentStep(1);
43
- const highlightArr: any = [...searchList];
44
- highlightArr.forEach((item: any) => {
45
- if (ids.includes(Number(item.id))) {
46
- item.highLight = true;
47
- }
48
- })
49
- highlightArr.sort((item1: any, item2: any) => {
50
- if (item1.highLight === item2.highLight) {
51
- return 0;
52
- }
53
- // 如果item1是highlight,放在前面
54
- if (item1.highLight) {
55
- return -1;
56
- }
57
- // 如果item2是highlight,放在后面
58
- return 1;
59
- })
60
- setSearchList(highlightArr);
61
- setTimeout(() => {
62
- setCurrentStep(2);
63
- }, timeout);
64
- };
65
-
66
- const handleReceiveHistory = () => {
67
- setThought(historyNode?.thinkingData?.thought);
68
- setThinking(historyNode.thinkingData);
69
- setConclusion(historyNode.conclusion);
70
- setReading(historyNode.readingData);
71
- setQuries(historyNode.queries);
72
- setSearchList(historyNode.searchList);
73
- setSelectedIds(historyNode.selectedIds);
74
- setSubQuestion(historyNode.subQuestion);
75
- setCurrentNode(2);
76
- setCollapseInfo([true, true]);
77
- setIsLoading(false);
78
- };
79
-
80
- const stashNodeInfo = () => {
81
- // 已完成节点信息存储
82
- const nodeInfo = {
83
- thinkingData,
84
- queries,
85
- readingData,
86
- searchList,
87
- conclusion,
88
- selectedIds,
89
- subQuestion
90
- };
91
- const obj: any = JSON.parse(window.localStorage.getItem('nodesInfo') || '{}');
92
- console.log('exsited history nodes-----', obj);
93
- obj[currentNode] = nodeInfo;
94
- window.localStorage.setItem('nodesInfo', JSON.stringify(obj));
95
- };
96
-
97
- const resetStatus = () => {
98
- // 记录节点信息
99
- stashNodeInfo();
100
- // 初始化组件状态
101
- console.log('reset status-------');
102
- setThought('');
103
- setCurrentStep(0);
104
- setCollapseInfo([true, true]);
105
- setSelectedIds([]);
106
- setSearchList([]);
107
- setThinking(null);
108
- setReading(null);
109
- setConclusion('');
110
- setSubQuestion('');
111
- };
112
-
113
- useEffect(() => {
114
- if (!historyNode) return;
115
- handleReceiveHistory();
116
- }, [historyNode]);
117
-
118
- useEffect(() => {
119
- if (!selectedIds.length) return;
120
- highLightSearchList(selectedIds);
121
- }, [selectedIds]);
122
-
123
- useEffect(() => {
124
- if (currentStep === 2) {
125
- setCollapseInfo([false, false]);
126
- } else {
127
- setCollapseInfo([true, true]);
128
- }
129
- }, [conclusion, currentStep]);
130
-
131
- useEffect(() => {
132
- // 如果是response节点就不要处理了,跟右边没关系
133
- if (nodeInfo?.current_node === 'response') return;
134
- if (nodeInfo && !currentNode) {
135
- setIsLoading(true);
136
- }
137
- if (nodeInfo?.current_node) {
138
- setCurrentNode(nodeInfo?.current_node);
139
- }
140
- if (nodeInfo?.current_node !== currentNode) {
141
- console.log('current node changed--------------', nodeInfo);
142
- }
143
- try {
144
- if (!(nodeInfo?.current_node && nodeInfo?.response?.nodes?.[nodeInfo.current_node]?.detail)) return;
145
- const nodeDetail = nodeInfo?.response?.nodes?.[nodeInfo.current_node]?.detail;
146
- // console.log('nodeInfo-------', nodeDetail);
147
- if (nodeDetail?.state === 0) {
148
- console.log('node is end------', nodeInfo);
149
- resetStatus();
150
- }
151
- if (nodeDetail?.state === 1) {
152
- setThought(nodeDetail.response);
153
- nodeDetail?.content && setSubQuestion(nodeDetail?.content);
154
- // 如果返回thought的时候还没有searchlist,那就是第一步的思考
155
- if (nodeDetail.actions?.[0]?.thought) {
156
- setThinking({
157
- thought: nodeDetail.actions?.[0]?.thought
158
- })
159
- }
160
- // 第二步的思考
161
- if (nodeDetail.actions?.[1]?.thought) {
162
- setReading({
163
- thought: nodeDetail.actions?.[1]?.thought
164
- })
165
- }
166
- setIsLoading(false);
167
- if (nodeDetail?.response && nodeDetail?.actions.length === 2) {
168
- setConclusion(nodeDetail?.response);
169
- }
170
- } else {
171
- setIsLoading(true);
172
- }
173
- if (nodeDetail?.actions?.length > 0) {
174
- // 搜索词
175
- if (nodeDetail.actions?.[0]?.args?.query?.length > 0) {
176
- setQuries(nodeDetail.actions[0]?.args?.query);
177
- }
178
- // 搜索信息
179
- if (nodeDetail.actions?.[0].result[0] && searchList.length === 0) {
180
- const content = JSON.parse(nodeDetail.actions[0].result[0].content);
181
- const arr: any = Object.keys(content).map(item => {
182
- return { id: item, ...content[item] };
183
- });
184
- setSearchList(arr);
185
- }
186
- if (nodeDetail.actions?.[1]?.args?.select_ids && selectedIds.length === 0) {
187
- setSelectedIds(nodeDetail.actions[1]?.args?.select_ids || []);
188
- }
189
- }
190
- } catch (err) {
191
- console.log('error from nodeinfo---');
192
- }
193
- }, [nodeInfo, currentNode]);
194
-
195
- return <div className={styles.rightContent}>
196
- {
197
- subQuestion && <div className={styles.toggleIcon} onClick={toggleRight}>
198
- <Tooltip placement="top" title="收起">
199
- <img src={PackIcon} />
200
- </Tooltip></div>
201
- }
202
- <div className={styles.titleNode}>{subQuestion}</div>
203
- {
204
- thought && <div className={classNames(
205
- styles.steps
206
- )}>
207
- <div className={styles.title}>
208
- <i></i>{"思考"}
209
- <div className={styles.open} onClick={() => { toggleCard(0) }}>
210
- <IconFont type={collapseInfo[0] ? "icon-shouqi" : "icon-xiangxiazhankai"} />
211
- </div>
212
- </div>
213
- <div className={classNames(
214
- styles.con,
215
- !collapseInfo[0] ? styles.collapsed : ""
216
- )}>
217
- <div>
218
- <ReactMarkdown rehypePlugins={[rehypeRaw]}>{replaceStr(thinkingData?.thought ? thinkingData?.thought : thought)}</ReactMarkdown>
219
- </div>
220
- {
221
- queries.length > 0 && <div className={styles.query}>
222
- <div className={styles.subTitle}><IconFont type="icon-SearchOutlined" />搜索关键词</div>
223
- {queries.map((item: string, index: number) => (<div key={`query-item-${item}`} className={classNames(styles.queryItem, styles.fadeIn)}>
224
- {item}
225
- </div>))}
226
- </div>
227
- }
228
- {
229
- searchList.length > 0 && currentStep === 0 && <div className={styles.searchList}>
230
- <div className={styles.subTitle}><IconFont type="icon-DocOutlined" />信息来源</div>
231
- <div className={styles.scrollCon} style={(searchList.length > 5) ? { height: '300px' } : {}}>
232
- <div className={styles.inner} style={(searchList.length > 5) ? {} : {}}>
233
- {
234
- searchList.map((item: any, num: number) => (
235
- <div className={classNames(
236
- styles.searchItem,
237
- item.highLight ? styles.highLight : ""
238
- )} key={`search-item-${item.url}`}>
239
- <p className={styles.summ}>{item.id}. {item?.title}</p>
240
- <p className={styles.url}>{item?.url}</p>
241
- </div>
242
- ))
243
- }
244
- </div>
245
- </div>
246
- </div>
247
- }
248
- </div>
249
- </div>
250
- }
251
- {
252
- currentStep > 0 && <div className={classNames(
253
- styles.steps
254
- )}>
255
- <div className={styles.title}>
256
- <i></i>{"信息来源"}
257
- <div className={styles.open} onClick={() => { toggleCard(1) }}>
258
- <IconFont type={collapseInfo[1] ? "icon-shouqi" : "icon-xiangxiazhankai"} />
259
- </div>
260
- </div>
261
- <div className={classNames(
262
- styles.con,
263
- !collapseInfo[1] ? styles.collapsed : ""
264
- )}>
265
- <div>
266
- <ReactMarkdown rehypePlugins={[rehypeRaw]}>{replaceStr(readingData?.thought ? readingData?.thought : thought)}</ReactMarkdown>
267
- </div>
268
- {
269
- searchList.length > 0 && <div className={styles.searchList}>
270
- <div className={styles.subTitle}><IconFont type="icon-DocOutlined" />信息来源</div>
271
- <div className={styles.scrollCon} style={(searchList.length > 5) ? { height: '300px' } : {}}>
272
- <div className={styles.inner} style={(searchList.length > 5) ? {} : {}}>
273
- {
274
- searchList.map((item: any, num: number) => (
275
- <div className={classNames(
276
- styles.searchItem,
277
- item.highLight ? styles.highLight : ""
278
- )} key={`search-item-${item.url}`}>
279
- <p className={styles.summ}>{item.id}. {item?.title}</p>
280
- <p className={styles.url}>{item?.url}</p>
281
- </div>
282
- ))
283
- }
284
- </div>
285
- </div>
286
- </div>
287
- }
288
- </div>
289
- </div>
290
- }
291
- {
292
- currentStep === 2 && <div className={classNames(
293
- styles.steps
294
- )}>
295
- <div className={styles.title}>
296
- <i></i>{"信息整合"}
297
- </div>
298
- <div className={styles.con}>
299
- <ReactMarkdown rehypePlugins={[rehypeRaw]}>{replaceStr(conclusion)}</ReactMarkdown>
300
- </div>
301
- </div>
302
- }
303
-
304
- {
305
- isLoading && <Loading />
306
- }
307
- </div>
308
- };
309
-
310
- export default ChatRight;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/React/src/components/mind-map-item/index.module.less DELETED
@@ -1,117 +0,0 @@
1
- article {
2
- padding: 6px 16px;
3
- border-radius: 8px;
4
- height: 38px;
5
- border: 1px solid transparent;
6
- background: #FFF;
7
- color: #121316;
8
- text-align: center;
9
- font-size: 14px;
10
- line-height: 24px;
11
- position: relative;
12
- box-sizing: border-box;
13
-
14
- &.loading {
15
- line-height: 20px;
16
- border-radius: 8px;
17
- overflow: hidden;
18
- border: 1px solid transparent;
19
- padding: 4px;
20
-
21
- span {
22
- color: #2126C0;
23
- background-color: #fff;
24
- border-radius: 4px;
25
- line-height: 24px;
26
- padding: 2px 12px;
27
- }
28
-
29
- .looping {
30
- --border-width: 4px;
31
- --follow-panel-linear-border: linear-gradient(91deg,
32
- #5551FF 0.58%,
33
- #FF87DE 100.36%);
34
-
35
- position: absolute;
36
- top: 0;
37
- left: 0;
38
- width: calc(100% + var(--border-width) * 2 - 8px);
39
- height: calc(100%);
40
- background: var(--follow-panel-linear-border);
41
- background-size: 300% 300%;
42
- background-position: 0 50%;
43
- animation: moveGradient 4s alternate infinite;
44
- }
45
- }
46
-
47
- &.disabled {
48
- border-radius: 8px;
49
- border: 1px solid #D7D8DD;
50
- color: rgba(18, 19, 22, 0.35);
51
- }
52
-
53
- &.finished {
54
- cursor: pointer;
55
- border: 1px solid #2126C0;
56
-
57
- .finishDot {
58
- position: absolute;
59
- top: 6px;
60
- right: 6px;
61
- width: 6px;
62
- height: 6px;
63
- background-color: #C9C0FE;
64
- border-radius: 50%;
65
- }
66
- }
67
-
68
- &.init {
69
- border: 1px solid transparent;
70
- cursor: auto;
71
- }
72
-
73
- span {
74
- display: block;
75
- white-space: nowrap;
76
- max-width: 160px;
77
- overflow: hidden;
78
- text-overflow: ellipsis;
79
- position: relative;
80
- z-index: 20;
81
- }
82
-
83
- span.status {
84
- color: #4082FE;
85
- }
86
-
87
- }
88
-
89
- ul.onlyone {
90
- &:before {
91
- opacity: 0;
92
- }
93
-
94
- >li {
95
- margin-left: 0px;
96
- }
97
-
98
- &>li:after {
99
- opacity: 0;
100
- }
101
-
102
- &>li:before {
103
- // left: 0;
104
- }
105
- }
106
-
107
- .endLine {
108
- border-bottom: 1px solid #D7D8DD;
109
- width: 3000px;
110
- transition: width 1s ease-in-out;
111
- }
112
-
113
- @keyframes moveGradient {
114
- 50% {
115
- background-position: 100% 50%;
116
- }
117
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/React/src/components/mind-map-item/index.tsx DELETED
@@ -1,50 +0,0 @@
1
- import { message } from 'antd';
2
- import styles from './index.module.less';
3
- import classNames from 'classnames';
4
-
5
- // 递归组件用于渲染mindMap中的节点
6
- const MindMapItem = ({ item, isEnd, selectNode }: any) => {
7
- // 递归渲染子节点
8
- const renderChildren = () => {
9
- if (item.children && item.children.length > 0) {
10
- return (
11
- <ul className={item.children.length === 1 ? styles.onlyone : ''}>
12
- {item.children.map((child: any) => (
13
- <MindMapItem key={child.name} item={child} isEnd={isEnd} selectNode={selectNode} />
14
- ))}
15
- </ul>
16
- );
17
- }
18
- return null;
19
- };
20
-
21
- const handleClick = () => {
22
- if (item.state !== 3) {
23
- message.warning('思考中,请稍后');
24
- return;
25
- }
26
- selectNode(item.name);
27
- };
28
-
29
- return (
30
- <li>
31
- <article
32
- onClick={handleClick}
33
- className={
34
- classNames(
35
- item.state === 1 ? styles.loading : item.state === 2 ? styles.disabled : item.state === 3 ? styles.finished : "",
36
- item.id === 0 ? styles.init : ''
37
- )}>
38
- <span>{item.name}</span>
39
- {item.state === 1 && <div className={styles.looping}></div>}
40
- {item.id !== 0 && <div className={styles.finishDot}></div>}
41
- </article>
42
- {item.children.length > 0 && renderChildren()}
43
- {
44
- isEnd && item.children?.length === 0 && <div className={classNames(styles.endLine, "endline")}></div>
45
- }
46
- </li>
47
- );
48
- };
49
-
50
- export default MindMapItem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/React/src/config/cgi.ts DELETED
@@ -1,2 +0,0 @@
1
- export const mode = import.meta.env.MODE;
2
- export const GET_SSE_DATA = '/solve';
 
 
 
frontend/React/src/global.d.ts CHANGED
@@ -1 +1 @@
1
- declare module 'event-source-polyfill';
 
1
+ declare module 'event-source-polyfill';
frontend/React/src/index.less CHANGED
@@ -8,8 +8,6 @@ html,
8
  font-family: "PingFang SC";
9
  font-size: 14px;
10
  line-height: 21px;
11
- min-width: 1280px;
12
- overflow-x: auto;
13
  }
14
 
15
  #global__message-container {
@@ -61,4 +59,4 @@ li> :nth-last-child(1).f {
61
  line-height: 14px;
62
  position: relative;
63
  top: -2px;
64
- }
 
8
  font-family: "PingFang SC";
9
  font-size: 14px;
10
  line-height: 21px;
 
 
11
  }
12
 
13
  #global__message-container {
 
59
  line-height: 14px;
60
  position: relative;
61
  top: -2px;
62
+ }
frontend/React/src/pages/mindsearch/assets/bookmark-icon.svg ADDED
frontend/React/src/pages/mindsearch/assets/empty-chat-right.svg ADDED
frontend/React/src/pages/mindsearch/assets/fold-icon.svg ADDED
frontend/React/src/pages/mindsearch/assets/logo.svg ADDED
frontend/React/src/pages/mindsearch/assets/logo1.svg ADDED
frontend/React/src/pages/mindsearch/assets/mindsearch-avatar.svg ADDED
frontend/React/src/pages/mindsearch/assets/pack-up-disabled.svg ADDED
frontend/React/src/pages/mindsearch/assets/pack-up.svg ADDED
frontend/React/src/pages/mindsearch/assets/sendIcon.svg ADDED
frontend/React/src/pages/mindsearch/assets/think-progress-icon.svg ADDED
frontend/React/src/pages/mindsearch/assets/unflod-icon.svg ADDED
frontend/React/src/pages/mindsearch/components/answer/index.module.less ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .answer {
2
+ display: flex;
3
+ justify-content: flex-start;
4
+ align-items: flex-start;
5
+ width: 100%;
6
+
7
+ .avatar {
8
+ width: 32px;
9
+ height: 32px;
10
+ margin-right: 16px;
11
+ border-radius: 50%;
12
+ flex-shrink: 0;
13
+
14
+ img {
15
+ width: 100%;
16
+ }
17
+ }
18
+
19
+ .reaponseAarea {
20
+ display: flex;
21
+ flex-direction: column;
22
+ width: calc(100% - 48px);
23
+ background-color: #F4F5F9;
24
+ padding: 12px 16px;
25
+ border-radius: 16px;
26
+ overflow-x: hidden;
27
+ }
28
+
29
+ .inner {
30
+ width: 100%;
31
+ overflow-x: hidden;
32
+ background-color: #fff;
33
+ border-radius: 16px;
34
+ border: 1px solid var(----line-2, #EBECF0);
35
+ box-sizing: border-box;
36
+ transition: all 0.5s ease;
37
+ margin-bottom: 16px;
38
+ position: relative;
39
+ }
40
+
41
+ .graphIcon {
42
+ padding: 2px 8px;
43
+ display: flex;
44
+ justify-content: center;
45
+ align-items: center;
46
+ border-radius: 8px;
47
+ border: 1px solid var(----line-2, #EBECF0);
48
+ background: var(---fill-0, #FFF);
49
+ color: #121316CC;
50
+ font-size: 14px;
51
+ line-height: 24px;
52
+ cursor: pointer;
53
+
54
+ svg {
55
+ margin-left: 4px;
56
+ }
57
+
58
+ &:hover {
59
+ background-color: #D7D8DD;
60
+ color: #121316CC;
61
+
62
+ svg path {
63
+ fill: #121316CC;
64
+ }
65
+ }
66
+ }
67
+
68
+ .showGraph {
69
+ width: 118px;
70
+ margin-bottom: 16px;
71
+ border-radius: 8px;
72
+ border: 1px solid var(----line-2, #EBECF0);
73
+ background: var(---fill-0, #FFF);
74
+ }
75
+
76
+ .closeGraph {
77
+ position: absolute;
78
+ right: 12px;
79
+ bottom: 12px;
80
+ }
81
+
82
+ .reaponse {
83
+ color: #121316;
84
+ font-size: 14px;
85
+ line-height: 24px;
86
+ padding: 18px 42px;
87
+ }
88
+
89
+ // h3 {
90
+ // font-size: 24px;
91
+ // font-weight: 600;
92
+ // line-height: 36px;
93
+ // margin: 0 0 16px 0;
94
+ // }
95
+
96
+ // h4 {
97
+ // font-size: 20px;
98
+ // font-weight: 600;
99
+ // line-height: 30px;
100
+ // margin: 0 0 8px 0;
101
+ // }
102
+ }
103
+
104
+ .draft {
105
+ width: 100%;
106
+ white-space: wrap;
107
+ display: flex;
108
+ justify-content: flex-start;
109
+ align-items: flex-start;
110
+ }
frontend/React/src/pages/mindsearch/components/answer/index.tsx ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import styles from './index.module.less';
2
+ import MindMapGraph from '../mind-map';
3
+ import { MindsearchContext } from '../../provider/context';
4
+ import MindSearchAvatar from '../../assets/mindsearch-avatar.svg';
5
+ import CustomMarkdown from '../custom-markdown';
6
+ import { useState, useEffect, useContext } from 'react';
7
+ import classNames from 'classnames';
8
+
9
+ interface IProps {
10
+ adjList: any;
11
+ isEnd: boolean;
12
+ response: string;
13
+ refList: any;
14
+ listId: number;
15
+ question: string;
16
+ handleNodeClick: (info: any, idx: number) => void;
17
+ }
18
+
19
+ const Answer = ({ refList = null, adjList, isEnd, response = '', listId, handleNodeClick, question = '' }: IProps) => {
20
+ const { chatIsOver } = useContext(MindsearchContext);
21
+ const [showGraph, setShowGraph] = useState(true);
22
+ // 整体的渲染树
23
+ const [renderData, setRenderData] = useState<any[]>([]);
24
+
25
+ const toggleGraph = () => {
26
+ setShowGraph(!showGraph);
27
+ };
28
+
29
+ const handleClick = (node: string) => {
30
+ handleNodeClick(node, listId);
31
+ };
32
+
33
+ const generateMapData = (arr: []) => {
34
+ const tempArr: any[] = arr.map((item: { name: string; id: number; state: number }) => {
35
+ if (item.name && adjList[item.name]) {
36
+ return {
37
+ ...item,
38
+ children: generateMapData(adjList?.[item.name]),
39
+ };
40
+ }
41
+ });
42
+ return tempArr;
43
+ };
44
+
45
+ const convertTreeData = () => {
46
+ const root: any = {
47
+ id: 0,
48
+ name: '原始问题',
49
+ state: 3,
50
+ children: generateMapData(adjList?.root || []),
51
+ };
52
+
53
+ // 返回包含根节点的数组
54
+ // console.log('renderData-----------', [root]);
55
+ setRenderData([root]);
56
+ };
57
+
58
+ useEffect(() => {
59
+ if (!adjList || Object.keys(adjList)?.length < 2) {
60
+ setRenderData([]);
61
+ return;
62
+ };
63
+ convertTreeData();
64
+ }, [adjList]);
65
+
66
+ return <div className={styles.answer}>
67
+ <div className={styles.avatar}>
68
+ <img src={MindSearchAvatar} alt="mindsearch-avatar" />
69
+ </div>
70
+ <div className={styles.reaponseAarea}>
71
+ {
72
+ showGraph ? <>
73
+ {
74
+ (renderData?.length > 0) && <div className={styles.inner}>
75
+ <MindMapGraph
76
+ listId={listId}
77
+ isEnd={isEnd}
78
+ renderData={renderData}
79
+ handleNodeClick={handleClick}
80
+ key={`graph-${question}`}
81
+ />
82
+ <div className={classNames(styles.graphIcon, styles.closeGraph)} onClick={toggleGraph}>
83
+ 收起
84
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
85
+ <path fillRule="evenodd" clipRule="evenodd" d="M2.66667 2C2.29848 2 2 2.29848 2 2.66667V6C2 6.36819 2.29848 6.66667 2.66667 6.66667C3.03486 6.66667 3.33333 6.36819 3.33333 6V3.33333H6C6.36819 3.33333 6.66667 3.03486 6.66667 2.66667C6.66667 2.29848 6.36819 2 6 2H2.66667ZM13.3333 14C13.7015 14 14 13.7015 14 13.3333V10C14 9.63181 13.7015 9.33333 13.3333 9.33333C12.9651 9.33333 12.6667 9.63181 12.6667 10V12.6667H10C9.63181 12.6667 9.33333 12.9651 9.33333 13.3333C9.33333 13.7015 9.63181 14 10 14H13.3333ZM6.25519 9.38392C6.17658 9.35132 6.09039 9.33333 6 9.33333H2.66667C2.29848 9.33333 2 9.63181 2 10C2 10.3682 2.29848 10.6667 2.66667 10.6667H4.39052L2.19526 12.8619C1.93491 13.1223 1.93491 13.5444 2.19526 13.8047C2.45561 14.0651 2.87772 14.0651 3.13807 13.8047L5.33333 11.6095L5.33333 13.3333C5.33333 13.7015 5.63181 14 6 14C6.36819 14 6.66667 13.7015 6.66667 13.3333V10C6.66667 9.81812 6.59383 9.65325 6.47574 9.53297L6.46703 9.52426C6.40414 9.46249 6.33203 9.41571 6.25519 9.38392ZM10.6667 2.66667C10.6667 2.29848 10.3682 2 10 2C9.63181 2 9.33333 2.29848 9.33333 2.66667V6C9.33333 6.36819 9.63181 6.66667 10 6.66667L13.3333 6.66667C13.7015 6.66667 14 6.36819 14 6C14 5.63181 13.7015 5.33333 13.3333 5.33333L11.6095 5.33333L13.8047 3.13807C14.0651 2.87772 14.0651 2.45561 13.8047 2.19526C13.5444 1.93491 13.1223 1.93491 12.8619 2.19526L10.6667 4.39053V2.66667Z" fill="#121316" fillOpacity="0.8" />
86
+ </svg>
87
+ </div>
88
+ </div>
89
+ }
90
+ </>
91
+ : <div className={classNames(styles.graphIcon, styles.showGraph)} onClick={toggleGraph}>
92
+ 查看思考节点
93
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
94
+ <path fillRule="evenodd" clipRule="evenodd" d="M2.66667 2C2.29848 2 2 2.29848 2 2.66667V6C2 6.36819 2.29848 6.66667 2.66667 6.66667C3.03486 6.66667 3.33333 6.36819 3.33333 6V3.33333H6C6.36819 3.33333 6.66667 3.03486 6.66667 2.66667C6.66667 2.29848 6.36819 2 6 2H2.66667ZM13.3333 14C13.7015 14 14 13.7015 14 13.3333V10C14 9.63181 13.7015 9.33333 13.3333 9.33333C12.9651 9.33333 12.6667 9.63181 12.6667 10V12.6667H10C9.63181 12.6667 9.33333 12.9651 9.33333 13.3333C9.33333 13.7015 9.63181 14 10 14H13.3333ZM6.25519 9.38392C6.17658 9.35132 6.09039 9.33333 6 9.33333H2.66667C2.29848 9.33333 2 9.63181 2 10C2 10.3682 2.29848 10.6667 2.66667 10.6667H4.39052L2.19526 12.8619C1.93491 13.1223 1.93491 13.5444 2.19526 13.8047C2.45561 14.0651 2.87772 14.0651 3.13807 13.8047L5.33333 11.6095L5.33333 13.3333C5.33333 13.7015 5.63181 14 6 14C6.36819 14 6.66667 13.7015 6.66667 13.3333V10C6.66667 9.81812 6.59383 9.65325 6.47574 9.53297L6.46703 9.52426C6.40414 9.46249 6.33203 9.41571 6.25519 9.38392ZM10.6667 2.66667C10.6667 2.29848 10.3682 2 10 2C9.63181 2 9.33333 2.29848 9.33333 2.66667V6C9.33333 6.36819 9.63181 6.66667 10 6.66667L13.3333 6.66667C13.7015 6.66667 14 6.36819 14 6C14 5.63181 13.7015 5.33333 13.3333 5.33333L11.6095 5.33333L13.8047 3.13807C14.0651 2.87772 14.0651 2.45561 13.8047 2.19526C13.5444 1.93491 13.1223 1.93491 12.8619 2.19526L10.6667 4.39053V2.66667Z" fill="#121316" fillOpacity="0.8" />
95
+ </svg>
96
+ </div>
97
+ }
98
+
99
+ {response && (
100
+ <div className={styles.response}>
101
+ <CustomMarkdown source={response} refList={chatIsOver ? refList : null} quoType="merge" />
102
+ </div>
103
+ )}
104
+ </div>
105
+ </div>
106
+ };
107
+
108
+ export default Answer;
frontend/React/src/{components/answer → pages/mindsearch/components/answer/loading-animation}/index.module.less RENAMED
@@ -1,79 +1,10 @@
1
- .answer {
2
- border-radius: 8px;
3
- background: rgba(33, 38, 192, 0.10);
4
- padding: 12px;
5
-
6
- .inner {
7
- width: 100%;
8
- background-color: #fff;
9
- border-radius: 4px;
10
- padding: 8px;
11
- box-sizing: border-box;
12
- transition: all 0.5s ease;
13
- margin-bottom: 18px;
14
- }
15
-
16
-
17
- .response {
18
- color: #121316;
19
- font-size: 14px;
20
- line-height: 24px;
21
- padding: 18px 42px;
22
-
23
- h3 {
24
- font-size: 24px;
25
- font-weight: 600;
26
- line-height: 36px;
27
- margin: 0 0 16px 0;
28
- }
29
-
30
- h4 {
31
- font-size: 20px;
32
- font-weight: 600;
33
- line-height: 30px;
34
- margin: 0 0 8px 0;
35
- }
36
-
37
- p {
38
- color: rgba(18, 19, 22, 0.80);
39
- font-size: 16px;
40
- font-weight: 400;
41
- line-height: 28px;
42
- margin: 0 0 16px 0;
43
- }
44
-
45
- ul {
46
- margin-bottom: 8px;
47
- padding-left: 22px;
48
- }
49
-
50
- li {
51
- color: rgba(18, 19, 22, 0.80);
52
- font-size: 16px;
53
- font-weight: 400;
54
- line-height: 28px;
55
-
56
- p {
57
- margin-bottom: 4px;
58
- }
59
- }
60
- }
61
- }
62
-
63
- .draft {
64
- width: 100%;
65
- white-space: wrap;
66
- // display: flex;
67
- // justify-content: flex-start;
68
- // align-items: flex-start;
69
-
70
- .loading,
71
- .loading>div {
72
  position: relative;
73
  box-sizing: border-box;
74
- }
75
 
76
- .loading {
77
  display: flex;
78
  justify-content: center;
79
  align-items: center;
@@ -85,32 +16,32 @@
85
  border-radius: 50%;
86
  margin-right: 3px;
87
  flex-shrink: 0;
88
- }
 
89
 
90
- .loading>div {
91
  display: inline-block;
92
  float: none;
93
  background-color: currentColor;
94
  border: 0 solid currentColor;
95
- }
96
 
97
- .loading>div:nth-child(1) {
98
  animation-delay: -200ms;
99
- }
100
 
101
- .loading>div:nth-child(2) {
102
  animation-delay: -100ms;
103
- }
104
 
105
- .loading>div:nth-child(3) {
106
  animation-delay: 0ms;
107
- }
108
 
109
- .loading>div {
110
  width: 3px;
111
  height: 3px;
112
  margin: 2px 1px;
113
  border-radius: 100%;
114
  animation: ball-pulse 1s ease infinite;
115
- }
116
- }
 
1
+ .loading,
2
+ .loading > div {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  position: relative;
4
  box-sizing: border-box;
5
+ }
6
 
7
+ .loading {
8
  display: flex;
9
  justify-content: center;
10
  align-items: center;
 
16
  border-radius: 50%;
17
  margin-right: 3px;
18
  flex-shrink: 0;
19
+ margin-top: 4px;
20
+ }
21
 
22
+ .loading > div {
23
  display: inline-block;
24
  float: none;
25
  background-color: currentColor;
26
  border: 0 solid currentColor;
27
+ }
28
 
29
+ .loading > div:nth-child(1) {
30
  animation-delay: -200ms;
31
+ }
32
 
33
+ .loading > div:nth-child(2) {
34
  animation-delay: -100ms;
35
+ }
36
 
37
+ .loading > div:nth-child(3) {
38
  animation-delay: 0ms;
39
+ }
40
 
41
+ .loading > div {
42
  width: 3px;
43
  height: 3px;
44
  margin: 2px 1px;
45
  border-radius: 100%;
46
  animation: ball-pulse 1s ease infinite;
47
+ }
 
frontend/React/src/pages/mindsearch/components/answer/loading-animation/index.tsx ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import styles from './index.module.less';
2
+
3
+ const LoadingAnimation = () => {
4
+ return (
5
+ <div className={styles.loading}>
6
+ <div />
7
+ <div />
8
+ <div />
9
+ </div>
10
+ );
11
+ };
12
+
13
+ export default LoadingAnimation;
frontend/React/src/pages/mindsearch/components/chat-right/components/empty-placeholder/index.module.less ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .emptyDiv {
2
+ width: 280px;
3
+ height: 100%;
4
+ margin: auto;
5
+ display: flex;
6
+ justify-content: center;
7
+ align-items: center;
8
+ flex-direction: column;
9
+
10
+ .pic {
11
+ margin-bottom: 8px;
12
+ }
13
+
14
+ p {
15
+ color: var(--80-text-4, rgba(18, 19, 22, 0.80));
16
+ text-align: center;
17
+ font-feature-settings: 'liga' off, 'clig' off;
18
+
19
+ /* 段落正文/常规text-1-paragraph-regular */
20
+ font-family: "PingFang SC";
21
+ font-size: 14px;
22
+ font-style: normal;
23
+ font-weight: 400;
24
+ line-height: 24px;
25
+ /* 171.429% */
26
+ }
27
+ }
frontend/React/src/pages/mindsearch/components/chat-right/components/empty-placeholder/index.tsx ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import styles from './index.module.less';
2
+ import EmptyRightChatImg from '../../../../assets/empty-chat-right.svg';
3
+
4
+ const EmptyPlaceHolder = () => {
5
+ return <>
6
+ <div className={styles.emptyDiv}>
7
+ <div className={styles.pic}>
8
+ <img src={EmptyRightChatImg} />
9
+ </div>
10
+ <p>
11
+ 请在节点图中选择节点后查看哦~
12
+ </p>
13
+ </div>
14
+ </>
15
+ };
16
+
17
+ export default EmptyPlaceHolder;
frontend/React/src/pages/mindsearch/components/chat-right/components/query-item/index.module.less ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .query {
2
+ &-Item {
3
+ display: inline-flex;
4
+ padding: 6px 12px 6px 0;
5
+ margin-right: 12px;
6
+ margin-bottom: 8px;
7
+ color: rgba(18, 19, 22, 0.8);
8
+ font-size: 14px;
9
+ line-height: 24px;
10
+ box-sizing: border-box;
11
+ overflow: hidden;
12
+ position: relative;
13
+
14
+ &:last-child {
15
+ &::after {
16
+ display: none;
17
+ }
18
+ }
19
+
20
+ &::after {
21
+ position: absolute;
22
+ right: 0;
23
+ top: 10px;
24
+ width: 1px;
25
+ height: 16px;
26
+ border-right: 1px solid #ebecf0;
27
+ content: '';
28
+ }
29
+ }
30
+ }
frontend/React/src/pages/mindsearch/components/chat-right/components/query-item/index.tsx ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import classNames from 'classnames';
2
+ import styles from './index.module.less';
3
+
4
+ interface IQueryItemProps {
5
+ item: string;
6
+ }
7
+ const QueryItem = ({ item }: IQueryItemProps) => {
8
+ return <div className={classNames(styles.queryItem, styles.fadeIn)}>{item}</div>;
9
+ };
10
+
11
+ export default QueryItem;
frontend/React/src/pages/mindsearch/components/chat-right/components/search-item/index.module.less ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .searchItem {
2
+ border-radius: 12px;
3
+ margin-bottom: 4px;
4
+ padding: 8px;
5
+ transition: all 0.5s ease-in-out;
6
+ display: flex;
7
+ justify-content: flex-start;
8
+ align-items: flex-start;
9
+ cursor: pointer;
10
+
11
+ .inner {
12
+ width: 100%;
13
+ overflow: hidden;
14
+ }
15
+
16
+ &:hover {
17
+ background-color: #ebecf0;
18
+ }
19
+
20
+ .num {
21
+ color: var(--60-text-3, rgba(18, 19, 22, 0.6));
22
+ font-size: 12px;
23
+ font-weight: 600;
24
+ line-height: 18px;
25
+ margin-right: 7px;
26
+ }
27
+
28
+ p {
29
+ white-space: wrap;
30
+ max-width: 95%;
31
+ overflow: hidden;
32
+ text-overflow: ellipsis;
33
+ margin: 0 !important;
34
+ }
35
+
36
+ // .origin {
37
+ // display: flex;
38
+ // justify-content: flex-start;
39
+ // align-items: center;
40
+ // margin-bottom: 2px;
41
+
42
+ // .icon {
43
+ // width: 16px;
44
+ // height: 16px;
45
+ // border-radius: 4px;
46
+ // background-color: #6f7f9b;
47
+ // margin-right: 2px;
48
+ // }
49
+
50
+ // span {
51
+ // color: var(--60-text-3, rgba(18, 19, 22, 0.60));
52
+ // font-size: 12px;
53
+ // font-weight: 400;
54
+ // line-height: 18px;
55
+ // }
56
+ // }
57
+
58
+ p.title {
59
+ overflow: hidden;
60
+ color: var(---Brand1-5, #3477eb);
61
+ text-overflow: ellipsis;
62
+ font-size: 14px;
63
+ line-height: 24px;
64
+ margin-bottom: 2px;
65
+ font-weight: normal;
66
+
67
+ a {
68
+ text-decoration: none;
69
+ color: var(---Brand1-5, #3477eb);
70
+ font-weight: normal;
71
+ }
72
+ }
73
+
74
+ p.url {
75
+ color: var(--60-text-3, rgba(18, 19, 22, 0.6));
76
+ font-size: 12px;
77
+ line-height: 18px;
78
+ height: 18px;
79
+ overflow: hidden;
80
+ }
81
+
82
+ p.summ {
83
+ color: rgba(18, 19, 22, 0.8);
84
+ font-size: 13px;
85
+ line-height: 20px;
86
+ white-space: wrap;
87
+ display: -webkit-box;
88
+ -webkit-box-orient: vertical;
89
+ -webkit-line-clamp: 2;
90
+ overflow: hidden;
91
+ text-overflow: ellipsis;
92
+ }
93
+
94
+ &.highLight {
95
+ background: var(--brand-11, #e6f2ff);
96
+
97
+ &:hover {
98
+ background-color: #b3d6ff;
99
+ }
100
+ }
101
+ }
frontend/React/src/pages/mindsearch/components/chat-right/components/search-item/index.tsx ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import classNames from 'classnames';
2
+ import styles from './index.module.less';
3
+
4
+ interface ISearchItemProps {
5
+ item: any;
6
+ }
7
+
8
+ const SearchItem = ({ item }: ISearchItemProps) => {
9
+ const openLink = (url: string) => {
10
+ window.open(url);
11
+ };
12
+ return (
13
+ <div
14
+ className={classNames(styles.searchItem, item.highLight ? styles.highLight : '')}
15
+ key={`search-item-${item.url}`}
16
+ onClick={() => {
17
+ openLink(item.url);
18
+ }}
19
+ >
20
+ <div className={styles.num}>{item.id}</div>
21
+ <div className={styles.inner}>
22
+ <p className={styles.url}>{item?.url}</p>
23
+ <p className={styles.title}>{item?.title}</p>
24
+ <p className={styles.summ}>{item?.summ}</p>
25
+ </div>
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export default SearchItem;
frontend/React/src/{components → pages/mindsearch/components}/chat-right/index.module.less RENAMED
@@ -1,28 +1,18 @@
1
  .rightContent {
2
  width: 44.44%;
 
3
  flex-shrink: 0;
4
  box-sizing: border-box;
5
- padding: 24px;
6
- border-radius: 8px;
7
- border: rgba(33, 38, 192, 0.10);
8
- background: rgba(255, 255, 255, 0.80);
9
- height: calc(100% - 24px);
10
- overflow-y: auto;
11
  position: relative;
12
-
13
- &::-webkit-scrollbar {
14
- width: 6px;
15
- }
16
-
17
- &::-webkit-scrollbar-track {
18
- background-color: rgba(255, 255, 255, 0);
19
- border-radius: 100px;
20
- }
21
-
22
- &::-webkit-scrollbar-thumb {
23
- background-color: rgba(255, 255, 255, 0);
24
- border-radius: 100px;
25
- }
26
 
27
  .toggleIcon {
28
  position: absolute;
@@ -35,8 +25,33 @@
35
  color: #121316;
36
  font-size: 24px;
37
  font-weight: 600;
38
- line-height: 36px;
39
- margin-bottom: 24px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  }
41
 
42
  .conclusion {
@@ -51,8 +66,6 @@
51
  }
52
 
53
  .steps {
54
- margin-bottom: 24px;
55
-
56
  .title {
57
  color: var(--100-text-5, #121316);
58
  font-size: 20px;
@@ -68,10 +81,11 @@
68
  right: 0;
69
  font-size: 20px;
70
  font-weight: normal;
 
71
 
72
  span {
73
  color: #121316;
74
- opacity: 0.6;
75
  }
76
  }
77
 
@@ -79,8 +93,8 @@
79
  width: 12px;
80
  height: 12px;
81
  border-radius: 50%;
82
- background-color: #2126C0;
83
- margin-right: 8px;
84
  }
85
  }
86
 
@@ -93,23 +107,61 @@
93
  .searchList {
94
  margin-top: 0 !important;
95
  border-radius: 8px;
96
- background: var(--fill-2, #F4F5F9);
97
  padding: 8px;
98
  }
99
  }
100
 
101
  .con {
102
  margin-left: 5px;
103
- padding-top: 8px;
104
  padding-left: 15px;
105
- border-left: 1px solid rgba(33, 38, 192, 0.20);
 
106
  height: auto;
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  &.collapsed {
109
  overflow: hidden;
110
  height: 0;
 
 
 
 
 
 
 
 
 
 
111
  padding-top: 0;
112
- transition: all 1s;
113
  }
114
  }
115
  }
@@ -125,41 +177,23 @@
125
  font-size: 14px;
126
  font-weight: 600;
127
  line-height: 24px;
128
- margin-bottom: 4px;
129
 
130
  span {
131
  margin-right: 4px;
132
  }
133
  }
134
 
135
- .query {
136
- &-Item {
137
- display: inline-flex;
138
- padding: 4px 8px;
139
- margin-right: 4px;
140
- margin-bottom: 4px;
141
- border-radius: 4px;
142
- border: 1px solid #EBECF0;
143
- color: rgba(18, 19, 22, 0.80);
144
- font-size: 14px;
145
- line-height: 24px;
146
- height: 32px;
147
- box-sizing: border-box;
148
- overflow: hidden;
149
- // animation: fadeIn linear 2s;
150
- }
151
- }
152
-
153
  .searchList {
154
  margin-top: 0 !important;
155
- border-radius: 8px;
156
- background: var(--fill-2, #F4F5F9);
157
  padding: 8px;
158
  }
159
 
160
  .searchList {
161
  .thought {
162
- color: rgba(18, 19, 22, 0.80);
163
  font-size: 14px;
164
  line-height: 24px;
165
  margin-bottom: 16px;
@@ -167,7 +201,8 @@
167
 
168
  .scrollCon {
169
  padding-right: 6px;
170
- max-height: 300px;
 
171
  overflow-y: auto;
172
  position: relative;
173
  }
@@ -182,53 +217,15 @@
182
  }
183
 
184
  .scrollCon::-webkit-scrollbar-thumb {
185
- background-color: #d7d8dd;
186
- border-radius: 100px;
187
  }
188
 
189
  .inner {
190
  width: 100%;
191
  border-radius: 8px;
192
- background: var(--fill-2, #F4F5F9);
193
  transition: all 0.5s ease;
194
  box-sizing: border-box;
195
- padding: 8px;
196
- }
197
-
198
- .searchItem {
199
- border-radius: 8px;
200
- background: var(---fill-0, #FFF);
201
- margin-bottom: 6px;
202
- padding: 4px 8px;
203
- transition: all 0.5s ease-in-out;
204
-
205
- &.highLight {
206
- border: 1px solid var(---Success-6, #00B365);
207
- background: linear-gradient(0deg, rgba(218, 242, 228, 0.40) 0%, rgba(218, 242, 228, 0.40) 100%), #FFF;
208
- }
209
-
210
- p {
211
- white-space: nowrap;
212
- max-width: 95%;
213
- overflow: hidden;
214
- text-overflow: ellipsis;
215
- margin: 0;
216
- }
217
-
218
- p.summ {
219
- color: rgba(18, 19, 22, 0.80);
220
- font-size: 13px;
221
- line-height: 20px;
222
- margin-bottom: 2px;
223
- }
224
-
225
- p.url {
226
- color: var(--60-text-3, rgba(18, 19, 22, 0.60));
227
- font-size: 12px;
228
- line-height: 18px;
229
- padding-left: 20px;
230
- }
231
  }
232
  }
233
- }
234
-
 
1
  .rightContent {
2
  width: 44.44%;
3
+ max-width: 800px;
4
  flex-shrink: 0;
5
  box-sizing: border-box;
6
+ padding: 24px 0 24px 24px;
7
+ border-radius: 16px;
8
+ border: 1px solid var(----line-2, #ebecf0);
9
+ background: var(---fill-0, #fff);
10
+ height: 100%;
11
+ overflow: hidden;
12
  position: relative;
13
+ display: flex;
14
+ justify-content: flex-start;
15
+ flex-direction: column;
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  .toggleIcon {
18
  position: absolute;
 
25
  color: #121316;
26
  font-size: 24px;
27
  font-weight: 600;
28
+ line-height: 30px;
29
+ margin-bottom: 32px;
30
+ max-width: calc(100% - 40px);
31
+ }
32
+
33
+ .nodeInfo {
34
+ height: 100%;
35
+ overflow-y: auto;
36
+ padding-right: 24px;
37
+
38
+ &.forbidScroll {
39
+ overflow-y: hidden;
40
+ }
41
+
42
+ &::-webkit-scrollbar {
43
+ width: 6px;
44
+ }
45
+
46
+ &::-webkit-scrollbar-track {
47
+ background-color: rgba(255, 255, 255, 0);
48
+ border-radius: 100px;
49
+ }
50
+
51
+ &::-webkit-scrollbar-thumb {
52
+ background-color: rgba(255, 255, 255, 0);
53
+ border-radius: 100px;
54
+ }
55
  }
56
 
57
  .conclusion {
 
66
  }
67
 
68
  .steps {
 
 
69
  .title {
70
  color: var(--100-text-5, #121316);
71
  font-size: 20px;
 
81
  right: 0;
82
  font-size: 20px;
83
  font-weight: normal;
84
+ cursor: pointer;
85
 
86
  span {
87
  color: #121316;
88
+ // opacity: 0.6;
89
  }
90
  }
91
 
 
93
  width: 12px;
94
  height: 12px;
95
  border-radius: 50%;
96
+ background-color: #3477EB;
97
+ margin-right: 12px;
98
  }
99
  }
100
 
 
107
  .searchList {
108
  margin-top: 0 !important;
109
  border-radius: 8px;
110
+ background: var(--fill-2, #f4f5f9);
111
  padding: 8px;
112
  }
113
  }
114
 
115
  .con {
116
  margin-left: 5px;
117
+ padding-top: 12px;
118
  padding-left: 15px;
119
+ margin-bottom: 24px;
120
+ border-left: 1px solid #D7D8DD;
121
  height: auto;
122
 
123
+ &.limitHeight {
124
+ max-height: calc(100vh - 320px);
125
+ overflow-y: auto;
126
+
127
+ &::-webkit-scrollbar {
128
+ width: 6px;
129
+ }
130
+
131
+ &::-webkit-scrollbar-track {
132
+ background-color: rgba(255, 255, 255, 0);
133
+ border-radius: 100px;
134
+ }
135
+
136
+ &::-webkit-scrollbar-thumb {
137
+ background-color: rgba(255, 255, 255, 0);
138
+ border-radius: 100px;
139
+ }
140
+ }
141
+
142
+ .draft {
143
+ margin-bottom: 20px;
144
+ }
145
+
146
+ p {
147
+ margin: 0;
148
+ line-height: 24px;
149
+ }
150
+
151
  &.collapsed {
152
  overflow: hidden;
153
  height: 0;
154
+ padding-top: 24px;
155
+ margin-bottom: 0 !important;
156
+
157
+ // transition: all 1s;
158
+
159
+ }
160
+ }
161
+
162
+ &:last-child {
163
+ .collapsed {
164
  padding-top: 0;
 
165
  }
166
  }
167
  }
 
177
  font-size: 14px;
178
  font-weight: 600;
179
  line-height: 24px;
180
+ margin-bottom: 12px;
181
 
182
  span {
183
  margin-right: 4px;
184
  }
185
  }
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  .searchList {
188
  margin-top: 0 !important;
189
+ border-radius: 16px;
190
+ background: var(--fill-2, #f4f5f9);
191
  padding: 8px;
192
  }
193
 
194
  .searchList {
195
  .thought {
196
+ color: rgba(18, 19, 22, 0.8);
197
  font-size: 14px;
198
  line-height: 24px;
199
  margin-bottom: 16px;
 
201
 
202
  .scrollCon {
203
  padding-right: 6px;
204
+ height: auto;
205
+ max-height: 542px;
206
  overflow-y: auto;
207
  position: relative;
208
  }
 
217
  }
218
 
219
  .scrollCon::-webkit-scrollbar-thumb {
220
+ background-color: #ebecf0;
221
+ border-radius: 20px;
222
  }
223
 
224
  .inner {
225
  width: 100%;
226
  border-radius: 8px;
 
227
  transition: all 0.5s ease;
228
  box-sizing: border-box;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
  }
231
+ }
 
frontend/React/src/pages/mindsearch/components/chat-right/index.tsx ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect, useState, useRef } from 'react';
2
+ import styles from './index.module.less';
3
+ import classNames from 'classnames';
4
+ import CustomMarkdown from '../custom-markdown';
5
+ import IconFont from '../iconfont';
6
+ import PackIcon from '../../assets/pack-up.svg';
7
+ import PackDisableIcon from '../../assets/pack-up-disabled.svg';
8
+ import { Tooltip } from 'antd';
9
+ import QueryItem from './components/query-item';
10
+ import SearchItem from './components/search-item';
11
+ import Loading from '../loading';
12
+ import EmptyPlaceHolder from './components/empty-placeholder';
13
+
14
+ interface IProps {
15
+ nodeInfo: any;
16
+ stashInfo?: any;
17
+ historyNode?: any;
18
+ toggleRight: () => void;
19
+ chatIsOver?: Boolean;
20
+ }
21
+
22
+ const ChatRight = ({ nodeInfo, historyNode = null, toggleRight, chatIsOver = false }: IProps) => {
23
+ const [subQuestion, setSubQuestion] = useState('');
24
+ const [queries, setQuries] = useState<any>([]);
25
+ const [searchList, setSearchList] = useState<any>([]);
26
+ const [conclusionRef, setConclusionRef] = useState<any>(null);
27
+ const [isLoading, setIsLoading] = useState(false);
28
+ const [selectedIds, setSelectedIds] = useState([]);
29
+ const [currentStep, setCurrentStep] = useState(0);
30
+ const [conclusion, setConclusion] = useState('');
31
+ const [thinkingData, setThinking] = useState<any>(null);
32
+ const [readingData, setReading] = useState<any>(null);
33
+ const [isOutputing, setIsOutputing] = useState(false);
34
+
35
+ // steps展开收起的信息
36
+ const [collapseInfo, setCollapseInfo] = useState<boolean[]>([true, true, true]);
37
+ const [currentNode, setCurrentNode] = useState<any>();
38
+ // 展开收起
39
+ const toggleCard = (index: number) => {
40
+ const arr = [...collapseInfo];
41
+ arr[index] = !arr[index];
42
+ setCollapseInfo(arr);
43
+ };
44
+ // 高亮searchList
45
+ const highLightSearchList = (ids: any) => {
46
+ setCurrentStep(2);
47
+ const highlightArr: any = [...searchList];
48
+ highlightArr.forEach((item: any) => {
49
+ if (ids.includes(Number(item.id))) {
50
+ item.highLight = true;
51
+ }
52
+ });
53
+ highlightArr.sort((item1: any, item2: any) => {
54
+ if (item1.highLight === item2.highLight) {
55
+ return 0;
56
+ }
57
+ // 如果item1是highlight,放在前面
58
+ if (item1.highLight) {
59
+ return -1;
60
+ }
61
+ // 如果item2是highlight,放在后面
62
+ return 1;
63
+ });
64
+ setSearchList(highlightArr);
65
+ setCollapseInfo([false, false, true]);
66
+ };
67
+
68
+ const handleReceiveHistory = () => {
69
+ setCurrentNode(2);
70
+ setCollapseInfo([false, false, true]);
71
+ setIsLoading(false);
72
+ setThinking(historyNode.thinkingData);
73
+ setConclusion(historyNode.conclusion);
74
+ setReading(historyNode.readingData);
75
+ setQuries(historyNode.queries);
76
+ setSearchList(historyNode.searchList);
77
+ setConclusionRef(historyNode.conclusionRef);
78
+ setSelectedIds(historyNode.selectedIds);
79
+ setSubQuestion(historyNode.subQuestion);
80
+ };
81
+
82
+ const resetStatus = () => {
83
+ // 初始化组件状态
84
+ console.log('reset status-------');
85
+ setCurrentStep(0);
86
+ setCollapseInfo([true, true, true]);
87
+ setSelectedIds([]);
88
+ setSearchList([]);
89
+ setConclusionRef(null);
90
+ setThinking(null);
91
+ setReading(null);
92
+ setConclusion('');
93
+ setSubQuestion('');
94
+ };
95
+
96
+ const hideRight = () => {
97
+ if (isOutputing) return;
98
+ toggleRight();
99
+ };
100
+
101
+ useEffect(() => {
102
+ if (!historyNode) return;
103
+ handleReceiveHistory();
104
+ }, [historyNode]);
105
+
106
+ useEffect(() => {
107
+ if (!selectedIds.length) return;
108
+ highLightSearchList(selectedIds);
109
+ }, [selectedIds]);
110
+
111
+ useEffect(() => {
112
+ if (historyNode) return; // 有历史记录,不处理
113
+ try {
114
+ if (nodeInfo?.current_node !== currentNode) {
115
+ setCurrentNode(nodeInfo?.current_node);
116
+ resetStatus();
117
+ }
118
+ setIsOutputing(nodeInfo?.outputing);
119
+ if (!subQuestion && nodeInfo?.subQuestion) {
120
+ setSubQuestion(nodeInfo.subQuestion);
121
+ }
122
+ if (nodeInfo?.thinkingData) {
123
+ setThinking(nodeInfo.thinkingData);
124
+ }
125
+ if (nodeInfo?.readingData) {
126
+ setReading(nodeInfo.readingData);
127
+ }
128
+ if (nodeInfo?.queries?.length) {
129
+ setQuries(nodeInfo.queries);
130
+ }
131
+ if (nodeInfo?.searchList && !searchList?.length) {
132
+ setSearchList(nodeInfo.searchList);
133
+ setCurrentStep(1);
134
+ setCollapseInfo([false, true, true]);
135
+ }
136
+ if (nodeInfo?.selectedIds && !selectedIds?.length) {
137
+ setSelectedIds(nodeInfo.selectedIds);
138
+ }
139
+ if (nodeInfo?.conclusion) {
140
+ setConclusion(nodeInfo.conclusion);
141
+ }
142
+ if (nodeInfo?.conclusionRef) {
143
+ setConclusionRef(nodeInfo.conclusionRef);
144
+ }
145
+ } catch (err) {
146
+ console.log('[chat right]--error from nodeinfo---', err);
147
+ }
148
+ }, [nodeInfo, currentStep]);
149
+
150
+ return <div className={styles.rightContent} id="rightContent">
151
+ <div className={styles.toggleIcon} onClick={hideRight}>
152
+ <img src={PackIcon} />
153
+ </div>
154
+ {
155
+ currentNode ? <>
156
+ <div className={styles.titleNode}>{subQuestion}</div>
157
+ <div className={classNames(
158
+ styles.nodeInfo,
159
+ isOutputing ? styles.forbidScroll : ''
160
+ )}>
161
+ {thinkingData && (
162
+ <div className={classNames(styles.steps)}>
163
+ <div className={styles.title}>
164
+ <i></i>思考
165
+ <div
166
+ className={styles.open}
167
+ onClick={() => {
168
+ toggleCard(0);
169
+ }}
170
+ >
171
+ <IconFont type={collapseInfo[0] ? 'icon-shouqi' : 'icon-xiangxiazhankai'} />
172
+ </div>
173
+ </div>
174
+ <div className={classNames(styles.con, !collapseInfo[0] ? styles.collapsed : '')}>
175
+ <div>
176
+ <CustomMarkdown source={thinkingData} />
177
+ </div>
178
+ {queries.length > 0 && (
179
+ <div className={styles.query}>
180
+ <div className={styles.subTitle}>
181
+ <IconFont type="icon-SearchOutlined" />
182
+ 搜索关键词
183
+ </div>
184
+ {queries.map((item: string, index: number) => (
185
+ <QueryItem key={`query-item-${item}`} item={item} />
186
+ ))}
187
+ </div>
188
+ )}
189
+ {searchList.length > 0 && currentStep === 0 && (
190
+ <div className={styles.searchList}>
191
+ <div className={styles.subTitle}>
192
+ <IconFont type="icon-DocOutlined" />
193
+ 信息来源
194
+ </div>
195
+ <div className={styles.scrollCon} style={searchList.length > 5 ? { height: '542px' } : {}}>
196
+ <div className={styles.inner} style={searchList.length > 5 ? {} : {}}>
197
+ {searchList.map((item: any) => (
198
+ <SearchItem item={item} key={`search-item-${item.url}`} />
199
+ ))}
200
+ </div>
201
+ </div>
202
+ </div>
203
+ )}
204
+ </div>
205
+ </div>
206
+ )}
207
+ {currentStep > 0 && readingData && (
208
+ <div className={classNames(styles.steps)}>
209
+ <div className={styles.title}>
210
+ <i></i>信息来源
211
+ <div
212
+ className={styles.open}
213
+ onClick={() => {
214
+ toggleCard(1);
215
+ }}
216
+ >
217
+ <IconFont type={collapseInfo[1] ? 'icon-shouqi' : 'icon-xiangxiazhankai'} />
218
+ </div>
219
+ </div>
220
+ <div className={classNames(styles.con, !collapseInfo[1] ? styles.collapsed : '')}>
221
+ <div className={styles.draft}>
222
+ <CustomMarkdown source={readingData} />
223
+ </div>
224
+ {searchList.length > 0 && (
225
+ <div className={styles.searchList}>
226
+ <div className={styles.scrollCon} style={searchList.length > 5 ? { height: '542px' } : {}}>
227
+ <div className={styles.inner} style={searchList.length > 5 ? {} : {}}>
228
+ {searchList.map((item: any) => (
229
+ <SearchItem item={item} key={`search-item-${item.url}`} />
230
+ ))}
231
+ </div>
232
+ </div>
233
+ </div>
234
+ )}
235
+ </div>
236
+ </div>
237
+ )}
238
+ {
239
+ conclusion && (
240
+ <div className={classNames(styles.steps)}>
241
+ <div className={styles.title}>
242
+ <i></i>信息整合
243
+ <div
244
+ className={styles.open}
245
+ onClick={() => {
246
+ toggleCard(2);
247
+ }}
248
+ >
249
+ <IconFont type={collapseInfo[2] ? 'icon-shouqi' : 'icon-xiangxiazhankai'} />
250
+ </div>
251
+ </div>
252
+ <div
253
+ id="nodeConclusionModule"
254
+ className={classNames(
255
+ styles.con,
256
+ !collapseInfo[2] ? styles.collapsed : '',
257
+ isOutputing ? styles.limitHeight : ''
258
+ )}
259
+ >
260
+ <div id="conclusionInfo">
261
+ <CustomMarkdown source={conclusion} refList={chatIsOver ? conclusionRef : null} />
262
+ </div>
263
+ </div>
264
+ </div>
265
+ )}
266
+ {isLoading && <Loading />}
267
+ </div></> : <EmptyPlaceHolder />
268
+ }
269
+ </div>
270
+ };
271
+
272
+ export default ChatRight;
frontend/React/src/pages/mindsearch/components/custom-markdown/index.module.less ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .markdownCon {
2
+ display: flex;
3
+ flex-direction: column;
4
+ justify-content: flex-start;
5
+
6
+ h1 {
7
+ font-size: 26px;
8
+ }
9
+
10
+ h2 {
11
+ font-size: 24px;
12
+ }
13
+
14
+ h3 {
15
+ font-size: 20px;
16
+ }
17
+
18
+ h4 {
19
+ font-size: 18px;
20
+ }
21
+
22
+ h5,
23
+ h6 {
24
+ font-size: 16px;
25
+ }
26
+
27
+ p {
28
+ color: rgba(18, 19, 22, 0.8);
29
+ font-size: 16px;
30
+ font-weight: 400;
31
+ line-height: 28px;
32
+ margin: 0 0 16px 0;
33
+ }
34
+
35
+ ul {
36
+ margin-bottom: 8px;
37
+ padding-left: 22px;
38
+ }
39
+
40
+ li {
41
+ color: rgba(18, 19, 22, 0.8);
42
+ font-size: 16px;
43
+ font-weight: 400;
44
+ line-height: 28px;
45
+
46
+ p {
47
+ margin-bottom: 4px;
48
+ }
49
+ }
50
+
51
+ >p:last-child {
52
+ margin-bottom: 0;
53
+ }
54
+ }
55
+
56
+ .footerFlag {
57
+ width: 18px;
58
+ height: 18px;
59
+ display: inline-flex;
60
+ justify-content: center;
61
+ align-items: center;
62
+ border-radius: 4px;
63
+ background: var(--fill-2, #f4f5f9);
64
+ color: var(--35-text-2, rgba(18, 19, 22, 0.35));
65
+ font-size: 12px;
66
+ font-weight: 600;
67
+ margin-left: 2px;
68
+ cursor: pointer;
69
+ font-style: normal;
70
+
71
+ /* 150% */
72
+ &:hover {
73
+ background: var(---Brand1-5, #3477eb);
74
+ color: #fff;
75
+
76
+ svg path {
77
+ fill: #fff;
78
+ fill-opacity: 1;
79
+ }
80
+ }
81
+ }
82
+
83
+ // .mergeQuoLi {
84
+ // margin-bottom: 12px;
85
+ // }
86
+
87
+ li {
88
+ cursor: pointer;
89
+
90
+ .url {
91
+ color: var(--60-text-3, rgba(18, 19, 22, 0.6));
92
+ font-size: 12px;
93
+ font-weight: 400;
94
+ line-height: 18px;
95
+ max-width: 100%;
96
+ height: 18px;
97
+ overflow: hidden;
98
+ text-overflow: ellipsis;
99
+ }
100
+
101
+ .title {
102
+ color: var(---Brand1-5, #3477eb);
103
+ font-size: 14px;
104
+ line-height: 21px;
105
+ }
106
+
107
+ .summ {
108
+ white-space: wrap;
109
+ display: -webkit-box;
110
+ -webkit-box-orient: vertical;
111
+ -webkit-line-clamp: 2;
112
+ overflow: hidden;
113
+ text-overflow: ellipsis;
114
+ }
115
+ }
116
+
117
+ .line {
118
+ margin: 4px 8px;
119
+ border: 1px solid #ebecf0;
120
+ transform: scaleY(0.5);
121
+ }
122
+
123
+ :global {
124
+ .iQuoPopover {
125
+ max-width: 420px;
126
+ }
127
+
128
+ .mergeQuoPopover {
129
+ border-radius: 12px;
130
+ border: 1px solid var(----line-2, #ebecf0);
131
+ background: var(---fill-0, #fff);
132
+ box-shadow: 1px 3px 8px 0px rgba(0, 0, 0, 0.06);
133
+ max-height: 240px;
134
+ max-width: 420px;
135
+ overflow-y: auto;
136
+
137
+ .ant-popover-inner {
138
+ padding: 8px !important;
139
+ }
140
+
141
+ .ant-popover-inner-content .line:last-child {
142
+ display: none;
143
+ }
144
+
145
+ li {
146
+ border-radius: 8px;
147
+ padding: 8px;
148
+
149
+ &:hover {
150
+ background-color: #f4f5f9;
151
+ }
152
+ }
153
+ }
154
+
155
+ .mergeQuoPopover::-webkit-scrollbar {
156
+ width: 6px;
157
+ }
158
+
159
+ .mergeQuoPopover::-webkit-scrollbar-track {
160
+ background-color: rgba(255, 255, 255, 0);
161
+ border-radius: 100px;
162
+ }
163
+
164
+ .mergeQuoPopover::-webkit-scrollbar-thumb {
165
+ background-color: #ebecf0;
166
+ border-radius: 20px;
167
+ }
168
+ }
frontend/React/src/pages/mindsearch/components/custom-markdown/index.tsx ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ReactMarkdown from 'react-markdown';
2
+ import rehypeRaw from 'rehype-raw';
3
+ import { replaceStr, mergeReplaceToDiv } from '../../utils/tools';
4
+ import { Popover } from 'antd';
5
+ import classNames from 'classnames';
6
+ import styles from './index.module.less';
7
+ import { useEffect } from 'react';
8
+
9
+ interface IMarkdownProps {
10
+ source: string;
11
+ refList?: any;
12
+ quoType?: string;
13
+ chatIsOver?: boolean;
14
+ }
15
+
16
+ const CustomMarkdown = ({ source, refList = null, quoType = 'single', chatIsOver = false }: IMarkdownProps) => {
17
+ const linkToExtend = (url: string) => {
18
+ window.open(url);
19
+ };
20
+
21
+ const CustomI = ({ children, className, ...props }: any) => {
22
+ const content = refList
23
+ ? Object.keys(refList).map((item) => {
24
+ if (Number(item) === Number(children)) {
25
+ return (
26
+ <li
27
+ key={`ref-item-${item}`}
28
+ onClick={() => {
29
+ linkToExtend(refList[item].url);
30
+ }}
31
+ >
32
+ <div className={styles.url}>{refList[item].url}</div>
33
+ <div className={styles.title}>{refList[item].title}</div>
34
+ <div className={styles.summ}>{refList[item].summ}</div>
35
+ </li>
36
+ );
37
+ } else {
38
+ return null;
39
+ }
40
+ })
41
+ : null;
42
+ return className.includes('custom') ? (
43
+ <Popover overlayClassName="iQuoPopover" content={content} arrow={false} key={`iQuoPopover-${children}`}>
44
+ <b className={styles.footerFlag}>{children}</b>
45
+ </Popover>
46
+ ) : (
47
+ <code>{children}</code>
48
+ );
49
+ };
50
+
51
+ const CustomDiv = ({ children, className, ...props }: any) => {
52
+ const list = props['data-ids'].split(',');
53
+ const content = refList
54
+ ? Object.keys(refList).map((item) => {
55
+ if (list.includes(String(item))) {
56
+ return (
57
+ <>
58
+ <li
59
+ className={styles.mergeQuoLi}
60
+ key={`ref-item-${refList[item].title}`}
61
+ onClick={() => {
62
+ linkToExtend(refList[item].url);
63
+ }}
64
+ >
65
+ <div className={styles.url}>{refList[item].url}</div>
66
+ <div className={styles.title}>{refList[item].title}</div>
67
+ </li>
68
+ <div className={classNames(styles.line, 'line')} />
69
+ </>
70
+ );
71
+ } else {
72
+ return null;
73
+ }
74
+ })
75
+ : null;
76
+ return className.includes('mergeQuo') ? (
77
+ <Popover content={content} arrow={false} overlayClassName="mergeQuoPopover" key={`custom-dev-${props['data-ids']}`}>
78
+ <b className={styles.footerFlag}>
79
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
80
+ <path
81
+ fillRule="evenodd"
82
+ clipRule="evenodd"
83
+ d="M5.49491 2.65877C3.29849 4.06934 2.07828 5.71113 1.83428 7.58413C1.45443 10.4999 4.06198 11.9272 5.3832 10.6448C6.70443 9.36248 5.91243 7.73483 4.95574 7.28993C3.99904 6.84499 3.41399 6.99995 3.51604 6.40539C3.6181 5.81085 4.97919 4.16241 6.17496 3.39472C6.2543 3.32725 6.28448 3.19635 6.2081 3.09704C6.15784 3.03174 6.05929 2.90365 5.91243 2.71278C5.78401 2.54586 5.66099 2.55211 5.49491 2.65877Z"
84
+ fill="#121316"
85
+ fillOpacity="0.35"
86
+ />
87
+ <path
88
+ fillRule="evenodd"
89
+ clipRule="evenodd"
90
+ d="M11.2801 2.65877C9.08366 4.06934 7.86344 5.71113 7.61943 7.58413C7.2396 10.4999 9.84715 11.9272 11.1684 10.6448C12.4896 9.36248 11.6976 7.73483 10.7409 7.28993C9.78421 6.84499 9.19913 6.99995 9.30121 6.40539C9.40327 5.81085 10.7644 4.16241 11.9601 3.39472C12.0395 3.32725 12.0697 3.19635 11.9932 3.09704C11.943 3.03174 11.8445 2.90365 11.6976 2.71278C11.5692 2.54586 11.4462 2.55211 11.2801 2.65877Z"
91
+ fill="#121316"
92
+ fillOpacity="0.35"
93
+ />
94
+ </svg>
95
+ </b>
96
+ </Popover>
97
+ ) : (
98
+ <code>{children}</code>
99
+ );
100
+ };
101
+
102
+ return (
103
+ <div className={styles.markdownCon}>
104
+ <ReactMarkdown rehypePlugins={[rehypeRaw]} components={refList && Object.keys(refList)?.length ? { i: CustomI, span: CustomDiv } : {}}>
105
+ {
106
+ refList ?
107
+ quoType === 'merge' ? mergeReplaceToDiv(source) :
108
+ replaceStr(source) :
109
+ source
110
+ }
111
+ </ReactMarkdown>
112
+ </div>
113
+ );
114
+ };
115
+
116
+ export default CustomMarkdown;
frontend/React/src/{components → pages/mindsearch/components}/iconfont/index.tsx RENAMED
@@ -1,9 +1,9 @@
1
- import { createFromIconfontCN } from "@ant-design/icons";
2
 
3
  // //at.alicdn.com/t/c/font_3858115_yl9vl04f0jc.js
4
  const IconFont = createFromIconfontCN({
5
- // scriptUrl: "//static.openxlab.org.cn/cmg-animation-upload/iconfont.js",
6
- scriptUrl: "//at.alicdn.com/t/c/font_3858115_p8dw9q83s0h.js"
7
  });
8
 
9
  export default IconFont;
 
1
+ import { createFromIconfontCN } from '@ant-design/icons';
2
 
3
  // //at.alicdn.com/t/c/font_3858115_yl9vl04f0jc.js
4
  const IconFont = createFromIconfontCN({
5
+ // scriptUrl: "//static.openxlab.org.cn/cmg-animation-upload/iconfont.js",
6
+ scriptUrl: '//at.alicdn.com/t/c/font_3858115_p8dw9q83s0h.js',
7
  });
8
 
9
  export default IconFont;
frontend/React/src/{components → pages/mindsearch/components}/loading/index.module.less RENAMED
@@ -3,42 +3,42 @@
3
  position: relative;
4
  width: 1px;
5
  height: 1px;
6
- }
7
-
8
- .loading99:before,
9
- .loading99:after {
10
  position: absolute;
11
  display: inline-block;
12
  width: 15px;
13
  height: 15px;
14
- content: "";
15
  border-radius: 100%;
16
- background-color: #5551FF;
17
- }
18
-
19
- .loading99:before {
20
  left: -15px;
21
  animation: ball-pulse infinite 0.75s -0.4s cubic-bezier(0.2, 0.68, 0.18, 1.08);
22
- }
23
-
24
- .loading99:after {
25
  right: -15px;
26
  animation: ball-pulse infinite 0.75s cubic-bezier(0.2, 0.68, 0.18, 1.08);
27
- }
28
-
29
- @keyframes ball-pulse {
30
  0% {
31
- transform: scale(1);
32
- opacity: 1;
33
  }
34
-
35
  50% {
36
- transform: scale(0.1);
37
- opacity: 0.6;
38
  }
39
-
40
  100% {
41
- transform: scale(1);
42
- opacity: 1;
43
  }
44
- }
 
3
  position: relative;
4
  width: 1px;
5
  height: 1px;
6
+ }
7
+
8
+ .loading99:before,
9
+ .loading99:after {
10
  position: absolute;
11
  display: inline-block;
12
  width: 15px;
13
  height: 15px;
14
+ content: '';
15
  border-radius: 100%;
16
+ background-color: #5551ff;
17
+ }
18
+
19
+ .loading99:before {
20
  left: -15px;
21
  animation: ball-pulse infinite 0.75s -0.4s cubic-bezier(0.2, 0.68, 0.18, 1.08);
22
+ }
23
+
24
+ .loading99:after {
25
  right: -15px;
26
  animation: ball-pulse infinite 0.75s cubic-bezier(0.2, 0.68, 0.18, 1.08);
27
+ }
28
+
29
+ @keyframes ball-pulse {
30
  0% {
31
+ transform: scale(1);
32
+ opacity: 1;
33
  }
34
+
35
  50% {
36
+ transform: scale(0.1);
37
+ opacity: 0.6;
38
  }
39
+
40
  100% {
41
+ transform: scale(1);
42
+ opacity: 1;
43
  }
44
+ }
frontend/React/src/{components → pages/mindsearch/components}/loading/index.tsx RENAMED
@@ -1,6 +1,6 @@
1
  import styles from './index.module.less';
2
 
3
  const Loading = () => {
4
- return <div className={styles.loading99}></div>
5
  };
6
- export default Loading;
 
1
  import styles from './index.module.less';
2
 
3
  const Loading = () => {
4
+ return <div className={styles.loading99} />;
5
  };
6
+ export default Loading;
frontend/React/src/pages/mindsearch/components/mind-map-item/index.module.less ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ article {
2
+ padding: 6px 16px;
3
+ border-radius: 8px;
4
+ height: 38px;
5
+ border: 1px solid transparent;
6
+ background: #fff;
7
+ color: #121316;
8
+ text-align: center;
9
+ font-size: 14px;
10
+ line-height: 24px;
11
+ position: relative;
12
+ box-sizing: border-box;
13
+
14
+ &.loading {
15
+ line-height: 20px;
16
+ border-radius: 8px;
17
+ overflow: hidden;
18
+ border: 1px solid transparent;
19
+ padding: 4px;
20
+
21
+ span {
22
+ color: #3477eb;
23
+ background-color: #fff;
24
+ border-radius: 4px;
25
+ line-height: 24px;
26
+ padding: 2px 12px;
27
+ }
28
+
29
+ .looping {
30
+ --border-width: 4px;
31
+ --follow-panel-linear-border: linear-gradient(90deg, #3477eb 0.58%, #FFB4BA 100.36%);
32
+
33
+ position: absolute;
34
+ top: 0;
35
+ left: 0;
36
+ width: calc(100% + var(--border-width) * 3 - 8px);
37
+ height: 100%;
38
+ background: var(--follow-panel-linear-border);
39
+ background-size: 300% 300%;
40
+ background-position: 0 50%;
41
+ animation: moveGradient 4s linear infinite;
42
+ }
43
+ }
44
+
45
+ &.disabled {
46
+ border-radius: 8px;
47
+ border: 1px solid #d7d8dd;
48
+ color: rgba(18, 19, 22, 0.35);
49
+ }
50
+
51
+ &.finished {
52
+ cursor: pointer;
53
+ border: 1px solid #3477EB;
54
+
55
+ &:hover {
56
+ background-color: #E6F2FF;
57
+ }
58
+
59
+ .finishDot {
60
+ position: absolute;
61
+ top: 6px;
62
+ right: 6px;
63
+ width: 6px;
64
+ height: 6px;
65
+ background-color: #3477EB;
66
+ border-radius: 50%;
67
+ }
68
+ }
69
+
70
+ &.forbidden {
71
+ cursor: not-allowed;
72
+ }
73
+
74
+ &.emptyNode {
75
+ padding: 0 !important;
76
+ border: 0;
77
+ }
78
+
79
+ &.active {
80
+ border-radius: 8px;
81
+ border: 1px solid var(---Brand1-5, #3477EB);
82
+ background: var(---Brand1-5, #3477EB);
83
+ color: #fff;
84
+
85
+ &:hover {
86
+ border: 1px solid var(---Brand1-5, #3477EB);
87
+ background: var(---Brand1-5, #3477EB);
88
+ color: #fff;
89
+ }
90
+
91
+ .dot {
92
+ position: absolute;
93
+ top: 6px;
94
+ right: 6px;
95
+ width: 6px;
96
+ height: 6px;
97
+ background-color: #fff;
98
+ border-radius: 50%;
99
+ }
100
+ }
101
+
102
+ &.init {
103
+ border: 1px solid transparent;
104
+ cursor: auto;
105
+ }
106
+
107
+ span {
108
+ display: block;
109
+ white-space: nowrap;
110
+ max-width: 160px;
111
+ overflow: hidden;
112
+ text-overflow: ellipsis;
113
+ position: relative;
114
+ z-index: 20;
115
+ }
116
+
117
+ span.status {
118
+ color: #4082fe;
119
+ }
120
+ }
121
+
122
+ ul.onlyone {
123
+ &:before {
124
+ opacity: 0;
125
+ }
126
+
127
+ >li {
128
+ margin-left: 0px;
129
+ }
130
+
131
+ &>li:after {
132
+ opacity: 0;
133
+ }
134
+
135
+ &>li:before {
136
+ // left: 0;
137
+ }
138
+ }
139
+
140
+ .endLine {
141
+ border-bottom: 1px solid #d7d8dd;
142
+ width: 3000px;
143
+ transition: width 1s ease-in-out;
144
+ }
145
+
146
+ @keyframes moveGradient {
147
+ 50% {
148
+ background-position: 100% 50%;
149
+ }
150
+ }