@lobehub/chat 0.157.2 → 0.158.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.
Files changed (186) hide show
  1. package/.eslintignore +1 -2
  2. package/CHANGELOG.md +50 -0
  3. package/locales/ar/auth.json +2 -0
  4. package/locales/ar/common.json +2 -0
  5. package/locales/ar/metadata.json +18 -0
  6. package/locales/ar/plugin.json +5 -6
  7. package/locales/ar/tool.json +2 -1
  8. package/locales/bg-BG/auth.json +2 -0
  9. package/locales/bg-BG/common.json +2 -0
  10. package/locales/bg-BG/error.json +3 -3
  11. package/locales/bg-BG/metadata.json +18 -0
  12. package/locales/bg-BG/plugin.json +5 -6
  13. package/locales/bg-BG/tool.json +2 -1
  14. package/locales/de-DE/auth.json +2 -0
  15. package/locales/de-DE/common.json +2 -0
  16. package/locales/de-DE/metadata.json +18 -0
  17. package/locales/de-DE/plugin.json +5 -6
  18. package/locales/de-DE/tool.json +2 -1
  19. package/locales/en-US/auth.json +2 -0
  20. package/locales/en-US/common.json +3 -1
  21. package/locales/en-US/metadata.json +18 -0
  22. package/locales/en-US/plugin.json +5 -6
  23. package/locales/en-US/tool.json +2 -1
  24. package/locales/es-ES/auth.json +2 -0
  25. package/locales/es-ES/common.json +2 -0
  26. package/locales/es-ES/metadata.json +18 -0
  27. package/locales/es-ES/plugin.json +5 -6
  28. package/locales/es-ES/tool.json +2 -1
  29. package/locales/fr-FR/auth.json +2 -0
  30. package/locales/fr-FR/common.json +2 -0
  31. package/locales/fr-FR/metadata.json +18 -0
  32. package/locales/fr-FR/plugin.json +5 -6
  33. package/locales/fr-FR/tool.json +2 -1
  34. package/locales/it-IT/auth.json +2 -0
  35. package/locales/it-IT/common.json +2 -0
  36. package/locales/it-IT/metadata.json +18 -0
  37. package/locales/it-IT/plugin.json +5 -6
  38. package/locales/it-IT/tool.json +2 -1
  39. package/locales/ja-JP/auth.json +2 -0
  40. package/locales/ja-JP/common.json +2 -0
  41. package/locales/ja-JP/metadata.json +18 -0
  42. package/locales/ja-JP/plugin.json +5 -6
  43. package/locales/ja-JP/tool.json +2 -1
  44. package/locales/ko-KR/auth.json +2 -0
  45. package/locales/ko-KR/common.json +2 -0
  46. package/locales/ko-KR/metadata.json +18 -0
  47. package/locales/ko-KR/plugin.json +5 -6
  48. package/locales/ko-KR/tool.json +2 -1
  49. package/locales/nl-NL/auth.json +2 -0
  50. package/locales/nl-NL/common.json +51 -49
  51. package/locales/nl-NL/metadata.json +18 -0
  52. package/locales/nl-NL/plugin.json +63 -64
  53. package/locales/nl-NL/tool.json +2 -1
  54. package/locales/pl-PL/auth.json +2 -0
  55. package/locales/pl-PL/common.json +2 -0
  56. package/locales/pl-PL/error.json +3 -3
  57. package/locales/pl-PL/metadata.json +18 -0
  58. package/locales/pl-PL/plugin.json +98 -99
  59. package/locales/pl-PL/tool.json +2 -1
  60. package/locales/pt-BR/auth.json +2 -0
  61. package/locales/pt-BR/common.json +2 -0
  62. package/locales/pt-BR/metadata.json +18 -0
  63. package/locales/pt-BR/plugin.json +5 -6
  64. package/locales/pt-BR/tool.json +2 -1
  65. package/locales/ru-RU/auth.json +2 -0
  66. package/locales/ru-RU/common.json +2 -0
  67. package/locales/ru-RU/error.json +3 -3
  68. package/locales/ru-RU/metadata.json +18 -0
  69. package/locales/ru-RU/plugin.json +5 -6
  70. package/locales/ru-RU/tool.json +2 -1
  71. package/locales/tr-TR/auth.json +2 -0
  72. package/locales/tr-TR/common.json +2 -0
  73. package/locales/tr-TR/metadata.json +18 -0
  74. package/locales/tr-TR/plugin.json +5 -6
  75. package/locales/tr-TR/tool.json +2 -1
  76. package/locales/vi-VN/auth.json +2 -0
  77. package/locales/vi-VN/common.json +2 -0
  78. package/locales/vi-VN/metadata.json +18 -0
  79. package/locales/vi-VN/plugin.json +5 -6
  80. package/locales/vi-VN/tool.json +2 -1
  81. package/locales/zh-CN/auth.json +2 -0
  82. package/locales/zh-CN/common.json +2 -0
  83. package/locales/zh-CN/metadata.json +18 -0
  84. package/locales/zh-CN/plugin.json +5 -6
  85. package/locales/zh-CN/tool.json +2 -1
  86. package/locales/zh-TW/auth.json +2 -0
  87. package/locales/zh-TW/common.json +2 -0
  88. package/locales/zh-TW/metadata.json +18 -0
  89. package/locales/zh-TW/plugin.json +5 -6
  90. package/locales/zh-TW/tool.json +2 -1
  91. package/package.json +2 -1
  92. package/public/manifest.json +76 -16
  93. package/public/og/cover.png +0 -0
  94. package/public/screenshots/shot-1.desktop.png +0 -0
  95. package/public/screenshots/shot-1.mobile.png +0 -0
  96. package/public/screenshots/shot-2.desktop.png +0 -0
  97. package/public/screenshots/shot-2.mobile.png +0 -0
  98. package/public/screenshots/shot-3.desktop.png +0 -0
  99. package/public/screenshots/shot-3.mobile.png +0 -0
  100. package/public/screenshots/shot-4.desktop.png +0 -0
  101. package/public/screenshots/shot-4.mobile.png +0 -0
  102. package/public/screenshots/shot-5.desktop.png +0 -0
  103. package/public/screenshots/shot-5.mobile.png +0 -0
  104. package/src/app/(auth)/login/[[...login]]/page.tsx +14 -7
  105. package/src/app/(auth)/signup/[[...signup]]/page.tsx +14 -7
  106. package/src/app/(main)/(mobile)/me/(home)/__tests__/UserBanner.test.tsx +80 -0
  107. package/src/app/(main)/(mobile)/me/(home)/__tests__/useCategory.test.tsx +116 -0
  108. package/src/app/(main)/(mobile)/me/(home)/features/Category.tsx +15 -0
  109. package/src/app/(main)/(mobile)/me/(home)/features/UserBanner.tsx +37 -0
  110. package/src/app/(main)/(mobile)/me/(home)/features/useCategory.tsx +110 -0
  111. package/src/app/(main)/(mobile)/me/(home)/page.tsx +38 -0
  112. package/src/app/(main)/(mobile)/me/data/features/Category.tsx +48 -0
  113. package/src/app/(main)/(mobile)/me/data/features/Header.tsx +33 -0
  114. package/src/app/(main)/(mobile)/me/data/layout.tsx +13 -0
  115. package/src/app/(main)/(mobile)/me/data/loading.tsx +5 -0
  116. package/src/app/(main)/(mobile)/me/data/page.tsx +27 -0
  117. package/src/app/(main)/(mobile)/me/profile/features/Category.tsx +45 -0
  118. package/src/app/(main)/(mobile)/me/profile/features/Header.tsx +33 -0
  119. package/src/app/(main)/(mobile)/me/profile/layout.tsx +16 -0
  120. package/src/app/(main)/(mobile)/me/profile/loading.tsx +5 -0
  121. package/src/app/(main)/(mobile)/me/profile/page.tsx +28 -0
  122. package/src/app/(main)/(mobile)/me/settings/features/Category.tsx +15 -0
  123. package/src/app/(main)/(mobile)/me/settings/features/Header.tsx +33 -0
  124. package/src/app/(main)/(mobile)/me/settings/features/useCategory.tsx +57 -0
  125. package/src/app/(main)/(mobile)/me/settings/layout.tsx +13 -0
  126. package/src/app/(main)/(mobile)/me/settings/loading.tsx +5 -0
  127. package/src/app/(main)/(mobile)/me/settings/page.tsx +28 -0
  128. package/src/app/(main)/_layout/Mobile.tsx +5 -4
  129. package/src/app/(main)/chat/(workspace)/page.tsx +21 -1
  130. package/src/app/(main)/market/page.tsx +17 -8
  131. package/src/app/(main)/profile/[[...slugs]]/Client.tsx +74 -0
  132. package/src/app/(main)/profile/[[...slugs]]/page.tsx +21 -0
  133. package/src/app/(main)/profile/_layout/Mobile/Header.tsx +26 -0
  134. package/src/app/(main)/profile/_layout/Mobile/index.tsx +16 -0
  135. package/src/app/(main)/profile/layout.tsx +20 -0
  136. package/src/app/(main)/profile/loading.tsx +23 -0
  137. package/src/app/(main)/settings/_layout/Mobile/Header.tsx +2 -1
  138. package/src/app/(main)/settings/about/page.tsx +5 -2
  139. package/src/app/(main)/settings/agent/page.tsx +5 -3
  140. package/src/app/(main)/settings/common/page.tsx +5 -3
  141. package/src/app/(main)/settings/hooks/useCategory.tsx +7 -13
  142. package/src/app/(main)/settings/llm/page.tsx +5 -2
  143. package/src/app/(main)/settings/sync/page.tsx +5 -3
  144. package/src/app/(main)/settings/tts/page.tsx +5 -3
  145. package/src/app/(main)/welcome/page.tsx +19 -6
  146. package/src/app/@modal/layout.tsx +3 -0
  147. package/src/app/layout.tsx +1 -1
  148. package/src/app/metadata.ts +44 -54
  149. package/src/app/page.tsx +4 -4
  150. package/src/components/Cell/Divider.tsx +3 -2
  151. package/src/components/Cell/index.tsx +28 -18
  152. package/src/components/StructuredData/index.tsx +12 -0
  153. package/src/features/User/DataStatistics.tsx +3 -1
  154. package/src/features/User/UserLoginOrSignup.tsx +2 -2
  155. package/src/features/User/UserPanel/PanelContent.tsx +9 -3
  156. package/src/features/User/UserPanel/useMenu.tsx +45 -29
  157. package/src/features/User/__tests__/PanelContent.test.tsx +7 -0
  158. package/src/features/User/__tests__/useMenu.test.tsx +142 -0
  159. package/src/hooks/usePWAInstall.ts +18 -0
  160. package/src/layout/AuthProvider/Clerk/useAppearance.ts +5 -4
  161. package/src/locales/default/auth.ts +2 -0
  162. package/src/locales/default/common.ts +3 -1
  163. package/src/locales/default/index.ts +2 -0
  164. package/src/locales/default/metadata.ts +20 -0
  165. package/src/server/ld.ts +218 -0
  166. package/src/server/metadata.ts +96 -0
  167. package/src/server/translation.ts +11 -1
  168. package/src/store/user/slices/auth/selectors.ts +2 -1
  169. package/src/utils/genOG.ts +20 -0
  170. package/public/screenshots/screenshot-1.png +0 -0
  171. package/public/screenshots/screenshot-2.png +0 -0
  172. package/public/screenshots/screenshot-3.png +0 -0
  173. package/public/screenshots/screenshot-4.png +0 -0
  174. package/src/app/(auth)/login/[[...login]]/PageTitle.tsx +0 -13
  175. package/src/app/(auth)/profile/[[...slugs]]/PageTitle.tsx +0 -13
  176. package/src/app/(auth)/profile/[[...slugs]]/page.tsx +0 -14
  177. package/src/app/(auth)/signup/[[...signup]]/PageTitle.tsx +0 -13
  178. package/src/app/(main)/(mobile)/me/features/Cate.tsx +0 -33
  179. package/src/app/(main)/(mobile)/me/features/ExtraCate.tsx +0 -24
  180. package/src/app/(main)/(mobile)/me/features/useExtraCate.tsx +0 -62
  181. package/src/app/(main)/(mobile)/me/page.tsx +0 -32
  182. /package/public/icons/{maskable-icon-192x192.png → icon-192x192.maskable.png} +0 -0
  183. /package/public/icons/{maskable-icon-512x512.png → icon-512x512.maskable.png} +0 -0
  184. /package/src/app/(main)/(mobile)/me/{features → (home)/features}/Header.tsx +0 -0
  185. /package/src/app/(main)/(mobile)/me/{layout.tsx → (home)/layout.tsx} +0 -0
  186. /package/src/app/(main)/(mobile)/me/{loading.tsx → (home)/loading.tsx} +0 -0
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+
3
+ import { useRouter } from 'next/navigation';
4
+ import { memo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import { enableAuth } from '@/const/auth';
8
+ import DataStatistics from '@/features/User/DataStatistics';
9
+ import UserInfo from '@/features/User/UserInfo';
10
+ import UserLoginOrSignup from '@/features/User/UserLoginOrSignup';
11
+ import { useUserStore } from '@/store/user';
12
+ import { authSelectors } from '@/store/user/selectors';
13
+
14
+ const UserBanner = memo(() => {
15
+ const router = useRouter();
16
+ const isLoginWithAuth = useUserStore(authSelectors.isLoginWithAuth);
17
+
18
+ return (
19
+ <Flexbox gap={12} paddingBlock={8}>
20
+ {!enableAuth ? (
21
+ <>
22
+ <UserInfo />
23
+ <DataStatistics paddingInline={12} />
24
+ </>
25
+ ) : isLoginWithAuth ? (
26
+ <>
27
+ <UserInfo onClick={() => router.push('/me/profile')} />
28
+ <DataStatistics paddingInline={12} />
29
+ </>
30
+ ) : (
31
+ <UserLoginOrSignup onClick={() => router.push('/login')} />
32
+ )}
33
+ </Flexbox>
34
+ );
35
+ });
36
+
37
+ export default UserBanner;
@@ -0,0 +1,110 @@
1
+ import { DiscordIcon } from '@lobehub/ui';
2
+ import { Book, CircleUserRound, Database, Download, Feather, Settings2 } from 'lucide-react';
3
+ import { useRouter } from 'next/navigation';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { CellProps } from '@/components/Cell';
7
+ import { enableAuth } from '@/const/auth';
8
+ import { DISCORD, DOCUMENTS, FEEDBACK } from '@/const/url';
9
+ import { usePWAInstall } from '@/hooks/usePWAInstall';
10
+ import { useUserStore } from '@/store/user';
11
+ import { authSelectors } from '@/store/user/slices/auth/selectors';
12
+
13
+ import { useCategory as useSettingsCategory } from '../../settings/features/useCategory';
14
+
15
+ export const useCategory = () => {
16
+ const router = useRouter();
17
+ const { canInstall, install } = usePWAInstall();
18
+ const { t } = useTranslation(['common', 'setting', 'auth']);
19
+ const [isLogin, isLoginWithAuth, isLoginWithClerk] = useUserStore((s) => [
20
+ authSelectors.isLogin(s),
21
+ authSelectors.isLoginWithAuth(s),
22
+ authSelectors.isLoginWithClerk(s),
23
+ ]);
24
+
25
+ const profile: CellProps[] = [
26
+ {
27
+ icon: CircleUserRound,
28
+ key: 'profile',
29
+ label: t('userPanel.profile'),
30
+ onClick: () => router.push('/me/profile'),
31
+ },
32
+ ];
33
+
34
+ const settings: CellProps[] = [
35
+ {
36
+ icon: Settings2,
37
+ key: 'setting',
38
+ label: t('userPanel.setting'),
39
+ onClick: () => router.push('/me/settings'),
40
+ },
41
+ {
42
+ type: 'divider',
43
+ },
44
+ ];
45
+
46
+ const pwa: CellProps[] = [
47
+ {
48
+ icon: Download,
49
+ key: 'pwa',
50
+ label: t('installPWA'),
51
+ onClick: () => install(),
52
+ },
53
+ {
54
+ type: 'divider',
55
+ },
56
+ ];
57
+
58
+ const settingsWithoutAuth = [
59
+ ...useSettingsCategory(),
60
+ {
61
+ type: 'divider',
62
+ },
63
+ ];
64
+
65
+ const data: CellProps[] = [
66
+ {
67
+ icon: Database,
68
+ key: 'data',
69
+ label: t('userPanel.data'),
70
+ onClick: () => router.push('/me/data'),
71
+ },
72
+ {
73
+ type: 'divider',
74
+ },
75
+ ];
76
+
77
+ const helps: CellProps[] = [
78
+ {
79
+ icon: Book,
80
+ key: 'docs',
81
+ label: t('document'),
82
+ onClick: () => window.open(DOCUMENTS, '__blank'),
83
+ },
84
+ {
85
+ icon: Feather,
86
+ key: 'feedback',
87
+ label: t('feedback'),
88
+ onClick: () => window.open(FEEDBACK, '__blank'),
89
+ },
90
+ {
91
+ icon: DiscordIcon,
92
+ key: 'discord',
93
+ label: 'Discord',
94
+ onClick: () => window.open(DISCORD, '__blank'),
95
+ },
96
+ ];
97
+
98
+ const mainItems = [
99
+ {
100
+ type: 'divider',
101
+ },
102
+ ...(isLoginWithClerk ? profile : []),
103
+ ...(enableAuth ? (isLoginWithAuth ? settings : []) : settingsWithoutAuth),
104
+ ...(canInstall ? pwa : []),
105
+ ...(isLogin ? data : []),
106
+ ...helps,
107
+ ].filter(Boolean) as CellProps[];
108
+
109
+ return mainItems;
110
+ };
@@ -0,0 +1,38 @@
1
+ import { redirect } from 'next/navigation';
2
+ import { Center } from 'react-layout-kit';
3
+
4
+ import BrandWatermark from '@/components/BrandWatermark';
5
+ import { metadataModule } from '@/server/metadata';
6
+ import { translation } from '@/server/translation';
7
+ import { isMobileDevice } from '@/utils/responsive';
8
+
9
+ import Category from './features/Category';
10
+ import UserBanner from './features/UserBanner';
11
+
12
+ export const generateMetadata = async () => {
13
+ const { t } = await translation('common');
14
+ return metadataModule.generate({
15
+ title: t('tab.me'),
16
+ url: '/me',
17
+ });
18
+ };
19
+
20
+ const Page = () => {
21
+ const mobile = isMobileDevice();
22
+
23
+ if (!mobile) return redirect('/chat');
24
+
25
+ return (
26
+ <>
27
+ <UserBanner />
28
+ <Category />
29
+ <Center padding={16}>
30
+ <BrandWatermark />
31
+ </Center>
32
+ </>
33
+ );
34
+ };
35
+
36
+ Page.displayName = 'Me';
37
+
38
+ export default Page;
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import Cell, { CellProps } from '@/components/Cell';
7
+ import DataImporter from '@/features/DataImporter';
8
+ import { configService } from '@/services/config';
9
+
10
+ const Category = memo(() => {
11
+ const { t } = useTranslation('common');
12
+ const items: CellProps[] = [
13
+ {
14
+ key: 'allAgent',
15
+ label: t('exportType.allAgent'),
16
+ onClick: configService.exportAgents,
17
+ },
18
+ {
19
+ key: 'allAgentWithMessage',
20
+ label: t('exportType.allAgentWithMessage'),
21
+ onClick: configService.exportSessions,
22
+ },
23
+ {
24
+ key: 'globalSetting',
25
+ label: t('exportType.globalSetting'),
26
+ onClick: configService.exportSettings,
27
+ },
28
+ {
29
+ type: 'divider',
30
+ },
31
+ {
32
+ key: 'all',
33
+ label: t('exportType.all'),
34
+ onClick: configService.exportAll,
35
+ },
36
+ {
37
+ type: 'divider',
38
+ },
39
+ {
40
+ key: 'import',
41
+ label: <DataImporter>{t('import')}</DataImporter>,
42
+ },
43
+ ];
44
+
45
+ return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
46
+ });
47
+
48
+ export default Category;
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
4
+ import { useRouter } from 'next/navigation';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import { mobileHeaderSticky } from '@/styles/mobileHeader';
10
+
11
+ const Header = memo(() => {
12
+ const { t } = useTranslation('common');
13
+
14
+ const router = useRouter();
15
+ return (
16
+ <MobileNavBar
17
+ center={
18
+ <MobileNavBarTitle
19
+ title={
20
+ <Flexbox align={'center'} gap={4} horizontal>
21
+ {t('userPanel.data')}
22
+ </Flexbox>
23
+ }
24
+ />
25
+ }
26
+ onBackClick={() => router.push('/me')}
27
+ showBackButton
28
+ style={mobileHeaderSticky}
29
+ />
30
+ );
31
+ });
32
+
33
+ export default Header;
@@ -0,0 +1,13 @@
1
+ import { PropsWithChildren } from 'react';
2
+
3
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
4
+
5
+ import Header from './features/Header';
6
+
7
+ const Layout = ({ children }: PropsWithChildren) => {
8
+ return <MobileContentLayout header={<Header />}>{children}</MobileContentLayout>;
9
+ };
10
+
11
+ Layout.displayName = 'MeDataLayout';
12
+
13
+ export default Layout;
@@ -0,0 +1,5 @@
1
+ import SkeletonLoading from '@/components/SkeletonLoading';
2
+
3
+ export default () => {
4
+ return <SkeletonLoading paragraph={{ rows: 8 }} />;
5
+ };
@@ -0,0 +1,27 @@
1
+ import { redirect } from 'next/navigation';
2
+
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
5
+ import { isMobileDevice } from '@/utils/responsive';
6
+
7
+ import Category from './features/Category';
8
+
9
+ export const generateMetadata = async () => {
10
+ const { t } = await translation('common');
11
+ return metadataModule.generate({
12
+ title: t('userPanel.data'),
13
+ url: '/me/data',
14
+ });
15
+ };
16
+
17
+ const Page = () => {
18
+ const mobile = isMobileDevice();
19
+
20
+ if (!mobile) return redirect('/chat');
21
+
22
+ return <Category />;
23
+ };
24
+
25
+ Page.displayName = 'MeData';
26
+
27
+ export default Page;
@@ -0,0 +1,45 @@
1
+ 'use client';
2
+
3
+ import { LogOut, ShieldCheck, UserCircle } from 'lucide-react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ import Cell, { CellProps } from '@/components/Cell';
9
+ import { useUserStore } from '@/store/user';
10
+
11
+ const Category = memo(() => {
12
+ const router = useRouter();
13
+ const { t } = useTranslation('auth');
14
+ const signOut = useUserStore((s) => s.logout);
15
+ const items: CellProps[] = [
16
+ {
17
+ icon: UserCircle,
18
+ key: 'profile',
19
+ label: t('profile'),
20
+ onClick: () => router.push('/profile'),
21
+ },
22
+ {
23
+ icon: ShieldCheck,
24
+ key: 'security',
25
+ label: t('security'),
26
+ onClick: () => router.push('/profile/security'),
27
+ },
28
+ {
29
+ type: 'divider',
30
+ },
31
+ {
32
+ icon: LogOut,
33
+ key: 'logout',
34
+ label: t('signout', { ns: 'auth' }),
35
+ onClick: () => {
36
+ signOut();
37
+ router.push('/login');
38
+ },
39
+ },
40
+ ];
41
+
42
+ return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
43
+ });
44
+
45
+ export default Category;
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
4
+ import { useRouter } from 'next/navigation';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import { mobileHeaderSticky } from '@/styles/mobileHeader';
10
+
11
+ const Header = memo(() => {
12
+ const { t } = useTranslation('common');
13
+
14
+ const router = useRouter();
15
+ return (
16
+ <MobileNavBar
17
+ center={
18
+ <MobileNavBarTitle
19
+ title={
20
+ <Flexbox align={'center'} gap={4} horizontal>
21
+ {t('userPanel.profile')}
22
+ </Flexbox>
23
+ }
24
+ />
25
+ }
26
+ onBackClick={() => router.push('/me')}
27
+ showBackButton
28
+ style={mobileHeaderSticky}
29
+ />
30
+ );
31
+ });
32
+
33
+ export default Header;
@@ -0,0 +1,16 @@
1
+ import { notFound } from 'next/navigation';
2
+ import { PropsWithChildren } from 'react';
3
+
4
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
5
+ import { enableClerk } from '@/const/auth';
6
+
7
+ import Header from './features/Header';
8
+
9
+ const Layout = ({ children }: PropsWithChildren) => {
10
+ if (!enableClerk) return notFound();
11
+ return <MobileContentLayout header={<Header />}>{children}</MobileContentLayout>;
12
+ };
13
+
14
+ Layout.displayName = 'MeProfileLayout';
15
+
16
+ export default Layout;
@@ -0,0 +1,5 @@
1
+ import SkeletonLoading from '@/components/SkeletonLoading';
2
+
3
+ export default () => {
4
+ return <SkeletonLoading paragraph={{ rows: 8 }} />;
5
+ };
@@ -0,0 +1,28 @@
1
+ import { redirect } from 'next/navigation';
2
+
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
5
+ import { isMobileDevice } from '@/utils/responsive';
6
+
7
+ import Category from './features/Category';
8
+
9
+ export const generateMetadata = async () => {
10
+ const { t } = await translation('clerk');
11
+ return metadataModule.generate({
12
+ description: t('userProfile.navbar.title'),
13
+ title: t('userProfile.navbar.description'),
14
+ url: '/me/profile',
15
+ });
16
+ };
17
+
18
+ const Page = () => {
19
+ const mobile = isMobileDevice();
20
+
21
+ if (!mobile) return redirect('/profile');
22
+
23
+ return <Category />;
24
+ };
25
+
26
+ Page.displayName = 'MeProfile';
27
+
28
+ export default Page;
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import { memo } from 'react';
4
+
5
+ import Cell from '@/components/Cell';
6
+
7
+ import { useCategory } from './useCategory';
8
+
9
+ const Category = memo(() => {
10
+ const items = useCategory();
11
+
12
+ return items?.map((item, index) => <Cell key={item.key || index} {...item} />);
13
+ });
14
+
15
+ export default Category;
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
4
+ import { useRouter } from 'next/navigation';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import { mobileHeaderSticky } from '@/styles/mobileHeader';
10
+
11
+ const Header = memo(() => {
12
+ const { t } = useTranslation('common');
13
+
14
+ const router = useRouter();
15
+ return (
16
+ <MobileNavBar
17
+ center={
18
+ <MobileNavBarTitle
19
+ title={
20
+ <Flexbox align={'center'} gap={4} horizontal>
21
+ {t('userPanel.setting')}
22
+ </Flexbox>
23
+ }
24
+ />
25
+ }
26
+ onBackClick={() => router.push('/me')}
27
+ showBackButton
28
+ style={mobileHeaderSticky}
29
+ />
30
+ );
31
+ });
32
+
33
+ export default Header;
@@ -0,0 +1,57 @@
1
+ import { Tag } from 'antd';
2
+ import { Bot, Brain, Cloudy, Info, Mic2, Settings2 } from 'lucide-react';
3
+ import { useRouter } from 'next/navigation';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Flexbox } from 'react-layout-kit';
6
+ import urlJoin from 'url-join';
7
+
8
+ import { CellProps } from '@/components/Cell';
9
+ import { SettingsTabs } from '@/store/global/initialState';
10
+ import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
11
+
12
+ export const useCategory = () => {
13
+ const router = useRouter();
14
+ const { t } = useTranslation('setting');
15
+ const { enableWebrtc, showLLM } = useServerConfigStore(featureFlagsSelectors);
16
+
17
+ const items: CellProps[] = [
18
+ {
19
+ icon: Settings2,
20
+ key: SettingsTabs.Common,
21
+ label: t('tab.common'),
22
+ },
23
+ enableWebrtc && {
24
+ icon: Cloudy,
25
+ key: SettingsTabs.Sync,
26
+ label: (
27
+ <Flexbox align={'center'} gap={8} horizontal>
28
+ {t('tab.sync')}
29
+ <Tag bordered={false} color={'warning'}>
30
+ {t('tab.experiment')}
31
+ </Tag>
32
+ </Flexbox>
33
+ ),
34
+ },
35
+ showLLM && {
36
+ icon: Brain,
37
+ key: SettingsTabs.LLM,
38
+ label: t('tab.llm'),
39
+ },
40
+ { icon: Mic2, key: SettingsTabs.TTS, label: t('tab.tts') },
41
+ {
42
+ icon: Bot,
43
+ key: SettingsTabs.Agent,
44
+ label: t('tab.agent'),
45
+ },
46
+ {
47
+ icon: Info,
48
+ key: SettingsTabs.About,
49
+ label: t('tab.about'),
50
+ },
51
+ ].filter(Boolean) as CellProps[];
52
+
53
+ return items.map((item) => ({
54
+ ...item,
55
+ onClick: () => router.push(urlJoin('/settings', item.key as SettingsTabs)),
56
+ }));
57
+ };
@@ -0,0 +1,13 @@
1
+ import { PropsWithChildren } from 'react';
2
+
3
+ import MobileContentLayout from '@/components/server/MobileNavLayout';
4
+
5
+ import Header from './features/Header';
6
+
7
+ const Layout = ({ children }: PropsWithChildren) => {
8
+ return <MobileContentLayout header={<Header />}>{children}</MobileContentLayout>;
9
+ };
10
+
11
+ Layout.displayName = 'MeSettingsLayout';
12
+
13
+ export default Layout;
@@ -0,0 +1,5 @@
1
+ import SkeletonLoading from '@/components/SkeletonLoading';
2
+
3
+ export default () => {
4
+ return <SkeletonLoading paragraph={{ rows: 8 }} />;
5
+ };
@@ -0,0 +1,28 @@
1
+ import { redirect } from 'next/navigation';
2
+
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
5
+ import { isMobileDevice } from '@/utils/responsive';
6
+
7
+ import Category from './features/Category';
8
+
9
+ export const generateMetadata = async () => {
10
+ const { t } = await translation('setting');
11
+ return metadataModule.generate({
12
+ description: t('header.desc'),
13
+ title: t('header.title'),
14
+ url: '/me/settings',
15
+ });
16
+ };
17
+
18
+ const Page = () => {
19
+ const mobile = isMobileDevice();
20
+
21
+ if (!mobile) return redirect('/settings/common');
22
+
23
+ return <Category />;
24
+ };
25
+
26
+ Page.displayName = 'MeSettings';
27
+
28
+ export default Page;
@@ -1,24 +1,25 @@
1
1
  'use client';
