@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
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+
3
+ import { memo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import HeaderContent from '@/app/(main)/chat/settings/features/HeaderContent';
7
+ import Menu from '@/components/Menu';
8
+ import { useQuery } from '@/hooks/useQuery';
9
+ import { useQueryRoute } from '@/hooks/useQueryRoute';
10
+ import { ChatSettingsTabs } from '@/store/global/initialState';
11
+
12
+ import { useCategory } from './useCategory';
13
+
14
+ const CategoryContent = memo(() => {
15
+ const cateItems = useCategory();
16
+ const router = useQueryRoute();
17
+ const { tab = ChatSettingsTabs.Meta } = useQuery();
18
+
19
+ return (
20
+ <>
21
+ <Menu
22
+ items={cateItems}
23
+ onClick={({ key }) => {
24
+ router.replace('/chat/settings/modal', { query: { tab: key } });
25
+ }}
26
+ selectable
27
+ selectedKeys={[tab as any]}
28
+ variant={'compact'}
29
+ />
30
+ <Flexbox align={'center'} gap={8} paddingInline={8} width={'100%'}>
31
+ <HeaderContent modal />
32
+ </Flexbox>
33
+ </>
34
+ );
35
+ });
36
+
37
+ export default CategoryContent;
@@ -0,0 +1,54 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { Blocks, Bot, BrainCog, MessagesSquare, Mic2, UserCircle } from 'lucide-react';
3
+ import { useMemo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import type { MenuProps } from '@/components/Menu';
7
+ import { ChatSettingsTabs } from '@/store/global/initialState';
8
+
9
+ interface UseCategoryOptions {
10
+ mobile?: boolean;
11
+ }
12
+
13
+ export const useCategory = ({ mobile }: UseCategoryOptions = {}) => {
14
+ const { t } = useTranslation('setting');
15
+ const iconSize = mobile ? { fontSize: 20 } : undefined;
16
+
17
+ const cateItems: MenuProps['items'] = useMemo(
18
+ () => [
19
+ {
20
+ icon: <Icon icon={UserCircle} size={iconSize} />,
21
+ key: ChatSettingsTabs.Meta,
22
+ label: t('settingAgent.title'),
23
+ },
24
+ {
25
+ icon: <Icon icon={Bot} size={iconSize} />,
26
+ key: ChatSettingsTabs.Prompt,
27
+ label: t('settingAgent.prompt.title'),
28
+ },
29
+ {
30
+ icon: <Icon icon={MessagesSquare} size={iconSize} />,
31
+ key: ChatSettingsTabs.Chat,
32
+ label: t('settingChat.title'),
33
+ },
34
+ {
35
+ icon: <Icon icon={BrainCog} size={iconSize} />,
36
+ key: ChatSettingsTabs.Modal,
37
+ label: t('settingModel.title'),
38
+ },
39
+ {
40
+ icon: <Icon icon={Mic2} size={iconSize} />,
41
+ key: ChatSettingsTabs.TTS,
42
+ label: t('settingTTS.title'),
43
+ },
44
+ {
45
+ icon: <Icon icon={Blocks} size={iconSize} />,
46
+ key: ChatSettingsTabs.Plugin,
47
+ label: t('settingPlugin.title'),
48
+ },
49
+ ],
50
+ [t],
51
+ );
52
+
53
+ return cateItems;
54
+ };
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import { Skeleton } from 'antd';
4
+ import isEqual from 'fast-deep-equal';
5
+ import dynamic from 'next/dynamic';
6
+ import { PropsWithChildren, memo } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+
9
+ import StoreUpdater from '@/features/AgentSetting/StoreUpdater';
10
+ import { Provider, createStore } from '@/features/AgentSetting/store';
11
+ import { useAgentStore } from '@/store/agent';
12
+ import { agentSelectors } from '@/store/agent/slices/chat';
13
+ import { useSessionStore } from '@/store/session';
14
+ import { sessionMetaSelectors } from '@/store/session/selectors';
15
+
16
+ import SettingModalLayout from '../../../_layout/SettingModalLayout';
17
+
18
+ const CategoryContent = dynamic(() => import('./features/CategoryContent'), {
19
+ loading: () => <Skeleton paragraph={{ rows: 6 }} title={false} />,
20
+ ssr: false,
21
+ });
22
+
23
+ const Layout = memo<PropsWithChildren>(({ children }) => {
24
+ const { t } = useTranslation('setting');
25
+ const id = useSessionStore((s) => s.activeId);
26
+ const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
27
+ const meta = useSessionStore(sessionMetaSelectors.currentAgentMeta, isEqual);
28
+ const [updateAgentConfig] = useAgentStore((s) => [s.updateAgentConfig]);
29
+
30
+ const [updateAgentMeta] = useSessionStore((s) => [
31
+ s.updateSessionMeta,
32
+ sessionMetaSelectors.currentAgentTitle(s),
33
+ ]);
34
+
35
+ return (
36
+ <SettingModalLayout
37
+ category={<CategoryContent />}
38
+ desc={t('header.sessionDesc')}
39
+ title={t('header.session')}
40
+ >
41
+ <Provider createStore={createStore}>
42
+ <StoreUpdater
43
+ config={config}
44
+ id={id}
45
+ meta={meta}
46
+ onConfigChange={updateAgentConfig}
47
+ onMetaChange={updateAgentMeta}
48
+ />
49
+ {children}
50
+ </Provider>
51
+ </SettingModalLayout>
52
+ );
53
+ });
54
+
55
+ export default Layout;
@@ -0,0 +1,5 @@
1
+ import { Skeleton } from 'antd';
2
+
3
+ export default () => {
4
+ return <Skeleton paragraph={{ rows: 6 }} style={{ paddingBlock: 16 }} />;
5
+ };
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import dynamic from 'next/dynamic';
4
+
5
+ import { useQuery } from '@/hooks/useQuery';
6
+ import { ChatSettingsTabs } from '@/store/global/initialState';
7
+
8
+ import Skeleton from './loading';
9
+
10
+ const loading = () => <Skeleton />;
11
+
12
+ const AgentMeta = dynamic(() => import('@/features/AgentSetting/AgentMeta'), {
13
+ loading,
14
+ ssr: false,
15
+ });
16
+ const AgentChat = dynamic(() => import('@/features/AgentSetting/AgentChat'), {
17
+ loading,
18
+ ssr: false,
19
+ });
20
+ const AgentPrompt = dynamic(() => import('@/features/AgentSetting/AgentPrompt'), {
21
+ loading,
22
+ ssr: false,
23
+ });
24
+ const AgentPlugin = dynamic(() => import('@/features/AgentSetting/AgentPlugin'), {
25
+ loading,
26
+ ssr: false,
27
+ });
28
+ const AgentModal = dynamic(() => import('@/features/AgentSetting/AgentModal'), {
29
+ loading,
30
+ ssr: false,
31
+ });
32
+ const AgentTTS = dynamic(() => import('@/features/AgentSetting/AgentTTS'), { loading, ssr: false });
33
+
34
+ /**
35
+ * @description: Agent Settings Modal (intercepting route: /chat/settings/modal )
36
+ * @refs: https://github.com/lobehub/lobe-chat/discussions/2295#discussioncomment-9290942
37
+ */
38
+
39
+ const Page = () => {
40
+ const { tab = ChatSettingsTabs.Meta } = useQuery();
41
+ return (
42
+ <>
43
+ {tab === ChatSettingsTabs.Meta && <AgentMeta />}
44
+ {tab === ChatSettingsTabs.Prompt && <AgentPrompt modal />}
45
+ {tab === ChatSettingsTabs.Chat && <AgentChat />}
46
+ {tab === ChatSettingsTabs.Modal && <AgentModal />}
47
+ {tab === ChatSettingsTabs.TTS && <AgentTTS />}
48
+ {tab === ChatSettingsTabs.Plugin && <AgentPlugin />}
49
+ </>
50
+ );
51
+ };
52
+
53
+ Page.displayName = 'AgentSettingModal';
54
+
55
+ export default Page;
@@ -0,0 +1,3 @@
1
+ // This ensures that the modal is not rendered when it's not active.
2
+
3
+ export default () => null;
@@ -0,0 +1,5 @@
1
+ 'use client';
2
+
3
+ import dynamic from 'next/dynamic';
4
+
5
+ export default dynamic(() => import('@/components/Error'));
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+
3
+ import { Modal } from '@lobehub/ui';
4
+ import { useRouter } from 'next/navigation';
5
+ import { PropsWithChildren, memo, useState } from 'react';
6
+
7
+ const SessionSettingsModal = memo<PropsWithChildren>(({ children }) => {
8
+ const [open, setOpen] = useState(true);
9
+ const router = useRouter();
10
+
11
+ return (
12
+ <Modal
13
+ afterClose={() => {
14
+ router.back();
15
+ }}
16
+ footer={null}
17
+ onCancel={() => setOpen(false)}
18
+ open={open}
19
+ styles={{
20
+ body: { display: 'flex', minHeight: 'min(75vh, 750px)', overflow: 'hidden', padding: 0 },
21
+ }}
22
+ title={false}
23
+ width={1024}
24
+ >
25
+ {children}
26
+ </Modal>
27
+ );
28
+ });
29
+
30
+ export default SessionSettingsModal;
@@ -0,0 +1,5 @@
1
+ import { Skeleton } from 'antd';
2
+
3
+ export default () => {
4
+ return <Skeleton paragraph={{ rows: 6 }} style={{ padding: 56 }} />;
5
+ };
@@ -12,9 +12,10 @@ import { isMobileDevice } from '@/utils/responsive';
12
12
 
13
13
  type RootLayoutProps = {
14
14
  children: ReactNode;
15
+ modal: ReactNode;
15
16
  };
16
17
 
17
- const RootLayout = async ({ children }: RootLayoutProps) => {
18
+ const RootLayout = async ({ children, modal }: RootLayoutProps) => {
18
19
  const cookieStore = cookies();
19
20
 
20
21
  const lang = cookieStore.get(LOBE_LOCALE_COOKIE);
@@ -24,7 +25,10 @@ const RootLayout = async ({ children }: RootLayoutProps) => {
24
25
  <html dir={direction} lang={lang?.value || DEFAULT_LANG} suppressHydrationWarning>
25
26
  <body>
26
27
  <GlobalProvider>
27
- <AuthProvider>{children}</AuthProvider>
28
+ <AuthProvider>
29
+ {children}
30
+ {modal}
31
+ </AuthProvider>
28
32
  </GlobalProvider>
29
33
  <Analytics />
30
34
  <SpeedInsights />
@@ -5,6 +5,8 @@ import { memo } from 'react';
5
5
 
6
6
  const useStyles = createStyles(
7
7
  ({ css, token, isDarkMode }) => css`
8
+ flex: none;
9
+ width: 100%;
8
10
  height: 6px;
9
11
  background: ${isDarkMode ? token.colorBgContainer : token.colorBgLayout};
10
12
  `,
@@ -8,7 +8,11 @@ const { Item } = List;
8
8
  const useStyles = createStyles(({ css, token, isDarkMode }) => ({
9
9
  container: css`
10
10
  position: relative;
11
- padding-block: 16px !important;
11
+
12
+ gap: 12px;
13
+
14
+ padding: 16px !important;
15
+
12
16
  background: ${isDarkMode ? token.colorBgLayout : token.colorBgContainer};
13
17
  border-radius: 0;
14
18
  `,
@@ -38,6 +38,7 @@ const MobileContentLayout = ({
38
38
  {header}
39
39
  <Flexbox
40
40
  height="100%"
41
+ id={'lobe-mobile-scroll-container'}
41
42
  style={{
42
43
  overflowX: 'hidden',
43
44
  overflowY: 'auto',
@@ -1,10 +1,7 @@
1
- import { useResponsive } from 'antd-style';
2
- import { useRouter } from 'next/navigation';
3
-
1
+ import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
4
2
  import { useGlobalStore } from '@/store/global';
5
3
  import { useSessionStore } from '@/store/session';
6
4
  import { sessionSelectors } from '@/store/session/selectors';
7
- import { pathString } from '@/utils/url';
8
5
 
9
6
  import { OnAvatarsClick, RenderMessage } from '../types';
10
7
  import { AssistantMessage } from './Assistant';
@@ -22,18 +19,18 @@ export const renderMessages: Record<string, RenderMessage> = {
22
19
  export const useAvatarsClick = (): OnAvatarsClick => {
23
20
  const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
24
21
  const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
25
- const { mobile } = useResponsive();
26
- const router = useRouter();
22
+ const openChatSettings = useOpenChatSettings();
27
23
 
28
24
  return (role) => {
29
25
  switch (role) {
30
26
  case 'assistant': {
31
- return () =>
32
- isInbox
33
- ? router.push('/settings/agent')
34
- : mobile
35
- ? router.push(pathString('/chat/settings', { search: location.search }))
36
- : toggleSystemRole(true);
27
+ return () => {
28
+ if (!isInbox) {
29
+ toggleSystemRole(true);
30
+ } else {
31
+ openChatSettings();
32
+ }
33
+ };
37
34
  }
38
35
  }
39
36
  };
@@ -14,10 +14,12 @@ import { useMarketStore } from '@/store/market';
14
14
 
15
15
  const { Paragraph } = Typography;
16
16
 
17
- const useStyles = createStyles(({ css, token }) => ({
17
+ const useStyles = createStyles(({ css, token, responsive }) => ({
18
18
  card: css`
19
19
  position: relative;
20
20
 
21
+ overflow: hidden;
22
+
21
23
  height: 100%;
22
24
  min-height: 110px;
23
25
  padding: 16px;
@@ -30,6 +32,10 @@ const useStyles = createStyles(({ css, token }) => ({
30
32
  &:hover {
31
33
  background: ${token.colorBgElevated};
32
34
  }
35
+
36
+ ${responsive.mobile} {
37
+ min-height: 72px;
38
+ }
33
39
  `,
34
40
  cardDesc: css`
35
41
  margin-block: 0 !important;
@@ -48,15 +54,18 @@ const useStyles = createStyles(({ css, token }) => ({
48
54
  `,
49
55
  }));
50
56
 
51
- const AgentsSuggest = memo(() => {
57
+ const AgentsSuggest = memo<{ mobile?: boolean }>(({ mobile }) => {
52
58
  const { t } = useTranslation('welcome');
59
+
53
60
  const [sliceStart, setSliceStart] = useState(0);
54
61
  const useFetchAgentList = useMarketStore((s) => s.useFetchAgentList);
55
62
  const { isLoading } = useFetchAgentList();
56
63
  const agentList = useMarketStore((s) => s.agentList, isEqual);
57
64
  const { styles } = useStyles();
58
65
 
59
- const loadingCards = Array.from({ length: 4 }).map((_, index) => (
66
+ const agentLength = mobile ? 3 : 4;
67
+
68
+ const loadingCards = Array.from({ length: agentLength }).map((_, index) => (
60
69
  <Flexbox className={styles.card} key={index}>
61
70
  <Skeleton active avatar paragraph={{ rows: 2 }} title={false} />
62
71
  </Flexbox>
@@ -64,15 +73,15 @@ const AgentsSuggest = memo(() => {
64
73
 
65
74
  const cards = useMemo(
66
75
  () =>
67
- agentList.slice(sliceStart, sliceStart + 4).map((agent) => (
76
+ agentList.slice(sliceStart, sliceStart + agentLength).map((agent) => (
68
77
  <Link href={`/market?agent=${agent.identifier}`} key={agent.identifier}>
69
78
  <Flexbox className={styles.card} gap={8} horizontal>
70
79
  <Avatar avatar={agent.meta.avatar} style={{ flex: 'none' }} />
71
- <Flexbox gap={8}>
80
+ <Flexbox gap={mobile ? 2 : 8} style={{ overflow: 'hidden', width: '100%' }}>
72
81
  <Paragraph className={styles.cardTitle} ellipsis={{ rows: 1 }}>
73
82
  {agent.meta.title}
74
83
  </Paragraph>
75
- <Paragraph className={styles.cardDesc} ellipsis={{ rows: 2 }}>
84
+ <Paragraph className={styles.cardDesc} ellipsis={{ rows: mobile ? 1 : 2 }}>
76
85
  {agent.meta.description}
77
86
  </Paragraph>
78
87
  </Flexbox>
@@ -13,7 +13,7 @@ import { USAGE_DOCUMENTS } from '@/const/url';
13
13
  import { useSendMessage } from '@/features/ChatInput/useSend';
14
14
  import { useChatStore } from '@/store/chat';
15
15
 
16
- const useStyles = createStyles(({ css, token }) => ({
16
+ const useStyles = createStyles(({ css, token, responsive }) => ({
17
17
  card: css`
18
18
  cursor: pointer;
19
19
 
@@ -27,6 +27,10 @@ const useStyles = createStyles(({ css, token }) => ({
27
27
  &:hover {
28
28
  background: ${token.colorBgElevated};
29
29
  }
30
+
31
+ ${responsive.mobile} {
32
+ padding: 8px 16px;
33
+ }
30
34
  `,
31
35
  icon: css`
32
36
  color: ${token.colorTextSecondary};
@@ -52,10 +56,11 @@ const qa = shuffle([
52
56
  'q13',
53
57
  'q14',
54
58
  'q15',
55
- ]).slice(0, 5);
59
+ ]);
56
60
 
57
- const QuestionSuggest = memo(() => {
61
+ const QuestionSuggest = memo<{ mobile?: boolean }>(({ mobile }) => {
58
62
  const [updateInputMessage] = useChatStore((s) => [s.updateInputMessage]);
63
+
59
64
  const { t } = useTranslation('welcome');
60
65
  const { styles } = useStyles();
61
66
  const sendMessage = useSendMessage();
@@ -73,7 +78,7 @@ const QuestionSuggest = memo(() => {
73
78
  </Link>
74
79
  </Flexbox>
75
80
  <Flexbox gap={8} horizontal wrap={'wrap'}>
76
- {qa.map((item) => {
81
+ {qa.slice(0, mobile ? 2 : 5).map((item) => {
77
82
  const text = t(`guide.qa.${item}` as any);
78
83
  return (
79
84
  <Flexbox
@@ -6,6 +6,8 @@ import { memo, useEffect, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Center, Flexbox } from 'react-layout-kit';
8
8
 
9
+ import { useIsMobile } from '@/hooks/useIsMobile';
10
+
9
11
  import AgentsSuggest from './AgentsSuggest';
10
12
  import QuestionSuggest from './QuestionSuggest';
11
13
 
@@ -40,7 +42,7 @@ const InboxWelcome = memo(() => {
40
42
  const { t } = useTranslation('welcome');
41
43
  const [greeting, setGreeting] = useState<'morning' | 'noon' | 'afternoon' | 'night'>();
42
44
  const { styles } = useStyles();
43
-
45
+ const mobile = useIsMobile();
44
46
  useEffect(() => {
45
47
  const now = new Date();
46
48
  const hours = now.getHours();
@@ -66,8 +68,8 @@ const InboxWelcome = memo(() => {
66
68
  <Markdown className={styles.desc} variant={'chat'}>
67
69
  {t('guide.defaultMessage')}
68
70
  </Markdown>
69
- <AgentsSuggest />
70
- <QuestionSuggest />
71
+ <AgentsSuggest mobile={mobile} />
72
+ <QuestionSuggest mobile={mobile} />
71
73
  </Flexbox>
72
74
  </Center>
73
75
  );
@@ -22,7 +22,6 @@ const useStyles = createStyles(({ css, token }) => {
22
22
  background-color: transparent;
23
23
  }
24
24
  `,
25
-
26
25
  loader: css`
27
26
  transform: translateX(-${size * 2}px);
28
27
 
@@ -125,6 +124,9 @@ const useStyles = createStyles(({ css, token }) => {
125
124
  }
126
125
  }
127
126
  `,
127
+ wrapper: css`
128
+ font-size: inherit;
129
+ `,
128
130
  };
129
131
  });
130
132
 
@@ -237,6 +239,7 @@ const DataImporter = memo<DataImporterProps>(({ children, onFinishImport }) => {
237
239
 
238
240
  return false;
239
241
  }}
242
+ className={styles.wrapper}
240
243
  maxCount={1}
241
244
  showUploadList={false}
242
245
  >
@@ -6,6 +6,7 @@ import { rgba } from 'polished';
6
6
  import { memo, useMemo } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
 
9
+ import { useOpenSettings } from '@/hooks/useInterceptingRoutes';
9
10
  import { SidebarTabKey } from '@/store/global/initialState';
10
11
 
11
12
  const useStyles = createStyles(({ css, token }) => ({
@@ -24,6 +25,7 @@ interface Props {
24
25
  export default memo<Props>(({ className, tabBarKey }) => {
25
26
  const { t } = useTranslation('common');
26
27
  const { styles } = useStyles();
28
+ const openSettings = useOpenSettings();
27
29
  const router = useRouter();
28
30
  const items: MobileTabBarProps['items'] = useMemo(
29
31
  () => [
@@ -48,9 +50,7 @@ export default memo<Props>(({ className, tabBarKey }) => {
48
50
  {
49
51
  icon: (active) => <Icon className={active ? styles.active : undefined} icon={User} />,
50
52
  key: SidebarTabKey.Setting,
51
- onClick: () => {
52
- router.push('/settings');
53
- },
53
+ onClick: openSettings,
54
54
  title: t('tab.setting'),
55
55
  },
56
56
  ],
@@ -0,0 +1,45 @@
1
+ import { Tooltip } from '@lobehub/ui';
2
+ import { Tag } from 'antd';
3
+ import { useTheme } from 'antd-style';
4
+ import { CSSProperties, memo, useMemo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ export enum PlanType {
8
+ Preview = 'preview',
9
+ }
10
+
11
+ export interface PlanTagProps {
12
+ type?: PlanType;
13
+ }
14
+
15
+ const PlanTag = memo<PlanTagProps>(({ type = PlanType.Preview }) => {
16
+ const { t } = useTranslation('common');
17
+ const theme = useTheme();
18
+ const tag: {
19
+ desc: string;
20
+ style: CSSProperties;
21
+ title: string;
22
+ } = useMemo(() => {
23
+ switch (type) {
24
+ case PlanType.Preview: {
25
+ return {
26
+ desc: t('userPanel.preview'),
27
+ style: {
28
+ background: theme.colorFill,
29
+ },
30
+ title: 'Preview',
31
+ };
32
+ }
33
+ }
34
+ }, []);
35
+
36
+ return (
37
+ <Tooltip title={tag.desc}>
38
+ <Tag bordered={false} style={{ ...tag.style, borderRadius: 12, cursor: 'pointer' }}>
39
+ {tag.title}
40
+ </Tag>
41
+ </Tooltip>
42
+ );
43
+ });
44
+
45
+ export default PlanTag;
@@ -3,11 +3,13 @@
3
3
  import { createStyles } from 'antd-style';
4
4
  import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
- import { Flexbox } from 'react-layout-kit';
6
+ import { Flexbox, FlexboxProps } from 'react-layout-kit';
7
7
 
8
- import UserAvatar from './UserAvatar';
8
+ import PlanTag from '@/features/User/PlanTag';
9
9
 
10
- const DEFAULT_USERNAME = 'LobeChat Community Edition';
10
+ import UserAvatar, { type UserAvatarProps } from './UserAvatar';
11
+
12
+ const DEFAULT_USERNAME = 'LobeChat';
11
13
 
12
14
  const useStyles = createStyles(({ css, token }) => ({
13
15
  nickname: css`
@@ -21,19 +23,34 @@ const useStyles = createStyles(({ css, token }) => ({
21
23
  `,
22
24
  }));
23
25
 
24
- const UserInfo = memo<{ onClick?: () => void }>(({ onClick }) => {
26
+ export interface UserInfoProps extends FlexboxProps {
27
+ avatarProps?: Partial<UserAvatarProps>;
28
+ }
29
+
30
+ const UserInfo = memo<UserInfoProps>(({ avatarProps, ...rest }) => {
25
31
  const { t } = useTranslation('common');
26
32
  const { styles, theme } = useStyles();
27
33
 
28
34
  const DEFAULT_NICKNAME = t('userPanel.defaultNickname');
29
35
 
30
36
  return (
31
- <Flexbox align={'center'} gap={12} horizontal paddingBlock={12} paddingInline={16}>
32
- <UserAvatar background={theme.colorFill} onClick={onClick} size={48} />
33
- <Flexbox flex={1} gap={6}>
34
- <div className={styles.nickname}>{DEFAULT_NICKNAME}</div>
35
- <div className={styles.username}>{DEFAULT_USERNAME}</div>
37
+ <Flexbox
38
+ align={'center'}
39
+ gap={12}
40
+ horizontal
41
+ justify={'space-between'}
42
+ paddingBlock={12}
43
+ paddingInline={12}
44
+ {...rest}
45
+ >
46
+ <Flexbox align={'center'} gap={12} horizontal>
47
+ <UserAvatar background={theme.colorFill} size={48} {...avatarProps} />
48
+ <Flexbox flex={1} gap={6}>
49
+ <div className={styles.nickname}>{DEFAULT_NICKNAME}</div>
50
+ <div className={styles.username}>{DEFAULT_USERNAME}</div>
51
+ </Flexbox>
36
52
  </Flexbox>
53
+ <PlanTag />
37
54
  </Flexbox>
38
55
  );
39
56
  });