@lobehub/chat 0.82.8 → 0.83.0

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 (125) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/locales/en_US/error.json +2 -1
  3. package/locales/en_US/plugin.json +1 -0
  4. package/locales/en_US/setting.json +2 -1
  5. package/locales/ru_RU/error.json +2 -1
  6. package/locales/ru_RU/plugin.json +1 -0
  7. package/locales/ru_RU/setting.json +2 -1
  8. package/locales/zh_CN/error.json +1 -0
  9. package/locales/zh_CN/plugin.json +2 -1
  10. package/locales/zh_CN/setting.json +2 -1
  11. package/locales/zh_TW/error.json +2 -1
  12. package/locales/zh_TW/plugin.json +1 -0
  13. package/locales/zh_TW/setting.json +2 -1
  14. package/package.json +6 -5
  15. package/src/app/chat/{features/Header → (desktop)/features/ChatHeader}/Desktop.tsx +2 -2
  16. package/src/app/chat/{features/Header → (desktop)/features/ChatHeader}/index.tsx +1 -1
  17. package/src/app/chat/(desktop)/features/ChatInput/index.tsx +22 -0
  18. package/src/app/chat/{features/SessionList/Header → (desktop)/features/SessionHeader}/Desktop.tsx +1 -1
  19. package/src/app/chat/{features/SessionList/Header → (desktop)/features/SessionHeader}/index.tsx +1 -1
  20. package/src/app/chat/{features → (desktop)/features}/SessionList/Desktop.tsx +1 -1
  21. package/src/app/chat/(desktop)/features/SessionList/index.tsx +20 -0
  22. package/src/app/chat/{features/Sidebar → (desktop)/features/SideBar}/Desktop.tsx +4 -4
  23. package/src/app/chat/(desktop)/features/SideBar/index.tsx +20 -0
  24. package/src/app/chat/(desktop)/index.tsx +24 -0
  25. package/src/app/chat/{layout.desktop.tsx → (desktop)/layout/Desktop.tsx} +4 -4
  26. package/src/app/chat/(desktop)/layout/index.tsx +25 -0
  27. package/src/app/chat/(mobile)/features/ChatHeader.tsx +1 -0
  28. package/src/app/chat/(mobile)/features/ChatInput.tsx +14 -0
  29. package/src/app/chat/(mobile)/features/SessionList.tsx +12 -0
  30. package/src/app/chat/(mobile)/features/Topics.tsx +12 -0
  31. package/src/app/chat/(mobile)/index.tsx +20 -0
  32. package/src/app/chat/(mobile)/layout.tsx +15 -0
  33. package/src/app/chat/(mobile)/mobile/page.tsx +21 -0
  34. package/src/app/chat/{features/Header → components/ChatHeader}/Mobile.tsx +2 -1
  35. package/src/app/chat/{features/Header → components/ChatHeader}/ShareButton/Preview.tsx +3 -3
  36. package/src/app/chat/{features/Conversation/Input → components/ChatInput}/index.tsx +23 -21
  37. package/src/app/chat/components/Inspector/index.tsx +17 -0
  38. package/src/app/chat/{features → components}/SessionList/List/Item/index.tsx +2 -2
  39. package/src/app/chat/{features → components}/SessionList/Mobile.tsx +1 -1
  40. package/src/app/chat/{features → components}/SessionList/index.tsx +3 -10
  41. package/src/app/chat/features/Conversation/index.tsx +8 -4
  42. package/src/app/chat/features/PageTitle.tsx +19 -0
  43. package/src/app/chat/layout.tsx +14 -0
  44. package/src/app/chat/page.tsx +9 -4
  45. package/src/app/chat/settings/(desktop)/Header/index.tsx +13 -0
  46. package/src/app/chat/settings/{layout.desktop.tsx → (desktop)/layout.desktop.tsx} +5 -4
  47. package/src/app/chat/settings/(mobile)/Header.tsx +12 -0
  48. package/src/app/chat/settings/{layout.mobile.tsx → (mobile)/layout.mobile.tsx} +3 -1
  49. package/src/app/chat/settings/{features → components}/Header/index.tsx +4 -13
  50. package/src/app/chat/settings/layout.tsx +14 -0
  51. package/src/app/chat/settings/page.tsx +36 -5
  52. package/src/app/home/index.tsx +15 -11
  53. package/src/app/layout.tsx +4 -1
  54. package/src/app/metadata.ts +4 -1
  55. package/src/app/settings/agent/Agent.tsx +3 -3
  56. package/src/app/settings/agent/index.tsx +1 -0
  57. package/src/app/settings/common/Common.tsx +109 -117
  58. package/src/app/settings/features/SideBar/List.tsx +11 -5
  59. package/src/app/settings/llm/LLM/index.tsx +2 -2
  60. package/src/app/welcome/page.tsx +2 -6
  61. package/src/components/PageTitle.tsx +11 -0
  62. package/src/config/client.ts +13 -0
  63. package/src/const/locale.ts +9 -0
  64. package/src/const/settings.ts +1 -1
  65. package/src/const/url.ts +1 -6
  66. package/src/helpers/export.ts +2 -2
  67. package/src/layout/AppMobileLayout.tsx +15 -24
  68. package/src/layout/GlobalLayout/Locale.tsx +44 -0
  69. package/src/layout/GlobalLayout/LocaleLoading.tsx +21 -0
  70. package/src/layout/GlobalLayout/StoreHydration.tsx +28 -0
  71. package/src/layout/GlobalLayout/index.tsx +29 -41
  72. package/src/locales/create.ts +43 -26
  73. package/src/locales/default/setting.ts +2 -1
  74. package/src/locales/options.ts +6 -5
  75. package/src/services/agentMarket.ts +6 -4
  76. package/src/services/pluginMarket.ts +2 -2
  77. package/src/store/global/hooks/useSwitchSettingsOnInit.ts +5 -2
  78. package/src/store/global/{selectors.test.ts → selectors/settings.test.ts} +11 -11
  79. package/src/store/global/selectors/settings.ts +44 -0
  80. package/src/store/global/selectors.ts +1 -36
  81. package/src/store/global/slices/common.ts +3 -4
  82. package/src/types/locale.ts +4 -0
  83. package/src/types/settings.ts +2 -2
  84. package/src/utils/env.ts +2 -0
  85. package/src/utils/responsive.ts +1 -0
  86. package/src/utils/switchLang.ts +5 -1
  87. package/src/app/chat/features/Sidebar/index.tsx +0 -29
  88. package/src/app/chat/index.tsx +0 -54
  89. package/src/app/chat/layout.mobile.tsx +0 -20
  90. package/src/app/chat/mobile/index.tsx +0 -38
  91. package/src/app/chat/mobile/page.tsx +0 -7
  92. package/src/app/chat/settings/index.tsx +0 -51
  93. package/src/layout/GlobalLayout/style.ts +0 -18
  94. /package/src/app/chat/{features/Conversation/Input → (desktop)/features/ChatInput}/Desktop.tsx +0 -0
  95. /package/src/app/chat/{features/SessionList/Header/Mobile.tsx → (mobile)/features/SessionHeader.tsx} +0 -0
  96. /package/src/app/chat/{features/Header → components/ChatHeader}/PluginTag/PluginStatus.tsx +0 -0
  97. /package/src/app/chat/{features/Header → components/ChatHeader}/PluginTag/index.tsx +0 -0
  98. /package/src/app/chat/{features/Header → components/ChatHeader}/ShareButton/Inner.tsx +0 -0
  99. /package/src/app/chat/{features/Header → components/ChatHeader}/ShareButton/index.tsx +0 -0
  100. /package/src/app/chat/{features/Header → components/ChatHeader}/ShareButton/style.ts +0 -0
  101. /package/src/app/chat/{features/Conversation/Input → components/ChatInput}/ActionBar/ActionLeft.tsx +0 -0
  102. /package/src/app/chat/{features/Conversation/Input → components/ChatInput}/ActionBar/ActionRight.tsx +0 -0
  103. /package/src/app/chat/{features/Conversation/Input → components/ChatInput}/ActionBar/Token.tsx +0 -0
  104. /package/src/app/chat/{features/Conversation/Input → components/ChatInput}/Footer/SaveTopic.tsx +0 -0
  105. /package/src/app/chat/{features/Conversation/Input → components/ChatInput}/Footer/index.tsx +0 -0
  106. /package/src/app/chat/{features/Conversation/Input → components/ChatInput}/Mobile.tsx +0 -0
  107. /package/src/app/chat/{features/Sidebar → components/Inspector}/Mobile.tsx +0 -0
  108. /package/src/app/chat/{features/Sidebar → components/Inspector}/SystemRole/Header.tsx +0 -0
  109. /package/src/app/chat/{features/Sidebar → components/Inspector}/SystemRole/index.tsx +0 -0
  110. /package/src/app/chat/{features/Sidebar → components/Inspector}/SystemRole/style.ts +0 -0
  111. /package/src/app/chat/{features/Sidebar → components/Inspector}/Topic/DefaultContent.tsx +0 -0
  112. /package/src/app/chat/{features/Sidebar → components/Inspector}/Topic/SkeletonList.tsx +0 -0
  113. /package/src/app/chat/{features/Sidebar → components/Inspector}/Topic/TopicContent.tsx +0 -0
  114. /package/src/app/chat/{features/Sidebar → components/Inspector}/Topic/TopicItem.tsx +0 -0
  115. /package/src/app/chat/{features/Sidebar → components/Inspector}/Topic/index.tsx +0 -0
  116. /package/src/app/chat/{features/Sidebar → components/Inspector}/TopicSearchBar/index.tsx +0 -0
  117. /package/src/app/chat/{features → components}/SessionList/CollapseGroup/index.tsx +0 -0
  118. /package/src/app/chat/{features → components}/SessionList/Inbox/index.tsx +0 -0
  119. /package/src/app/chat/{features → components}/SessionList/List/AddButton.tsx +0 -0
  120. /package/src/app/chat/{features → components}/SessionList/List/Item/Actions.tsx +0 -0
  121. /package/src/app/chat/{features → components}/SessionList/List/SkeletonList.tsx +0 -0
  122. /package/src/app/chat/{features → components}/SessionList/List/index.tsx +0 -0
  123. /package/src/app/chat/settings/{features → (desktop)}/Header/Desktop.tsx +0 -0
  124. /package/src/app/chat/settings/{features → components}/Header/Mobile.tsx +0 -0
  125. /package/src/{components/AppTheme/index.tsx → layout/GlobalLayout/AppTheme.tsx} +0 -0