2
2
 
3
3
  import { usePathname } from 'next/navigation';
4
+ import qs from 'query-string';
4
5
  import { memo } from 'react';
5
6
 
6
7
  import { useQuery } from '@/hooks/useQuery';
7
8
 
8
9
  import { LayoutProps } from './type';
9
10
 
10
- const MOBILE_IGNORE_NAV_ROUTES = ['/settings/', '/chat/'];
11
+ const MOBILE_NAV_ROUTES = new Set(['/chat', '/market', '/me']);
11
12
 
12
13
  const Layout = memo(({ children, nav }: LayoutProps) => {
13
14
  const { showMobileWorkspace } = useQuery();
14
15
  const pathname = usePathname();
15
- const hideNav =
16
- showMobileWorkspace || MOBILE_IGNORE_NAV_ROUTES.some((path) => pathname.startsWith(path));
16
+ const { url } = qs.parseUrl(pathname);
17
+ const showNav = !showMobileWorkspace && MOBILE_NAV_ROUTES.has(url);
17
18
 
18
19
  return (
19
20
  <>
20
21
  {children}
21
- {!hideNav && nav}
22
+ {showNav && nav}
22
23
  </>
23
24
  );
24
25
  });
@@ -1,13 +1,33 @@
1
+ import StructuredData from '@/components/StructuredData';
2
+ import { ldModule } from '@/server/ld';
3
+ import { metadataModule } from '@/server/metadata';
4
+ import { translation } from '@/server/translation';
1
5
  import { isMobileDevice } from '@/utils/responsive';
2
6
 
3
7
  import PageTitle from '../features/PageTitle';
4
8
  import TelemetryNotification from './features/TelemetryNotification';
5
9
 
6
- const Page = () => {
10
+ export const generateMetadata = async () => {
11
+ const { t } = await translation('metadata');
12
+ return metadataModule.generate({
13
+ description: t('chat.description'),
14
+ title: t('chat.title'),
15
+ url: '/chat',
16
+ });
17
+ };
18
+
19
+ const Page = async () => {
7
20
  const mobile = isMobileDevice();
21
+ const { t } = await translation('metadata');
22
+ const ld = ldModule.generate({
23
+ description: t('chat.description'),
24
+ title: t('chat.title'),
25
+ url: '/chat',
26
+ });
8
27
 
9
28
  return (
10
29
  <>
30
+ <StructuredData ld={ld} />
11
31
  <PageTitle />
12
32
  <TelemetryNotification mobile={mobile} />
13
33
  </>