@lobehub/chat 0.161.8 → 0.161.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/docs/self-hosting/advanced/feature-flags.mdx +45 -0
  3. package/docs/self-hosting/advanced/feature-flags.zh-CN.mdx +42 -0
  4. package/docs/self-hosting/environment-variables/basic.mdx +11 -2
  5. package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +11 -2
  6. package/package.json +1 -1
  7. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/DragUpload.tsx +92 -42
  8. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +2 -2
  9. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files.tsx +4 -3
  10. package/src/app/(main)/settings/llm/components/ProviderConfig/index.tsx +5 -2
  11. package/src/app/(main)/settings/llm/components/ProviderModelList/CustomModelOption.tsx +6 -7
  12. package/src/app/(main)/settings/llm/components/ProviderModelList/{ModelConfigModal.tsx → ModelConfigModal/Form.tsx} +19 -63
  13. package/src/app/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/index.tsx +78 -0
  14. package/src/app/(main)/settings/llm/components/ProviderModelList/Option.tsx +35 -11
  15. package/src/app/(main)/settings/llm/components/ProviderModelList/index.tsx +15 -18
  16. package/src/app/(main)/settings/system-agent/features/Translation.tsx +0 -2
  17. package/src/components/FileList/ImageFileItem.tsx +1 -1
  18. package/src/components/ModelProviderIcon/index.tsx +2 -2
  19. package/src/components/ModelSelect/index.tsx +5 -14
  20. package/src/const/meta.ts +1 -2
  21. package/src/features/User/UserPanel/PanelContent.tsx +1 -1
  22. package/src/hooks/useSyncData.ts +3 -1
  23. package/src/layout/AuthProvider/Clerk/useAppearance.ts +1 -1
  24. package/src/layout/GlobalProvider/StoreInitialization.tsx +17 -9
  25. package/src/layout/GlobalProvider/index.tsx +1 -1
  26. package/src/locales/default/components.ts +1 -0
  27. package/src/services/message/client.test.ts +0 -24
  28. package/src/services/message/client.ts +0 -5
  29. package/src/services/message/type.ts +0 -1
  30. package/src/services/user/client.test.ts +100 -0
  31. package/src/services/user/client.ts +16 -14
  32. package/src/services/user/index.ts +0 -2
  33. package/src/services/user/type.ts +2 -4
  34. package/src/store/user/initialState.ts +10 -1
  35. package/src/store/user/selectors.ts +3 -7
  36. package/src/store/user/slices/auth/action.test.ts +5 -87
  37. package/src/store/user/slices/auth/action.ts +3 -58
  38. package/src/store/user/slices/auth/initialState.ts +2 -1
  39. package/src/store/user/slices/common/action.test.ts +196 -20
  40. package/src/store/user/slices/common/action.ts +55 -26
  41. package/src/store/user/slices/common/initialState.ts +9 -0
  42. package/src/store/user/slices/modelList/action.test.ts +363 -0
  43. package/src/store/user/slices/{settings/actions/llm.ts → modelList/action.ts} +66 -60
  44. package/src/store/user/slices/modelList/initialState.ts +15 -0
  45. package/src/store/user/slices/modelList/selectors/index.ts +2 -0
  46. package/src/store/user/slices/{settings → modelList}/selectors/modelConfig.test.ts +3 -2
  47. package/src/store/user/slices/{settings → modelList}/selectors/modelConfig.ts +1 -1
  48. package/src/store/user/slices/{settings → modelList}/selectors/modelProvider.test.ts +7 -7
  49. package/src/store/user/slices/{settings → modelList}/selectors/modelProvider.ts +2 -4
  50. package/src/store/user/slices/preference/action.test.ts +0 -52
  51. package/src/store/user/slices/preference/action.ts +1 -17
  52. package/src/store/user/slices/preference/initialState.ts +0 -5
  53. package/src/store/user/slices/preference/selectors.test.ts +2 -2
  54. package/src/store/user/slices/preference/selectors.ts +1 -1
  55. package/src/store/user/slices/settings/{actions/general.ts → action.ts} +5 -5
  56. package/src/store/user/slices/settings/initialState.ts +0 -12
  57. package/src/store/user/slices/settings/selectors/index.ts +0 -3
  58. package/src/store/user/slices/sync/action.test.ts +19 -5
  59. package/src/store/user/slices/sync/action.ts +9 -6
  60. package/src/store/user/slices/{settings/selectors/sync.ts → sync/selectors.ts} +2 -2
  61. package/src/store/user/store.ts +5 -2
  62. package/src/styles/antdOverride.ts +6 -0
  63. package/src/types/serverConfig.ts +3 -1
  64. package/src/types/user/index.ts +13 -0
  65. package/src/utils/parseModels.test.ts +121 -1
  66. package/src/utils/parseModels.ts +9 -4
  67. package/src/store/user/slices/settings/actions/index.ts +0 -18
  68. package/src/store/user/slices/settings/actions/llm.test.ts +0 -136
  69. /package/src/app/(main)/settings/llm/components/ProviderModelList/{MaxTokenSlider.tsx → ModelConfigModal/MaxTokenSlider.tsx} +0 -0
  70. /package/src/store/user/slices/{settings → modelList}/reducers/customModelCard.test.ts +0 -0
  71. /package/src/store/user/slices/{settings → modelList}/reducers/customModelCard.ts +0 -0
  72. /package/src/store/user/slices/settings/{actions/general.test.ts → action.test.ts} +0 -0
  73. /package/src/store/user/slices/settings/selectors/__snapshots__/{selectors.test.ts.snap → settings.test.ts.snap} +0 -0
  74. /package/src/store/user/slices/settings/selectors/{selectors.test.ts → settings.test.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.161.10](https://github.com/lobehub/lobe-chat/compare/v0.161.9...v0.161.10)
6
+
7
+ <sup>Released on **2024-05-23**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Refactor user store and fix custom model list form.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Refactor user store and fix custom model list form, closes [#2620](https://github.com/lobehub/lobe-chat/issues/2620) ([81ea886](https://github.com/lobehub/lobe-chat/commit/81ea886))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 0.161.9](https://github.com/lobehub/lobe-chat/compare/v0.161.8...v0.161.9)
31
+
32
+ <sup>Released on **2024-05-23**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Fix image style and improve drag upload box.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Fix image style and improve drag upload box, closes [#2610](https://github.com/lobehub/lobe-chat/issues/2610) ([5e1a4d6](https://github.com/lobehub/lobe-chat/commit/5e1a4d6))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 0.161.8](https://github.com/lobehub/lobe-chat/compare/v0.161.7...v0.161.8)
6
56
 
7
57
  <sup>Released on **2024-05-22**</sup>
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: LobeChat Feature Flags Environment Variables Configuration Guide
3
+ description: >-
4
+ Learn how to use environment variables to customize LobeChat's feature flags,
5
+ including controlling whether a feature is enabled or disabled, or enabling or
6
+ disabling features for specific user groups or environments as needed.
7
+ tags:
8
+ - LobeChat
9
+ - Environment Variables
10
+ - Configuration Guide
11
+ - Feature Flags
12
+ ---
13
+
14
+ # Feature Flags
15
+
16
+ In addition to basic environment variable configuration, LobeChat also offers feature flags to control whether a feature is enabled globally, or to enable or disable features for specific user groups or environments as needed.
17
+
18
+ ## Feature Flags Environment Variable `FEATURE_FLAGS`
19
+
20
+ - Type: Optional
21
+ - Description: Used to control LobeChat's feature functionalities. Supports multiple feature flags, using `+` to add a feature and `-` to disable a feature. Separate multiple feature flags with a comma `,` and enclose the entire value in quotes `"` to avoid parsing errors.
22
+ - Default: `-`
23
+ - Example: `"-welcome_suggest"`
24
+
25
+ All features are controlled by the `FEATURE_FLAGS` variable as the sole configuration variable.
26
+
27
+ You can achieve various feature combinations using the above configuration syntax. All feature flags are Boolean values, enabled with `+` and disabled with `-`.
28
+
29
+ <Callout type={'tip'}>
30
+ Attention: Unlike the `OPENAI_MODEL_LIST` variable, the `FEATURE_FLAGS` variable does not support the `all` keyword. You need to manually control all feature flags (otherwise, they will adopt their default values).
31
+ </Callout>
32
+
33
+ | Configuration Item | Description | Default Value |
34
+ | ------------------------- | --------------------------------- | ------------- |
35
+ | `webrtc_sync` | Enables WebRTC sync functionality.| Enabled |
36
+ | `language_model_settings` | Enables language model settings. | Enabled |
37
+ | `openai_api_key` | Allows users to customize the OpenAI API Key. | Enabled |
38
+ | `openai_proxy_url` | Allows users to customize the OpenAI proxy URL. | Enabled |
39
+ | `create_session` | Allows users to create sessions. | Enabled |
40
+ | `edit_agent` | Allows users to edit assistants. | Enabled |
41
+ | `dalle` | Enables the DALL-E functionality. | Enabled |
42
+ | `check_updates` | Allows checking for updates. | Enabled |
43
+ | `welcome_suggest` | Displays welcome suggestions. | Enabled |
44
+
45
+ You can always check the [featureFlags](https://github.com/lobehub/lobe-chat/blob/main/src/config/featureFlags/schema.ts) to get the latest list of feature flags.
@@ -0,0 +1,42 @@
1
+ ---
2
+ title: LobeChat 特性标志环境变量配置指南
3
+ description: 了解如何使用环境变量自定义 LobeChat 的特性标志,包括控制否启用某个功能、或者根据需要对特定用户群体或环境启用或禁用功能。
4
+ tags:
5
+ - LobeChat
6
+ - 环境变量
7
+ - 配置指南
8
+ - 特征标志
9
+ ---
10
+
11
+ # 特性标志
12
+
13
+ 除了基础的环境变量配置外,LobeChat 还提供了一些特性标志(Feature Flags),用于控制是否全局启用某个功能,或者根据需要对特定用户群体或环境启用或禁用功能。
14
+
15
+ ## 特性标志环境变量 `FEATURE_FLAGS`
16
+
17
+ - 类型:可选
18
+ - 描述:用于控制 LobeChat 的特性功能,支持多个功能标志,使用 `+` 增加一个功能,使用 `-` 来关闭一个功能,多个功能标志之间使用英文逗号 `,` 隔开,最外层建议添加引号 `"` 以避免解析错误。
19
+ - 默认值:`-`
20
+ - 示例:`"-welcome_suggest"`
21
+
22
+ 所有的功能统一以特性标志 `FEATURE_FLAGS` 作为唯一的配置变量。
23
+
24
+ 你可以通过上述配置语法来实现更多的功能组合。所有的功能配置项都是布尔类型,通过 `+` 来启用,通过 `-` 来关闭。
25
+
26
+ <Callout type={'tip'}>
27
+ 注意:与 `OPENAI_MODEL_LIST` 变量不同,`FEATURE_FLAGS` 变量不支持 `all` 关键字,你需要手动控制所有的功能标志(否则它们会采用对应的默认值)。
28
+ </Callout>
29
+
30
+ | 配置项 | 解释 | 默认值 |
31
+ | ------------------------- | -------------------------------- | ------ |
32
+ | `webrtc_sync` | 启用 WebRTC 同步功能。 | 开启 |
33
+ | `language_model_settings` | 启用语言模型设置。 | 开启 |
34
+ | `openai_api_key` | 允许用户自定义 OpenAI API Key。 | 开启 |
35
+ | `openai_proxy_url` | 允许用户自定义 OpenAI 代理 URL。 | 开启 |
36
+ | `create_session` | 允许用户创建会话。 | 开启 |
37
+ | `edit_agent` | 允许用户编辑助手。 | 开启 |
38
+ | `dalle` | 启用 DALL-E 功能。 | 开启 |
39
+ | `check_updates` | 允许检查更新。 | 开启 |
40
+ | `welcome_suggest` | 显示欢迎建议。 | 开启 |
41
+
42
+ 你可以随时检查 [featureFlags](https://github.com/lobehub/lobe-chat/blob/main/src/config/featureFlags/schema.ts) 以获取最新的特性标志列表。
@@ -36,7 +36,7 @@ LobeChat provides some additional configuration options during deployment, which
36
36
 
37
37
  When using the `random` mode, a random API Key will be selected from the available multiple API Keys.
38
38
 
39
- When using the `turn` mode, the API Keys will be retrieved in a round-robin manner according to the specified order.
39
+ When using the `turn` mode, the API Keys will be retrieved in a polling manner according to the specified order.
40
40
 
41
41
  ### `NEXT_PUBLIC_BASE_PATH`
42
42
 
@@ -50,7 +50,7 @@ When using the `turn` mode, the API Keys will be retrieved in a round-robin mann
50
50
  - Type: Optional
51
51
  - Description: Used to configure the default settings for the LobeChat default agent. It supports various data types and structures, including key-value pairs, nested fields, array values, and more.
52
52
  - Default: -
53
- - Example: `'model=gpt-4-1106-preview;params.max_tokens=300;plugins=search-engine,lobe-image-designer`
53
+ - Example: `'model=gpt-4-1106-preview;params.max_tokens=300;plugins=search-engine,lobe-image-designer'`
54
54
 
55
55
  The `DEFAULT_AGENT_CONFIG` is used to configure the default settings for the LobeChat default agent. It supports various data types and structures, including key-value pairs, nested fields, array values, and more. The table below provides detailed information on the configuration options, examples, and corresponding explanations for the `DEFAULT_AGENT_CONFIG` environment variable:
56
56
 
@@ -71,6 +71,15 @@ Further reading:
71
71
 
72
72
  - [[RFC] 022 - Default Assistant Parameters Configuration via Environment Variables](https://github.com/lobehub/lobe-chat/discussions/913)
73
73
 
74
+ ### `FEATURE_FLAGS`
75
+
76
+ - Type: Optional
77
+ - Description: Used to control LobeChat's feature functionalities. Supports multiple feature flags, using `+` to add a feature and `-` to disable a feature. Separate multiple feature flags with a comma `,` and enclose the entire value in quotes `"` to avoid parsing errors.
78
+ - Default: `-`
79
+ - Example: `"-welcome_suggest"`
80
+
81
+ For specific content, please refer to the [Feature Flags](/docs/self-hosting/advanced/feature-flags) documentation.
82
+
74
83
  ## Plugin Service
75
84
 
76
85
  ### `PLUGINS_INDEX_URL`
@@ -32,7 +32,7 @@ LobeChat 在部署时提供了一些额外的配置项,你可以使用环境
32
32
 
33
33
  使用 `random` 模式下,将在多个 API Keys 中随机获取一个 API Key。
34
34
 
35
- 使用 `turn` 模式下,将按照填写的顺序,轮训获取得到 API Key。
35
+ 使用 `turn` 模式下,将按照填写的顺序,轮询获取得到 API Key。
36
36
 
37
37
  ### `NEXT_PUBLIC_BASE_PATH`
38
38
 
@@ -46,7 +46,7 @@ LobeChat 在部署时提供了一些额外的配置项,你可以使用环境
46
46
  - 类型:可选
47
47
  - 描述:用于配置 LobeChat 默认助理的默认配置。它支持多种数据类型和结构,包括键值对、嵌套字段、数组值等。
48
48
  - 默认值:`-`
49
- - 示例:`'model=gpt-4-1106-preview;params.max_tokens=300;plugins=search-engine,lobe-image-designer`
49
+ - 示例:`'model=gpt-4-1106-preview;params.max_tokens=300;plugins=search-engine,lobe-image-designer'`
50
50
 
51
51
  `DEFAULT_AGENT_CONFIG` 用于配置 LobeChat 默认助理的默认配置。它支持多种数据类型和结构,包括键值对、嵌套字段、数组值等。下表详细说明了 `DEFAULT_AGENT_CONFIG` 环境变量的配置项、示例以及相应解释:
52
52
 
@@ -67,6 +67,15 @@ LobeChat 在部署时提供了一些额外的配置项,你可以使用环境
67
67
 
68
68
  - [[RFC] 022 - 环境变量配置默认助手参数](https://github.com/lobehub/lobe-chat/discussions/913)
69
69
 
70
+ ### `FEATURE_FLAGS`
71
+
72
+ - 类型:可选
73
+ - 描述:用于控制 LobeChat 的特性功能,支持多个功能标志,使用 `+` 增加一个功能,使用 `-` 来关闭一个功能,多个功能标志之间使用英文逗号 `,` 隔开,最外层建议添加引号 `"` 以避免解析错误。
74
+ - 默认值:`-`
75
+ - 示例:`"-welcome_suggest"`
76
+
77
+ 具体的内容可以参见 [特性标志](/zh/docs/self-hosting/advanced/feature-flags) 中的说明。
78
+
70
79
  ## 插件服务
71
80
 
72
81
  ### `PLUGINS_INDEX_URL`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.161.8",
3
+ "version": "0.161.10",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -1,8 +1,9 @@
1
1
  import { Icon } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
3
  import { FileImage, FileText, FileUpIcon } from 'lucide-react';
4
- import { rgba } from 'polished';
4
+ import { darken, lighten } from 'polished';
5
5
  import { memo, useEffect, useRef, useState } from 'react';
6
+ import { createPortal } from 'react-dom';
6
7
  import { useTranslation } from 'react-i18next';
7
8
  import { Center, Flexbox } from 'react-layout-kit';
8
9
 
@@ -12,50 +13,57 @@ import { useFileStore } from '@/store/file';
12
13
  import { useUserStore } from '@/store/user';
13
14
  import { modelProviderSelectors } from '@/store/user/selectors';
14
15
 
15
- const useStyles = createStyles(({ css, token, stylish }) => {
16
+ const DRAGGING_ROOT_ID = 'dragging-root';
17
+ const getContainer = () => document.querySelector(`#${DRAGGING_ROOT_ID}`);
18
+ const BLOCK_SIZE = 64;
19
+ const ICON_SIZE = 36;
20
+
21
+ const useStyles = createStyles(({ css, token }) => {
16
22
  return {
17
23
  container: css`
18
- width: 300px;
19
- height: 300px;
20
- padding: 16px;
21
-
22
- color: ${token.colorWhite};
24
+ width: 320px;
25
+ height: 200px;
26
+ padding: ${token.borderRadiusLG + 4}px;
23
27
 
24
28
  background: ${token.geekblue};
25
29
  border-radius: 16px;
26
- box-shadow:
27
- ${rgba(token.geekblue, 0.1)} 0 1px 1px 0 inset,
28
- ${rgba(token.geekblue, 0.1)} 0 50px 100px -20px,
29
- ${rgba(token.geekblue, 0.3)} 0 30px 60px -30px;
30
30
  `,
31
31
  content: css`
32
32
  width: 100%;
33
33
  height: 100%;
34
34
  padding: 16px;
35
35
 
36
- border: 2px dashed ${token.colorWhite};
37
- border-radius: 12px;
36
+ border: 1.5px dashed ${token.colorBorder};
37
+ border-radius: ${token.borderRadiusLG}px;
38
38
  `,
39
39
  desc: css`
40
- color: ${rgba(token.colorTextLightSolid, 0.6)};
40
+ color: #fff;
41
+ `,
42
+ icon: css`
43
+ color: ${darken(0.05, token.geekblue)};
44
+ background: ${lighten(0.38, token.geekblue)};
45
+ border-radius: ${token.borderRadiusLG}px;
46
+ `,
47
+ iconGroup: css`
48
+ margin-top: -44px;
41
49
  `,
42
50
  title: css`
43
- font-size: 24px;
51
+ font-size: 20px;
44
52
  font-weight: bold;
53
+ color: #fff;
45
54
  `,
46
55
  wrapper: css`
47
56
  position: fixed;
48
- z-index: 10000000;
49
- top: 0;
50
- left: 0;
57
+ z-index: 9999;
58
+ inset: 0;
51
59
 
52
60
  width: 100%;
53
61
  height: 100%;
54
62
 
55
- transition: all 0.3s ease-in-out;
56
-
57
63
  background: ${token.colorBgMask};
58
- ${stylish.blur};
64
+ backdrop-filter: blur(4px);
65
+
66
+ transition: all 0.3s ease-in-out;
59
67
  `,
60
68
  };
61
69
  });
@@ -70,7 +78,7 @@ const handleDragOver = (e: DragEvent) => {
70
78
  };
71
79
 
72
80
  const DragUpload = memo(() => {
73
- const { styles } = useStyles();
81
+ const { styles, theme } = useStyles();
74
82
  const { t } = useTranslation('chat');
75
83
  const [isDragging, setIsDragging] = useState(false);
76
84
  // When a file is dragged to a different area, the 'dragleave' event may be triggered,
@@ -151,6 +159,17 @@ const DragUpload = memo(() => {
151
159
  uploadImages(files);
152
160
  };
153
161
 
162
+ useEffect(() => {
163
+ if (getContainer()) return;
164
+ const root = document.createElement('div');
165
+ root.id = DRAGGING_ROOT_ID;
166
+ document.body.append(root);
167
+
168
+ return () => {
169
+ root.remove();
170
+ };
171
+ }, []);
172
+
154
173
  useEffect(() => {
155
174
  window.addEventListener('dragenter', handleDragEnter);
156
175
  window.addEventListener('dragover', handleDragOver);
@@ -167,28 +186,59 @@ const DragUpload = memo(() => {
167
186
  };
168
187
  }, [handleDragEnter, handleDragOver, handleDragLeave, handleDrop, handlePaste]);
169
188
 
170
- return (
171
- isDragging && (
172
- <Center className={styles.wrapper}>
173
- <div className={styles.container}>
174
- <Center className={styles.content} gap={40}>
175
- <Flexbox horizontal>
176
- <Icon icon={FileImage} size={{ fontSize: 64, strokeWidth: 1 }} />
177
- <Icon icon={FileUpIcon} size={{ fontSize: 64, strokeWidth: 1 }} />
178
- <Icon icon={FileText} size={{ fontSize: 64, strokeWidth: 1 }} />
189
+ if (!isDragging) return;
190
+
191
+ return createPortal(
192
+ <Center className={styles.wrapper}>
193
+ <div className={styles.container}>
194
+ <Center className={styles.content} gap={12}>
195
+ <Flexbox className={styles.iconGroup} horizontal>
196
+ <Center
197
+ className={styles.icon}
198
+ height={BLOCK_SIZE * 1.25}
199
+ style={{
200
+ background: lighten(0.32, theme.geekblue),
201
+ transform: 'rotateZ(-20deg) translateX(10px)',
202
+ }}
203
+ width={BLOCK_SIZE}
204
+ >
205
+ <Icon icon={FileImage} size={{ fontSize: ICON_SIZE, strokeWidth: 1.5 }} />
206
+ </Center>
207
+ <Center
208
+ className={styles.icon}
209
+ height={BLOCK_SIZE * 1.25}
210
+ style={{
211
+ transform: 'translateY(-12px)',
212
+ zIndex: 1,
213
+ }}
214
+ width={BLOCK_SIZE}
215
+ >
216
+ <Icon icon={FileUpIcon} size={{ fontSize: ICON_SIZE, strokeWidth: 1.5 }} />
217
+ </Center>
218
+ <Center
219
+ className={styles.icon}
220
+ height={BLOCK_SIZE * 1.25}
221
+ style={{
222
+ background: lighten(0.32, theme.geekblue),
223
+ transform: 'rotateZ(20deg) translateX(-10px)',
224
+ }}
225
+ width={BLOCK_SIZE}
226
+ >
227
+ <Icon icon={FileText} size={{ fontSize: ICON_SIZE, strokeWidth: 1.5 }} />
228
+ </Center>
229
+ </Flexbox>
230
+ <Flexbox align={'center'} gap={8} style={{ textAlign: 'center' }}>
231
+ <Flexbox className={styles.title}>
232
+ {t(enabledFiles ? 'upload.dragFileTitle' : 'upload.dragTitle')}
179
233
  </Flexbox>
180
- <Flexbox align={'center'} gap={8} style={{ textAlign: 'center' }}>
181
- <Flexbox className={styles.title}>
182
- {t(enabledFiles ? 'upload.dragFileTitle' : 'upload.dragTitle')}
183
- </Flexbox>
184
- <Flexbox className={styles.desc}>
185
- {t(enabledFiles ? 'upload.dragFileDesc' : 'upload.dragDesc')}
186
- </Flexbox>
234
+ <Flexbox className={styles.desc}>
235
+ {t(enabledFiles ? 'upload.dragFileDesc' : 'upload.dragDesc')}
187
236
  </Flexbox>
188
- </Center>
189
- </div>
190
- </Center>
191
- )
237
+ </Flexbox>
238
+ </Center>
239
+ </div>
240
+ </Center>,
241
+ getContainer()!,
192
242
  );
193
243
  });
194
244
 
@@ -101,7 +101,7 @@ const Footer = memo<FooterProps>(({ setExpand }) => {
101
101
  horizontal
102
102
  padding={'0 24px'}
103
103
  >
104
- <Flexbox align={'center'} gap={8} horizontal>
104
+ <Flexbox align={'center'} gap={8} horizontal style={{ overflow: 'hidden' }}>
105
105
  {canUpload && (
106
106
  <>
107
107
  <DragUpload />
@@ -109,7 +109,7 @@ const Footer = memo<FooterProps>(({ setExpand }) => {
109
109
  </>
110
110
  )}
111
111
  </Flexbox>
112
- <Flexbox align={'center'} gap={8} horizontal>
112
+ <Flexbox align={'center'} flex={'none'} gap={8} horizontal>
113
113
  <Flexbox
114
114
  gap={4}
115
115
  horizontal
@@ -1,4 +1,5 @@
1
1
  import { memo } from 'react';
2
+ import { Flexbox } from 'react-layout-kit';
2
3
 
3
4
  import EditableFileList from '@/components/FileList/EditableFileList';
4
5
  import { useFileStore } from '@/store/file';
@@ -9,9 +10,9 @@ const Files = memo(() => {
9
10
  if (!inputFilesList || inputFilesList?.length === 0) return null;
10
11
 
11
12
  return (
12
- <div style={{ position: 'relative', width: '100vw' }}>
13
- <EditableFileList alwaysShowClose items={inputFilesList} padding={'0 8px'} />
14
- </div>
13
+ <Flexbox paddingBlock={4} style={{ position: 'relative' }}>
14
+ <EditableFileList alwaysShowClose items={inputFilesList} padding={'4px 8px 8px'} />
15
+ </Flexbox>
15
16
  );
16
17
  });
17
18
 
@@ -50,6 +50,8 @@ interface ProviderConfigProps {
50
50
  canDeactivate?: boolean;
51
51
  checkModel?: string;
52
52
  checkerItem?: FormItemProps;
53
+ className?: string;
54
+ hideSwitch?: boolean;
53
55
  modelList?: {
54
56
  azureDeployName?: boolean;
55
57
  notFoundContent?: ReactNode;
@@ -81,11 +83,12 @@ const ProviderConfig = memo<ProviderConfigProps>(
81
83
  checkerItem,
82
84
  modelList,
83
85
  showBrowserRequest,
86
+ className,
84
87
  }) => {
85
88
  const { t } = useTranslation('setting');
86
89
  const { t: modelT } = useTranslation('modelProvider');
87
90
  const [form] = Form.useForm();
88
- const { styles } = useStyles();
91
+ const { cx, styles } = useStyles();
89
92
  const [
90
93
  toggleProviderEnabled,
91
94
  setSettings,
@@ -192,7 +195,7 @@ const ProviderConfig = memo<ProviderConfigProps>(
192
195
 
193
196
  return (
194
197
  <Form
195
- className={styles.form}
198
+ className={cx(styles.form, className)}
196
199
  form={form}
197
200
  items={[model]}
198
201
  onValuesChange={debounce(setSettings, 100)}
@@ -71,18 +71,17 @@ const CustomModelOption = memo<CustomModelOptionProps>(({ id, provider }) => {
71
71
  e.stopPropagation();
72
72
  e.preventDefault();
73
73
 
74
- const isConfirm = await modal.confirm({
74
+ await modal.confirm({
75
75
  centered: true,
76
76
  content: s('llm.customModelCards.confirmDelete'),
77
77
  okButtonProps: { danger: true },
78
+ onOk: async () => {
79
+ // delete model and deactivate id
80
+ await dispatchCustomModelCards(provider, { id, type: 'delete' });
81
+ await removeEnabledModels(provider, id);
82
+ },
78
83
  type: 'warning',
79
84
  });
80
-
81
- // delete model and deactive id
82
- if (isConfirm) {
83
- await dispatchCustomModelCards(provider, { id, type: 'delete' });
84
- await removeEnabledModels(provider, id);
85
- }
86
85
  }}
87
86
  title={t('delete')}
88
87
  />
@@ -1,71 +1,28 @@
1
- import { Modal } from '@lobehub/ui';
2
- import { Button, Checkbox, Form, Input } from 'antd';
3
- import isEqual from 'fast-deep-equal';
4
- import { memo } from 'react';
1
+ import { Checkbox, Form, FormInstance, Input } from 'antd';
2
+ import { memo, useEffect } from 'react';
5
3
  import { useTranslation } from 'react-i18next';
6
4
 
7
- import { useUserStore } from '@/store/user';
8
- import { modelConfigSelectors } from '@/store/user/selectors';
5
+ import { ChatModelCard } from '@/types/llm';
9
6
 
10
7
  import MaxTokenSlider from './MaxTokenSlider';
11
8
 
12
- interface ModelConfigModalProps {
13
- provider?: string;
9
+ interface ModelConfigFormProps {
10
+ initialValues?: ChatModelCard;
11
+ onFormInstanceReady: (instance: FormInstance) => void;
14
12
  showAzureDeployName?: boolean;
15
13
  }
16
- const ModelConfigModal = memo<ModelConfigModalProps>(({ showAzureDeployName, provider }) => {
17
- const [formInstance] = Form.useForm();
18
- const { t } = useTranslation('setting');
19
- const { t: tc } = useTranslation('common');
20
14
 
21
- const [open, id, editingProvider, dispatchCustomModelCards, toggleEditingCustomModelCard] =
22
- useUserStore((s) => [
23
- !!s.editingCustomCardModel && provider === s.editingCustomCardModel?.provider,
24
- s.editingCustomCardModel?.id,
25
- s.editingCustomCardModel?.provider,
26
- s.dispatchCustomModelCards,
27
- s.toggleEditingCustomModelCard,
28
- ]);
15
+ const ModelConfigForm = memo<ModelConfigFormProps>(
16
+ ({ showAzureDeployName, onFormInstanceReady, initialValues }) => {
17
+ const { t } = useTranslation('setting');
29
18
 
30
- const modelCard = useUserStore(
31
- modelConfigSelectors.getCustomModelCard({ id, provider: editingProvider }),
32
- isEqual,
33
- );
19
+ const [formInstance] = Form.useForm();
34
20
 
35
- const closeModal = () => {
36
- toggleEditingCustomModelCard(undefined);
37
- };
21
+ useEffect(() => {
22
+ onFormInstanceReady(formInstance);
23
+ }, []);
38
24
 
39
- return (
40
- <Modal
41
- destroyOnClose
42
- footer={[
43
- <Button key="cancel" onClick={closeModal}>
44
- {tc('cancel')}
45
- </Button>,
46
-
47
- <Button
48
- key="ok"
49
- onClick={() => {
50
- if (!editingProvider || !id) return;
51
- const data = formInstance.getFieldsValue();
52
-
53
- dispatchCustomModelCards(editingProvider as any, { id, type: 'update', value: data });
54
-
55
- closeModal();
56
- }}
57
- style={{ marginInlineStart: '16px' }}
58
- type="primary"
59
- >
60
- {tc('ok')}
61
- </Button>,
62
- ]}
63
- maskClosable
64
- onCancel={closeModal}
65
- open={open}
66
- title={t('llm.customModelCards.modelConfig.modalTitle')}
67
- zIndex={1051} // Select is 1050
68
- >
25
+ return (
69
26
  <div
70
27
  onClick={(e) => {
71
28
  e.stopPropagation();
@@ -77,9 +34,8 @@ const ModelConfigModal = memo<ModelConfigModalProps>(({ showAzureDeployName, pro
77
34
  <Form
78
35
  colon={false}
79
36
  form={formInstance}
80
- initialValues={modelCard}
37
+ initialValues={initialValues}
81
38
  labelCol={{ span: 4 }}
82
- preserve={false}
83
39
  style={{ marginTop: 16 }}
84
40
  wrapperCol={{ offset: 1, span: 18 }}
85
41
  >
@@ -136,7 +92,7 @@ const ModelConfigModal = memo<ModelConfigModalProps>(({ showAzureDeployName, pro
136
92
  </Form.Item>
137
93
  </Form>
138
94
  </div>
139
- </Modal>
140
- );
141
- });
142
- export default ModelConfigModal;
95
+ );
96
+ },
97
+ );
98
+ export default ModelConfigForm;