@lobehub/chat 0.155.4 → 0.155.5

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 (131) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -2
  3. package/README.zh-CN.md +8 -8
  4. package/package.json +1 -1
  5. package/src/app/(main)/(mobile)/me/features/Header.tsx +2 -9
  6. package/src/app/(main)/(mobile)/me/loading.tsx +1 -12
  7. package/src/app/(main)/(mobile)/me/page.tsx +2 -5
  8. package/src/app/(main)/@nav/_layout/Mobile.tsx +1 -1
  9. package/src/app/(main)/_layout/Mobile.tsx +5 -1
  10. package/src/app/(main)/chat/(workspace)/@conversation/default.tsx +23 -0
  11. package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/index.tsx +6 -2
  12. package/src/app/(main)/chat/{(mobile)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Mobile}/index.tsx +9 -4
  13. package/src/app/(main)/chat/(workspace)/@topic/default.tsx +19 -0
  14. package/src/app/(main)/chat/{(desktop)/features/SideBar/SystemRole/index.tsx → (workspace)/@topic/features/SystemRole/SystemRoleContent.tsx} +2 -0
  15. package/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/index.tsx +18 -0
  16. package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Header.tsx +2 -0
  17. package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/index.tsx +26 -28
  18. package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/TopicSearchBar/index.tsx +4 -2
  19. package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/index.tsx +3 -4
  20. package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/ChatHeader/HeaderAction.tsx +2 -0
  21. package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/ChatHeader/Main.tsx +3 -1
  22. package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/ChatHeader/Tags.tsx +1 -1
  23. package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/ChatHeader/index.tsx +1 -2
  24. package/src/app/(main)/chat/{(desktop)/features → (workspace)/_layout/Desktop}/HotKeys.tsx +2 -0
  25. package/src/app/(main)/chat/{(desktop)/features/SideBar/index.tsx → (workspace)/_layout/Desktop/TopicPanel.tsx} +23 -20
  26. package/src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx +35 -0
  27. package/src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/index.tsx +35 -0
  28. package/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx +26 -0
  29. package/src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx +21 -0
  30. package/src/app/(main)/chat/(workspace)/_layout/type.ts +7 -0
  31. package/src/app/(main)/chat/{features → (workspace)/features}/PluginTag/index.tsx +2 -0
  32. package/src/app/(main)/chat/{features → (workspace)/features}/SettingButton.tsx +3 -1
  33. package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/index.tsx +5 -6
  34. package/src/app/(main)/chat/{features/TelemetryNotification/index.tsx → (workspace)/features/TelemetryNotification.tsx} +2 -0
  35. package/src/app/(main)/chat/(workspace)/features/useWorkspaceModal.tsx +27 -0
  36. package/src/app/(main)/chat/(workspace)/layout.ts +11 -0
  37. package/src/app/(main)/chat/(workspace)/page.tsx +19 -0
  38. package/src/app/(main)/chat/@session/_layout/Desktop/PanelBody.tsx +22 -0
  39. package/src/app/(main)/chat/{_layout → @session/_layout}/Desktop/SessionHeader.tsx +2 -0
  40. package/src/app/(main)/chat/@session/_layout/Desktop/index.tsx +15 -0
  41. package/src/app/(main)/chat/{(mobile)/features → @session/_layout/Mobile}/SessionHeader.tsx +7 -20
  42. package/src/app/(main)/chat/@session/_layout/Mobile/index.tsx +19 -0
  43. package/src/app/(main)/chat/@session/default.tsx +23 -0
  44. package/src/app/(main)/chat/{components/SessionHydration/index.tsx → @session/features/SessionHydration.tsx} +1 -0
  45. package/src/app/(main)/chat/{features → @session/features}/SessionListContent/DefaultMode.tsx +3 -3
  46. package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Inbox/index.tsx +2 -2
  47. package/src/app/(main)/chat/{features → @session/features}/SessionListContent/List/index.tsx +3 -4
  48. package/src/app/(main)/chat/{features → @session/features}/SessionListContent/ListItem/index.tsx +12 -10
  49. package/src/app/(main)/chat/{features → @session/features}/SessionListContent/SearchMode.tsx +4 -2
  50. package/src/app/(main)/chat/{features → @session/features}/SessionListContent/index.tsx +2 -0
  51. package/src/app/(main)/chat/{features/SessionSearchBar/index.tsx → @session/features/SessionSearchBar.tsx} +3 -5
  52. package/src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx +79 -0
  53. package/src/app/(main)/chat/_layout/Desktop/index.tsx +11 -7
  54. package/src/app/(main)/chat/_layout/Mobile.tsx +52 -0
  55. package/src/app/(main)/chat/_layout/type.ts +1 -0
  56. package/src/app/(main)/chat/error.tsx +5 -0
  57. package/src/app/(main)/chat/features/Migration/index.tsx +3 -8
  58. package/src/app/(main)/chat/not-found.tsx +3 -0
  59. package/src/app/(main)/chat/settings/_layout/Mobile/Header.tsx +3 -4
  60. package/src/app/(main)/chat/settings/features/HeaderContent.tsx +2 -2
  61. package/src/app/(main)/chat/settings/features/SubmitAgentButton/index.tsx +2 -2
  62. package/src/app/(main)/market/@detail/features/Header.tsx +2 -2
  63. package/src/components/Cell/Divider.tsx +2 -2
  64. package/src/components/Cell/index.tsx +2 -2
  65. package/src/components/StoreHydration/ChatHydration/index.tsx +2 -2
  66. package/src/const/session.ts +2 -0
  67. package/src/const/url.ts +5 -1
  68. package/src/features/ChatInput/ActionBar/Tools/index.tsx +3 -2
  69. package/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx +1 -1
  70. package/src/features/Conversation/components/InboxWelcome/index.tsx +2 -2
  71. package/src/features/Conversation/components/SkeletonList.tsx +21 -8
  72. package/src/features/Conversation/components/VirtualizedList/index.tsx +19 -17
  73. package/src/features/Conversation/index.tsx +12 -31
  74. package/src/features/PluginStore/InstalledPluginList.tsx +28 -21
  75. package/src/features/PluginStore/OnlineList.tsx +4 -10
  76. package/src/features/PluginStore/PluginItem/Action.tsx +3 -2
  77. package/src/features/PluginStore/PluginItem/index.tsx +2 -0
  78. package/src/features/PluginStore/index.tsx +4 -2
  79. package/src/features/User/UserAvatar.tsx +2 -1
  80. package/src/features/User/UserPanel/useMenu.tsx +1 -1
  81. package/src/layout/GlobalProvider/AppTheme.tsx +7 -17
  82. package/src/store/global/action.ts +2 -0
  83. package/src/store/global/initialState.ts +2 -0
  84. package/src/styles/global.ts +12 -9
  85. package/src/styles/mobileHeader.ts +1 -1
  86. package/src/app/(main)/(mobile)/me/features/AvatarBanner.tsx +0 -27
  87. package/src/app/(main)/(mobile)/me/features/style.ts +0 -29
  88. package/src/app/(main)/chat/(desktop)/features/Conversation.tsx +0 -19
  89. package/src/app/(main)/chat/(desktop)/index.tsx +0 -22
  90. package/src/app/(main)/chat/(mobile)/features/SessionList.tsx +0 -17
  91. package/src/app/(main)/chat/(mobile)/features/TopicList.tsx +0 -29
  92. package/src/app/(main)/chat/(mobile)/index.tsx +0 -26
  93. package/src/app/(main)/chat/(mobile)/mobile/ChatHeader/index.tsx +0 -56
  94. package/src/app/(main)/chat/(mobile)/mobile/page.tsx +0 -26
  95. package/src/app/(main)/chat/_layout/Desktop/SessionList.tsx +0 -39
  96. package/src/app/(main)/chat/_layout/Mobile/index.tsx +0 -9
  97. package/src/app/(main)/chat/page.tsx +0 -25
  98. package/src/features/FolderPanel/index.tsx +0 -60
  99. package/src/utils/screen.ts +0 -14
  100. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Footer/DragUpload.tsx +0 -0
  101. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Footer/LocalFiles.tsx +0 -0
  102. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Footer/SendMore.tsx +0 -0
  103. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Footer/index.tsx +0 -0
  104. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/Header/index.tsx +0 -0
  105. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/TextArea.test.tsx +0 -0
  106. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/TextArea.tsx +0 -0
  107. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/__tests__/useAutoFocus.test.ts +0 -0
  108. /package/src/app/(main)/chat/{(desktop)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Desktop}/useAutoFocus.ts +0 -0
  109. /package/src/app/(main)/chat/{(mobile)/features/ChatInput → (workspace)/@conversation/features/ChatInput/Mobile}/Files.tsx +0 -0
  110. /package/src/app/(main)/chat/{(desktop)/features/SideBar → (workspace)/@topic/features}/SystemRole/style.ts +0 -0
  111. /package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/DefaultContent.tsx +0 -0
  112. /package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/SkeletonList.tsx +0 -0
  113. /package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/TopicContent.tsx +0 -0
  114. /package/src/app/(main)/chat/{features → (workspace)/@topic/features}/TopicListContent/Topic/TopicItem.tsx +0 -0
  115. /package/src/app/(main)/chat/{(mobile)/mobile → (workspace)/_layout/Mobile}/ChatHeader/ChatHeaderTitle.tsx +0 -0
  116. /package/src/app/(main)/chat/{features → (workspace)/features}/PluginTag/PluginStatus.tsx +0 -0
  117. /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/Preview.tsx +0 -0
  118. /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/ShareModal.tsx +0 -0
  119. /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/style.ts +0 -0
  120. /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/type.ts +0 -0
  121. /package/src/app/(main)/chat/{features → (workspace)/features}/ShareButton/useScreenshot.ts +0 -0
  122. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/CollapseGroup/Actions.tsx +0 -0
  123. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/CollapseGroup/index.tsx +0 -0
  124. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/List/AddButton.tsx +0 -0
  125. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/List/Item/Actions.tsx +0 -0
  126. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/List/Item/index.tsx +0 -0
  127. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx +0 -0
  128. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Modals/ConfigGroupModal/index.tsx +0 -0
  129. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Modals/CreateGroupModal.tsx +0 -0
  130. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/Modals/RenameGroupModal.tsx +0 -0
  131. /package/src/app/(main)/chat/{features → @session/features}/SessionListContent/SkeletonList.tsx +0 -0
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+
3
+ import { createStyles } from 'antd-style';
4
+ import { memo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import Migration from '@/app/(main)/chat/features/Migration';
8
+ import { useQuery } from '@/hooks/useQuery';
9
+
10
+ import { LayoutProps } from './type';
11
+
12
+ const useStyles = createStyles(({ css, token }) => ({
13
+ active: css`
14
+ display: unset;
15
+ `,
16
+ main: css`
17
+ position: relative;
18
+ overflow: hidden;
19
+ display: none;
20
+ background: ${token.colorBgLayout};
21
+ `,
22
+ }));
23
+
24
+ const Layout = memo<LayoutProps>(({ children, session }) => {
25
+ const { showMobileWorkspace } = useQuery();
26
+ const { cx, styles } = useStyles();
27
+
28
+ return (
29
+ <>
30
+ <Flexbox
31
+ className={cx(styles.main, !showMobileWorkspace && styles.active)}
32
+ height="100%"
33
+ width="100%"
34
+ >
35
+ {session}
36
+ </Flexbox>
37
+ <Flexbox
38
+ className={cx(styles.main, showMobileWorkspace && styles.active)}
39
+ height="100%"
40
+ id={'lobe-workspace-mobile'}
41
+ width="100%"
42
+ >
43
+ {children}
44
+ </Flexbox>
45
+ <Migration />
46
+ </>
47
+ );
48
+ });
49
+
50
+ Layout.displayName = 'MobileChatLayout';
51
+
52
+ export default Layout;
@@ -2,4 +2,5 @@ import { ReactNode } from 'react';
2
2
 
3
3
  export interface LayoutProps {
4
4
  children: ReactNode;
5
+ session: ReactNode;
5
6
  }
@@ -0,0 +1,5 @@
1
+ 'use client';
2
+
3
+ import dynamic from 'next/dynamic';
4
+
5
+ export default dynamic(() => import('@/components/Error'));
@@ -3,13 +3,13 @@
3
3
  import { Spin } from 'antd';
4
4
  import { createStore, getMany } from 'idb-keyval';
5
5
  import dynamic from 'next/dynamic';
6
- import { PropsWithChildren, memo, useEffect, useState } from 'react';
6
+ import { memo, useEffect, useState } from 'react';
7
7
 
8
8
  import { MIGRATE_KEY, V1DB_NAME, V1DB_TABLE_NAME } from './const';
9
9
 
10
10
  const Modal = dynamic(() => import('./Modal'), { loading: () => <Spin fullscreen />, ssr: false });
11
11
 
12
- const Migration = memo<PropsWithChildren>(({ children }) => {
12
+ const Migration = memo(() => {
13
13
  const [dbState, setDbState] = useState(null);
14
14
  const [open, setOpen] = useState(false);
15
15
 
@@ -33,12 +33,7 @@ const Migration = memo<PropsWithChildren>(({ children }) => {
33
33
  checkMigration();
34
34
  }, []);
35
35
 
36
- return (
37
- <>
38
- {open && <Modal open={open} setOpen={setOpen} state={dbState} />}
39
- {children}
40
- </>
41
- );
36
+ return open && <Modal open={open} setOpen={setOpen} state={dbState} />;
42
37
  });
43
38
 
44
39
  export default Migration;
@@ -0,0 +1,3 @@
1
+ import dynamic from 'next/dynamic';
2
+
3
+ export default dynamic(() => import('@/components/404'));
@@ -1,23 +1,22 @@
1
1
  'use client';
2
2
 
3
3
  import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
4
- import { useRouter } from 'next/navigation';
5
4
  import { memo } from 'react';
6
5
  import { useTranslation } from 'react-i18next';
7
6
 
7
+ import { useQueryRoute } from '@/hooks/useQueryRoute';
8
8
  import { mobileHeaderSticky } from '@/styles/mobileHeader';
9
- import { pathString } from '@/utils/url';
10
9
 
11
10
  import HeaderContent from '../../features/HeaderContent';
12
11
 
13
12
  const Header = memo(() => {
14
13
  const { t } = useTranslation('setting');
15
- const router = useRouter();
14
+ const router = useQueryRoute();
16
15
 
17
16
  return (
18
17
  <MobileNavBar
19
18
  center={<MobileNavBarTitle title={t('header.session')} />}
20
- onBackClick={() => router.push(pathString('/chat/mobile', { search: location.search }))}
19
+ onBackClick={() => router.push('/chat')}
21
20
  right={<HeaderContent />}
22
21
  showBackButton
23
22
  style={mobileHeaderSticky}
@@ -1,12 +1,12 @@
1
1
  import { ActionIcon, Icon } from '@lobehub/ui';
2
2
  import { Button, Dropdown, MenuProps } from 'antd';
3
- import { useResponsive } from 'antd-style';
4
3
  import { HardDriveDownload } from 'lucide-react';
5
4
  import { memo, useMemo } from 'react';
6
5
  import { useTranslation } from 'react-i18next';
7
6
 
8
7
  import { HEADER_ICON_SIZE } from '@/const/layoutTokens';
9
8
  import { configService } from '@/services/config';
9
+ import { useServerConfigStore } from '@/store/serverConfig';
10
10
  import { useSessionStore } from '@/store/session';
11
11
 
12
12
  import SubmitAgentButton from './SubmitAgentButton';
@@ -15,7 +15,7 @@ export const HeaderContent = memo<{ mobile?: boolean; modal?: boolean }>(({ moda
15
15
  const { t } = useTranslation('setting');
16
16
  const id = useSessionStore((s) => s.activeId);
17
17
 
18
- const { mobile } = useResponsive();
18
+ const mobile = useServerConfigStore((s) => s.isMobile);
19
19
 
20
20
  const items = useMemo<MenuProps['items']>(
21
21
  () => [
@@ -1,17 +1,17 @@
1
1
  import { ActionIcon, Icon } from '@lobehub/ui';
2
2
  import { Button } from 'antd';
3
- import { useResponsive } from 'antd-style';
4
3
  import { Share2 } from 'lucide-react';
5
4
  import { memo, useState } from 'react';
6
5
  import { useTranslation } from 'react-i18next';
7
6
 
8
7
  import { HEADER_ICON_SIZE } from '@/const/layoutTokens';
8
+ import { useServerConfigStore } from '@/store/serverConfig';
9
9
 
10
10
  import SubmitAgentModal from './SubmitAgentModal';
11
11
 
12
12
  const SubmitAgentButton = memo<{ modal?: boolean }>(({ modal }) => {
13
13
  const { t } = useTranslation('setting');
14
- const { mobile } = useResponsive();
14
+ const mobile = useServerConfigStore((s) => s.isMobile);
15
15
  const [isModalOpen, setIsModalOpen] = useState(false);
16
16
 
17
17
  return (
@@ -8,8 +8,8 @@ import { useTranslation } from 'react-i18next';
8
8
  import { Center, Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { SESSION_CHAT_URL } from '@/const/url';
11
- import { useIsMobile } from '@/hooks/useIsMobile';
12
11
  import { agentMarketSelectors, useMarketStore } from '@/store/market';
12
+ import { useServerConfigStore } from '@/store/serverConfig';
13
13
  import { useSessionStore } from '@/store/session';
14
14
 
15
15
  import { useStyles } from './style';
@@ -29,7 +29,7 @@ const Header = memo(() => {
29
29
  const { meta, createAt, author, homepage, config } = agentItem;
30
30
  const { title, description, tags } = meta;
31
31
 
32
- const isMobile = useIsMobile();
32
+ const isMobile = useServerConfigStore((s) => s.isMobile);
33
33
 
34
34
  const handleAddAgentAndConverse = async () => {
35
35
  if (!agentItem) return;
@@ -4,11 +4,11 @@ import { createStyles } from 'antd-style';
4
4
  import { memo } from 'react';
5
5
 
6
6
  const useStyles = createStyles(
7
- ({ css, token, isDarkMode }) => css`
7
+ ({ css, token }) => css`
8
8
  flex: none;
9
9
  width: 100%;
10
10
  height: 6px;
11
- background: ${isDarkMode ? token.colorBgContainer : token.colorBgLayout};
11
+ background: ${token.colorFillTertiary};
12
12
  `,
13
13
  );
14
14
 
@@ -5,7 +5,7 @@ import { ReactNode, memo } from 'react';
5
5
 
6
6
  const { Item } = List;
7
7
 
8
- const useStyles = createStyles(({ css, token, isDarkMode }) => ({
8
+ const useStyles = createStyles(({ css, token }) => ({
9
9
  container: css`
10
10
  position: relative;
11
11
 
@@ -13,7 +13,7 @@ const useStyles = createStyles(({ css, token, isDarkMode }) => ({
13
13
 
14
14
  padding: 16px !important;
15
15
 
16
- background: ${isDarkMode ? token.colorBgLayout : token.colorBgContainer};
16
+ background: ${token.colorBgLayout};
17
17
  border-radius: 0;
18
18
  `,
19
19
  }));
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useQueryState } from 'nuqs';
4
- import { memo, useEffect } from 'react';
4
+ import { memo, useLayoutEffect } from 'react';
5
5
  import { createStoreUpdater } from 'zustand-utils';
6
6
 
7
7
  import { useChatStore } from '@/store/chat';
@@ -14,7 +14,7 @@ const ChatHydration = memo(() => {
14
14
  const [topic, setTopic] = useQueryState('topic', { history: 'replace', throttleMs: 500 });
15
15
  useStoreUpdater('activeTopicId', topic);
16
16
 
17
- useEffect(() => {
17
+ useLayoutEffect(() => {
18
18
  const unsubscribe = useChatStore.subscribe(
19
19
  (s) => s.activeTopicId,
20
20
  (state) => {
@@ -5,6 +5,8 @@ import { merge } from '@/utils/merge';
5
5
 
6
6
  export const INBOX_SESSION_ID = 'inbox';
7
7
 
8
+ export const WELCOME_GUIDE_CHAT_ID = 'welcome';
9
+
8
10
  export const DEFAULT_AGENT_LOBE_SESSION: LobeAgentSession = {
9
11
  config: DEFAULT_AGENT_CONFIG,
10
12
  createdAt: Date.now(),
package/src/const/url.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import qs from 'query-string';
1
2
  import urlJoin from 'url-join';
2
3
 
3
4
  import { withBasePath } from '@/utils/basePath';
@@ -40,7 +41,10 @@ export const AGENTS_INDEX_GITHUB = 'https://github.com/lobehub/lobe-chat-agents'
40
41
  export const AGENTS_INDEX_GITHUB_ISSUE = urlJoin(AGENTS_INDEX_GITHUB, 'issues/new');
41
42
 
42
43
  export const SESSION_CHAT_URL = (id: string = INBOX_SESSION_ID, mobile?: boolean) =>
43
- mobile ? `/chat/mobile?session=${id}` : `/chat?session=${id}`;
44
+ qs.stringifyUrl({
45
+ query: mobile ? { session: id, showMobileWorkspace: mobile } : { session: id },
46
+ url: '/chat',
47
+ });
44
48
 
45
49
  export const imageUrl = (filename: string) => withBasePath(`/images/${filename}`);
46
50
 
@@ -4,10 +4,11 @@ import { createStyles } from 'antd-style';
4
4
  import type { ItemType } from 'antd/es/menu/hooks/useItems';
5
5
  import isEqual from 'fast-deep-equal';
6
6
  import { ArrowRight, Blocks, Store, ToyBrick } from 'lucide-react';
7
- import { memo, useState } from 'react';
7
+ import { memo } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
  import { Flexbox } from 'react-layout-kit';
10
10
 
11
+ import { useWorkspaceModal } from '@/app/(main)/chat/(workspace)/features/useWorkspaceModal';
11
12
  import PluginStore from '@/features/PluginStore';
12
13
  import { useAgentStore } from '@/store/agent';
13
14
  import { agentSelectors } from '@/store/agent/selectors';
@@ -45,7 +46,7 @@ const Tools = memo(() => {
45
46
  .filter((i) => !builtinList.some((b) => b.identifier === i)).length,
46
47
  );
47
48
 
48
- const [open, setOpen] = useState(false);
49
+ const [open, setOpen] = useWorkspaceModal();
49
50
  const { styles } = useStyles();
50
51
 
51
52
  const model = useAgentStore(agentSelectors.currentAgentModel);
@@ -63,7 +63,7 @@ const AgentsSuggest = memo<{ mobile?: boolean }>(({ mobile }) => {
63
63
  const agentList = useMarketStore((s) => s.agentList, isEqual);
64
64
  const { styles } = useStyles();
65
65
 
66
- const agentLength = mobile ? 3 : 4;
66
+ const agentLength = mobile ? 2 : 4;
67
67
 
68
68
  const loadingCards = Array.from({ length: agentLength }).map((_, index) => (
69
69
  <Flexbox className={styles.card} key={index}>
@@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next';
7
7
  import { Center, Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import { useGreeting } from '@/hooks/useGreeting';
10
- import { useIsMobile } from '@/hooks/useIsMobile';
10
+ import { useServerConfigStore } from '@/store/serverConfig';
11
11
 
12
12
  import AgentsSuggest from './AgentsSuggest';
13
13
  import QuestionSuggest from './QuestionSuggest';
@@ -42,7 +42,7 @@ const useStyles = createStyles(({ css, responsive }) => ({
42
42
  const InboxWelcome = memo(() => {
43
43
  const { t } = useTranslation('welcome');
44
44
  const { styles } = useStyles();
45
- const mobile = useIsMobile();
45
+ const mobile = useServerConfigStore((s) => s.isMobile);
46
46
  const greeting = useGreeting();
47
47
 
48
48
  return (
@@ -1,13 +1,20 @@
1
+ 'use client';
2
+
1
3
  import { Skeleton } from 'antd';
2
4
  import { createStyles } from 'antd-style';
3
5
  import { memo } from 'react';
4
6
  import { Flexbox } from 'react-layout-kit';
5
7
 
6
8
  const useStyles = createStyles(({ css, prefixCls }) => ({
7
- user: css`
9
+ message: css`
8
10
  display: flex;
11
+ gap: 12px;
12
+ .${prefixCls}-skeleton-header {
13
+ padding: 0;
14
+ }
15
+ `,
16
+ user: css`
9
17
  flex-direction: row-reverse;
10
- gap: 16px;
11
18
 
12
19
  .${prefixCls}-skeleton-paragraph {
13
20
  display: flex;
@@ -20,18 +27,24 @@ interface SkeletonListProps {
20
27
  mobile?: boolean;
21
28
  }
22
29
  const SkeletonList = memo<SkeletonListProps>(({ mobile }) => {
23
- const { styles } = useStyles();
30
+ const { cx, styles } = useStyles();
24
31
 
25
32
  return (
26
- <Flexbox gap={24} padding={12} style={{ marginTop: 24 + (mobile ? 0 : 64) }}>
33
+ <Flexbox gap={24} padding={mobile ? 8 : 12} style={{ marginTop: 24 + (mobile ? 0 : 64) }}>
34
+ <Skeleton
35
+ active
36
+ avatar={{ size: mobile ? 32 : 40 }}
37
+ className={styles.message}
38
+ paragraph={{ width: mobile ? ['80%', '40%'] : ['50%', '30%'] }}
39
+ title={false}
40
+ />
27
41
  <Skeleton
28
42
  active
29
- avatar={{ size: 40 }}
30
- className={styles.user}
31
- paragraph={{ width: ['50%', '30%'] }}
43
+ avatar={{ size: mobile ? 32 : 40 }}
44
+ className={cx(styles.message, styles.user)}
45
+ paragraph={{ width: mobile ? ['80%', '40%'] : ['50%', '30%'] }}
32
46
  title={false}
33
47
  />
34
- <Skeleton active avatar={{ size: 40 }} paragraph={{ width: ['50%', '30%'] }} title={false} />
35
48
  </Flexbox>
36
49
  );
37
50
  });
@@ -1,11 +1,13 @@
1
+ 'use client';
2
+
1
3
  import isEqual from 'fast-deep-equal';
2
4
  import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
3
5
  import { Flexbox } from 'react-layout-kit';
4
6
  import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
5
7
 
8
+ import { WELCOME_GUIDE_CHAT_ID } from '@/const/session';
6
9
  import { useChatStore } from '@/store/chat';
7
10
  import { chatSelectors } from '@/store/chat/selectors';
8
- import { isMobileScreen } from '@/utils/screen';
9
11
 
10
12
  import { useInitConversation } from '../../hooks/useInitConversation';
11
13
  import AutoScroll from '../AutoScroll';
@@ -13,26 +15,11 @@ import Item from '../ChatItem';
13
15
  import InboxWelcome from '../InboxWelcome';
14
16
  import SkeletonList from '../SkeletonList';
15
17
 
16
- const WELCOME_ID = 'welcome';
17
-
18
- const itemContent = (index: number, id: string) => {
19
- const isMobile = isMobileScreen();
20
-
21
- if (id === WELCOME_ID) return <InboxWelcome />;
22
-
23
- return index === 0 ? (
24
- <div style={{ height: 24 + (isMobile ? 0 : 64) }} />
25
- ) : (
26
- <Item id={id} index={index - 1} />
27
- );
28
- };
29
-
30
18
  interface VirtualizedListProps {
31
19
  mobile?: boolean;
32
20
  }
33
21
  const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
34
22
  useInitConversation();
35
-
36
23
  const virtuosoRef = useRef<VirtuosoHandle>(null);
37
24
  const [atBottom, setAtBottom] = useState(true);
38
25
  const [isScrolling, setIsScrolling] = useState(false);
@@ -44,7 +31,9 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
44
31
 
45
32
  const data = useChatStore((s) => {
46
33
  const showInboxWelcome = chatSelectors.showInboxWelcome(s);
47
- const ids = showInboxWelcome ? [WELCOME_ID] : chatSelectors.currentChatIDsWithGuideMessage(s);
34
+ const ids = showInboxWelcome
35
+ ? [WELCOME_GUIDE_CHAT_ID]
36
+ : chatSelectors.currentChatIDsWithGuideMessage(s);
48
37
  return ['empty', ...ids];
49
38
  }, isEqual);
50
39
 
@@ -65,6 +54,19 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
65
54
  // overscan should be 1.5 times the height of the window
66
55
  const overscan = typeof window !== 'undefined' ? window.innerHeight * 1.5 : 0;
67
56
 
57
+ const itemContent = useCallback(
58
+ (index: number, id: string) => {
59
+ if (id === WELCOME_GUIDE_CHAT_ID) return <InboxWelcome />;
60
+
61
+ return index === 0 ? (
62
+ <div style={{ height: 24 + (mobile ? 0 : 64) }} />
63
+ ) : (
64
+ <Item id={id} index={index - 1} />
65
+ );
66
+ },
67
+ [mobile],
68
+ );
69
+
68
70
  return chatLoading ? (
69
71
  <SkeletonList mobile={mobile} />
70
72
  ) : (
@@ -1,49 +1,30 @@
1
- import { createStyles } from 'antd-style';
2
- import { ReactNode, Suspense, lazy, memo } from 'react';
1
+ import { Suspense, lazy } from 'react';
3
2
  import { Flexbox } from 'react-layout-kit';
4
3
 
5
- import ChatHydration from '@/components/StoreHydration/ChatHydration';
6
-
7
4
  import SkeletonList from './components/SkeletonList';
8
5
 
9
6
  const ChatList = lazy(() => import('./components/VirtualizedList'));
10
7
 
11
- const useStyles = createStyles(
12
- ({ css, responsive }) => css`
13
- position: relative;
14
- overflow-y: auto;
15
- height: 100%;
16
-
17
- ${responsive.mobile} {
18
- width: 100%;
19
- }
20
- `,
21
- );
22
-
23
8
  interface ConversationProps {
24
- chatInput: ReactNode;
25
9
  mobile?: boolean;
26
10
  }
27
11
 
28
- const Conversation = memo<ConversationProps>(({ chatInput, mobile }) => {
29
- const { styles } = useStyles();
30
-
12
+ const Conversation = ({ mobile }: ConversationProps) => {
31
13
  return (
32
14
  <Flexbox
33
15
  flex={1}
34
- height={'100%'}
35
- // `relative` is required, ChatInput's absolute position needs it
36
- style={{ position: 'relative' }}
16
+ style={{
17
+ overflowX: 'hidden',
18
+ overflowY: 'auto',
19
+ position: 'relative',
20
+ }}
21
+ width={'100%'}
37
22
  >
38
- <div className={styles}>
39
- <Suspense fallback={<SkeletonList mobile={mobile} />}>
40
- <ChatList mobile={mobile} />
41
- </Suspense>
42
- </div>
43
- {chatInput}
44
- <ChatHydration />
23
+ <Suspense fallback={<SkeletonList mobile={mobile} />}>
24
+ <ChatList mobile={mobile} />
25
+ </Suspense>
45
26
  </Flexbox>
46
27
  );
47
- });
28
+ };
48
29
 
49
30
  export default Conversation;
@@ -1,10 +1,11 @@
1
1
  import { SearchBar } from '@lobehub/ui';
2
- import { useResponsive } from 'antd-style';
3
2
  import isEqual from 'fast-deep-equal';
4
- import { memo, useState } from 'react';
3
+ import { memo, useMemo, useState } from 'react';
5
4
  import { useTranslation } from 'react-i18next';
6
5
  import { Flexbox } from 'react-layout-kit';
6
+ import { Virtuoso } from 'react-virtuoso';
7
7
 
8
+ import { useServerConfigStore } from '@/store/serverConfig';
8
9
  import { useToolStore } from '@/store/tool';
9
10
  import { pluginSelectors } from '@/store/tool/selectors';
10
11
 
@@ -14,39 +15,45 @@ import PluginItem from './PluginItem';
14
15
  export const InstalledPluginList = memo(() => {
15
16
  const { t } = useTranslation('plugin');
16
17
  const [keywords, setKeywords] = useState<string>();
17
- const { mobile } = useResponsive();
18
+ const mobile = useServerConfigStore((s) => s.isMobile);
18
19
  const installedPlugins = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
19
20
 
21
+ const filteredPluginList = useMemo(
22
+ () =>
23
+ installedPlugins.filter((item) =>
24
+ [item.meta?.title, item.meta?.description, item.author, ...(item.meta?.tags || [])]
25
+ .filter(Boolean)
26
+ .join('')
27
+ .toLowerCase()
28
+ .includes((keywords || '')?.toLowerCase()),
29
+ ),
30
+ [installedPlugins, keywords],
31
+ );
32
+
20
33
  return (
21
34
  <>
22
- <Flexbox align={'center'} gap={16} horizontal justify={'space-between'}>
35
+ <Flexbox align={'center'} gap={8} horizontal justify={'space-between'}>
23
36
  <Flexbox flex={1}>
24
37
  <SearchBar
25
38
  allowClear
26
39
  onChange={(e) => setKeywords(e.target.value)}
27
40
  placeholder={t('store.placeholder')}
41
+ style={{ flex: 1, width: '100%' }}
28
42
  type={mobile ? 'block' : 'ghost'}
29
43
  value={keywords}
30
44
  />
31
45
  </Flexbox>
32
- <Flexbox gap={8} horizontal>
33
- <AddPluginButton />
34
- </Flexbox>
35
- </Flexbox>
36
-
37
- <Flexbox gap={24}>
38
- {installedPlugins
39
- .filter((item) =>
40
- [item.meta?.title, item.meta?.description, item.author, ...(item.meta?.tags || [])]
41
- .filter(Boolean)
42
- .join('')
43
- .toLowerCase()
44
- .includes((keywords || '')?.toLowerCase()),
45
- )
46
- .map((i) => (
47
- <PluginItem {...i} key={i.identifier} />
48
- ))}
46
+ <AddPluginButton />
49
47
  </Flexbox>
48
+ <Virtuoso
49
+ itemContent={(index) => {
50
+ const item = filteredPluginList[index];
51
+ return <PluginItem key={item.identifier} {...item} />;
52
+ }}
53
+ overscan={400}
54
+ style={{ height: 500, marginInline: -16 }}
55
+ totalCount={filteredPluginList.length}
56
+ />
50
57
  </>
51
58
  );
52
59
  });
@@ -1,6 +1,5 @@
1
1
  import { Icon, SearchBar } from '@lobehub/ui';
2
2
  import { Empty } from 'antd';
3
- import { useResponsive } from 'antd-style';
4
3
  import isEqual from 'fast-deep-equal';
5
4
  import { ServerCrash } from 'lucide-react';
6
5
  import { memo, useMemo, useState } from 'react';
@@ -9,6 +8,7 @@ import { Center, Flexbox } from 'react-layout-kit';
9
8
  import { Virtuoso } from 'react-virtuoso';
10
9
 
11
10
  import AddPluginButton from '@/features/PluginStore/AddPluginButton';
11
+ import { useServerConfigStore } from '@/store/serverConfig';
12
12
  import { useToolStore } from '@/store/tool';
13
13
  import { pluginSelectors, pluginStoreSelectors } from '@/store/tool/selectors';
14
14
 
@@ -18,7 +18,7 @@ import PluginItem from './PluginItem';
18
18
  export const OnlineList = memo(() => {
19
19
  const { t } = useTranslation('plugin');
20
20
  const [keywords, setKeywords] = useState<string>();
21
- const { mobile } = useResponsive();
21
+ const mobile = useServerConfigStore((s) => s.isMobile);
22
22
  const pluginStoreList = useToolStore((s) => {
23
23
  const custom = pluginSelectors.installedCustomPluginMetaList(s);
24
24
  const store = pluginStoreSelectors.onlinePluginStore(s);
@@ -58,7 +58,6 @@ export const OnlineList = memo(() => {
58
58
  </Flexbox>
59
59
  <AddPluginButton />
60
60
  </Flexbox>
61
-
62
61
  {isLoading ? (
63
62
  <Loading />
64
63
  ) : isEmpty ? (
@@ -76,15 +75,10 @@ export const OnlineList = memo(() => {
76
75
  <Virtuoso
77
76
  itemContent={(index) => {
78
77
  const item = filteredPluginList[index];
79
-
80
- return (
81
- <Flexbox key={item.identifier} paddingBlock={12}>
82
- <PluginItem {...item} />
83
- </Flexbox>
84
- );
78
+ return <PluginItem key={item.identifier} {...item} />;
85
79
  }}
86
80
  overscan={400}
87
- style={{ height: 500 }}
81
+ style={{ height: 500, marginInline: -16 }}
88
82
  totalCount={filteredPluginList.length}
89
83
  />
90
84
  )}