diff --git a/src/app/StyleRegistry.tsx b/src/app/StyleRegistry.tsx
new file mode 100644
index 0000000000..51a2c9e755
--- /dev/null
+++ b/src/app/StyleRegistry.tsx
@@ -0,0 +1,23 @@
+'use client';
+
+import { StyleProvider, extractStaticStyle } from 'antd-style';
+import { useServerInsertedHTML } from 'next/navigation';
+import { PropsWithChildren, useRef } from 'react';
+
+const StyleRegistry = ({ children }: PropsWithChildren) => {
+ const isInsert = useRef(false);
+
+ useServerInsertedHTML(() => {
+ // avoid duplicate css insert
+ // refs: https://github.com/vercel/next.js/discussions/49354#discussioncomment-6279917
+ if (isInsert.current) return;
+
+ isInsert.current = true;
+
+ return extractStaticStyle().map((item) => item.style);
+ });
+
+ return {children};
+};
+
+export default StyleRegistry;
diff --git a/src/app/chat/mobile/page.page.tsx b/src/app/chat/mobile/page.page.tsx
new file mode 100644
index 0000000000..4e4a731752
--- /dev/null
+++ b/src/app/chat/mobile/page.page.tsx
@@ -0,0 +1,7 @@
+import Page from '@/pages/chat/mobile';
+
+const Index = () => {
+ return ;
+};
+
+export default Index;
diff --git a/src/app/chat/page.page.tsx b/src/app/chat/page.page.tsx
new file mode 100644
index 0000000000..031e8e3b65
--- /dev/null
+++ b/src/app/chat/page.page.tsx
@@ -0,0 +1,7 @@
+import Page from '@/pages/chat';
+
+const Index = () => {
+ return ;
+};
+
+export default Index;
diff --git a/src/app/chat/settings/page.page.tsx b/src/app/chat/settings/page.page.tsx
new file mode 100644
index 0000000000..12119a1db9
--- /dev/null
+++ b/src/app/chat/settings/page.page.tsx
@@ -0,0 +1,7 @@
+import Page from '@/pages/chat/setting';
+
+const Index = () => {
+ return ;
+};
+
+export default Index;
diff --git a/src/app/layout.page.tsx b/src/app/layout.page.tsx
new file mode 100644
index 0000000000..90b4915121
--- /dev/null
+++ b/src/app/layout.page.tsx
@@ -0,0 +1,45 @@
+import { Analytics } from '@vercel/analytics/react';
+import { Metadata } from 'next';
+import { cookies } from 'next/headers';
+import { PropsWithChildren } from 'react';
+
+import {
+ LOBE_THEME_APPEARANCE,
+ LOBE_THEME_NEUTRAL_COLOR,
+ LOBE_THEME_PRIMARY_COLOR,
+} from '@/const/theme';
+import Layout from '@/layout/GlobalLayout';
+
+import StyleRegistry from './StyleRegistry';
+
+export const metadata: Metadata = {
+ manifest: '/manifest.json',
+ title: 'LobeChat',
+};
+
+const RootLayout = ({ children }: PropsWithChildren) => {
+ // get default theme config to use with ssr
+ const cookieStore = cookies();
+ const appearance = cookieStore.get(LOBE_THEME_APPEARANCE);
+ const neutralColor = cookieStore.get(LOBE_THEME_NEUTRAL_COLOR);
+ const primaryColor = cookieStore.get(LOBE_THEME_PRIMARY_COLOR);
+
+ return (
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+};
+
+export default RootLayout;
diff --git a/src/app/market/page.page.tsx b/src/app/market/page.page.tsx
new file mode 100644
index 0000000000..4922ba629c
--- /dev/null
+++ b/src/app/market/page.page.tsx
@@ -0,0 +1,7 @@
+import Page from '@/pages/market';
+
+const Index = () => {
+ return ;
+};
+
+export default Index;
diff --git a/src/app/page.page.tsx b/src/app/page.page.tsx
new file mode 100644
index 0000000000..10950f8100
--- /dev/null
+++ b/src/app/page.page.tsx
@@ -0,0 +1,7 @@
+import Page from '@/pages/home';
+
+const Index = () => {
+ return ;
+};
+
+export default Index;
diff --git a/src/app/settings/page.page.tsx b/src/app/settings/page.page.tsx
new file mode 100644
index 0000000000..80a480fe65
--- /dev/null
+++ b/src/app/settings/page.page.tsx
@@ -0,0 +1,7 @@
+import Page from '@/pages/settings';
+
+const Index = () => {
+ return ;
+};
+
+export default Index;
diff --git a/src/app/welcome/page.page.tsx b/src/app/welcome/page.page.tsx
new file mode 100644
index 0000000000..fbba61e0e3
--- /dev/null
+++ b/src/app/welcome/page.page.tsx
@@ -0,0 +1,7 @@
+import Page from '@/pages/welcome';
+
+const Index = () => {
+ return ;
+};
+
+export default Index;
diff --git a/src/components/AppTheme/index.tsx b/src/components/AppTheme/index.tsx
new file mode 100644
index 0000000000..eb0286d07a
--- /dev/null
+++ b/src/components/AppTheme/index.tsx
@@ -0,0 +1,61 @@
+import { NeutralColors, PrimaryColors, ThemeProvider } from '@lobehub/ui';
+import { ThemeAppearance } from 'antd-style';
+import { ReactNode, memo, useEffect } from 'react';
+
+import {
+ LOBE_THEME_APPEARANCE,
+ LOBE_THEME_NEUTRAL_COLOR,
+ LOBE_THEME_PRIMARY_COLOR,
+} from '@/const/theme';
+import { useGlobalStore } from '@/store/global';
+import { GlobalStyle } from '@/styles';
+import { setCookie } from '@/utils/cookie';
+
+export interface AppThemeProps {
+ children?: ReactNode;
+ defaultAppearance?: ThemeAppearance;
+ defaultNeutralColor?: NeutralColors;
+ defaultPrimaryColor?: PrimaryColors;
+}
+
+const AppTheme = memo(
+ ({ children, defaultAppearance, defaultPrimaryColor, defaultNeutralColor }) => {
+ console.log('server:appearance', defaultAppearance);
+ console.log('server:primaryColor', defaultPrimaryColor);
+ console.log('server:neutralColor', defaultNeutralColor);
+ const themeMode = useGlobalStore((s) => s.settings.themeMode);
+
+ const [primaryColor, neutralColor] = useGlobalStore((s) => [
+ s.settings.primaryColor,
+ s.settings.neutralColor,
+ ]);
+
+ useEffect(() => {
+ console.log(primaryColor);
+ setCookie(LOBE_THEME_PRIMARY_COLOR, primaryColor);
+ }, [primaryColor]);
+
+ useEffect(() => {
+ setCookie(LOBE_THEME_NEUTRAL_COLOR, neutralColor);
+ }, [neutralColor]);
+
+ return (
+ {
+ setCookie(LOBE_THEME_APPEARANCE, appearance);
+ }}
+ themeMode={themeMode}
+ >
+
+ {children}
+
+ );
+ },
+);
+
+export default AppTheme;
diff --git a/src/const/settings.ts b/src/const/settings.ts
index ff70f0382b..ad48574e9a 100644
--- a/src/const/settings.ts
+++ b/src/const/settings.ts
@@ -13,9 +13,7 @@ export const DEFAULT_BASE_SETTINGS: GlobalBaseSettings = {
avatar: '',
fontSize: 14,
language: 'zh-CN',
- neutralColor: '',
password: '',
- primaryColor: '',
themeMode: 'auto',
};
diff --git a/src/const/theme.ts b/src/const/theme.ts
new file mode 100644
index 0000000000..e0643be8f5
--- /dev/null
+++ b/src/const/theme.ts
@@ -0,0 +1,5 @@
+export const LOBE_THEME_APPEARANCE = 'LOBE_THEME_APPEARANCE';
+
+export const LOBE_THEME_PRIMARY_COLOR = 'LOBE_THEME_PRIMARY_COLOR';
+
+export const LOBE_THEME_NEUTRAL_COLOR = 'LOBE_THEME_NEUTRAL_COLOR';
diff --git a/src/features/MobileTabBar/index.tsx b/src/features/MobileTabBar/index.tsx
index 796d7435c3..51b8c23bab 100644
--- a/src/features/MobileTabBar/index.tsx
+++ b/src/features/MobileTabBar/index.tsx
@@ -1,7 +1,7 @@
import { Icon, MobileTabBar, type MobileTabBarProps } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { Bot, MessageSquare } from 'lucide-react';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { rgba } from 'polished';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -20,6 +20,7 @@ export default memo<{ className?: string }>(({ className }) => {
const [tab, setTab] = useGlobalStore((s) => [s.sidebarKey, s.switchSideBar]);
const { t } = useTranslation('common');
const { styles } = useStyles();
+ const router = useRouter();
const items: MobileTabBarProps['items'] = useMemo(
() => [
{
@@ -28,7 +29,7 @@ export default memo<{ className?: string }>(({ className }) => {
),
key: 'chat',
onClick: () => {
- Router.push('/chat');
+ router.push('/chat');
},
title: t('tab.chat'),
},
@@ -36,7 +37,7 @@ export default memo<{ className?: string }>(({ className }) => {
icon: (active) => ,
key: 'market',
onClick: () => {
- Router.push({ hash: '', pathname: `/market` });
+ router.push('/market', { hash: '' });
},
title: t('tab.market'),
},
diff --git a/src/features/SideBar/BottomActions.tsx b/src/features/SideBar/BottomActions.tsx
index 86efaceb88..c7faa95d72 100644
--- a/src/features/SideBar/BottomActions.tsx
+++ b/src/features/SideBar/BottomActions.tsx
@@ -10,7 +10,7 @@ import {
Settings,
Settings2,
} from 'lucide-react';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -25,6 +25,7 @@ export interface BottomActionProps {
}
const BottomActions = memo(({ tab, setTab }) => {
+ const router = useRouter();
const { t } = useTranslation('common');
const { exportSessions, exportSettings, exportAll, exportAgents } = useExportConfig();
@@ -101,7 +102,7 @@ const BottomActions = memo(({ tab, setTab }) => {
label: t('setting'),
onClick: () => {
setTab('settings');
- Router.push('/settings');
+ router.push('/settings');
},
},
],
diff --git a/src/features/SideBar/TopActions.tsx b/src/features/SideBar/TopActions.tsx
index 679c70fb4b..031c8313b3 100644
--- a/src/features/SideBar/TopActions.tsx
+++ b/src/features/SideBar/TopActions.tsx
@@ -1,6 +1,6 @@
import { ActionIcon } from '@lobehub/ui';
import { Bot, MessageSquare } from 'lucide-react';
-import Router from 'next/router';
+import { usePathname, useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -13,6 +13,8 @@ export interface TopActionProps {
}
const TopActions = memo(({ tab, setTab }) => {
+ const pathname = usePathname();
+ const router = useRouter();
const { t } = useTranslation('common');
const switchBackToChat = useSessionStore((s) => s.switchBackToChat);
return (
@@ -22,7 +24,7 @@ const TopActions = memo(({ tab, setTab }) => {
icon={MessageSquare}
onClick={() => {
// 如果已经在 chat 路径下了,那么就不用再跳转了
- if (Router.asPath.startsWith('/chat')) return;
+ if (pathname?.startsWith('/chat')) return;
switchBackToChat();
setTab('chat');
}}
@@ -34,8 +36,8 @@ const TopActions = memo(({ tab, setTab }) => {
active={tab === 'market'}
icon={Bot}
onClick={() => {
- if (Router.asPath.startsWith('/market')) return;
- Router.push('/market');
+ if (pathname?.startsWith('/market')) return;
+ router.push('/market');
setTab('market');
}}
placement={'right'}
diff --git a/src/layout/index.tsx b/src/layout/GlobalLayout/index.tsx
similarity index 54%
rename from src/layout/index.tsx
rename to src/layout/GlobalLayout/index.tsx
index 5fce95d2fd..3c5200899d 100644
--- a/src/layout/index.tsx
+++ b/src/layout/GlobalLayout/index.tsx
@@ -1,16 +1,17 @@
-import { ThemeProvider, lobeCustomTheme } from '@lobehub/ui';
+'use client';
+
import { App, ConfigProvider } from 'antd';
-import { useThemeMode } from 'antd-style';
import 'antd/dist/reset.css';
import Zh_CN from 'antd/locale/zh_CN';
import { changeLanguage } from 'i18next';
-import { PropsWithChildren, memo, useCallback, useEffect } from 'react';
+import { useRouter } from 'next/navigation';
+import { PropsWithChildren, memo, useEffect } from 'react';
+import AppTheme, { AppThemeProps } from '@/components/AppTheme';
import { createI18nNext } from '@/locales/create';
import { useGlobalStore, useOnFinishHydrationGlobal } from '@/store/global';
import { usePluginStore } from '@/store/plugin';
import { useOnFinishHydrationSession, useSessionStore } from '@/store/session';
-import { GlobalStyle } from '@/styles';
import { useStyles } from './style';
@@ -19,14 +20,26 @@ const i18n = createI18nNext();
const Layout = memo(({ children }) => {
const { styles } = useStyles();
+ const router = useRouter();
+
+ useEffect(() => {
+ // refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated
+ useSessionStore.persist.rehydrate();
+ useGlobalStore.persist.rehydrate();
+ usePluginStore.persist.rehydrate();
+ }, []);
+
useOnFinishHydrationGlobal((state) => {
i18n.then(() => {
changeLanguage(state.settings.language);
});
});
- useOnFinishHydrationSession((s) => {
+ useOnFinishHydrationSession((s, store) => {
usePluginStore.getState().checkLocalEnabledPlugins(s.sessions);
+
+ // add router instance to store
+ store.setState({ router });
});
return (
@@ -36,31 +49,10 @@ const Layout = memo(({ children }) => {
);
});
-export default memo(({ children }: PropsWithChildren) => {
- useEffect(() => {
- // refs: https://github.com/pmndrs/zustand/blob/main/docs/integrations/persisting-store-data.md#hashydrated
- useSessionStore.persist.rehydrate();
- useGlobalStore.persist.rehydrate();
- usePluginStore.persist.rehydrate();
- }, []);
+const ThemeWrapper = ({ children, ...theme }: AppThemeProps) => (
+
+ {children}
+
+);
- const themeMode = useGlobalStore((s) => s.settings.themeMode);
- const [primaryColor, neutralColor] = useGlobalStore((s) => [
- s.settings.primaryColor,
- s.settings.neutralColor,
- ]);
- const { browserPrefers } = useThemeMode();
- const isDarkMode = themeMode === 'auto' ? browserPrefers === 'dark' : themeMode === 'dark';
-
- const genCustomToken: any = useCallback(
- () => lobeCustomTheme({ isDarkMode, neutralColor, primaryColor }),
- [primaryColor, neutralColor, isDarkMode],
- );
-
- return (
-
-
- {children}
-
- );
-});
+export default ThemeWrapper;
diff --git a/src/layout/style.ts b/src/layout/GlobalLayout/style.ts
similarity index 100%
rename from src/layout/style.ts
rename to src/layout/GlobalLayout/style.ts
diff --git a/src/pages/_app.page.tsx b/src/pages/_app.page.tsx
deleted file mode 100644
index 14bf5c53b1..0000000000
--- a/src/pages/_app.page.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Analytics } from '@vercel/analytics/react';
-import type { AppProps } from 'next/app';
-
-import Layout from '@/layout';
-
-export default ({ Component, pageProps }: AppProps) => (
-
-
-
-
-);
diff --git a/src/pages/_document.page.tsx b/src/pages/_document.page.tsx
deleted file mode 100644
index dee2001179..0000000000
--- a/src/pages/_document.page.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { Meta } from '@lobehub/ui';
-import { StyleProvider, extractStaticStyle } from 'antd-style';
-import Document, { DocumentContext, Head, Html, Main, NextScript } from 'next/document';
-
-class MyDocument extends Document {
- static async getInitialProps(ctx: DocumentContext) {
- const page = await ctx.renderPage({
- enhanceApp: (App) => (props) => (
-
-
-
- ),
- });
-
- const styles = extractStaticStyle(page.html).map((item) => item.style);
-
- const initialProps = await Document.getInitialProps(ctx);
-
- return {
- ...initialProps,
- styles: (
- <>
- {initialProps.styles}
- {styles}
- >
- ),
- };
- }
-
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
-
-export default MyDocument;
diff --git a/src/pages/chat/features/Header/Desktop.tsx b/src/pages/chat/features/Header/Desktop.tsx
index 20917fc4ba..9b40aa0b8d 100644
--- a/src/pages/chat/features/Header/Desktop.tsx
+++ b/src/pages/chat/features/Header/Desktop.tsx
@@ -2,7 +2,7 @@ import { SiOpenai } from '@icons-pack/react-simple-icons';
import { ActionIcon, Avatar, ChatHeader, ChatHeaderTitle, Tag } from '@lobehub/ui';
import { Skeleton } from 'antd';
import { PanelRightClose, PanelRightOpen, Settings } from 'lucide-react';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
@@ -16,6 +16,7 @@ import ShareButton from './ShareButton';
const Header = memo(() => {
const init = useSessionChatInit();
+ const router = useRouter();
const { t } = useTranslation('common');
@@ -80,7 +81,7 @@ const Header = memo(() => {
{
- Router.push({ hash: location.hash, pathname: `/chat/setting` });
+ router.push('/chat/settings', { hash: location.hash });
}}
size={{ fontSize: 24 }}
title={t('header.session', { ns: 'setting' })}
diff --git a/src/pages/chat/features/Header/Mobile.tsx b/src/pages/chat/features/Header/Mobile.tsx
index 9dfe172979..ba91304faf 100644
--- a/src/pages/chat/features/Header/Mobile.tsx
+++ b/src/pages/chat/features/Header/Mobile.tsx
@@ -1,6 +1,6 @@
import { ActionIcon, MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
import { LayoutList, Settings } from 'lucide-react';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,6 +10,7 @@ import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
const MobileHeader = memo(() => {
const { t } = useTranslation('common');
+ const router = useRouter();
const [isInbox, title, model] = useSessionStore((s) => [
sessionSelectors.isInboxSession(s),
@@ -24,7 +25,7 @@ const MobileHeader = memo(() => {
return (
}
- onBackClick={() => Router.push({ hash: null, pathname: `/chat` })}
+ onBackClick={() => router.push('/chat', { hash: null })}
right={
<>
toggleConfig()} />
@@ -32,7 +33,7 @@ const MobileHeader = memo(() => {
{
- Router.push({ hash: location.hash, pathname: `/chat/setting` });
+ router.push('/chat/settings', { hash: location.hash });
}}
/>
)}
diff --git a/src/pages/chat/features/SessionList/Header/Mobile.tsx b/src/pages/chat/features/SessionList/Header/Mobile.tsx
index 4686d3bdf2..d0cb04d2e3 100644
--- a/src/pages/chat/features/SessionList/Header/Mobile.tsx
+++ b/src/pages/chat/features/SessionList/Header/Mobile.tsx
@@ -1,7 +1,7 @@
import { ActionIcon, Logo, MobileNavBar } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { MessageSquarePlus, Settings2 } from 'lucide-react';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { memo } from 'react';
import AvatarWithUpload from '@/features/AvatarWithUpload';
@@ -19,6 +19,7 @@ export const useStyles = createStyles(({ css, token }) => ({
const Header = memo(() => {
const [createSession] = useSessionStore((s) => [s.createSession]);
+ const router = useRouter();
return (
{
{
- Router.push({ pathname: `/settings` });
+ router.push('/settings');
}}
/>
>
diff --git a/src/pages/chat/index.page.tsx b/src/pages/chat/index.tsx
similarity index 98%
rename from src/pages/chat/index.page.tsx
rename to src/pages/chat/index.tsx
index fb05fbb81c..14be013f86 100644
--- a/src/pages/chat/index.page.tsx
+++ b/src/pages/chat/index.tsx
@@ -1,3 +1,5 @@
+'use client';
+
import { useResponsive } from 'antd-style';
import Head from 'next/head';
import { memo } from 'react';
diff --git a/src/pages/chat/mobile/index.page.tsx b/src/pages/chat/mobile/index.tsx
similarity index 98%
rename from src/pages/chat/mobile/index.page.tsx
rename to src/pages/chat/mobile/index.tsx
index c831341c0b..7cb0e7b1d1 100644
--- a/src/pages/chat/mobile/index.page.tsx
+++ b/src/pages/chat/mobile/index.tsx
@@ -1,3 +1,5 @@
+'use client';
+
import Head from 'next/head';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
diff --git a/src/pages/chat/setting/features/Header/Desktop.tsx b/src/pages/chat/setting/features/Header/Desktop.tsx
index 1cf1a5745c..7eb4737fda 100644
--- a/src/pages/chat/setting/features/Header/Desktop.tsx
+++ b/src/pages/chat/setting/features/Header/Desktop.tsx
@@ -1,15 +1,16 @@
import { ChatHeader, ChatHeaderTitle } from '@lobehub/ui';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { ReactNode, memo } from 'react';
import { useTranslation } from 'react-i18next';
const Header = memo<{ children: ReactNode }>(({ children }) => {
const { t } = useTranslation('setting');
+ const router = useRouter();
return (
}
- onBackClick={() => Router.push({ hash: location.hash, pathname: `/chat` })}
+ onBackClick={() => router.push('/chat', { hash: location.hash })}
right={children}
showBackButton
/>
diff --git a/src/pages/chat/setting/features/Header/Mobile.tsx b/src/pages/chat/setting/features/Header/Mobile.tsx
index bc52e747c4..cbe53085b2 100644
--- a/src/pages/chat/setting/features/Header/Mobile.tsx
+++ b/src/pages/chat/setting/features/Header/Mobile.tsx
@@ -1,15 +1,16 @@
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { type ReactNode, memo } from 'react';
import { useTranslation } from 'react-i18next';
const Header = memo<{ children: ReactNode }>(({ children }) => {
const { t } = useTranslation('setting');
+ const router = useRouter();
return (
}
- onBackClick={() => Router.push({ hash: location.hash, pathname: `/chat/mobile` })}
+ onBackClick={() => router.push('/chat/mobile', { hash: location.hash })}
right={children}
showBackButton
/>
diff --git a/src/pages/chat/setting/index.page.tsx b/src/pages/chat/setting/index.tsx
similarity index 98%
rename from src/pages/chat/setting/index.page.tsx
rename to src/pages/chat/setting/index.tsx
index 4604805cfd..f38d11ee3c 100644
--- a/src/pages/chat/setting/index.page.tsx
+++ b/src/pages/chat/setting/index.tsx
@@ -1,3 +1,5 @@
+'use client';
+
import { useResponsive } from 'antd-style';
import isEqual from 'fast-deep-equal';
import Head from 'next/head';
diff --git a/src/pages/index/Loading.tsx b/src/pages/home/Loading.tsx
similarity index 100%
rename from src/pages/index/Loading.tsx
rename to src/pages/home/Loading.tsx
diff --git a/src/pages/index.page.tsx b/src/pages/home/index.tsx
similarity index 61%
rename from src/pages/index.page.tsx
rename to src/pages/home/index.tsx
index b8a9699caa..9405e07c95 100644
--- a/src/pages/index.page.tsx
+++ b/src/pages/home/index.tsx
@@ -1,10 +1,16 @@
-import { memo, useEffect } from 'react';
+'use client';
-import { useSessionHydrated, useSessionStore } from '@/store/session';
+import { memo } from 'react';
+
+import Loading from '@/pages/home/Loading';
+import {
+ useEffectAfterSessionHydrated,
+ useSessionHydrated,
+ useSessionStore,
+} from '@/store/session';
import { sessionSelectors } from '@/store/session/selectors';
-import Loading from './index/Loading';
-import Welcome from './welcome/index.page';
+import Welcome from '../welcome';
const Home = memo(() => {
const hydrated = useSessionHydrated();
@@ -13,7 +19,7 @@ const Home = memo(() => {
s.switchSession,
]);
- useEffect(() => {
+ useEffectAfterSessionHydrated(() => {
if (hasSession) switchSession();
}, [hasSession]);
diff --git a/src/pages/market/features/Header/Mobile.tsx b/src/pages/market/features/Header/Mobile.tsx
index 3848ce5107..ba140d1fea 100644
--- a/src/pages/market/features/Header/Mobile.tsx
+++ b/src/pages/market/features/Header/Mobile.tsx
@@ -1,11 +1,13 @@
import { ActionIcon, Logo, MobileNavBar } from '@lobehub/ui';
import { Settings2 } from 'lucide-react';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { memo } from 'react';
import AvatarWithUpload from '@/features/AvatarWithUpload';
const Header = memo(() => {
+ const router = useRouter();
+
return (
}
@@ -14,7 +16,7 @@ const Header = memo(() => {
{
- Router.push({ pathname: `/settings` });
+ router.push('/settings');
}}
/>
}
diff --git a/src/pages/market/index.page.tsx b/src/pages/market/index.tsx
similarity index 98%
rename from src/pages/market/index.page.tsx
rename to src/pages/market/index.tsx
index 7fb5de4b7a..2886194052 100644
--- a/src/pages/market/index.page.tsx
+++ b/src/pages/market/index.tsx
@@ -1,3 +1,5 @@
+'use client';
+
import { useResponsive } from 'antd-style';
import Head from 'next/head';
import { memo, useEffect } from 'react';
diff --git a/src/pages/settings/features/Header/Mobile.tsx b/src/pages/settings/features/Header/Mobile.tsx
index 300dbc7b70..133e2bc152 100644
--- a/src/pages/settings/features/Header/Mobile.tsx
+++ b/src/pages/settings/features/Header/Mobile.tsx
@@ -1,15 +1,16 @@
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
-import Router from 'next/router';
+import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const Header = memo(() => {
const { t } = useTranslation('setting');
+ const router = useRouter();
return (
}
- onBackClick={() => Router.push('/chat')}
+ onBackClick={() => router.push('/chat')}
showBackButton
/>
);
diff --git a/src/pages/settings/index.page.tsx b/src/pages/settings/index.tsx
similarity index 98%
rename from src/pages/settings/index.page.tsx
rename to src/pages/settings/index.tsx
index 1034a78fa2..c1189b496b 100644
--- a/src/pages/settings/index.page.tsx
+++ b/src/pages/settings/index.tsx
@@ -1,3 +1,5 @@
+'use client';
+
import { useResponsive } from 'antd-style';
import Head from 'next/head';
import { memo } from 'react';
diff --git a/src/pages/welcome/index.page.tsx b/src/pages/welcome/index.tsx
similarity index 98%
rename from src/pages/welcome/index.page.tsx
rename to src/pages/welcome/index.tsx
index f02318f94e..fb3fd3537a 100644
--- a/src/pages/welcome/index.page.tsx
+++ b/src/pages/welcome/index.tsx
@@ -1,3 +1,5 @@
+'use client';
+
import { useResponsive } from 'antd-style';
import Head from 'next/head';
import { memo } from 'react';
diff --git a/src/store/session/hooks/useOnFinishHydrationSession.ts b/src/store/session/hooks/useOnFinishHydrationSession.ts
index 6596a4441d..707ce506ab 100644
--- a/src/store/session/hooks/useOnFinishHydrationSession.ts
+++ b/src/store/session/hooks/useOnFinishHydrationSession.ts
@@ -1,4 +1,5 @@
import { useEffect } from 'react';
+import { StoreApi, UseBoundStore } from 'zustand';
import { SessionStore, useSessionStore } from '../store';
@@ -6,11 +7,13 @@ import { SessionStore, useSessionStore } from '../store';
* 当 Session 水合完毕后才会执行的 useEffect
* @param fn
*/
-export const useOnFinishHydrationSession = (fn: (state: SessionStore) => void) => {
+export const useOnFinishHydrationSession = (
+ fn: (state: SessionStore, store: UseBoundStore>) => void,
+) => {
useEffect(() => {
// 只有当水合完毕后再开始做操作
useSessionStore.persist.onFinishHydration(() => {
- fn(useSessionStore.getState());
+ fn(useSessionStore.getState(), useSessionStore);
});
}, []);
};
diff --git a/src/store/session/slices/session/action.ts b/src/store/session/slices/session/action.ts
index 613cb90c22..e1f2b41a88 100644
--- a/src/store/session/slices/session/action.ts
+++ b/src/store/session/slices/session/action.ts
@@ -1,6 +1,5 @@
import { produce } from 'immer';
import { merge } from 'lodash-es';
-import Router from 'next/router';
import { DeepPartial } from 'utility-types';
import { StateCreator } from 'zustand/vanilla';
@@ -154,22 +153,22 @@ export const createSessionSlice: StateCreator<
},
switchBackToChat: () => {
- const { activeId } = get();
+ const { activeId, router } = get();
const id = activeId || INBOX_SESSION_ID;
get().activeSession(id);
- Router.push(SESSION_CHAT_URL(id, get().isMobile));
+ router?.push(SESSION_CHAT_URL(id, get().isMobile));
},
switchSession: (sessionId = INBOX_SESSION_ID) => {
- const { isMobile } = get();
+ const { isMobile, router } = get();
// mobile also should switch session due to chat mobile route is different
// fix https://github.com/lobehub/lobe-chat/issues/163
if (!isMobile && get().activeId === sessionId) return;
get().activeSession(sessionId);
- Router.push(SESSION_CHAT_URL(sessionId, isMobile));
+ router?.push(SESSION_CHAT_URL(sessionId, isMobile));
},
});
diff --git a/src/store/session/slices/session/initialState.ts b/src/store/session/slices/session/initialState.ts
index 8ea6043641..aa2791b2ab 100644
--- a/src/store/session/slices/session/initialState.ts
+++ b/src/store/session/slices/session/initialState.ts
@@ -1,4 +1,5 @@
import { merge } from 'lodash-es';
+import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context';
import { DEFAULT_AGENT_META, DEFAULT_INBOX_AVATAR } from '@/const/meta';
import { LobeAgentConfig, LobeAgentSession, LobeSessionType } from '@/types/session';
@@ -14,6 +15,7 @@ export interface SessionState {
// 默认会话
inbox: LobeAgentSession;
isMobile?: boolean;
+ router?: AppRouterInstance;
searchKeywords: string;
sessions: Record;
topicSearchKeywords: string;
diff --git a/src/types/settings.ts b/src/types/settings.ts
index 0570d13ad6..50ee15fd7b 100644
--- a/src/types/settings.ts
+++ b/src/types/settings.ts
@@ -37,9 +37,9 @@ export interface GlobalBaseSettings {
*/
historyCount?: number;
language: Locales;
- neutralColor: NeutralColors | '';
+ neutralColor?: NeutralColors;
password: string;
- primaryColor: PrimaryColors | '';
+ primaryColor?: PrimaryColors;
themeMode: ThemeMode;
}
diff --git a/src/utils/cookie.ts b/src/utils/cookie.ts
new file mode 100644
index 0000000000..d7a3bfa988
--- /dev/null
+++ b/src/utils/cookie.ts
@@ -0,0 +1,4 @@
+export const setCookie = (key: string, value: string | undefined) => {
+ // eslint-disable-next-line unicorn/no-document-cookie
+ document.cookie = `${key}=${value};`;
+};
diff --git a/tsconfig.json b/tsconfig.json
index baf061b36e..84a8305f3b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -19,7 +19,12 @@
"types": ["vitest/globals"],
"paths": {
"@/*": ["./src/*"]
- }
+ },
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
},
"exclude": ["node_modules"],
"include": [
@@ -29,7 +34,8 @@
"tests",
"**/*.ts",
"**/*.d.ts",
- "**/*.tsx"
+ "**/*.tsx",
+ ".next/types/**/*.ts"
],
"ts-node": {
"compilerOptions": {