@lobehub/chat 1.116.1 → 1.116.3
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 +42 -0
- package/apps/desktop/package.json +1 -1
- package/changelog/v1.json +14 -0
- package/package.json +1 -1
- package/src/app/(backend)/trpc/desktop/[trpc]/route.ts +26 -0
- package/src/app/(backend)/trpc/trpc.test.ts +10 -0
- package/src/app/[variants]/(main)/_layout/Desktop/DesktopLayoutContainer.tsx +30 -0
- package/src/app/[variants]/(main)/_layout/Desktop/index.tsx +6 -19
- package/src/app/[variants]/(main)/chat/_layout/Desktop/SessionPanel.tsx +25 -21
- package/src/app/[variants]/(main)/settings/_layout/Desktop/Header.tsx +28 -3
- package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +17 -24
- package/src/app/desktop/devtools/page.tsx +89 -0
- package/src/app/desktop/layout.tsx +31 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,48 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.116.3](https://github.com/lobehub/lobe-chat/compare/v1.116.2...v1.116.3)
|
6
|
+
|
7
|
+
<sup>Released on **2025-08-28**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix desktop route error.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Fix desktop route error, closes [#8962](https://github.com/lobehub/lobe-chat/issues/8962) ([27a4b34](https://github.com/lobehub/lobe-chat/commit/27a4b34))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
### [Version 1.116.2](https://github.com/lobehub/lobe-chat/compare/v1.116.1...v1.116.2)
|
31
|
+
|
32
|
+
<sup>Released on **2025-08-28**</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 1.116.1](https://github.com/lobehub/lobe-chat/compare/v1.116.0...v1.116.1)
|
6
48
|
|
7
49
|
<sup>Released on **2025-08-27**</sup>
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,18 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"Fix desktop route error."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-08-28",
|
9
|
+
"version": "1.116.3"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {},
|
13
|
+
"date": "2025-08-28",
|
14
|
+
"version": "1.116.2"
|
15
|
+
},
|
2
16
|
{
|
3
17
|
"children": {},
|
4
18
|
"date": "2025-08-27",
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.116.
|
3
|
+
"version": "1.116.3",
|
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",
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
|
2
|
+
import type { NextRequest } from 'next/server';
|
3
|
+
|
4
|
+
import { pino } from '@/libs/logger';
|
5
|
+
import { createLambdaContext } from '@/libs/trpc/lambda/context';
|
6
|
+
import { desktopRouter } from '@/server/routers/desktop';
|
7
|
+
|
8
|
+
const handler = (req: NextRequest) =>
|
9
|
+
fetchRequestHandler({
|
10
|
+
/**
|
11
|
+
* @link https://trpc.io/docs/v11/context
|
12
|
+
*/
|
13
|
+
createContext: () => createLambdaContext(req),
|
14
|
+
|
15
|
+
endpoint: '/trpc/desktop',
|
16
|
+
|
17
|
+
onError: ({ error, path, type }) => {
|
18
|
+
pino.info(`Error in tRPC handler (desktop) on path: ${path}, type: ${type}`);
|
19
|
+
console.error(error);
|
20
|
+
},
|
21
|
+
|
22
|
+
req,
|
23
|
+
router: desktopRouter,
|
24
|
+
});
|
25
|
+
|
26
|
+
export { handler as GET, handler as POST };
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { existsSync } from 'fs';
|
2
|
+
import { join } from 'path';
|
3
|
+
import { describe, expect, it } from 'vitest';
|
4
|
+
|
5
|
+
describe('Desktop TRPC Route', () => {
|
6
|
+
it('should have desktop directory', () => {
|
7
|
+
const desktopPath = join(__dirname, 'desktop');
|
8
|
+
expect(existsSync(desktopPath)).toBe(true);
|
9
|
+
});
|
10
|
+
});
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { useTheme } from 'antd-style';
|
2
|
+
import { usePathname } from 'next/navigation';
|
3
|
+
import { PropsWithChildren, memo } from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
import SideBar from './SideBar';
|
7
|
+
|
8
|
+
const DesktopLayoutContainer = memo<PropsWithChildren>(({ children }) => {
|
9
|
+
const theme = useTheme();
|
10
|
+
const pathname = usePathname();
|
11
|
+
const hideSideBar = pathname.startsWith('/settings');
|
12
|
+
return (
|
13
|
+
<>
|
14
|
+
{!hideSideBar && <SideBar />}
|
15
|
+
<Flexbox
|
16
|
+
style={{
|
17
|
+
background: theme.colorBgLayout,
|
18
|
+
borderInlineStart: `1px solid ${theme.colorBorderSecondary}`,
|
19
|
+
borderStartStartRadius: !hideSideBar ? 12 : undefined,
|
20
|
+
borderTop: `1px solid ${theme.colorBorderSecondary}`,
|
21
|
+
overflow: 'hidden',
|
22
|
+
}}
|
23
|
+
width={'100%'}
|
24
|
+
>
|
25
|
+
{children}
|
26
|
+
</Flexbox>
|
27
|
+
</>
|
28
|
+
);
|
29
|
+
});
|
30
|
+
export default DesktopLayoutContainer;
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
import { useTheme } from 'antd-style';
|
4
4
|
import dynamic from 'next/dynamic';
|
5
|
-
import { usePathname } from 'next/navigation';
|
6
5
|
import { PropsWithChildren, Suspense, memo } from 'react';
|
7
6
|
import { HotkeysProvider } from 'react-hotkeys-hook';
|
8
7
|
import { Flexbox } from 'react-layout-kit';
|
@@ -15,6 +14,7 @@ import { usePlatform } from '@/hooks/usePlatform';
|
|
15
14
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
16
15
|
import { HotkeyScopeEnum } from '@/types/hotkey';
|
17
16
|
|
17
|
+
import DesktopLayoutContainer from './DesktopLayoutContainer';
|
18
18
|
import RegisterHotkeys from './RegisterHotkeys';
|
19
19
|
import SideBar from './SideBar';
|
20
20
|
|
@@ -24,11 +24,7 @@ const Layout = memo<PropsWithChildren>(({ children }) => {
|
|
24
24
|
const { isPWA } = usePlatform();
|
25
25
|
const theme = useTheme();
|
26
26
|
|
27
|
-
const pathname = usePathname();
|
28
27
|
const { showCloudPromotion } = useServerConfigStore(featureFlagsSelectors);
|
29
|
-
|
30
|
-
// setting page not show sidebar
|
31
|
-
const hideSideBar = isDesktop && pathname.startsWith('/settings');
|
32
28
|
return (
|
33
29
|
<HotkeysProvider initiallyActiveScopes={[HotkeyScopeEnum.Global]}>
|
34
30
|
{isDesktop && <TitleBar />}
|
@@ -48,22 +44,13 @@ const Layout = memo<PropsWithChildren>(({ children }) => {
|
|
48
44
|
}}
|
49
45
|
width={'100%'}
|
50
46
|
>
|
51
|
-
{!hideSideBar && <SideBar />}
|
52
47
|
{isDesktop ? (
|
53
|
-
<
|
54
|
-
style={{
|
55
|
-
background: theme.colorBgLayout,
|
56
|
-
borderInlineStart: `1px solid ${theme.colorBorderSecondary}`,
|
57
|
-
borderStartStartRadius: !hideSideBar ? 12 : undefined,
|
58
|
-
borderTop: `1px solid ${theme.colorBorderSecondary}`,
|
59
|
-
overflow: 'hidden',
|
60
|
-
}}
|
61
|
-
width={'100%'}
|
62
|
-
>
|
63
|
-
{children}
|
64
|
-
</Flexbox>
|
48
|
+
<DesktopLayoutContainer>{children}</DesktopLayoutContainer>
|
65
49
|
) : (
|
66
|
-
|
50
|
+
<>
|
51
|
+
<SideBar />
|
52
|
+
{children}
|
53
|
+
</>
|
67
54
|
)}
|
68
55
|
</Flexbox>
|
69
56
|
<HotkeyHelperPanel />
|
@@ -3,7 +3,7 @@
|
|
3
3
|
import { DraggablePanel, DraggablePanelContainer, type DraggablePanelProps } from '@lobehub/ui';
|
4
4
|
import { createStyles, useResponsive } from 'antd-style';
|
5
5
|
import isEqual from 'fast-deep-equal';
|
6
|
-
import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
6
|
+
import { PropsWithChildren, memo, useEffect, useMemo, useState } from 'react';
|
7
7
|
|
8
8
|
import { withSuspense } from '@/components/withSuspense';
|
9
9
|
import { FOLDER_WIDTH } from '@/const/layoutTokens';
|
@@ -69,26 +69,30 @@ const SessionPanel = memo<PropsWithChildren>(({ children }) => {
|
|
69
69
|
if (!md) updatePreference({ showSessionPanel: false });
|
70
70
|
}, [md, cacheExpand]);
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
{
|
89
|
-
|
90
|
-
|
91
|
-
|
72
|
+
const SessionPanel = useMemo(() => {
|
73
|
+
return (
|
74
|
+
<DraggablePanel
|
75
|
+
className={styles.panel}
|
76
|
+
defaultSize={{ width: tmpWidth }}
|
77
|
+
// 当进入 pin 模式下,不可展开
|
78
|
+
expand={!isPinned && sessionExpandable}
|
79
|
+
expandable={!isPinned}
|
80
|
+
maxWidth={400}
|
81
|
+
minWidth={FOLDER_WIDTH}
|
82
|
+
mode={md ? 'fixed' : 'float'}
|
83
|
+
onExpandChange={handleExpand}
|
84
|
+
onSizeChange={handleSizeChange}
|
85
|
+
placement="left"
|
86
|
+
size={{ height: '100%', width: sessionsWidth }}
|
87
|
+
>
|
88
|
+
<DraggablePanelContainer style={{ flex: 'none', height: '100%', minWidth: FOLDER_WIDTH }}>
|
89
|
+
{children}
|
90
|
+
</DraggablePanelContainer>
|
91
|
+
</DraggablePanel>
|
92
|
+
);
|
93
|
+
}, [sessionsWidth, md, isPinned, sessionExpandable, tmpWidth]);
|
94
|
+
|
95
|
+
return SessionPanel;
|
92
96
|
});
|
93
97
|
|
94
98
|
export default withSuspense(SessionPanel);
|
@@ -1,14 +1,20 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
|
-
import { ActionIcon } from '@lobehub/ui';
|
3
|
+
import { ActionIcon, Tag } from '@lobehub/ui';
|
4
4
|
import { ChatHeader } from '@lobehub/ui/chat';
|
5
5
|
import { Drawer, type DrawerProps } from 'antd';
|
6
6
|
import { createStyles } from 'antd-style';
|
7
7
|
import { Menu } from 'lucide-react';
|
8
|
+
import { usePathname } from 'next/navigation';
|
8
9
|
import { ReactNode, memo, useState } from 'react';
|
10
|
+
import { useTranslation } from 'react-i18next';
|
9
11
|
import { Flexbox } from 'react-layout-kit';
|
10
12
|
|
11
13
|
import BrandWatermark from '@/components/BrandWatermark';
|
14
|
+
// 新增:引入 SettingsTabs
|
15
|
+
import { useActiveSettingsKey } from '@/hooks/useActiveTabKey';
|
16
|
+
import { useProviderName } from '@/hooks/useProviderName';
|
17
|
+
import { SettingsTabs } from '@/store/global/initialState';
|
12
18
|
|
13
19
|
const useStyles = createStyles(({ token, css }) => ({
|
14
20
|
container: css`
|
@@ -26,12 +32,31 @@ const useStyles = createStyles(({ token, css }) => ({
|
|
26
32
|
|
27
33
|
interface HeaderProps extends Pick<DrawerProps, 'getContainer'> {
|
28
34
|
children: ReactNode;
|
29
|
-
title
|
35
|
+
title?: ReactNode;
|
30
36
|
}
|
31
37
|
|
32
38
|
const Header = memo<HeaderProps>(({ children, getContainer, title }) => {
|
33
39
|
const [open, setOpen] = useState(false);
|
34
40
|
const { styles, theme } = useStyles();
|
41
|
+
const activeKey = useActiveSettingsKey();
|
42
|
+
const providerName = useProviderName(activeKey);
|
43
|
+
|
44
|
+
const pathname = usePathname();
|
45
|
+
const { t } = useTranslation('setting');
|
46
|
+
|
47
|
+
const isProvider = pathname.includes('/settings/provider/');
|
48
|
+
const dynamicTitle = title ? (
|
49
|
+
title
|
50
|
+
) : (
|
51
|
+
<>
|
52
|
+
{isProvider ? providerName : t(`tab.${activeKey}`)}
|
53
|
+
{activeKey === SettingsTabs.Sync && (
|
54
|
+
<Tag bordered={false} color={'gold'}>
|
55
|
+
{t('tab.experiment')}
|
56
|
+
</Tag>
|
57
|
+
)}
|
58
|
+
</>
|
59
|
+
);
|
35
60
|
|
36
61
|
return (
|
37
62
|
<>
|
@@ -47,7 +72,7 @@ const Header = memo<HeaderProps>(({ children, getContainer, title }) => {
|
|
47
72
|
onClick={() => setOpen(true)}
|
48
73
|
size={{ blockSize: 32, size: 18 }}
|
49
74
|
/>
|
50
|
-
{
|
75
|
+
{dynamicTitle}
|
51
76
|
</Flexbox>
|
52
77
|
}
|
53
78
|
/>
|
@@ -1,18 +1,13 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
|
-
import { Tag } from '@lobehub/ui';
|
4
3
|
import { useResponsive, useTheme } from 'antd-style';
|
5
4
|
import { usePathname } from 'next/navigation';
|
6
|
-
import { memo, useRef } from 'react';
|
7
|
-
import { useTranslation } from 'react-i18next';
|
5
|
+
import { PropsWithChildren, memo, useEffect, useRef } from 'react';
|
8
6
|
import { Flexbox } from 'react-layout-kit';
|
9
7
|
|
10
8
|
import InitClientDB from '@/features/InitClientDB';
|
11
9
|
import Footer from '@/features/Setting/Footer';
|
12
10
|
import SettingContainer from '@/features/Setting/SettingContainer';
|
13
|
-
import { useActiveSettingsKey } from '@/hooks/useActiveTabKey';
|
14
|
-
import { useProviderName } from '@/hooks/useProviderName';
|
15
|
-
import { SettingsTabs } from '@/store/global/initialState';
|
16
11
|
|
17
12
|
import { LayoutProps } from '../type';
|
18
13
|
import Header from './Header';
|
@@ -20,17 +15,25 @@ import SideBar from './SideBar';
|
|
20
15
|
|
21
16
|
const SKIP_PATHS = ['/settings/provider', '/settings/agent'];
|
22
17
|
|
18
|
+
const ContentContainer = memo<PropsWithChildren>(({ children }) => {
|
19
|
+
const pathname = usePathname();
|
20
|
+
const isSkip = SKIP_PATHS.some((path) => pathname.includes(path));
|
21
|
+
|
22
|
+
return isSkip ? (
|
23
|
+
children
|
24
|
+
) : (
|
25
|
+
<SettingContainer addonAfter={<Footer />}>{children}</SettingContainer>
|
26
|
+
);
|
27
|
+
});
|
28
|
+
|
23
29
|
const Layout = memo<LayoutProps>(({ children, category }) => {
|
24
30
|
const ref = useRef<any>(null);
|
25
31
|
const { md = true } = useResponsive();
|
26
|
-
const { t } = useTranslation('setting');
|
27
|
-
const activeKey = useActiveSettingsKey();
|
28
32
|
const theme = useTheme();
|
29
|
-
const pathname = usePathname();
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
+
useEffect(() => {
|
35
|
+
console.log('settings render');
|
36
|
+
});
|
34
37
|
|
35
38
|
return (
|
36
39
|
<Flexbox
|
@@ -42,19 +45,9 @@ const Layout = memo<LayoutProps>(({ children, category }) => {
|
|
42
45
|
{md ? (
|
43
46
|
<SideBar>{category}</SideBar>
|
44
47
|
) : (
|
45
|
-
<Header
|
46
|
-
getContainer={() => ref.current}
|
47
|
-
title={
|
48
|
-
<>
|
49
|
-
{isProvider ? providerName : t(`tab.${activeKey}`)}
|
50
|
-
{activeKey === SettingsTabs.Sync && <Tag color={'gold'}>{t('tab.experiment')}</Tag>}
|
51
|
-
</>
|
52
|
-
}
|
53
|
-
>
|
54
|
-
{category}
|
55
|
-
</Header>
|
48
|
+
<Header getContainer={() => ref.current!}>{category}</Header>
|
56
49
|
)}
|
57
|
-
|
50
|
+
<ContentContainer>{children}</ContentContainer>
|
58
51
|
<InitClientDB />
|
59
52
|
</Flexbox>
|
60
53
|
);
|
@@ -0,0 +1,89 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { ActionIcon, SideNav } from '@lobehub/ui';
|
4
|
+
import { Cog, DatabaseIcon } from 'lucide-react';
|
5
|
+
import { memo, useState } from 'react';
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
7
|
+
|
8
|
+
import { BRANDING_NAME } from '@/const/branding';
|
9
|
+
import PostgresViewer from '@/features/DevPanel/PostgresViewer';
|
10
|
+
import SystemInspector from '@/features/DevPanel/SystemInspector';
|
11
|
+
import { useStyles } from '@/features/DevPanel/features/FloatPanel';
|
12
|
+
import { electronStylish } from '@/styles/electron';
|
13
|
+
|
14
|
+
const DevTools = memo(() => {
|
15
|
+
const { styles, theme, cx } = useStyles();
|
16
|
+
|
17
|
+
const items = [
|
18
|
+
{
|
19
|
+
children: <PostgresViewer />,
|
20
|
+
icon: <DatabaseIcon size={16} />,
|
21
|
+
key: 'Postgres Viewer',
|
22
|
+
},
|
23
|
+
{
|
24
|
+
children: <SystemInspector />,
|
25
|
+
icon: <Cog size={16} />,
|
26
|
+
key: 'System Status',
|
27
|
+
},
|
28
|
+
];
|
29
|
+
|
30
|
+
const [tab, setTab] = useState<string>(items[0].key);
|
31
|
+
|
32
|
+
return (
|
33
|
+
<Flexbox height={'100%'} style={{ overflow: 'hidden', position: 'relative' }} width={'100%'}>
|
34
|
+
<Flexbox
|
35
|
+
align={'center'}
|
36
|
+
className={cx(`panel-drag-handle`, styles.header, electronStylish.draggable)}
|
37
|
+
horizontal
|
38
|
+
justify={'center'}
|
39
|
+
>
|
40
|
+
<Flexbox align={'baseline'} gap={6} horizontal>
|
41
|
+
<b>{BRANDING_NAME} Dev Tools</b>
|
42
|
+
<span style={{ color: theme.colorTextDescription }}>/</span>
|
43
|
+
<span style={{ color: theme.colorTextDescription }}>{tab}</span>
|
44
|
+
</Flexbox>
|
45
|
+
</Flexbox>
|
46
|
+
<Flexbox
|
47
|
+
height={'100%'}
|
48
|
+
horizontal
|
49
|
+
style={{ background: theme.colorBgLayout, overflow: 'hidden', position: 'relative' }}
|
50
|
+
width={'100%'}
|
51
|
+
>
|
52
|
+
<SideNav
|
53
|
+
bottomActions={[]}
|
54
|
+
style={{
|
55
|
+
background: 'transparent',
|
56
|
+
width: 48,
|
57
|
+
}}
|
58
|
+
topActions={items.map((item) => (
|
59
|
+
<ActionIcon
|
60
|
+
active={tab === item.key}
|
61
|
+
icon={item.icon}
|
62
|
+
key={item.key}
|
63
|
+
onClick={() => setTab(item.key)}
|
64
|
+
title={item.key}
|
65
|
+
tooltipProps={{
|
66
|
+
placement: 'right',
|
67
|
+
}}
|
68
|
+
/>
|
69
|
+
))}
|
70
|
+
/>
|
71
|
+
{items.map((item) => (
|
72
|
+
<Flexbox
|
73
|
+
flex={1}
|
74
|
+
height={'100%'}
|
75
|
+
key={item.key}
|
76
|
+
style={{
|
77
|
+
display: tab === item.key ? 'flex' : 'none',
|
78
|
+
overflow: 'hidden',
|
79
|
+
}}
|
80
|
+
>
|
81
|
+
{item.children}
|
82
|
+
</Flexbox>
|
83
|
+
))}
|
84
|
+
</Flexbox>
|
85
|
+
</Flexbox>
|
86
|
+
);
|
87
|
+
});
|
88
|
+
|
89
|
+
export default DevTools;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { notFound } from 'next/navigation';
|
2
|
+
import { NuqsAdapter } from 'nuqs/adapters/next/app';
|
3
|
+
import { ReactNode } from 'react';
|
4
|
+
|
5
|
+
import { isDesktop } from '@/const/version';
|
6
|
+
import GlobalLayout from '@/layout/GlobalProvider';
|
7
|
+
import { ServerConfigStoreProvider } from '@/store/serverConfig/Provider';
|
8
|
+
|
9
|
+
interface RootLayoutProps {
|
10
|
+
children: ReactNode;
|
11
|
+
}
|
12
|
+
|
13
|
+
const RootLayout = async ({ children }: RootLayoutProps) => {
|
14
|
+
if (!isDesktop) return notFound();
|
15
|
+
|
16
|
+
return (
|
17
|
+
<html dir="ltr" suppressHydrationWarning>
|
18
|
+
<body>
|
19
|
+
<NuqsAdapter>
|
20
|
+
<ServerConfigStoreProvider>
|
21
|
+
<GlobalLayout appearance={'auto'} isMobile={false} locale={''}>
|
22
|
+
{children}
|
23
|
+
</GlobalLayout>
|
24
|
+
</ServerConfigStoreProvider>
|
25
|
+
</NuqsAdapter>
|
26
|
+
</body>
|
27
|
+
</html>
|
28
|
+
);
|
29
|
+
};
|
30
|
+
|
31
|
+
export default RootLayout;
|