@lobehub/chat 0.161.12 → 0.161.14
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.
- package/.github/ISSUE_TEMPLATE/1_bug_report.yml +33 -27
- package/.github/ISSUE_TEMPLATE/1_bug_report_cn.yml +32 -27
- package/CHANGELOG.md +42 -0
- package/docs/self-hosting/environment-variables/model-provider.mdx +14 -0
- package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +14 -0
- package/package.json +5 -4
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +3 -2
- package/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/SystemRoleContent.tsx +2 -1
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx +2 -1
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx +7 -6
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx +3 -2
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx +2 -1
- package/src/app/(main)/chat/@session/features/SessionListContent/DefaultMode.tsx +5 -5
- package/src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx +5 -3
- package/src/features/PWAInstall/index.tsx +51 -1
- package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -2
- package/src/layout/GlobalProvider/index.tsx +32 -8
- package/src/store/global/action.test.ts +23 -22
- package/src/store/global/action.ts +28 -29
- package/src/store/global/initialState.ts +19 -23
- package/src/store/global/selectors.ts +20 -3
- package/src/utils/localStorage.ts +1 -1
|
@@ -3,55 +3,61 @@ description: 'Report an bug'
|
|
|
3
3
|
title: '[Bug] '
|
|
4
4
|
labels: ['🐛 Bug']
|
|
5
5
|
body:
|
|
6
|
-
- type:
|
|
6
|
+
- type: checkboxes
|
|
7
7
|
attributes:
|
|
8
|
-
label: '
|
|
8
|
+
label: '📦 Environment'
|
|
9
9
|
options:
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- Android
|
|
16
|
-
- Other
|
|
10
|
+
- label: 'Official'
|
|
11
|
+
- label: 'Official Preview'
|
|
12
|
+
- label: 'Vercel / Zeabur / Sealos'
|
|
13
|
+
- label: 'Docker'
|
|
14
|
+
- label: 'Other'
|
|
17
15
|
validations:
|
|
18
16
|
required: true
|
|
19
|
-
- type:
|
|
17
|
+
- type: input
|
|
20
18
|
attributes:
|
|
21
|
-
label: '
|
|
19
|
+
label: '📌 Version'
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
|
|
23
|
+
- type: checkboxes
|
|
24
|
+
attributes:
|
|
25
|
+
label: '💻 Operating System'
|
|
22
26
|
options:
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
- Other
|
|
27
|
+
- label: 'Windows'
|
|
28
|
+
- label: 'macOS'
|
|
29
|
+
- label: 'Ubuntu'
|
|
30
|
+
- label: 'Other Linux'
|
|
31
|
+
- label: 'iOS'
|
|
32
|
+
- label: 'Android'
|
|
33
|
+
- label: 'Other'
|
|
27
34
|
validations:
|
|
28
35
|
required: true
|
|
29
|
-
|
|
30
|
-
- type: dropdown
|
|
36
|
+
- type: checkboxes
|
|
31
37
|
attributes:
|
|
32
38
|
label: '🌐 Browser'
|
|
33
39
|
options:
|
|
34
|
-
- Chrome
|
|
35
|
-
- Edge
|
|
36
|
-
- Safari
|
|
37
|
-
- Firefox
|
|
38
|
-
- Other
|
|
40
|
+
- label: 'Chrome'
|
|
41
|
+
- label: 'Edge'
|
|
42
|
+
- label: 'Safari'
|
|
43
|
+
- label: 'Firefox'
|
|
44
|
+
- label: 'Other'
|
|
39
45
|
validations:
|
|
40
46
|
required: true
|
|
41
47
|
- type: textarea
|
|
42
48
|
attributes:
|
|
43
49
|
label: '🐛 Bug Description'
|
|
44
|
-
description: A clear and concise description of the bug.
|
|
50
|
+
description: A clear and concise description of the bug, if the above option is `Other`, please also explain in detail.
|
|
45
51
|
validations:
|
|
46
52
|
required: true
|
|
47
|
-
- type: textarea
|
|
48
|
-
attributes:
|
|
49
|
-
label: '🚦 Expected Behavior'
|
|
50
|
-
description: A clear and concise description of what you expected to happen.
|
|
51
53
|
- type: textarea
|
|
52
54
|
attributes:
|
|
53
55
|
label: '📷 Recurrence Steps'
|
|
54
56
|
description: A clear and concise description of how to recurrence.
|
|
57
|
+
- type: textarea
|
|
58
|
+
attributes:
|
|
59
|
+
label: '🚦 Expected Behavior'
|
|
60
|
+
description: A clear and concise description of what you expected to happen.
|
|
55
61
|
- type: textarea
|
|
56
62
|
attributes:
|
|
57
63
|
label: '📝 Additional Information'
|
|
@@ -3,56 +3,61 @@ description: '反馈一个问题缺陷'
|
|
|
3
3
|
title: '[Bug] '
|
|
4
4
|
labels: ['🐛 Bug']
|
|
5
5
|
body:
|
|
6
|
-
- type:
|
|
6
|
+
- type: checkboxes
|
|
7
7
|
attributes:
|
|
8
|
-
label: '
|
|
8
|
+
label: '📦 部署环境'
|
|
9
9
|
options:
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
- label: 'Official'
|
|
11
|
+
- label: 'Official Preview'
|
|
12
|
+
- label: 'Vercel / Zeabur / Sealos'
|
|
13
|
+
- label: 'Docker'
|
|
14
|
+
- label: 'Other'
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: input
|
|
18
|
+
attributes:
|
|
19
|
+
label: '📌 软件版本'
|
|
17
20
|
validations:
|
|
18
21
|
required: true
|
|
19
22
|
|
|
20
|
-
- type:
|
|
23
|
+
- type: checkboxes
|
|
21
24
|
attributes:
|
|
22
|
-
label: '
|
|
25
|
+
label: '💻 系统环境'
|
|
23
26
|
options:
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
- Other
|
|
27
|
+
- label: 'Windows'
|
|
28
|
+
- label: 'macOS'
|
|
29
|
+
- label: 'Ubuntu'
|
|
30
|
+
- label: 'Other Linux'
|
|
31
|
+
- label: 'iOS'
|
|
32
|
+
- label: 'Android'
|
|
33
|
+
- label: 'Other'
|
|
28
34
|
validations:
|
|
29
35
|
required: true
|
|
30
|
-
|
|
31
|
-
- type: dropdown
|
|
36
|
+
- type: checkboxes
|
|
32
37
|
attributes:
|
|
33
38
|
label: '🌐 浏览器'
|
|
34
39
|
options:
|
|
35
|
-
- Chrome
|
|
36
|
-
- Edge
|
|
37
|
-
- Safari
|
|
38
|
-
- Firefox
|
|
39
|
-
- Other
|
|
40
|
+
- label: 'Chrome'
|
|
41
|
+
- label: 'Edge'
|
|
42
|
+
- label: 'Safari'
|
|
43
|
+
- label: 'Firefox'
|
|
44
|
+
- label: 'Other'
|
|
40
45
|
validations:
|
|
41
46
|
required: true
|
|
42
47
|
- type: textarea
|
|
43
48
|
attributes:
|
|
44
49
|
label: '🐛 问题描述'
|
|
45
|
-
description:
|
|
50
|
+
description: 请提供一个清晰且简洁的问题描述,若上述选项为`Other`,也请详细说明。
|
|
46
51
|
validations:
|
|
47
52
|
required: true
|
|
48
|
-
- type: textarea
|
|
49
|
-
attributes:
|
|
50
|
-
label: '🚦 期望结果'
|
|
51
|
-
description: 请提供一个清晰且简洁的描述,说明您期望发生什么。
|
|
52
53
|
- type: textarea
|
|
53
54
|
attributes:
|
|
54
55
|
label: '📷 复现步骤'
|
|
55
56
|
description: 请提供一个清晰且简洁的描述,说明如何复现问题。
|
|
57
|
+
- type: textarea
|
|
58
|
+
attributes:
|
|
59
|
+
label: '🚦 期望结果'
|
|
60
|
+
description: 请提供一个清晰且简洁的描述,说明您期望发生什么。
|
|
56
61
|
- type: textarea
|
|
57
62
|
attributes:
|
|
58
63
|
label: '📝 补充信息'
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.161.14](https://github.com/lobehub/lobe-chat/compare/v0.161.13...v0.161.14)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-05-24**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Refactor the global app status and fix PWA installer.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Code refactoring
|
|
19
|
+
|
|
20
|
+
- **misc**: Refactor the global app status and fix PWA installer, closes [#2637](https://github.com/lobehub/lobe-chat/issues/2637) ([1f70305](https://github.com/lobehub/lobe-chat/commit/1f70305))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 0.161.13](https://github.com/lobehub/lobe-chat/compare/v0.161.12...v0.161.13)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2024-05-24**</sup>
|
|
33
|
+
|
|
34
|
+
<br/>
|
|
35
|
+
|
|
36
|
+
<details>
|
|
37
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
38
|
+
|
|
39
|
+
</details>
|
|
40
|
+
|
|
41
|
+
<div align="right">
|
|
42
|
+
|
|
43
|
+
[](#readme-top)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
5
47
|
### [Version 0.161.12](https://github.com/lobehub/lobe-chat/compare/v0.161.11...v0.161.12)
|
|
6
48
|
|
|
7
49
|
<sup>Released on **2024-05-23**</sup>
|
|
@@ -15,6 +15,13 @@ When deploying LobeChat, a rich set of environment variables related to model se
|
|
|
15
15
|
|
|
16
16
|
## OpenAI
|
|
17
17
|
|
|
18
|
+
### `ENABLED_OPENAI`
|
|
19
|
+
|
|
20
|
+
- Type:Optional
|
|
21
|
+
- Description:Enables OpenAI as a model provider by default, turns off the OpenAI service when set to `0`
|
|
22
|
+
- Default:`1`
|
|
23
|
+
- Example:`0`
|
|
24
|
+
|
|
18
25
|
### `OPENAI_API_KEY`
|
|
19
26
|
|
|
20
27
|
- Type: Required
|
|
@@ -189,6 +196,13 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
|
189
196
|
|
|
190
197
|
## Ollama
|
|
191
198
|
|
|
199
|
+
### `ENABLED_OLLAMA`
|
|
200
|
+
|
|
201
|
+
- Type:Optional
|
|
202
|
+
- Description:Enables Ollama as a model provider by default, turns off the Ollama service when set to `0`
|
|
203
|
+
- Default:`1`
|
|
204
|
+
- Example:`0`
|
|
205
|
+
|
|
192
206
|
### `OLLAMA_PROXY_URL`
|
|
193
207
|
|
|
194
208
|
- Type: Required
|
|
@@ -15,6 +15,13 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
|
|
15
15
|
|
|
16
16
|
## OpenAI
|
|
17
17
|
|
|
18
|
+
### `ENABLED_OPENAI`
|
|
19
|
+
|
|
20
|
+
- 类型:可选
|
|
21
|
+
- 描述:默认启用 OpenAI 作为模型供应商,当设为 0 时关闭 OpenAI 服务
|
|
22
|
+
- 默认值:`1`
|
|
23
|
+
- 示例:`0`
|
|
24
|
+
|
|
18
25
|
### `OPENAI_API_KEY`
|
|
19
26
|
|
|
20
27
|
- 类型:必选
|
|
@@ -187,6 +194,13 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
|
|
187
194
|
|
|
188
195
|
## Ollama
|
|
189
196
|
|
|
197
|
+
### `ENABLED_OLLAMA`
|
|
198
|
+
|
|
199
|
+
- 类型:可选
|
|
200
|
+
- 描述:默认启用 Ollama 作为模型供应商,当设为 0 时关闭 Ollama 服务
|
|
201
|
+
- 默认值:`1`
|
|
202
|
+
- 示例:`0`
|
|
203
|
+
|
|
190
204
|
### `OLLAMA_PROXY_URL`
|
|
191
205
|
|
|
192
206
|
- 类型:必选
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.161.
|
|
3
|
+
"version": "0.161.14",
|
|
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",
|
|
@@ -95,10 +95,10 @@
|
|
|
95
95
|
"@google/generative-ai": "^0.11.3",
|
|
96
96
|
"@icons-pack/react-simple-icons": "^9.5.0",
|
|
97
97
|
"@khmyznikov/pwa-install": "^0.3.9",
|
|
98
|
-
"@lobehub/chat-plugin-sdk": "
|
|
99
|
-
"@lobehub/chat-plugins-gateway": "
|
|
98
|
+
"@lobehub/chat-plugin-sdk": "^1.32.3",
|
|
99
|
+
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
|
100
100
|
"@lobehub/icons": "^1.22.0",
|
|
101
|
-
"@lobehub/tts": "
|
|
101
|
+
"@lobehub/tts": "^1.24.1",
|
|
102
102
|
"@lobehub/ui": "^1.139.0",
|
|
103
103
|
"@microsoft/fetch-event-source": "^2.0.1",
|
|
104
104
|
"@next/third-parties": "^14.2.3",
|
|
@@ -158,6 +158,7 @@
|
|
|
158
158
|
"remark": "^14.0.3",
|
|
159
159
|
"remark-gfm": "^3.0.1",
|
|
160
160
|
"remark-html": "^15.0.2",
|
|
161
|
+
"resolve-accept-language": "^3.1.4",
|
|
161
162
|
"rtl-detect": "^1.1.2",
|
|
162
163
|
"semver": "^7.6.2",
|
|
163
164
|
"sharp": "^0.33.4",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
HEADER_HEIGHT,
|
|
11
11
|
} from '@/const/layoutTokens';
|
|
12
12
|
import { useGlobalStore } from '@/store/global';
|
|
13
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
13
14
|
|
|
14
15
|
import Footer from './Footer';
|
|
15
16
|
import Head from './Header';
|
|
@@ -19,8 +20,8 @@ const DesktopChatInput = memo(() => {
|
|
|
19
20
|
const [expand, setExpand] = useState<boolean>(false);
|
|
20
21
|
|
|
21
22
|
const [inputHeight, updatePreference] = useGlobalStore((s) => [
|
|
22
|
-
|
|
23
|
-
s.
|
|
23
|
+
systemStatusSelectors.inputHeight(s),
|
|
24
|
+
s.updateSystemStatus,
|
|
24
25
|
]);
|
|
25
26
|
|
|
26
27
|
return (
|
|
@@ -15,6 +15,7 @@ import { useAgentStore } from '@/store/agent';
|
|
|
15
15
|
import { agentSelectors } from '@/store/agent/selectors';
|
|
16
16
|
import { useGlobalStore } from '@/store/global';
|
|
17
17
|
import { ChatSettingsTabs } from '@/store/global/initialState';
|
|
18
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
18
19
|
import { useSessionStore } from '@/store/session';
|
|
19
20
|
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
20
21
|
|
|
@@ -35,7 +36,7 @@ const SystemRole = memo(() => {
|
|
|
35
36
|
]);
|
|
36
37
|
|
|
37
38
|
const [showSystemRole, toggleSystemRole] = useGlobalStore((s) => [
|
|
38
|
-
|
|
39
|
+
systemStatusSelectors.showSystemRole(s),
|
|
39
40
|
s.toggleSystemRole,
|
|
40
41
|
]);
|
|
41
42
|
|
|
@@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
7
7
|
|
|
8
8
|
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
9
9
|
import { useGlobalStore } from '@/store/global';
|
|
10
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
10
11
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
11
12
|
|
|
12
13
|
import SettingButton from '../../../features/SettingButton';
|
|
@@ -16,7 +17,7 @@ const HeaderAction = memo(() => {
|
|
|
16
17
|
const { t } = useTranslation('chat');
|
|
17
18
|
|
|
18
19
|
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
19
|
-
|
|
20
|
+
systemStatusSelectors.showChatSideBar(s),
|
|
20
21
|
s.toggleChatSideBar,
|
|
21
22
|
]);
|
|
22
23
|
|
|
@@ -7,13 +7,14 @@ import { Suspense, memo } from 'react';
|
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { Flexbox } from 'react-layout-kit';
|
|
9
9
|
|
|
10
|
-
import { useInitAgentConfig } from '@/app/(main)/chat/(workspace)/_layout/useInitAgentConfig';
|
|
11
10
|
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
12
11
|
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
|
13
12
|
import { useGlobalStore } from '@/store/global';
|
|
13
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
14
14
|
import { useSessionStore } from '@/store/session';
|
|
15
15
|
import { sessionMetaSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
16
16
|
|
|
17
|
+
import { useInitAgentConfig } from '../../useInitAgentConfig';
|
|
17
18
|
import Tags from './Tags';
|
|
18
19
|
|
|
19
20
|
const Main = memo(() => {
|
|
@@ -34,7 +35,8 @@ const Main = memo(() => {
|
|
|
34
35
|
|
|
35
36
|
const displayTitle = isInbox ? t('inbox.title') : title;
|
|
36
37
|
const displayDesc = isInbox ? t('inbox.desc') : description;
|
|
37
|
-
const showSessionPanel = useGlobalStore(
|
|
38
|
+
const showSessionPanel = useGlobalStore(systemStatusSelectors.showSessionPanel);
|
|
39
|
+
const updateSystemStatus = useGlobalStore((s) => s.updateSystemStatus);
|
|
38
40
|
|
|
39
41
|
return !init ? (
|
|
40
42
|
<Flexbox horizontal>
|
|
@@ -52,10 +54,9 @@ const Main = memo(() => {
|
|
|
52
54
|
aria-label={t('agentsAndConversations')}
|
|
53
55
|
icon={showSessionPanel ? PanelLeftClose : PanelLeftOpen}
|
|
54
56
|
onClick={() => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
showSessionPanel: !currentShowSessionPanel,
|
|
57
|
+
updateSystemStatus({
|
|
58
|
+
sessionsWidth: showSessionPanel ? 0 : 320,
|
|
59
|
+
showSessionPanel: !showSessionPanel,
|
|
59
60
|
});
|
|
60
61
|
}}
|
|
61
62
|
size={DESKTOP_HEADER_ICON_SIZE}
|
|
@@ -8,6 +8,7 @@ import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
|
|
8
8
|
import SafeSpacing from '@/components/SafeSpacing';
|
|
9
9
|
import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
|
|
10
10
|
import { useGlobalStore } from '@/store/global';
|
|
11
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
11
12
|
|
|
12
13
|
const useStyles = createStyles(({ css, token }) => ({
|
|
13
14
|
content: css`
|
|
@@ -28,10 +29,10 @@ const TopicPanel = memo(({ children }: PropsWithChildren) => {
|
|
|
28
29
|
const { styles } = useStyles();
|
|
29
30
|
const { md = true, lg = true } = useResponsive();
|
|
30
31
|
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
31
|
-
|
|
32
|
+
systemStatusSelectors.showChatSideBar(s),
|
|
32
33
|
s.toggleChatSideBar,
|
|
33
|
-
s.isPreferenceInit,
|
|
34
34
|
]);
|
|
35
|
+
|
|
35
36
|
const [cacheExpand, setCacheExpand] = useState<boolean>(Boolean(showAgentSettings));
|
|
36
37
|
|
|
37
38
|
const handleExpand = (expand: boolean) => {
|
|
@@ -5,12 +5,13 @@ import { PropsWithChildren, memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { useGlobalStore } from '@/store/global';
|
|
8
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
8
9
|
|
|
9
10
|
import { useWorkspaceModal } from '../../features/useWorkspaceModal';
|
|
10
11
|
|
|
11
12
|
const Topics = memo(({ children }: PropsWithChildren) => {
|
|
12
13
|
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
13
|
-
|
|
14
|
+
systemStatusSelectors.mobileShowTopic(s),
|
|
14
15
|
s.toggleMobileTopic,
|
|
15
16
|
]);
|
|
16
17
|
const [open, setOpen] = useWorkspaceModal(showAgentSettings, toggleConfig);
|
|
@@ -4,7 +4,7 @@ import { memo, useMemo, useState } from 'react';
|
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { useGlobalStore } from '@/store/global';
|
|
7
|
-
import {
|
|
7
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
8
8
|
import { useSessionStore } from '@/store/session';
|
|
9
9
|
import { sessionSelectors } from '@/store/session/selectors';
|
|
10
10
|
import { SessionDefaultGroup } from '@/types/session';
|
|
@@ -30,9 +30,9 @@ const DefaultMode = memo(() => {
|
|
|
30
30
|
const customSessionGroups = useSessionStore(sessionSelectors.customSessionGroups, isEqual);
|
|
31
31
|
const pinnedSessions = useSessionStore(sessionSelectors.pinnedSessions, isEqual);
|
|
32
32
|
|
|
33
|
-
const [sessionGroupKeys,
|
|
34
|
-
|
|
35
|
-
s.
|
|
33
|
+
const [sessionGroupKeys, updateSystemStatus] = useGlobalStore((s) => [
|
|
34
|
+
systemStatusSelectors.sessionGroupKeys(s),
|
|
35
|
+
s.updateSystemStatus,
|
|
36
36
|
]);
|
|
37
37
|
|
|
38
38
|
const items = useMemo(
|
|
@@ -80,7 +80,7 @@ const DefaultMode = memo(() => {
|
|
|
80
80
|
onChange={(keys) => {
|
|
81
81
|
const expandSessionGroupKeys = typeof keys === 'string' ? [keys] : keys;
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
updateSystemStatus({ expandSessionGroupKeys });
|
|
84
84
|
}}
|
|
85
85
|
/>
|
|
86
86
|
{activeGroupId && (
|
|
@@ -7,6 +7,7 @@ import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import { FOLDER_WIDTH } from '@/const/layoutTokens';
|
|
9
9
|
import { useGlobalStore } from '@/store/global';
|
|
10
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
10
11
|
|
|
11
12
|
export const useStyles = createStyles(({ css, token }) => ({
|
|
12
13
|
panel: css`
|
|
@@ -21,10 +22,11 @@ const SessionPanel = memo<PropsWithChildren>(({ children }) => {
|
|
|
21
22
|
|
|
22
23
|
const { styles } = useStyles();
|
|
23
24
|
const [sessionsWidth, sessionExpandable, updatePreference] = useGlobalStore((s) => [
|
|
24
|
-
s
|
|
25
|
-
|
|
26
|
-
s.
|
|
25
|
+
systemStatusSelectors.sessionWidth(s),
|
|
26
|
+
systemStatusSelectors.showSessionPanel(s),
|
|
27
|
+
s.updateSystemStatus,
|
|
27
28
|
]);
|
|
29
|
+
|
|
28
30
|
const [cacheExpand, setCacheExpand] = useState<boolean>(Boolean(sessionExpandable));
|
|
29
31
|
const [tmpWidth, setWidth] = useState(sessionsWidth);
|
|
30
32
|
if (tmpWidth !== sessionsWidth) setWidth(sessionsWidth);
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import dynamic from 'next/dynamic';
|
|
4
|
-
import { memo } from 'react';
|
|
4
|
+
import { memo, useEffect, useLayoutEffect } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { PWA_INSTALL_ID } from '@/const/layoutTokens';
|
|
8
|
+
import { usePWAInstall } from '@/hooks/usePWAInstall';
|
|
8
9
|
import { usePlatform } from '@/hooks/usePlatform';
|
|
10
|
+
import { useGlobalStore } from '@/store/global';
|
|
11
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
12
|
+
import { useUserStore } from '@/store/user';
|
|
9
13
|
|
|
10
14
|
// @ts-ignore
|
|
11
15
|
const PWA: any = dynamic(() => import('@khmyznikov/pwa-install/dist/pwa-install.react.js'), {
|
|
@@ -15,6 +19,52 @@ const PWA: any = dynamic(() => import('@khmyznikov/pwa-install/dist/pwa-install.
|
|
|
15
19
|
const PWAInstall = memo(() => {
|
|
16
20
|
const { t } = useTranslation('metadata');
|
|
17
21
|
const { isPWA } = usePlatform();
|
|
22
|
+
|
|
23
|
+
const { install, canInstall } = usePWAInstall();
|
|
24
|
+
|
|
25
|
+
const isShowPWAGuide = useUserStore((s) => s.isShowPWAGuide);
|
|
26
|
+
const [hidePWAInstaller, updateSystemStatus] = useGlobalStore((s) => [
|
|
27
|
+
systemStatusSelectors.hidePWAInstaller(s),
|
|
28
|
+
s.updateSystemStatus,
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
// we need to make the pwa installer hidden by default
|
|
32
|
+
useLayoutEffect(() => {
|
|
33
|
+
sessionStorage.setItem('pwa-hide-install', 'true');
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
const pwaInstall =
|
|
37
|
+
// eslint-disable-next-line unicorn/prefer-query-selector
|
|
38
|
+
typeof window === 'undefined' ? undefined : document.getElementById(PWA_INSTALL_ID);
|
|
39
|
+
|
|
40
|
+
// add an event listener to control the user close installer action
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!pwaInstall) return;
|
|
43
|
+
|
|
44
|
+
const handler = (e: Event) => {
|
|
45
|
+
const event = e as CustomEvent;
|
|
46
|
+
|
|
47
|
+
// it means user hide installer
|
|
48
|
+
if (event.detail.message === 'dismissed') {
|
|
49
|
+
updateSystemStatus({ hidePWAInstaller: true });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
pwaInstall.addEventListener('pwa-user-choice-result-event', handler);
|
|
54
|
+
return () => {
|
|
55
|
+
pwaInstall.removeEventListener('pwa-user-choice-result-event', handler);
|
|
56
|
+
};
|
|
57
|
+
}, [pwaInstall]);
|
|
58
|
+
|
|
59
|
+
// trigger the PWA guide on demand
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!canInstall || hidePWAInstaller) return;
|
|
62
|
+
|
|
63
|
+
if (isShowPWAGuide) {
|
|
64
|
+
install();
|
|
65
|
+
}
|
|
66
|
+
}, [canInstall, hidePWAInstaller, isShowPWAGuide]);
|
|
67
|
+
|
|
18
68
|
if (isPWA) return null;
|
|
19
69
|
return <PWA description={t('chat.description')} id={PWA_INSTALL_ID} />;
|
|
20
70
|
});
|
|
@@ -24,11 +24,11 @@ const StoreInitialization = memo(() => {
|
|
|
24
24
|
|
|
25
25
|
const { serverConfig } = useServerConfigStore();
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const useInitSystemStatus = useGlobalStore((s) => s.useInitSystemStatus);
|
|
28
28
|
|
|
29
29
|
const useFetchDefaultAgentConfig = useAgentStore((s) => s.useFetchDefaultAgentConfig);
|
|
30
30
|
// init the system preference
|
|
31
|
-
|
|
31
|
+
useInitSystemStatus();
|
|
32
32
|
useFetchDefaultAgentConfig();
|
|
33
33
|
|
|
34
34
|
useInitUserState(isLogin, serverConfig, {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import dynamic from 'next/dynamic';
|
|
2
|
-
import { cookies } from 'next/headers';
|
|
3
|
-
import { FC,
|
|
2
|
+
import { cookies, headers } from 'next/headers';
|
|
3
|
+
import { FC, PropsWithChildren } from 'react';
|
|
4
|
+
import { resolveAcceptLanguage } from 'resolve-accept-language';
|
|
4
5
|
|
|
5
6
|
import { getDebugConfig } from '@/config/debug';
|
|
6
7
|
import { getServerFeatureFlagsValue } from '@/config/featureFlags';
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
LOBE_THEME_NEUTRAL_COLOR,
|
|
11
12
|
LOBE_THEME_PRIMARY_COLOR,
|
|
12
13
|
} from '@/const/theme';
|
|
14
|
+
import { locales } from '@/locales/resources';
|
|
13
15
|
import { getServerGlobalConfig } from '@/server/globalConfig';
|
|
14
16
|
import { ServerConfigStoreProvider } from '@/store/serverConfig';
|
|
15
17
|
import { getAntdLocale } from '@/utils/locale';
|
|
@@ -31,11 +33,27 @@ if (process.env.NODE_ENV === 'development') {
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
const parserFallbackLang = () => {
|
|
37
|
+
/**
|
|
38
|
+
* The arguments are as follows:
|
|
39
|
+
*
|
|
40
|
+
* 1) The HTTP accept-language header.
|
|
41
|
+
* 2) The available locales (they must contain the default locale).
|
|
42
|
+
* 3) The default locale.
|
|
43
|
+
*/
|
|
44
|
+
let fallbackLang: string = resolveAcceptLanguage(
|
|
45
|
+
headers().get('accept-language') || '',
|
|
46
|
+
// Invalid locale identifier 'ar'. A valid locale should follow the BCP 47 'language-country' format.
|
|
47
|
+
locales.map((locale) => (locale === 'ar' ? 'ar-EG' : locale)),
|
|
48
|
+
'en-US',
|
|
49
|
+
);
|
|
50
|
+
// if match the ar-EG then fallback to ar
|
|
51
|
+
if (fallbackLang === 'ar-EG') fallbackLang = 'ar';
|
|
52
|
+
|
|
53
|
+
return fallbackLang;
|
|
54
|
+
};
|
|
37
55
|
|
|
38
|
-
const GlobalLayout = async ({ children }:
|
|
56
|
+
const GlobalLayout = async ({ children }: PropsWithChildren) => {
|
|
39
57
|
// get default theme config to use with ssr
|
|
40
58
|
const cookieStore = cookies();
|
|
41
59
|
const appearance = cookieStore.get(LOBE_THEME_APPEARANCE);
|
|
@@ -44,7 +62,13 @@ const GlobalLayout = async ({ children }: GlobalLayoutProps) => {
|
|
|
44
62
|
|
|
45
63
|
// get default locale config to use with ssr
|
|
46
64
|
const defaultLang = cookieStore.get(LOBE_LOCALE_COOKIE);
|
|
47
|
-
const
|
|
65
|
+
const fallbackLang = parserFallbackLang();
|
|
66
|
+
|
|
67
|
+
// if it's a new user, there's no cookie
|
|
68
|
+
// So we need to use the fallback language parsed by accept-language
|
|
69
|
+
const userLocale = defaultLang?.value || fallbackLang;
|
|
70
|
+
|
|
71
|
+
const antdLocale = await getAntdLocale(userLocale);
|
|
48
72
|
|
|
49
73
|
// get default feature flags to use with ssr
|
|
50
74
|
const serverFeatureFlags = getServerFeatureFlagsValue();
|
|
@@ -52,7 +76,7 @@ const GlobalLayout = async ({ children }: GlobalLayoutProps) => {
|
|
|
52
76
|
const isMobile = isMobileDevice();
|
|
53
77
|
return (
|
|
54
78
|
<StyleRegistry>
|
|
55
|
-
<Locale antdLocale={antdLocale} defaultLang={
|
|
79
|
+
<Locale antdLocale={antdLocale} defaultLang={userLocale}>
|
|
56
80
|
<AppTheme
|
|
57
81
|
defaultAppearance={appearance?.value}
|
|
58
82
|
defaultNeutralColor={neutralColor?.value as any}
|
|
@@ -34,11 +34,11 @@ describe('createPreferenceSlice', () => {
|
|
|
34
34
|
const { result } = renderHook(() => useGlobalStore());
|
|
35
35
|
|
|
36
36
|
act(() => {
|
|
37
|
-
useGlobalStore.getState().
|
|
37
|
+
useGlobalStore.getState().updateSystemStatus({ showChatSideBar: false });
|
|
38
38
|
result.current.toggleChatSideBar();
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
expect(result.current.
|
|
41
|
+
expect(result.current.status.showChatSideBar).toBe(true);
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
44
|
|
|
@@ -48,10 +48,11 @@ describe('createPreferenceSlice', () => {
|
|
|
48
48
|
const groupId = 'group-id';
|
|
49
49
|
|
|
50
50
|
act(() => {
|
|
51
|
+
useGlobalStore.setState({ isStatusInit: true });
|
|
51
52
|
result.current.toggleExpandSessionGroup(groupId, true);
|
|
52
53
|
});
|
|
53
54
|
|
|
54
|
-
expect(result.current.
|
|
55
|
+
expect(result.current.status.expandSessionGroupKeys).toContain(groupId);
|
|
55
56
|
});
|
|
56
57
|
});
|
|
57
58
|
|
|
@@ -60,10 +61,11 @@ describe('createPreferenceSlice', () => {
|
|
|
60
61
|
const { result } = renderHook(() => useGlobalStore());
|
|
61
62
|
|
|
62
63
|
act(() => {
|
|
64
|
+
useGlobalStore.setState({ isStatusInit: true });
|
|
63
65
|
result.current.toggleMobileTopic();
|
|
64
66
|
});
|
|
65
67
|
|
|
66
|
-
expect(result.current.
|
|
68
|
+
expect(result.current.status.mobileShowTopic).toBe(true);
|
|
67
69
|
});
|
|
68
70
|
});
|
|
69
71
|
|
|
@@ -72,23 +74,24 @@ describe('createPreferenceSlice', () => {
|
|
|
72
74
|
const { result } = renderHook(() => useGlobalStore());
|
|
73
75
|
|
|
74
76
|
act(() => {
|
|
77
|
+
useGlobalStore.setState({ isStatusInit: true });
|
|
75
78
|
result.current.toggleSystemRole(true);
|
|
76
79
|
});
|
|
77
80
|
|
|
78
|
-
expect(result.current.
|
|
81
|
+
expect(result.current.status.showSystemRole).toBe(true);
|
|
79
82
|
});
|
|
80
83
|
});
|
|
81
84
|
|
|
82
85
|
describe('updatePreference', () => {
|
|
83
|
-
it('should update
|
|
86
|
+
it('should update status', () => {
|
|
84
87
|
const { result } = renderHook(() => useGlobalStore());
|
|
85
|
-
const
|
|
88
|
+
const status = { inputHeight: 200 };
|
|
86
89
|
|
|
87
90
|
act(() => {
|
|
88
|
-
result.current.
|
|
91
|
+
result.current.updateSystemStatus(status);
|
|
89
92
|
});
|
|
90
93
|
|
|
91
|
-
expect(result.current.
|
|
94
|
+
expect(result.current.status.inputHeight).toEqual(200);
|
|
92
95
|
});
|
|
93
96
|
});
|
|
94
97
|
|
|
@@ -144,13 +147,12 @@ describe('createPreferenceSlice', () => {
|
|
|
144
147
|
});
|
|
145
148
|
|
|
146
149
|
describe('useInitGlobalPreference', () => {
|
|
147
|
-
it('should init global
|
|
148
|
-
vi.spyOn(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
).mockReturnValueOnce({} as any);
|
|
150
|
+
it('should init global status if there is empty object', async () => {
|
|
151
|
+
vi.spyOn(useGlobalStore.getState().statusStorage, 'getFromLocalStorage').mockReturnValueOnce(
|
|
152
|
+
{} as any,
|
|
153
|
+
);
|
|
152
154
|
|
|
153
|
-
const { result } = renderHook(() => useGlobalStore().
|
|
155
|
+
const { result } = renderHook(() => useGlobalStore().useInitSystemStatus(), {
|
|
154
156
|
wrapper: withSWR,
|
|
155
157
|
});
|
|
156
158
|
|
|
@@ -158,17 +160,16 @@ describe('createPreferenceSlice', () => {
|
|
|
158
160
|
expect(result.current.data).toEqual({});
|
|
159
161
|
});
|
|
160
162
|
|
|
161
|
-
expect(useGlobalStore.getState().
|
|
163
|
+
expect(useGlobalStore.getState().status).toEqual(initialState.status);
|
|
162
164
|
});
|
|
163
165
|
|
|
164
166
|
it('should update with data', async () => {
|
|
165
167
|
const { result } = renderHook(() => useGlobalStore());
|
|
166
|
-
vi.spyOn(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
).mockReturnValueOnce({ inputHeight: 300 } as any);
|
|
168
|
+
vi.spyOn(useGlobalStore.getState().statusStorage, 'getFromLocalStorage').mockReturnValueOnce({
|
|
169
|
+
inputHeight: 300,
|
|
170
|
+
} as any);
|
|
170
171
|
|
|
171
|
-
const { result: hooks } = renderHook(() => result.current.
|
|
172
|
+
const { result: hooks } = renderHook(() => result.current.useInitSystemStatus(), {
|
|
172
173
|
wrapper: withSWR,
|
|
173
174
|
});
|
|
174
175
|
|
|
@@ -176,7 +177,7 @@ describe('createPreferenceSlice', () => {
|
|
|
176
177
|
expect(hooks.current.data).toEqual({ inputHeight: 300 });
|
|
177
178
|
});
|
|
178
179
|
|
|
179
|
-
expect(result.current.
|
|
180
|
+
expect(result.current.status.inputHeight).toEqual(300);
|
|
180
181
|
});
|
|
181
182
|
});
|
|
182
183
|
});
|
|
@@ -13,9 +13,9 @@ import type { GlobalStore } from '@/store/global/index';
|
|
|
13
13
|
import { merge } from '@/utils/merge';
|
|
14
14
|
import { setNamespace } from '@/utils/storeDebug';
|
|
15
15
|
|
|
16
|
-
import type {
|
|
16
|
+
import type { SystemStatus } from './initialState';
|
|
17
17
|
|
|
18
|
-
const n = setNamespace('
|
|
18
|
+
const n = setNamespace('g');
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* 设置操作
|
|
@@ -26,9 +26,9 @@ export interface GlobalStoreAction {
|
|
|
26
26
|
toggleExpandSessionGroup: (id: string, expand: boolean) => void;
|
|
27
27
|
toggleMobileTopic: (visible?: boolean) => void;
|
|
28
28
|
toggleSystemRole: (visible?: boolean) => void;
|
|
29
|
-
|
|
29
|
+
updateSystemStatus: (status: Partial<SystemStatus>, action?: any) => void;
|
|
30
30
|
useCheckLatestVersion: (enabledCheck?: boolean) => SWRResponse<string>;
|
|
31
|
-
|
|
31
|
+
useInitSystemStatus: () => SWRResponse;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export const globalActionSlice: StateCreator<
|
|
@@ -42,13 +42,13 @@ export const globalActionSlice: StateCreator<
|
|
|
42
42
|
},
|
|
43
43
|
toggleChatSideBar: (newValue) => {
|
|
44
44
|
const showChatSideBar =
|
|
45
|
-
typeof newValue === 'boolean' ? newValue : !get().
|
|
45
|
+
typeof newValue === 'boolean' ? newValue : !get().status.showChatSideBar;
|
|
46
46
|
|
|
47
|
-
get().
|
|
47
|
+
get().updateSystemStatus({ showChatSideBar }, n('toggleAgentPanel', newValue));
|
|
48
48
|
},
|
|
49
49
|
toggleExpandSessionGroup: (id, expand) => {
|
|
50
|
-
const {
|
|
51
|
-
const nextExpandSessionGroup = produce(
|
|
50
|
+
const { status } = get();
|
|
51
|
+
const nextExpandSessionGroup = produce(status.expandSessionGroupKeys, (draft: string[]) => {
|
|
52
52
|
if (expand) {
|
|
53
53
|
if (draft.includes(id)) return;
|
|
54
54
|
draft.push(id);
|
|
@@ -57,26 +57,29 @@ export const globalActionSlice: StateCreator<
|
|
|
57
57
|
if (index !== -1) draft.splice(index, 1);
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
|
-
get().
|
|
60
|
+
get().updateSystemStatus({ expandSessionGroupKeys: nextExpandSessionGroup });
|
|
61
61
|
},
|
|
62
62
|
toggleMobileTopic: (newValue) => {
|
|
63
63
|
const mobileShowTopic =
|
|
64
|
-
typeof newValue === 'boolean' ? newValue : !get().
|
|
64
|
+
typeof newValue === 'boolean' ? newValue : !get().status.mobileShowTopic;
|
|
65
65
|
|
|
66
|
-
get().
|
|
66
|
+
get().updateSystemStatus({ mobileShowTopic }, n('toggleMobileTopic', newValue));
|
|
67
67
|
},
|
|
68
68
|
toggleSystemRole: (newValue) => {
|
|
69
|
-
const showSystemRole =
|
|
70
|
-
typeof newValue === 'boolean' ? newValue : !get().preference.mobileShowTopic;
|
|
69
|
+
const showSystemRole = typeof newValue === 'boolean' ? newValue : !get().status.mobileShowTopic;
|
|
71
70
|
|
|
72
|
-
get().
|
|
71
|
+
get().updateSystemStatus({ showSystemRole }, n('toggleMobileTopic', newValue));
|
|
73
72
|
},
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
updateSystemStatus: (status, action) => {
|
|
74
|
+
// Status cannot be modified when it is not initialized
|
|
75
|
+
if (!get().isStatusInit) return;
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
const nextStatus = merge(get().status, status);
|
|
78
|
+
if (isEqual(get().status, nextStatus)) return;
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
set({ status: nextStatus }, false, action || n('updateSystemStatus'));
|
|
81
|
+
|
|
82
|
+
get().statusStorage.saveToLocalStorage(nextStatus);
|
|
80
83
|
},
|
|
81
84
|
|
|
82
85
|
useCheckLatestVersion: (enabledCheck = true) =>
|
|
@@ -89,19 +92,15 @@ export const globalActionSlice: StateCreator<
|
|
|
89
92
|
},
|
|
90
93
|
}),
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
useClientDataSWR<
|
|
94
|
-
'
|
|
95
|
-
() => get().
|
|
95
|
+
useInitSystemStatus: () =>
|
|
96
|
+
useClientDataSWR<SystemStatus>(
|
|
97
|
+
'initSystemStatus',
|
|
98
|
+
() => get().statusStorage.getFromLocalStorage(),
|
|
96
99
|
{
|
|
97
|
-
onSuccess: (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
set({ isPreferenceInit: true });
|
|
101
|
-
|
|
102
|
-
if (isEqual(get().preference, nextPreference)) return;
|
|
100
|
+
onSuccess: (status) => {
|
|
101
|
+
set({ isStatusInit: true }, false, 'setStatusInit');
|
|
103
102
|
|
|
104
|
-
|
|
103
|
+
get().updateSystemStatus(status, 'initSystemStatus');
|
|
105
104
|
},
|
|
106
105
|
},
|
|
107
106
|
),
|
|
@@ -29,9 +29,10 @@ export enum SettingsTabs {
|
|
|
29
29
|
TTS = 'tts',
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export interface
|
|
32
|
+
export interface SystemStatus {
|
|
33
33
|
// which sessionGroup should expand
|
|
34
34
|
expandSessionGroupKeys: string[];
|
|
35
|
+
hidePWAInstaller?: boolean;
|
|
35
36
|
inputHeight: number;
|
|
36
37
|
mobileShowTopic?: boolean;
|
|
37
38
|
sessionsWidth: number;
|
|
@@ -40,37 +41,32 @@ export interface GlobalPreference {
|
|
|
40
41
|
showSystemRole?: boolean;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
export interface
|
|
44
|
-
/**
|
|
45
|
-
* the user preference, which only store in local storage
|
|
46
|
-
*/
|
|
47
|
-
preference: GlobalPreference;
|
|
48
|
-
preferenceStorage: AsyncLocalStorage<GlobalPreference>;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface GlobalCommonState {
|
|
44
|
+
export interface GlobalState {
|
|
52
45
|
hasNewVersion?: boolean;
|
|
53
46
|
isMobile?: boolean;
|
|
54
|
-
|
|
47
|
+
isStatusInit?: boolean;
|
|
55
48
|
latestVersion?: string;
|
|
56
49
|
router?: AppRouterInstance;
|
|
57
50
|
sidebarKey: SidebarTabKey;
|
|
51
|
+
status: SystemStatus;
|
|
52
|
+
statusStorage: AsyncLocalStorage<SystemStatus>;
|
|
58
53
|
}
|
|
59
54
|
|
|
60
|
-
export
|
|
55
|
+
export const INITIAL_STATUS = {
|
|
56
|
+
expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default],
|
|
57
|
+
hidePWAInstaller: false,
|
|
58
|
+
inputHeight: 200,
|
|
59
|
+
mobileShowTopic: false,
|
|
60
|
+
sessionsWidth: 320,
|
|
61
|
+
showChatSideBar: true,
|
|
62
|
+
showSessionPanel: true,
|
|
63
|
+
showSystemRole: false,
|
|
64
|
+
} satisfies SystemStatus;
|
|
61
65
|
|
|
62
66
|
export const initialState: GlobalState = {
|
|
63
67
|
isMobile: false,
|
|
64
|
-
|
|
65
|
-
preference: {
|
|
66
|
-
expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default],
|
|
67
|
-
inputHeight: 200,
|
|
68
|
-
mobileShowTopic: false,
|
|
69
|
-
sessionsWidth: 320,
|
|
70
|
-
showChatSideBar: true,
|
|
71
|
-
showSessionPanel: true,
|
|
72
|
-
showSystemRole: false,
|
|
73
|
-
},
|
|
74
|
-
preferenceStorage: new AsyncLocalStorage('LOBE_GLOBAL_PREFERENCE'),
|
|
68
|
+
isStatusInit: false,
|
|
75
69
|
sidebarKey: SidebarTabKey.Chat,
|
|
70
|
+
status: INITIAL_STATUS,
|
|
71
|
+
statusStorage: new AsyncLocalStorage('LOBE_SYSTEM_STATUS'),
|
|
76
72
|
};
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import { GlobalStore } from '@/store/global';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { INITIAL_STATUS } from './initialState';
|
|
3
4
|
|
|
4
5
|
const sessionGroupKeys = (s: GlobalStore): string[] =>
|
|
5
|
-
s.
|
|
6
|
+
s.status.expandSessionGroupKeys || INITIAL_STATUS.expandSessionGroupKeys;
|
|
7
|
+
|
|
8
|
+
const showSystemRole = (s: GlobalStore) => s.status.showSystemRole;
|
|
9
|
+
const mobileShowTopic = (s: GlobalStore) => s.status.mobileShowTopic;
|
|
10
|
+
const showChatSideBar = (s: GlobalStore) => s.status.showChatSideBar;
|
|
11
|
+
const showSessionPanel = (s: GlobalStore) => s.status.showSessionPanel;
|
|
12
|
+
const hidePWAInstaller = (s: GlobalStore) => s.status.hidePWAInstaller;
|
|
13
|
+
|
|
14
|
+
const sessionWidth = (s: GlobalStore) => s.status.sessionsWidth;
|
|
15
|
+
const inputHeight = (s: GlobalStore) => s.status.inputHeight;
|
|
6
16
|
|
|
7
|
-
export const
|
|
17
|
+
export const systemStatusSelectors = {
|
|
18
|
+
hidePWAInstaller,
|
|
19
|
+
inputHeight,
|
|
20
|
+
mobileShowTopic,
|
|
8
21
|
sessionGroupKeys,
|
|
22
|
+
sessionWidth,
|
|
23
|
+
showChatSideBar,
|
|
24
|
+
showSessionPanel,
|
|
25
|
+
showSystemRole,
|
|
9
26
|
};
|
|
@@ -2,7 +2,7 @@ const PREV_KEY = 'LOBE_GLOBAL';
|
|
|
2
2
|
|
|
3
3
|
// LOBE_PREFERENCE for userStore
|
|
4
4
|
// LOBE_GLOBAL_PREFERENCE for globalStore
|
|
5
|
-
type StorageKey = 'LOBE_PREFERENCE' | '
|
|
5
|
+
type StorageKey = 'LOBE_PREFERENCE' | 'LOBE_SYSTEM_STATUS';
|
|
6
6
|
|
|
7
7
|
export class AsyncLocalStorage<State> {
|
|
8
8
|
private storageKey: StorageKey;
|