@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 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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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.0",
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",
@@ -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
- LOBE_THEME_APPEARANCE,
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
- import StyleRegistry from './StyleRegistry';
15
+ const { ENABLE_OAUTH_SSO = false } = getServerConfig();
19
16
 
20
- const { ENABLE_OAUTH_SSO } = getServerConfig();
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
- const appearance = cookieStore.get(LOBE_THEME_APPEARANCE);
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
- <StyleRegistry>
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,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import { Icon } from '@lobehub/ui';
2
4
  import { App, FloatButton, Spin } from 'antd';
3
5
  import { DatabaseIcon, Loader2 } from 'lucide-react';
@@ -1,7 +1,11 @@
1
+ 'use client';
2
+
1
3
  import { ConfigProvider, NeutralColors, PrimaryColors, ThemeProvider } from '@lobehub/ui';
2
- import { ThemeAppearance } from 'antd-style';
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}>{children}</ConfigProvider>
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 = (e: string) => {
53
- setLang(e);
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;
@@ -1,3 +1,5 @@
1
+ 'use client';
2
+
1
3
  import { useResponsive } from 'antd-style';
2
4
  import { useRouter } from 'next/navigation';
3
5
  import { memo, useEffect } from 'react';
@@ -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;