@@ -1,56 +1,44 @@
1
1
  'use client';
2
2
 
3
- import { App, ConfigProvider } from 'antd';
3
+ import { App } from 'antd';
4
+ import { createStyles } from 'antd-style';
4
5
  import 'antd/dist/reset.css';
5
- import Zh_CN from 'antd/locale/zh_CN';
6
- import { useRouter } from 'next/navigation';
7
- import { PropsWithChildren, memo, useEffect } from 'react';
6
+ import { PropsWithChildren, memo } from 'react';
8
7
 
9
- import AppTheme, { AppThemeProps } from '@/components/AppTheme';
10
- import { createI18nNext } from '@/locales/create';
11
- import { useGlobalStore, useOnFinishHydrationGlobal } from '@/store/global';
12
- import { usePluginStore } from '@/store/plugin';
13
- import { useOnFinishHydrationSession, useSessionStore } from '@/store/session';
14
- import { switchLang } from '@/utils/switchLang';
8
+ import AppTheme, { AppThemeProps } from './AppTheme';
9
+ import Locale from './Locale';
10
+ import StoreHydration from './StoreHydration';
15
11
 
16
- import { useStyles } from './style';
12
+ const useStyles = createStyles(({ css, token }) => ({
13
+ bg: css`
14
+ overflow-y: scroll;
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: center;
17
18
 
18
- const i18n = createI18nNext();
19
+ height: 100%;
19
20
 
20
- const Layout = memo<PropsWithChildren>(({ children }) => {
21
- const { styles } = useStyles();
22
-
23
- const router = useRouter();
24
-
25
- useEffect(() => {
26
- // refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated
27
- useSessionStore.persist.rehydrate();
28
- useGlobalStore.persist.rehydrate();
29
- usePluginStore.persist.rehydrate();
30
- }, []);
31
-
32
- useOnFinishHydrationGlobal((state) => {
33
- i18n.then(() => switchLang(state.settings.language));
34
- });
21
+ background: ${token.colorBgLayout};
22
+ `,
23
+ }));
35
24
 
