@lobehub/chat 0.152.12 → 0.153.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 (83) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +8 -8
  3. package/README.zh-CN.md +8 -8
  4. package/locales/ar/common.json +2 -0
  5. package/locales/bg-BG/common.json +2 -0
  6. package/locales/de-DE/common.json +2 -0
  7. package/locales/en-US/common.json +2 -0
  8. package/locales/es-ES/common.json +2 -0
  9. package/locales/fr-FR/common.json +2 -0
  10. package/locales/it-IT/common.json +2 -0
  11. package/locales/ja-JP/common.json +2 -0
  12. package/locales/ko-KR/common.json +2 -0
  13. package/locales/nl-NL/common.json +2 -0
  14. package/locales/pl-PL/common.json +2 -0
  15. package/locales/pt-BR/common.json +2 -0
  16. package/locales/ru-RU/common.json +2 -0
  17. package/locales/tr-TR/common.json +2 -0
  18. package/locales/vi-VN/common.json +2 -0
  19. package/locales/zh-CN/common.json +2 -0
  20. package/locales/zh-TW/common.json +2 -0
  21. package/package.json +1 -1
  22. package/src/app/(main)/(mobile)/me/features/AvatarBanner.tsx +12 -40
  23. package/src/app/(main)/(mobile)/me/features/Header.tsx +37 -0
  24. package/src/app/(main)/(mobile)/me/features/style.ts +29 -0
  25. package/src/app/(main)/(mobile)/me/layout.tsx +7 -1
  26. package/src/app/(main)/(mobile)/me/loading.tsx +22 -7
  27. package/src/app/(main)/(mobile)/me/page.tsx +4 -5
  28. package/src/app/(main)/@nav/_layout/Mobile.tsx +2 -2
  29. package/src/app/(main)/chat/(desktop)/features/ChatHeader/Main.tsx +4 -9
  30. package/src/app/(main)/chat/(desktop)/features/SideBar/SystemRole/index.tsx +5 -6
  31. package/src/app/(main)/chat/features/SettingButton.tsx +3 -17
  32. package/src/app/(main)/chat/features/TopicListContent/Header.tsx +1 -1
  33. package/src/app/(main)/chat/settings/_layout/Desktop/Header.tsx +2 -1
  34. package/src/app/(main)/chat/settings/_layout/Mobile/Header.tsx +2 -1
  35. package/src/app/(main)/chat/settings/modal/page.tsx +23 -0
  36. package/src/app/(main)/market/@detail/features/{AgentDetailContent/index.tsx → AgentDetailContent.tsx} +6 -12
  37. package/src/app/(main)/market/@detail/features/Banner.tsx +46 -0
  38. package/src/app/(main)/market/@detail/features/{AgentDetailContent/Header.tsx → Header.tsx} +4 -18
  39. package/src/app/(main)/market/@detail/features/{AgentDetailContent/Loading.tsx → Loading.tsx} +5 -4
  40. package/src/app/(main)/market/@detail/features/{AgentDetailContent/style.ts → style.ts} +0 -3
  41. package/src/app/(main)/market/features/AgentCard/AgentCardBanner.tsx +13 -8
  42. package/src/app/(main)/market/loading.tsx +13 -1
  43. package/src/app/(main)/settings/@category/features/CategoryContent.tsx +5 -1
  44. package/src/app/(main)/settings/modal/page.tsx +27 -0
  45. package/src/app/@modal/(.)settings/modal/index.tsx +40 -0
  46. package/src/app/@modal/(.)settings/modal/layout.tsx +32 -0
  47. package/src/app/@modal/(.)settings/modal/loading.tsx +5 -0
  48. package/src/app/@modal/(.)settings/modal/page.tsx +19 -0
  49. package/src/app/@modal/_layout/SettingModalLayout.tsx +59 -0
  50. package/src/app/@modal/chat/(.)settings/modal/features/CategoryContent.tsx +37 -0
  51. package/src/app/@modal/chat/(.)settings/modal/features/useCategory.tsx +54 -0
  52. package/src/app/@modal/chat/(.)settings/modal/layout.tsx +55 -0
  53. package/src/app/@modal/chat/(.)settings/modal/loading.tsx +5 -0
  54. package/src/app/@modal/chat/(.)settings/modal/page.tsx +55 -0
  55. package/src/app/@modal/default.tsx +3 -0
  56. package/src/app/@modal/error.tsx +5 -0
  57. package/src/app/@modal/layout.tsx +30 -0
  58. package/src/app/@modal/loading.tsx +5 -0
  59. package/src/app/layout.tsx +6 -2
  60. package/src/components/Cell/Divider.tsx +2 -0
  61. package/src/components/Cell/index.tsx +5 -1
  62. package/src/components/server/MobileNavLayout.tsx +1 -0
  63. package/src/features/Conversation/Messages/index.ts +9 -12
  64. package/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx +15 -6
  65. package/src/features/Conversation/components/InboxWelcome/QuestionSuggest.tsx +9 -4
  66. package/src/features/Conversation/components/InboxWelcome/index.tsx +5 -3
  67. package/src/features/DataImporter/index.tsx +4 -1
  68. package/src/features/MobileTabBar/index.tsx +3 -3
  69. package/src/features/User/PlanTag.tsx +45 -0
  70. package/src/features/User/UserInfo.tsx +26 -9
  71. package/src/features/User/UserPanel/useMenu.tsx +31 -16
  72. package/src/hooks/useInterceptingRoutes.test.ts +70 -0
  73. package/src/hooks/useInterceptingRoutes.ts +46 -0
  74. package/src/hooks/useQuery.test.ts +0 -1
  75. package/src/hooks/useQuery.ts +2 -1
  76. package/src/layout/GlobalProvider/StoreInitialization.tsx +12 -5
  77. package/src/locales/default/common.ts +4 -2
  78. package/src/store/global/initialState.ts +9 -0
  79. package/src/styles/mobileHeader.ts +7 -0
  80. package/src/features/User/UserPanel/UserInfo.tsx +0 -35
  81. /package/src/app/(main)/market/@detail/features/{AgentDetailContent/Comment.tsx → Comment.tsx} +0 -0
  82. /package/src/app/(main)/market/@detail/features/{AgentDetailContent/TokenTag.tsx → TokenTag.tsx} +0 -0
  83. /package/src/{app/(main)/chat/components → components}/SidebarHeader/index.tsx +0 -0
