@lobehub/chat 0.142.0 → 0.142.1
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/CHANGELOG.md +17 -0
- package/package.json +1 -1
- package/src/app/layout.tsx +13 -25
- package/src/app/settings/common/Theme.tsx +1 -1
- package/src/features/DebugUI/index.tsx +2 -0
- package/src/layout/{GlobalLayout → GlobalProvider}/AppTheme.tsx +28 -3
- package/src/layout/{GlobalLayout → GlobalProvider}/Locale.tsx +16 -26
- package/src/layout/{GlobalLayout → GlobalProvider}/StoreHydration.tsx +2 -0
- package/src/layout/GlobalProvider/index.tsx +62 -0
- package/src/store/global/slices/common/action.test.ts +2 -2
- package/src/store/global/slices/common/action.ts +1 -1
- package/src/utils/locale.ts +16 -0
- package/src/layout/GlobalLayout/index.tsx +0 -75
- /package/src/{app → layout/GlobalProvider}/StyleRegistry.tsx +0 -0
- /package/src/utils/{switchLang.ts → client/switchLang.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.142.1](https://github.com/lobehub/lobe-chat/compare/v0.142.0...v0.142.1)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-03-25**</sup>
|
|
8
|
+
|
|
9
|
+
<br/>
|
|
10
|
+
|
|
11
|
+
<details>
|
|
12
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
13
|
+
|
|
14
|
+
</details>
|
|
15
|
+
|
|
16
|
+
<div align="right">
|
|
17
|
+
|
|
18
|
+
[](#readme-top)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
5
22
|
## [Version 0.142.0](https://github.com/lobehub/lobe-chat/compare/v0.141.2...v0.142.0)
|
|
6
23
|
|
|
7
24
|
<sup>Released on **2024-03-25**</sup>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.142.
|
|
3
|
+
"version": "0.142.1",
|
|
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",
|
package/src/app/layout.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SpeedInsights } from '@vercel/speed-insights/next';
|
|
2
2
|
import { ResolvingViewport } from 'next';
|
|
3
|
+
import { SessionProvider } from 'next-auth/react';
|
|
3
4
|
import { cookies } from 'next/headers';
|
|
4
5
|
import { PropsWithChildren } from 'react';
|
|
5
6
|
import { isRtlLang } from 'rtl-detect';
|
|
@@ -7,41 +8,28 @@ import { isRtlLang } from 'rtl-detect';
|
|
|
7
8
|
import Analytics from '@/components/Analytics';
|
|
8
9
|
import { getServerConfig } from '@/config/server';
|
|
9
10
|
import { DEFAULT_LANG, LOBE_LOCALE_COOKIE } from '@/const/locale';
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
LOBE_THEME_NEUTRAL_COLOR,
|
|
13
|
-
LOBE_THEME_PRIMARY_COLOR,
|
|
14
|
-
} from '@/const/theme';
|
|
15
|
-
import Layout from '@/layout/GlobalLayout';
|
|
11
|
+
import GlobalProvider from '@/layout/GlobalProvider';
|
|
12
|
+
import { API_ENDPOINTS } from '@/services/_url';
|
|
16
13
|
import { isMobileDevice } from '@/utils/responsive';
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
const { ENABLE_OAUTH_SSO = false } = getServerConfig();
|
|
19
16
|
|
|
20
|
-
const {
|
|
21
|
-
|
|
22
|
-
const RootLayout = ({ children }: PropsWithChildren) => {
|
|
23
|
-
// get default theme config to use with ssr
|
|
17
|
+
const RootLayout = async ({ children }: PropsWithChildren) => {
|
|
24
18
|
const cookieStore = cookies();
|
|
25
|
-
|
|
26
|
-
const neutralColor = cookieStore.get(LOBE_THEME_NEUTRAL_COLOR);
|
|
27
|
-
const primaryColor = cookieStore.get(LOBE_THEME_PRIMARY_COLOR);
|
|
19
|
+
|
|
28
20
|
const lang = cookieStore.get(LOBE_LOCALE_COOKIE);
|
|
29
21
|
const direction = isRtlLang(lang?.value || DEFAULT_LANG) ? 'rtl' : 'ltr';
|
|
30
22
|
|
|
23
|
+
const content = ENABLE_OAUTH_SSO ? (
|
|
24
|
+
<SessionProvider basePath={API_ENDPOINTS.oauth}>{children}</SessionProvider>
|
|
25
|
+
) : (
|
|
26
|
+
children
|
|
27
|
+
);
|
|
28
|
+
|
|
31
29
|
return (
|
|
32
30
|
<html dir={direction} lang={lang?.value || DEFAULT_LANG} suppressHydrationWarning>
|
|
33
31
|
<body>
|
|
34
|
-
<
|
|
35
|
-
<Layout
|
|
36
|
-
defaultAppearance={appearance?.value}
|
|
37
|
-
defaultLang={lang?.value}
|
|
38
|
-
defaultNeutralColor={neutralColor?.value as any}
|
|
39
|
-
defaultPrimaryColor={primaryColor?.value as any}
|
|
40
|
-
enableOAuthSSO={ENABLE_OAUTH_SSO}
|
|
41
|
-
>
|
|
42
|
-
{children}
|
|
43
|
-
</Layout>
|
|
44
|
-
</StyleRegistry>
|
|
32
|
+
<GlobalProvider>{content}</GlobalProvider>
|
|
45
33
|
<Analytics />
|
|
46
34
|
<SpeedInsights />
|
|
47
35
|
</body>
|
|
@@ -12,7 +12,7 @@ import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
|
12
12
|
import { localeOptions } from '@/locales/resources';
|
|
13
13
|
import { useGlobalStore } from '@/store/global';
|
|
14
14
|
import { settingsSelectors } from '@/store/global/selectors';
|
|
15
|
-
import { switchLang } from '@/utils/switchLang';
|
|
15
|
+
import { switchLang } from '@/utils/client/switchLang';
|
|
16
16
|
|
|
17
17
|
import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from '../features/ThemeSwatches';
|
|
18
18
|
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
1
3
|
import { ConfigProvider, NeutralColors, PrimaryColors, ThemeProvider } from '@lobehub/ui';
|
|
2
|
-
import {
|
|
4
|
+
import { App } from 'antd';
|
|
5
|
+
import { ThemeAppearance, createStyles } from 'antd-style';
|
|
6
|
+
import 'antd/dist/reset.css';
|
|
3
7
|
import Image from 'next/image';
|
|
4
|
-
import { ReactNode, memo, useEffect } from 'react';
|
|
8
|
+
import { PropsWithChildren, ReactNode, memo, useEffect } from 'react';
|
|
5
9
|
|
|
6
10
|
import {
|
|
7
11
|
LOBE_THEME_APPEARANCE,
|
|
@@ -13,6 +17,25 @@ import { settingsSelectors } from '@/store/global/selectors';
|
|
|
13
17
|
import { GlobalStyle } from '@/styles';
|
|
14
18
|
import { setCookie } from '@/utils/cookie';
|
|
15
19
|
|
|
20
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
21
|
+
bg: css`
|
|
22
|
+
overflow-y: hidden;
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
align-items: center;
|
|
26
|
+
|
|
27
|
+
height: 100%;
|
|
28
|
+
|
|
29
|
+
background: ${token.colorBgLayout};
|
|
30
|
+
`,
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const Container = memo<PropsWithChildren>(({ children }) => {
|
|
34
|
+
const { styles } = useStyles();
|
|
35
|
+
|
|
36
|
+
return <App className={styles.bg}>{children}</App>;
|
|
37
|
+
});
|
|
38
|
+
|
|
16
39
|
export interface AppThemeProps {
|
|
17
40
|
children?: ReactNode;
|
|
18
41
|
defaultAppearance?: ThemeAppearance;
|
|
@@ -53,7 +76,9 @@ const AppTheme = memo<AppThemeProps>(
|
|
|
53
76
|
themeMode={themeMode}
|
|
54
77
|
>
|
|
55
78
|
<GlobalStyle />
|
|
56
|
-
<ConfigProvider config={{ imgAs: Image } as any}>
|
|
79
|
+
<ConfigProvider config={{ imgAs: Image } as any}>
|
|
80
|
+
<Container>{children}</Container>
|
|
81
|
+
</ConfigProvider>
|
|
57
82
|
</ThemeProvider>
|
|
58
83
|
);
|
|
59
84
|
},
|
|
@@ -1,39 +1,22 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
1
3
|
import { ConfigProvider } from 'antd';
|
|
2
4
|
import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
|
3
5
|
import { isRtlLang } from 'rtl-detect';
|
|
4
|
-
import useSWR from 'swr';
|
|
5
6
|
|
|
6
7
|
import { createI18nNext } from '@/locales/create';
|
|
7
|
-
import { normalizeLocale } from '@/locales/resources';
|
|
8
8
|
import { isOnServerSide } from '@/utils/env';
|
|
9
|
-
|
|
10
|
-
const getAntdLocale = async (lang?: string) => {
|
|
11
|
-
let normalLang = normalizeLocale(lang);
|
|
12
|
-
|
|
13
|
-
// due to antd only have ar-EG locale, we need to convert ar to ar-EG
|
|
14
|
-
// refs: https://ant.design/docs/react/i18n
|
|
15
|
-
|
|
16
|
-
// And we don't want to handle it in `normalizeLocale` function
|
|
17
|
-
// because of other locale files are all `ar` not `ar-EG`
|
|
18
|
-
if (normalLang === 'ar') normalLang = 'ar-EG';
|
|
19
|
-
|
|
20
|
-
const { default: locale } = await import(`antd/locale/${normalLang.replace('-', '_')}.js`);
|
|
21
|
-
|
|
22
|
-
return locale;
|
|
23
|
-
};
|
|
9
|
+
import { getAntdLocale } from '@/utils/locale';
|
|
24
10
|
|
|
25
11
|
interface LocaleLayoutProps extends PropsWithChildren {
|
|
12
|
+
antdLocale?: any;
|
|
26
13
|
defaultLang?: string;
|
|
27
14
|
}
|
|
28
15
|
|
|
29
|
-
const Locale = memo<LocaleLayoutProps>(({ children, defaultLang }) => {
|
|
16
|
+
const Locale = memo<LocaleLayoutProps>(({ children, defaultLang, antdLocale }) => {
|
|
30
17
|
const [i18n] = useState(createI18nNext(defaultLang));
|
|
31
18
|
const [lang, setLang] = useState(defaultLang);
|
|
32
|
-
|
|
33
|
-
const { data: locale } = useSWR(['antd-locale', lang], ([, key]) => getAntdLocale(key), {
|
|
34
|
-
dedupingInterval: 0,
|
|
35
|
-
revalidateOnFocus: false,
|
|
36
|
-
});
|
|
19
|
+
const [locale, setLocale] = useState(antdLocale);
|
|
37
20
|
|
|
38
21
|
// if run on server side, init i18n instance everytime
|
|
39
22
|
if (isOnServerSide) {
|
|
@@ -49,15 +32,20 @@ const Locale = memo<LocaleLayoutProps>(({ children, defaultLang }) => {
|
|
|
49
32
|
|
|
50
33
|
// handle i18n instance language change
|
|
51
34
|
useEffect(() => {
|
|
52
|
-
const handleLang = (
|
|
53
|
-
setLang(
|
|
35
|
+
const handleLang = async (lng: string) => {
|
|
36
|
+
setLang(lng);
|
|
37
|
+
|
|
38
|
+
if (lang === lng) return;
|
|
39
|
+
|
|
40
|
+
const newLocale = await getAntdLocale(lng);
|
|
41
|
+
setLocale(newLocale);
|
|
54
42
|
};
|
|
55
43
|
|
|
56
44
|
i18n.instance.on('languageChanged', handleLang);
|
|
57
45
|
return () => {
|
|
58
46
|
i18n.instance.off('languageChanged', handleLang);
|
|
59
47
|
};
|
|
60
|
-
}, [i18n]);
|
|
48
|
+
}, [i18n, lang]);
|
|
61
49
|
|
|
62
50
|
// detect document direction
|
|
63
51
|
const documentDir = isRtlLang(lang!) ? 'rtl' : 'ltr';
|
|
@@ -69,4 +57,6 @@ const Locale = memo<LocaleLayoutProps>(({ children, defaultLang }) => {
|
|
|
69
57
|
);
|
|
70
58
|
});
|
|
71
59
|
|
|
60
|
+
Locale.displayName = 'Locale';
|
|
61
|
+
|
|
72
62
|
export default Locale;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import dynamic from 'next/dynamic';
|
|
2
|
+
import { cookies } from 'next/headers';
|
|
3
|
+
import { FC, ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
import { getClientConfig } from '@/config/client';
|
|
6
|
+
import { LOBE_LOCALE_COOKIE } from '@/const/locale';
|
|
7
|
+
import {
|
|
8
|
+
LOBE_THEME_APPEARANCE,
|
|
9
|
+
LOBE_THEME_NEUTRAL_COLOR,
|
|
10
|
+
LOBE_THEME_PRIMARY_COLOR,
|
|
11
|
+
} from '@/const/theme';
|
|
12
|
+
import { getAntdLocale } from '@/utils/locale';
|
|
13
|
+
|
|
14
|
+
import AppTheme from './AppTheme';
|
|
15
|
+
import Locale from './Locale';
|
|
16
|
+
import StoreHydration from './StoreHydration';
|
|
17
|
+
import StyleRegistry from './StyleRegistry';
|
|
18
|
+
|
|
19
|
+
let DebugUI: FC = () => null;
|
|
20
|
+
|
|
21
|
+
// we need use Constant Folding to remove code below in production
|
|
22
|
+
// refs: https://webpack.js.org/plugins/internal-plugins/#constplugin
|
|
23
|
+
if (process.env.NODE_ENV === 'development') {
|
|
24
|
+
// eslint-disable-next-line unicorn/no-lonely-if
|
|
25
|
+
if (getClientConfig().DEBUG_MODE) {
|
|
26
|
+
DebugUI = dynamic(() => import('@/features/DebugUI'), { ssr: false }) as FC;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface GlobalLayoutProps {
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const GlobalLayout = async ({ children }: GlobalLayoutProps) => {
|
|
35
|
+
// get default theme config to use with ssr
|
|
36
|
+
const cookieStore = cookies();
|
|
37
|
+
const appearance = cookieStore.get(LOBE_THEME_APPEARANCE);
|
|
38
|
+
const neutralColor = cookieStore.get(LOBE_THEME_NEUTRAL_COLOR);
|
|
39
|
+
const primaryColor = cookieStore.get(LOBE_THEME_PRIMARY_COLOR);
|
|
40
|
+
|
|
41
|
+
// get default locale config to use with ssr
|
|
42
|
+
const defaultLang = cookieStore.get(LOBE_LOCALE_COOKIE);
|
|
43
|
+
const antdLocale = await getAntdLocale(defaultLang?.value);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<StyleRegistry>
|
|
47
|
+
<AppTheme
|
|
48
|
+
defaultAppearance={appearance?.value}
|
|
49
|
+
defaultNeutralColor={neutralColor?.value as any}
|
|
50
|
+
defaultPrimaryColor={primaryColor?.value as any}
|
|
51
|
+
>
|
|
52
|
+
<Locale antdLocale={antdLocale} defaultLang={defaultLang?.value}>
|
|
53
|
+
<StoreHydration />
|
|
54
|
+
{children}
|
|
55
|
+
<DebugUI />
|
|
56
|
+
</Locale>
|
|
57
|
+
</AppTheme>
|
|
58
|
+
</StyleRegistry>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default GlobalLayout;
|
|
@@ -7,11 +7,11 @@ import { globalService } from '@/services/global';
|
|
|
7
7
|
import { userService } from '@/services/user';
|
|
8
8
|
import { useGlobalStore } from '@/store/global';
|
|
9
9
|
import { GlobalServerConfig } from '@/types/settings';
|
|
10
|
-
import { switchLang } from '@/utils/switchLang';
|
|
10
|
+
import { switchLang } from '@/utils/client/switchLang';
|
|
11
11
|
|
|
12
12
|
vi.mock('zustand/traditional');
|
|
13
13
|
|
|
14
|
-
vi.mock('@/utils/switchLang', () => ({
|
|
14
|
+
vi.mock('@/utils/client/switchLang', () => ({
|
|
15
15
|
switchLang: vi.fn(),
|
|
16
16
|
}));
|
|
17
17
|
|
|
@@ -12,10 +12,10 @@ import { UserConfig, userService } from '@/services/user';
|
|
|
12
12
|
import type { GlobalStore } from '@/store/global';
|
|
13
13
|
import type { GlobalServerConfig, GlobalSettings } from '@/types/settings';
|
|
14
14
|
import { OnSyncEvent, PeerSyncStatus } from '@/types/sync';
|
|
15
|
+
import { switchLang } from '@/utils/client/switchLang';
|
|
15
16
|
import { merge } from '@/utils/merge';
|
|
16
17
|
import { browserInfo } from '@/utils/platform';
|
|
17
18
|
import { setNamespace } from '@/utils/storeDebug';
|
|
18
|
-
import { switchLang } from '@/utils/switchLang';
|
|
19
19
|
|
|
20
20
|
import { preferenceSelectors } from '../preference/selectors';
|
|
21
21
|
import { settingsSelectors, syncSettingsSelectors } from '../settings/selectors';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { normalizeLocale } from '@/locales/resources';
|
|
2
|
+
|
|
3
|
+
export const getAntdLocale = async (lang?: string) => {
|
|
4
|
+
let normalLang = normalizeLocale(lang);
|
|
5
|
+
|
|
6
|
+
// due to antd only have ar-EG locale, we need to convert ar to ar-EG
|
|
7
|
+
// refs: https://ant.design/docs/react/i18n
|
|
8
|
+
|
|
9
|
+
// And we don't want to handle it in `normalizeLocale` function
|
|
10
|
+
// because of other locale files are all `ar` not `ar-EG`
|
|
11
|
+
if (normalLang === 'ar') normalLang = 'ar-EG';
|
|
12
|
+
|
|
13
|
+
const { default: locale } = await import(`antd/locale/${normalLang.replace('-', '_')}.js`);
|
|
14
|
+
|
|
15
|
+
return locale;
|
|
16
|
+
};
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { App } from 'antd';
|
|
4
|
-
import { createStyles } from 'antd-style';
|
|
5
|
-
import 'antd/dist/reset.css';
|
|
6
|
-
import { SessionProvider } from 'next-auth/react';
|
|
7
|
-
import dynamic from 'next/dynamic';
|
|
8
|
-
import { FC, PropsWithChildren, memo } from 'react';
|
|
9
|
-
|
|
10
|
-
import { getClientConfig } from '@/config/client';
|
|
11
|
-
import { API_ENDPOINTS } from '@/services/_url';
|
|
12
|
-
|
|
13
|
-
import AppTheme, { AppThemeProps } from './AppTheme';
|
|
14
|
-
import Locale from './Locale';
|
|
15
|
-
import StoreHydration from './StoreHydration';
|
|
16
|
-
|
|
17
|
-
let DebugUI: FC = () => null;
|
|
18
|
-
|
|
19
|
-
// we need use Constant Folding to remove code below in production
|
|
20
|
-
// refs: https://webpack.js.org/plugins/internal-plugins/#constplugin
|
|
21
|
-
if (process.env.NODE_ENV === 'development') {
|
|
22
|
-
// eslint-disable-next-line unicorn/no-lonely-if
|
|
23
|
-
if (getClientConfig().DEBUG_MODE) {
|
|
24
|
-
DebugUI = dynamic(() => import('@/features/DebugUI'), { ssr: false }) as FC;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const useStyles = createStyles(({ css, token }) => ({
|
|
29
|
-
bg: css`
|
|
30
|
-
overflow-y: hidden;
|
|
31
|
-
display: flex;
|
|
32
|
-
flex-direction: column;
|
|
33
|
-
align-items: center;
|
|
34
|
-
|
|
35
|
-
height: 100%;
|
|
36
|
-
|
|
37
|
-
background: ${token.colorBgLayout};
|
|
38
|
-
`,
|
|
39
|
-
}));
|
|
40
|
-
|
|
41
|
-
const Container = memo<PropsWithChildren>(({ children }) => {
|
|
42
|
-
const { styles } = useStyles();
|
|
43
|
-
|
|
44
|
-
return <App className={styles.bg}>{children}</App>;
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
interface GlobalLayoutProps extends AppThemeProps {
|
|
48
|
-
defaultLang?: string;
|
|
49
|
-
enableOAuthSSO?: boolean;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const GlobalLayout = ({
|
|
53
|
-
children,
|
|
54
|
-
defaultLang,
|
|
55
|
-
enableOAuthSSO = false,
|
|
56
|
-
...theme
|
|
57
|
-
}: GlobalLayoutProps) => {
|
|
58
|
-
const content = (
|
|
59
|
-
<AppTheme {...theme}>
|
|
60
|
-
<Locale defaultLang={defaultLang}>
|
|
61
|
-
<StoreHydration />
|
|
62
|
-
<Container>{children}</Container>
|
|
63
|
-
<DebugUI />
|
|
64
|
-
</Locale>
|
|
65
|
-
</AppTheme>
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
return enableOAuthSSO ? (
|
|
69
|
-
<SessionProvider basePath={API_ENDPOINTS.oauth}>{content}</SessionProvider>
|
|
70
|
-
) : (
|
|
71
|
-
content
|
|
72
|
-
);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export default GlobalLayout;
|
|
File without changes
|
|
File without changes
|