36
- useOnFinishHydrationSession((s, store) => {
37
- usePluginStore.getState().checkLocalEnabledPlugins(s.sessions);
38
-
39
- // add router instance to store
40
- store.setState({ router });
41
- });
25
+ const Container = memo<PropsWithChildren>(({ children }) => {
26
+ const { styles } = useStyles();
42
27
 
43
- return (
44
- <ConfigProvider locale={Zh_CN}>
45
- <App className={styles.bg}>{children}</App>
46
- </ConfigProvider>
47
- );
28
+ return <App className={styles.bg}>{children}</App>;
48
29
  });
49
30
 
50
- const ThemeWrapper = ({ children, ...theme }: AppThemeProps) => (
31
+ interface GlobalLayoutProps extends AppThemeProps {
32
+ defaultLang?: string;
33
+ }
34
+
35
+ const GlobalLayout = ({ children, defaultLang, ...theme }: GlobalLayoutProps) => (
51
36
  <AppTheme {...theme}>
52
- <Layout>{children}</Layout>
37
+ <Locale lang={defaultLang}>
38
+ <StoreHydration />
39
+ <Container>{children}</Container>
40
+ </Locale>
53
41
  </AppTheme>
54
42
  );
55
43
 
56
- export default ThemeWrapper;
44
+ export default GlobalLayout;
@@ -1,36 +1,53 @@
1
1
  import i18n from 'i18next';
2
2
  import LanguageDetector from 'i18next-browser-languagedetector';
3
- import { isArray } from 'lodash-es';
4
3
  import { initReactI18next } from 'react-i18next';
5
4
 
5
+ import { getClientConfig } from '@/config/client';
6
+ import { DEFAULT_LANG, LOBE_LOCALE_COOKIE } from '@/const/locale';
7
+ import resources from '@/locales/resources';
6
8
  import type { Namespaces } from '@/types/locale';
9
+ import { isOnServerSide } from '@/utils/env';
7
10
 
8
- import resources from './resources';
11
+ const { I18N_DEBUG, I18N_DEBUG_BROWSER, I18N_DEBUG_SERVER } = getClientConfig();
12
+ const debugMode = I18N_DEBUG ?? isOnServerSide ? I18N_DEBUG_SERVER : I18N_DEBUG_BROWSER;
9
13
 
10
- const isDev = process.env.NODE_ENV === 'development';
14
+ export const createI18nNext = (lang?: string) => {
15
+ const ns: Namespaces[] = [
16
+ 'error',
17
+ 'common',
18
+ 'empty',
19
+ 'error',
20
+ 'market',
21
+ 'plugin',
22
+ 'setting',
23
+ 'welcome',
24
+ ];
11
25
 
12
- export const createI18nNext = (params: { namespace?: Namespaces[] | Namespaces } = {}) => {
13
- const { namespace } = params;
26
+ const instance = i18n.use(initReactI18next).use(LanguageDetector);
27
+ // not find a good way to handle suspense loading
28
+ // .use(
29
+ // resourcesToBackend(
30
+ // isDev ? resources : (lng: string, ns: string) => import(`../../locales/${lng}/${ns}.json`),
31
+ // ),
32
+ // )
14
33
 
15
- const ns: Namespaces[] = isArray(namespace)
16
- ? ['error', 'common', ...namespace]
17
- : (['error', 'common', namespace].filter(Boolean) as Namespaces[]);
18
-
19
- return i18n
20
- .use(LanguageDetector)
21
- .use(initReactI18next)
22
- .init({
23
- debug: isDev,
24
- defaultNS: ns,
25
- detection: {
26
- caches: [],
27
- },
28
- fallbackLng: isDev ? 'zh-CN' : 'en-US',
29
- interpolation: {
30
- escapeValue: false,
31
- },
32
- lng: isDev ? 'zh-CN' : 'en-US',
33
- ns,
34
- resources,
35
- });
34
+ return {
35
+ init: () =>
36
+ instance.init({
37
+ debug: debugMode,
38
+ defaultNS: ['error', 'common'],
39
+ detection: {
40
+ caches: ['cookie'],
41
+ lookupCookie: LOBE_LOCALE_COOKIE,
42
+ },
43
+ fallbackLng: DEFAULT_LANG,
44
+ interpolation: {
45
+ escapeValue: false,
46
+ },
47
+ lng: lang,
48
+ ns,
49
+ resources,
50
+ }),
51
+ instance,
52
+ };
36
53
  };
@@ -198,7 +198,8 @@ export default {
198
198
  title: '字体大小',
199
199
  },
200
200
  lang: {
201
- title: '语言设置',
201
+ autoMode: '跟随系统',
202
+ title: '语言',
202
203
  },
203
204
  neutralColor: {
204
205
  desc: '不同色彩倾向的灰阶自定义',
@@ -1,11 +1,10 @@
1
- import type { SelectProps } from 'antd';
2
1
 
3
2
  import type { Locales } from '@/types/locale';
4
3
 
5
- type LocaleOptions = SelectProps['options'] &
6
- {
7
- value: Locales;
8
- }[];
4
+ type LocaleOptions = {
5
+ label: string;
6
+ value: Locales;
7
+ }[];
9
8
 
10
9
  export const localeOptions: LocaleOptions = [
11
10
  {
@@ -25,3 +24,5 @@ export const localeOptions: LocaleOptions = [
25
24
  value: 'ru-RU',
26
25
  },
27
26
  ] as LocaleOptions;
27
+
28
+ export const supportLangs: string[] = localeOptions.map((i) => i.value);
@@ -1,11 +1,11 @@
1
- import { getAgentIndexJSON, getAgentJSON } from '@/const/url';
2
- import { useGlobalStore } from '@/store/global';
1
+ import { getAgentJSON } from '@/const/url';
2
+ import { settingsSelectors, useGlobalStore } from '@/store/global';
3
3
 
4
4
  /**
5
5
  * 请求助手列表
6
6
  */
7
7
  export const getAgentList = async () => {
8
- const res = await fetch(getAgentIndexJSON(useGlobalStore.getState().settings.language));
8
+ const res = await fetch(settingsSelectors.currentLanguage(useGlobalStore.getState()));
9
9
 
10
10
  return res.json();
11
11
  };
@@ -15,7 +15,9 @@ export const getAgentList = async () => {
15
15
  */
16
16
  export const getAgentManifest = async (identifier?: string) => {
17
17
  if (!identifier) return;
18
- const res = await fetch(getAgentJSON(identifier, useGlobalStore.getState().settings.language));
18
+ const res = await fetch(
19
+ getAgentJSON(identifier, settingsSelectors.currentLanguage(useGlobalStore.getState())),
20
+ );
19
21
 
20
22
  return res.json();
21
23
  };
@@ -1,11 +1,11 @@
1
1
  import { getPluginIndexJSON } from '@/const/url';
2
- import { useGlobalStore } from '@/store/global';
2
+ import { settingsSelectors, useGlobalStore } from '@/store/global';
3
3
 
4
4
  /**
5
5
  * fetch Plugin Market List
6
6
  */
7
7
  export const getPluginList = async () => {
8
- const url = getPluginIndexJSON(useGlobalStore.getState().settings.language);
8
+ const url = getPluginIndexJSON(settingsSelectors.currentLanguage(useGlobalStore.getState()));
9
9
 
10
10
  const res = await fetch(url);
11
11
 
@@ -1,9 +1,12 @@
1
+ import { useOnFinishHydrationGlobal } from '@/store/global';
2
+
1
3
  import { SettingsTabs } from '../initialState';
2
- import { useGlobalStore } from '../store';
3
4
 
4
5
  /**
5
6
  * 切换设置侧边栏选项
6
7
  */
7
8
  export const useSwitchSideBarOnInit = (key: SettingsTabs) => {
8
- useGlobalStore.getState().switchSettingTabs(key);
9
+ useOnFinishHydrationGlobal((store) => {
10
+ store.switchSettingTabs(key);
11
+ });
9
12
  };
@@ -1,9 +1,9 @@
1
1
  import { LanguageModel } from '@/types/llm';
2
2
 
3
- import { globalSelectors } from './selectors';
4
- import { GlobalStore } from './store';
3
+ import { GlobalStore } from '../store';
4
+ import { settingsSelectors } from './settings';
5
5
 
6
- describe('globalSelectors', () => {
6
+ describe('settingsSelectors', () => {
7
7
  describe('currentSettings', () => {
8
8
  it('should merge DEFAULT_SETTINGS and s.settings correctly', () => {
9
9
  const s = {
@@ -36,7 +36,7 @@ describe('globalSelectors', () => {
36
36
  },
37
37
  } as unknown as GlobalStore;
38
38
 
39
- const result = globalSelectors.currentSettings(s);
39
+ const result = settingsSelectors.currentSettings(s);
40
40
 
41
41
  expect(result).toEqual({
42
42
  avatar: 'avatar.jpg',
@@ -93,7 +93,7 @@ describe('globalSelectors', () => {
93
93
  // },
94
94
  // };
95
95
  //
96
- // const result = globalSelectors.defaultAgent(s);
96
+ // const result = settingsSelectors.defaultAgent(s);
97
97
  //
98
98
  //
99
99
  //
@@ -114,9 +114,9 @@ describe('globalSelectors', () => {
114
114
  // },
115
115
  // };
116
116
  //
117
- // const result = globalSelectors.defaultAgentConfig(s);
117
+ // const result = settingsSelectors.defaultAgentConfig(s);
118
118
  //
119
- // const defaultAgent = globalSelectors.defaultAgent(s);
119
+ // const defaultAgent = settingsSelectors.defaultAgent(s);
120
120
  // const expected = merge({}, DEFAULT_AGENT_CONFIG, defaultAgent.config);
121
121
  //
122
122
  // expect(result).toEqual(expected);
@@ -136,9 +136,9 @@ describe('globalSelectors', () => {
136
136
  // },
137
137
  // };
138
138
  //
139
- // const result = globalSelectors.defaultAgentMeta(s);
139
+ // const result = settingsSelectors.defaultAgentMeta(s);
140
140
  //
141
- // const defaultAgent = globalSelectors.defaultAgent(s);
141
+ // const defaultAgent = settingsSelectors.defaultAgent(s);
142
142
  // const expected = merge({}, DEFAULT_AGENT_META, defaultAgent.meta);
143
143
  //
144
144
  // expect(result).toEqual(expected);
@@ -183,7 +183,7 @@ describe('globalSelectors', () => {
183
183
  // },
184
184
  // };
185
185
  //
186
- // const result = globalSelectors.exportSettings(s);
186
+ // const result = settingsSelectors.exportSettings(s);
187
187
  //
188
188
  // const expected = {
189
189
  // avatar: 'avatar.jpg',
@@ -255,7 +255,7 @@ describe('globalSelectors', () => {
255
255
  // },
256
256
  // };
257
257
  //
258
- // const result = globalSelectors.exportSettings(s);
258
+ // const result = settingsSelectors.exportSettings(s);
259
259
  //
260
260
  // expect(result).toBeInstanceOf(GlobalSettings);
261
261
  // });
@@ -0,0 +1,44 @@
1
+ import { DEFAULT_AGENT_META } from '@/const/meta';
2
+ import { DEFAULT_AGENT, DEFAULT_AGENT_CONFIG, DEFAULT_SETTINGS } from '@/const/settings';
3
+ import { Locales } from '@/locales/resources';
4
+ import { GlobalSettings } from '@/types/settings';
5
+ import { merge } from '@/utils/merge';
6
+
7
+ import { GlobalStore } from '../store';
8
+
9
+ const currentSettings = (s: GlobalStore) => merge(DEFAULT_SETTINGS, s.settings);
10
+
11
+ const defaultAgent = (s: GlobalStore) => merge(DEFAULT_AGENT, s.settings.defaultAgent);
12
+
13
+ const defaultAgentConfig = (s: GlobalStore) => merge(DEFAULT_AGENT_CONFIG, defaultAgent(s).config);
14
+
15
+ const defaultAgentMeta = (s: GlobalStore) => merge(DEFAULT_AGENT_META, defaultAgent(s).meta);
16
+
17
+ const openAIAPIKeySelectors = (s: GlobalStore) =>
18
+ s.settings.languageModel.openAI.OPENAI_API_KEY || s.settings.OPENAI_API_KEY;
19
+
20
+ export const exportSettings = (s: GlobalStore) => {
21
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
22
+ const { OPENAI_API_KEY: _, password: __, ...settings } = s.settings;
23
+
24
+ return settings as GlobalSettings;
25
+ };
26
+
27
+ const currentLanguage = (s: GlobalStore) => {
28
+ const locale = s.settings.language;
29
+ if (locale === 'auto') {
30
+ return navigator.language as Locales;
31
+ }
32
+
33
+ return locale;
34
+ };
35
+
36
+ export const settingsSelectors = {
37
+ currentLanguage,
38
+ currentSettings,
39
+ defaultAgent,
40
+ defaultAgentConfig,
41
+ defaultAgentMeta,
42
+ exportSettings,
43
+ openAIAPI: openAIAPIKeySelectors,
44
+ };
@@ -1,36 +1 @@
1
- import { DEFAULT_AGENT_META } from '@/const/meta';
2
- import { DEFAULT_AGENT, DEFAULT_AGENT_CONFIG, DEFAULT_SETTINGS } from '@/const/settings';
3
- import { GlobalSettings } from '@/types/settings';
4
- import { merge } from '@/utils/merge';
5
-
6
- import { GlobalStore } from './store';
7
-
8
- const currentSettings = (s: GlobalStore) => merge(DEFAULT_SETTINGS, s.settings);
9
-
10
- const defaultAgent = (s: GlobalStore) => merge(DEFAULT_AGENT, s.settings.defaultAgent);
11
-
12
- const defaultAgentConfig = (s: GlobalStore) => merge(DEFAULT_AGENT_CONFIG, defaultAgent(s).config);
13
-
14
- const defaultAgentMeta = (s: GlobalStore) => merge(DEFAULT_AGENT_META, defaultAgent(s).meta);
15
-
16
- const openAIAPIKeySelectors = (s: GlobalStore) =>
17
- s.settings.languageModel.openAI.OPENAI_API_KEY || s.settings.OPENAI_API_KEY;
18
-
19
- export const exportSettings = (s: GlobalStore) => {
20
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
21
- const { OPENAI_API_KEY: _, password: __, ...settings } = s.settings;
22
-
23
- return settings as GlobalSettings;
24
- };
25
-
26
- export const globalSelectors = {
27
- currentSettings,
28
- defaultAgent,
29
- defaultAgentConfig,
30
- defaultAgentMeta,
31
- exportSettings,
32
- };
33
-
34
- export const settingsSelectors = {
35
- openAIAPI: openAIAPIKeySelectors,
36
- };
1
+ export * from './selectors/settings';
@@ -1,7 +1,7 @@
1
1
  import { produce } from 'immer';
2
- import { merge } from 'lodash-es';
3
2
  import type { StateCreator } from 'zustand/vanilla';
4
3
 
4
+ import { merge } from '@/utils/merge';
5
5
  import { setNamespace } from '@/utils/storeDebug';
6
6
 
7
7
  import type { GlobalPreference, GlobalState, Guide, SidebarTabKey } from '../initialState';
@@ -47,14 +47,13 @@ export const createCommonSlice: StateCreator<
47
47
  },
48
48
  updateGuideState: (guide) => {
49
49
  const { updatePreference } = get();
50
- const nextGuide = merge({}, get().preference.guide, guide);
50
+ const nextGuide = merge(get().preference.guide, guide);
51
51
  updatePreference({ guide: nextGuide });
52
52
  },
53
-
54
53
  updatePreference: (preference, action) => {
55
54
  set(
56
55
  produce((draft: GlobalState) => {
57
- draft.preference = merge({}, draft.preference, preference);
56
+ draft.preference = merge(draft.preference, preference);
58
57
  }),
59
58
  false,
60
59
  action,
@@ -1 +1,5 @@
1
+ import { Locales } from '@/locales/resources';
2
+
1
3
  export type * from '@/locales/resources';
4
+
5
+ export type LocaleMode = Locales | 'auto';
@@ -1,7 +1,7 @@
1
1
  import type { NeutralColors, PrimaryColors } from '@lobehub/ui';
2
2
  import type { ThemeMode } from 'antd-style';
3
3
 
4
- import type { Locales } from '@/locales/resources';
4
+ import { LocaleMode } from '@/types/locale';
5
5
  import type { LobeAgentSession } from '@/types/session';
6
6
 
7
7
  export interface GlobalBaseSettings {
@@ -36,7 +36,7 @@ export interface GlobalBaseSettings {
36
36
  * @deprecated
37
37
  */
38
38
  historyCount?: number;
39
- language: Locales;
39
+ language: LocaleMode;
40
40
  neutralColor?: NeutralColors;
41
41
  password: string;
42
42
  primaryColor?: PrimaryColors;
package/src/utils/env.ts CHANGED
@@ -1 +1,3 @@
1
1
  export const isDev = process.env.NODE_ENV === 'development';
2
+
3
+ export const isOnServerSide = typeof window === 'undefined';
@@ -12,6 +12,7 @@ export const isMobileDevice = () => {
12
12
  const { get } = headers();
13
13
  const ua = get('user-agent');
14
14
 
15
+ // console.log(ua);
15
16
  const device = new UAParser(ua || '').getDevice();
16
17
 
17
18
  return device.type === 'mobile';
@@ -1,6 +1,10 @@
1
1
  import { changeLanguage } from 'i18next';
2
2
 
3
- export const switchLang = (lang: string) => {
3
+ import { LocaleMode } from '@/types/locale';
4
+
5
+ export const switchLang = (locale: LocaleMode) => {
6
+ const lang = locale === 'auto' ? navigator.language : locale;
7
+
4
8
  changeLanguage(lang);
5
9
  document.documentElement.lang = lang;
6
10
  };
@@ -1,29 +0,0 @@
1
- import { useResponsive } from 'antd-style';
2
- import { memo } from 'react';
3
- import { Flexbox } from 'react-layout-kit';
4
-
5
- import Desktop from './Desktop';
6
- import Mobile from './Mobile';
7
- import { Topic } from './Topic';
8
- import TopicSearchBar from './TopicSearchBar';
9
-
10
- const SideBar = memo(() => {
11
- const { mobile } = useResponsive();
12
-
13
- const Render = mobile ? Mobile : Desktop;
14
-
15
- return (
16
- <Render>
17
- <Flexbox height={'100%'} style={{ overflow: 'hidden' }}>
18
- <Flexbox padding={16}>
19
- <TopicSearchBar />
20
- </Flexbox>
21
- <Flexbox gap={16} paddingInline={16} style={{ overflowY: 'auto', position: 'relative' }}>
22
- <Topic />
23
- </Flexbox>
24
- </Flexbox>
25
- </Render>
26
- );
27
- });
28
-
29
- export default SideBar;
@@ -1,54 +0,0 @@
1
- 'use client';
2
-
3
- import { useResponsive } from 'antd-style';
4
- import Head from 'next/head';
5
- import { memo } from 'react';
6
- import { Flexbox } from 'react-layout-kit';
7
-
8
- import { useSessionStore } from '@/store/session';
9
- import { useEffectAfterSessionHydrated } from '@/store/session/hooks';
10
- import { agentSelectors } from '@/store/session/selectors';
11
- import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
12
-
13
- import Conversation from './features/Conversation';
14
- import Header from './features/Header';
15
- import SideBar from './features/Sidebar';
16
- import DesktopLayout from './layout.desktop';
17
- import MobileLayout from './layout.mobile';
18
-
19
- const Chat = memo(() => {
20
- const { mobile } = useResponsive();
21
- const [avatar, title] = useSessionStore((s) => [
22
- agentSelectors.currentAgentAvatar(s),
23
- agentSelectors.currentAgentTitle(s),
24
- ]);
25
- const pageTitle = genSiteHeadTitle([avatar, title].filter(Boolean).join(' '));
26
- const RenderLayout = mobile ? MobileLayout : DesktopLayout;
27
-
28
- useEffectAfterSessionHydrated(
29
- (store) => {
30
- store.setState({ isMobile: mobile });
31
- },
32
- [mobile],
33
- );
34
-
35
- return (
36
- <>
37
- <Head>
38
- <title>{pageTitle}</title>
39
- </Head>
40
- <RenderLayout>
41
- {!mobile && (
42
- <>
43
- <Header />
44
- <Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
45
- <Conversation />
46
- <SideBar />
47
- </Flexbox>
48
- </>
49
- )}
50
- </RenderLayout>
51
- </>
52
- );
53
- });
54
- export default Chat;
@@ -1,20 +0,0 @@
1
- import { memo } from 'react';
2
-
3
- import AppMobileLayout from '@/layout/AppMobileLayout';
4
- import { useSwitchSideBarOnInit } from '@/store/global';
5
- import { SidebarTabKey } from '@/store/global/initialState';
6
-
7
- import { Sessions } from './features/SessionList';
8
- import Header from './features/SessionList/Header';
9
-
10
- const ChatMobileLayout = memo(() => {
11
- useSwitchSideBarOnInit(SidebarTabKey.Chat);
12
-
13
- return (
14
- <AppMobileLayout navBar={<Header />} showTabBar>
15
- <Sessions />
16
- </AppMobileLayout>
17
- );
18
- });
19
-
20
- export default ChatMobileLayout;
@@ -1,38 +0,0 @@
1
- 'use client';
2
-
3
- import Head from 'next/head';
4
- import { memo } from 'react';
5
- import { Flexbox } from 'react-layout-kit';
6
-
7
- import AppMobileLayout from '@/layout/AppMobileLayout';
8
- import { useSessionStore } from '@/store/session';
9
- import { agentSelectors } from '@/store/session/selectors';
10
- import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
11
-
12
- import Conversation from '../features/Conversation';
13
- import Header from '../features/Header';
14
- import SideBar from '../features/Sidebar';
15
-
16
- const Chat = memo(() => {
17
- const [avatar, title] = useSessionStore((s) => [
18
- agentSelectors.currentAgentAvatar(s),
19
- agentSelectors.currentAgentTitle(s),
20
- ]);
21
-
22
- const pageTitle = genSiteHeadTitle([avatar, title].filter(Boolean).join(' '));
23
-
24
- return (
25
- <>
26
- <Head>
27
- <title>{pageTitle}</title>
28
- </Head>
29
- <AppMobileLayout navBar={<Header />}>
30
- <Flexbox height={'calc(100vh - 44px)'} horizontal>
31
- <Conversation mobile />
32
- <SideBar />
33
- </Flexbox>
34
- </AppMobileLayout>
35
- </>
36
- );
37
- });
38
- export default Chat;
@@ -1,7 +0,0 @@
1
- import Page from './index';
2
-
3
- const Index = () => {
4
- return <Page />;
5
- };
6
-
7
- export default Index;