@@ -1,4 +1,4 @@
1
- import { DiscordIcon, Icon } from '@lobehub/ui';
1
+ import { ActionIcon, DiscordIcon, Icon } from '@lobehub/ui';
2
2
  import { Badge } from 'antd';
3
3
  import {
4
4
  Book,
@@ -7,22 +7,29 @@ import {
7
7
  HardDriveUpload,
8
8
  LifeBuoy,
9
9
  Mail,
10
+ Maximize,
10
11
  Settings2,
11
12
  } from 'lucide-react';
12
13
  import Link from 'next/link';
13
14
  import { PropsWithChildren, useCallback } from 'react';
14
15
  import { useTranslation } from 'react-i18next';
15
16
  import { Flexbox } from 'react-layout-kit';
17
+ import urlJoin from 'url-join';
16
18
 
17
19
  import { type MenuProps } from '@/components/Menu';
18
20
  import { DISCORD, DOCUMENTS, EMAIL_SUPPORT, GITHUB_ISSUES } from '@/const/url';
19
21
  import DataImporter from '@/features/DataImporter';
22
+ import { useOpenSettings } from '@/hooks/useInterceptingRoutes';
23
+ import { useQueryRoute } from '@/hooks/useQueryRoute';
20
24
  import { configService } from '@/services/config';
25
+ import { SettingsTabs } from '@/store/global/initialState';
21
26
 
22
27
  import { useNewVersion } from './useNewVersion';
23
28
 
24
29
  export const useMenu = () => {
30
+ const router = useQueryRoute();
25
31
  const hasNewVersion = useNewVersion();
32
+ const openSettings = useOpenSettings();
26
33
  const { t } = useTranslation(['common', 'setting']);
27
34
 
28
35
  const NewVersionBadge = useCallback(
@@ -38,6 +45,29 @@ export const useMenu = () => {
38
45
  [t],
39
46
  );
40
47
 
48
+ const settings: MenuProps['items'] = [
49
+ {
50
+ icon: <Icon icon={Settings2} />,
51
+ key: 'setting',
52
+ label: (
53
+ <Flexbox align={'center'} horizontal>
54
+ <Flexbox flex={1} horizontal onClick={openSettings}>
55
+ <NewVersionBadge showBadge={hasNewVersion}>{t('userPanel.setting')}</NewVersionBadge>
56
+ </Flexbox>
57
+ <ActionIcon
58
+ icon={Maximize}
59
+ onClick={() => router.push(urlJoin('/settings', SettingsTabs.Common))}
60
+ size={'small'}
61
+ title={t('fullscreen')}
62
+ />
63
+ </Flexbox>
64
+ ),
65
+ },
66
+ {
67
+ type: 'divider',
68
+ },
69
+ ];
70
+
41
71
  const exports: MenuProps['items'] = [
42
72
  {
43
73
  icon: <Icon icon={HardDriveUpload} />,
@@ -79,21 +109,6 @@ export const useMenu = () => {
79
109
  },
80
110
  ];
81
111
 
82
- const settings: MenuProps['items'] = [
83
- {
84
- icon: <Icon icon={Settings2} />,
85
- key: 'setting',
86
- label: (
87
- <Link href={'/settings'}>
88
- <NewVersionBadge showBadge={hasNewVersion}>{t('userPanel.setting')}</NewVersionBadge>
89
- </Link>
90
- ),
91
- },
92
- {
93
- type: 'divider',
94
- },
95
- ];
96
-
97
112
  const helps: MenuProps['items'] = [
98
113
  {
99
114
  icon: <Icon icon={DiscordIcon} />,
@@ -0,0 +1,70 @@
1
+ import { renderHook } from '@testing-library/react';
2
+ import urlJoin from 'url-join';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+
5
+ import { INBOX_SESSION_ID } from '@/const/session';
6
+ import { useIsMobile } from '@/hooks/useIsMobile';
7
+ import { useGlobalStore } from '@/store/global';
8
+ import { ChatSettingsTabs, SettingsTabs, SidebarTabKey } from '@/store/global/initialState';
9
+ import { useSessionStore } from '@/store/session';
10
+
11
+ import { useOpenChatSettings, useOpenSettings } from './useInterceptingRoutes';
12
+
13
+ // Mocks
14
+ vi.mock('next/navigation', () => ({
15
+ useRouter: vi.fn(() => ({
16
+ push: vi.fn((href) => href),
17
+ replace: vi.fn((href) => href),
18
+ })),
19
+ }));
20
+ vi.mock('@/hooks/useQuery', () => ({
21
+ useQuery: vi.fn(() => ({})),
22
+ }));
23
+ vi.mock('@/hooks/useIsMobile', () => ({
24
+ useIsMobile: vi.fn(),
25
+ }));
26
+ vi.mock('@/store/session', () => ({
27
+ useSessionStore: vi.fn(),
28
+ }));
29
+ vi.mock('@/store/global', () => ({
30
+ useGlobalStore: {
31
+ setState: vi.fn(),
32
+ },
33
+ }));
34
+
35
+ describe('useOpenSettings', () => {
36
+ it('should handle mobile route correctly', () => {
37
+ vi.mocked(useIsMobile).mockReturnValue(true);
38
+ const { result } = renderHook(() => useOpenSettings(SettingsTabs.Common));
39
+ expect(result.current()).toBe('/settings/common');
40
+ });
41
+
42
+ it('should handle desktop route correctly', () => {
43
+ vi.mocked(useIsMobile).mockReturnValue(false);
44
+ const { result } = renderHook(() => useOpenSettings(SettingsTabs.Agent));
45
+ expect(result.current()).toBe('/settings/modal?tab=agent');
46
+ });
47
+ });
48
+
49
+ describe('useOpenChatSettings', () => {
50
+ it('should handle inbox session id correctly', () => {
51
+ vi.mocked(useSessionStore).mockReturnValue(INBOX_SESSION_ID);
52
+ const { result } = renderHook(() => useOpenChatSettings());
53
+
54
+ expect(result.current()).toBe('/settings/modal?session=inbox&tab=agent'); // Assuming openSettings returns a function
55
+ });
56
+
57
+ it('should handle mobile route for chat settings', () => {
58
+ vi.mocked(useSessionStore).mockReturnValue('123');
59
+ vi.mocked(useIsMobile).mockReturnValue(true);
60
+ const { result } = renderHook(() => useOpenChatSettings(ChatSettingsTabs.Meta));
61
+ expect(result.current()).toBe('/chat/settings');
62
+ });
63
+
64
+ it('should handle desktop route for chat settings with session and tab', () => {
65
+ vi.mocked(useSessionStore).mockReturnValue('456');
66
+ vi.mocked(useIsMobile).mockReturnValue(false);
67
+ const { result } = renderHook(() => useOpenChatSettings(ChatSettingsTabs.Meta));
68
+ expect(result.current()).toBe('/chat/settings/modal?session=456&tab=meta');
69
+ });
70
+ });
@@ -0,0 +1,46 @@
1
+ import { useMemo } from 'react';
2
+ import urlJoin from 'url-join';
3
+
4
+ import { INBOX_SESSION_ID } from '@/const/session';
5
+ import { useIsMobile } from '@/hooks/useIsMobile';
6
+ import { useQueryRoute } from '@/hooks/useQueryRoute';
7
+ import { useGlobalStore } from '@/store/global';
8
+ import { ChatSettingsTabs, SettingsTabs, SidebarTabKey } from '@/store/global/initialState';
9
+ import { useSessionStore } from '@/store/session';
10
+
11
+ export const useOpenSettings = (tab: SettingsTabs = SettingsTabs.Common) => {
12
+ const activeId = useSessionStore((s) => s.activeId);
13
+ const router = useQueryRoute();
14
+ const mobile = useIsMobile();
15
+
16
+ return useMemo(() => {
17
+ if (mobile) {
18
+ return () => router.push(urlJoin('/settings', tab));
19
+ } else {
20
+ // use Intercepting Routes on Desktop
21
+ return () => router.push('/settings/modal', { query: { session: activeId, tab } });
22
+ }
23
+ }, [mobile, tab, activeId, router]);
24
+ };
25
+
26
+ export const useOpenChatSettings = (tab: ChatSettingsTabs = ChatSettingsTabs.Meta) => {
27
+ const activeId = useSessionStore((s) => s.activeId);
28
+ const openSettings = useOpenSettings(SettingsTabs.Agent);
29
+ const router = useQueryRoute();
30
+ const mobile = useIsMobile();
31
+
32
+ return useMemo(() => {
33
+ if (activeId === INBOX_SESSION_ID) {
34
+ useGlobalStore.setState({
35
+ sidebarKey: SidebarTabKey.Setting,
36
+ });
37
+ return openSettings;
38
+ }
39
+ if (mobile) {
40
+ return () => router.push('/chat/settings');
41
+ } else {
42
+ // use Intercepting Routes on Desktop
43
+ return () => router.push('/chat/settings/modal', { query: { session: activeId, tab } });
44
+ }
45
+ }, [openSettings, mobile, activeId, router, tab]);
46
+ };
@@ -1,5 +1,4 @@
1
1
  import { renderHook } from '@testing-library/react';
2
- import { useSearchParams } from 'next/navigation';
3
2
  import { describe, expect, it, vi } from 'vitest';
4
3
 
5
4
  import { useQuery } from './useQuery';
@@ -1,7 +1,8 @@
1
1
  import { useSearchParams } from 'next/navigation';
2
2
  import qs from 'query-string';
3
+ import { useMemo } from 'react';
3
4
 
4
5
  export const useQuery = () => {
5
6
  const rawQuery = useSearchParams();
6
- return qs.parse(rawQuery.toString());
7
+ return useMemo(() => qs.parse(rawQuery.toString()), [rawQuery]);
7
8
  };
@@ -49,12 +49,19 @@ const StoreInitialization = memo(() => {
49
49
 
50
50
  useEffect(() => {
51
51
  router.prefetch('/chat');
52
- router.prefetch('/chat/settings');
53
52
  router.prefetch('/market');
54
- router.prefetch('/settings/common');
55
- router.prefetch('/settings/agent');
56
- router.prefetch('/settings/sync');
57
- }, [router]);
53
+
54
+ if (mobile) {
55
+ router.prefetch('/me');
56
+ router.prefetch('/chat/settings');
57
+ router.prefetch('/settings/common');
58
+ router.prefetch('/settings/agent');
59
+ router.prefetch('/settings/sync');
60
+ } else {
61
+ router.prefetch('/chat/settings/modal');
62
+ router.prefetch('/settings/modal');
63
+ }
64
+ }, [router, mobile]);
58
65
 
59
66
  return null;
60
67
  });
@@ -28,8 +28,9 @@ export default {
28
28
  },
29
29
  feedback: '反馈与建议',
30
30
  follow: '在 {{name}} 上关注我们',
31
- historyRange: '历史范围',
31
+ fullscreen: '全屏模式',
32
32
 
33
+ historyRange: '历史范围',
33
34
  import: '导入配置',
34
35
  importModal: {
35
36
  finish: {
@@ -136,6 +137,7 @@ export default {
136
137
  me: '我',
137
138
  setting: '设置',
138
139
  },
140
+
139
141
  telemetry: {
140
142
  allow: '允许',
141
143
  deny: '拒绝',
@@ -143,7 +145,6 @@ export default {
143
145
  learnMore: '了解更多',
144
146
  title: '帮助 LobeChat 做得更好',
145
147
  },
146
-
147
148
  temp: '临时',
148
149
  terms: '服务条款',
149
150
  updateAgent: '更新助理信息',
@@ -162,6 +163,7 @@ export default {
162
163
  help: '帮助中心',
163
164
  moveGuide: '设置按钮搬到这里啦',
164
165
  plans: '订阅方案',
166
+ preview: '预览版',
165
167
  profile: '账户管理',
166
168
  setting: '应用设置',
167
169
  usages: '用量统计',
@@ -10,6 +10,15 @@ export enum SidebarTabKey {
10
10
  Setting = 'settings',
11
11
  }
12
12
 
13
+ export enum ChatSettingsTabs {
14
+ Chat = 'chat',
15
+ Meta = 'meta',
16
+ Modal = 'modal',
17
+ Plugin = 'plugin',
18
+ Prompt = 'prompt',
19
+ TTS = 'tts',
20
+ }
21
+
13
22
  export enum SettingsTabs {
14
23
  About = 'about',
15
24
  Agent = 'agent',
@@ -6,3 +6,10 @@ export const mobileHeaderSticky: CSSProperties = {
6
6
  width: '100%',
7
7
  zIndex: 100,
8
8
  };
9
+
10
+ export const mobileHeaderFixed: CSSProperties = {
11
+ position: 'fixed',
12
+ top: 0,
13
+ width: '100%',
14
+ zIndex: 100,
15
+ };
@@ -1,35 +0,0 @@
1
- import { createStyles } from 'antd-style';
2
- import { memo } from 'react';
3
- import { Flexbox } from 'react-layout-kit';
4
-
5
- import UserAvatar from '@/features/User/UserAvatar';
6
-
7
- const useStyles = createStyles(({ css, token }) => ({
8
- nickname: css`
9
- font-size: 16px;
10
- font-weight: bold;
11
- line-height: 1;
12
- `,
13
- username: css`
14
- line-height: 1;
15
- color: ${token.colorTextDescription};
16
- `,
17
- }));
18
-
19
- // TODO
20
-
21
- const UserInfo = memo<{ onClick?: () => void }>(({ onClick }) => {
22
- const { styles, theme } = useStyles();
23
-
24
- return (
25
- <Flexbox align={'center'} gap={12} horizontal paddingBlock={12} paddingInline={16}>
26
- <UserAvatar background={theme.colorFill} onClick={onClick} size={48} />
27
- <Flexbox flex={1} gap={6}>
28
- <div className={styles.nickname}>{'社区版用户'}</div>
29
- <div className={styles.username}> {'Community Edition'}</div>
30
- </Flexbox>
31
- </Flexbox>
32
- );
33
- });
34
-
35
- export default UserInfo;