From abf4e868ac4b17b2aa48439d1e8b016be8443216 Mon Sep 17 00:00:00 2001 From: CanisMinor Date: Wed, 8 May 2024 20:43:59 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20perf:=20refactor=20Chat=20?= =?UTF-8?q?Layout=20to=20improve=20performance=20(#2339)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * โ™ป๏ธ refactor: Refactor Chat Layout * โœ… test: Fix test * ๐Ÿ› fix: Fix ci * ๐Ÿ”ง chore: Rename workspace * ๐Ÿ”ง chore: Revert sync style * ๐Ÿ› fix: Fix review problem * ๐Ÿ’„ style: Fix style * ๐Ÿ”ง chore: Revert useInterceptingRoutes * ๐Ÿ› fix: Fix review problem * ๐Ÿ’„ style: Remove animation * ๐Ÿ› fix: Fix panel * ๐Ÿ› fix: Fix mobile style * ๐Ÿ› fix: Fix Workspace Modal * ๐Ÿ› fix: FIx ios keyboard scroll back * ๐Ÿ› fix: Fix lint * ๐Ÿ”ง chore: prepare to rebase * ๐Ÿ› fix: Fix PWA * ๐Ÿ› fix: Fix safearea --- .../(mobile)/me/features/AvatarBanner.tsx | 27 ------- .../(main)/(mobile)/me/features/Header.tsx | 11 +-- src/app/(main)/(mobile)/me/features/style.ts | 29 ------- src/app/(main)/(mobile)/me/loading.tsx | 13 +-- src/app/(main)/(mobile)/me/page.tsx | 7 +- src/app/(main)/@nav/_layout/Mobile.tsx | 2 +- src/app/(main)/_layout/Mobile.tsx | 6 +- .../chat/(desktop)/features/Conversation.tsx | 19 ----- src/app/(main)/chat/(desktop)/index.tsx | 22 ------ .../chat/(mobile)/features/SessionList.tsx | 17 ---- .../chat/(mobile)/features/TopicList.tsx | 29 ------- src/app/(main)/chat/(mobile)/index.tsx | 26 ------ .../chat/(mobile)/mobile/ChatHeader/index.tsx | 56 ------------- src/app/(main)/chat/(mobile)/mobile/page.tsx | 26 ------ .../(workspace)/@conversation/default.tsx | 23 ++++++ .../ChatInput/Desktop}/Footer/DragUpload.tsx | 0 .../ChatInput/Desktop}/Footer/LocalFiles.tsx | 0 .../ChatInput/Desktop}/Footer/SendMore.tsx | 0 .../ChatInput/Desktop}/Footer/index.tsx | 0 .../ChatInput/Desktop}/Header/index.tsx | 0 .../ChatInput/Desktop}/TextArea.test.tsx | 0 .../features/ChatInput/Desktop}/TextArea.tsx | 0 .../Desktop}/__tests__/useAutoFocus.test.ts | 0 .../features/ChatInput/Desktop}/index.tsx | 8 +- .../ChatInput/Desktop}/useAutoFocus.ts | 0 .../features/ChatInput/Mobile}/Files.tsx | 0 .../features/ChatInput/Mobile}/index.tsx | 13 ++- .../chat/(workspace)/@topic/default.tsx | 19 +++++ .../SystemRole/SystemRoleContent.tsx} | 2 + .../@topic/features/SystemRole/index.tsx | 18 +++++ .../@topic/features}/SystemRole/style.ts | 0 .../features/TopicListContent/Header.tsx | 2 + .../TopicListContent/Topic/DefaultContent.tsx | 0 .../TopicListContent/Topic/SkeletonList.tsx | 0 .../TopicListContent/Topic/TopicContent.tsx | 0 .../TopicListContent/Topic/TopicItem.tsx | 0 .../features/TopicListContent/Topic/index.tsx | 54 ++++++------- .../TopicListContent/TopicSearchBar/index.tsx | 6 +- .../features/TopicListContent/index.tsx | 7 +- .../Desktop}/ChatHeader/HeaderAction.tsx | 2 + .../_layout/Desktop}/ChatHeader/Main.tsx | 4 +- .../_layout/Desktop}/ChatHeader/Tags.tsx | 2 +- .../_layout/Desktop}/ChatHeader/index.tsx | 3 +- .../_layout/Desktop}/HotKeys.tsx | 2 + .../_layout/Desktop/TopicPanel.tsx} | 43 +++++----- .../(workspace)/_layout/Desktop/index.tsx | 35 ++++++++ .../Mobile}/ChatHeader/ChatHeaderTitle.tsx | 0 .../_layout/Mobile/ChatHeader/index.tsx | 35 ++++++++ .../(workspace)/_layout/Mobile/TopicModal.tsx | 26 ++++++ .../chat/(workspace)/_layout/Mobile/index.tsx | 21 +++++ .../(main)/chat/(workspace)/_layout/type.ts | 7 ++ .../features/PluginTag/PluginStatus.tsx | 0 .../features/PluginTag/index.tsx | 2 + .../features/SettingButton.tsx | 4 +- .../features/ShareButton/Preview.tsx | 0 .../features/ShareButton/ShareModal.tsx | 0 .../features/ShareButton/index.tsx | 11 ++- .../features/ShareButton/style.ts | 0 .../features/ShareButton/type.ts | 0 .../features/ShareButton/useScreenshot.ts | 0 .../features/TelemetryNotification.tsx} | 2 + .../features/useWorkspaceModal.tsx | 27 +++++++ src/app/(main)/chat/(workspace)/layout.ts | 11 +++ src/app/(main)/chat/(workspace)/page.tsx | 19 +++++ .../@session/_layout/Desktop/PanelBody.tsx | 22 ++++++ .../_layout/Desktop/SessionHeader.tsx | 2 + .../chat/@session/_layout/Desktop/index.tsx | 15 ++++ .../_layout/Mobile}/SessionHeader.tsx | 27 ++----- .../chat/@session/_layout/Mobile/index.tsx | 19 +++++ src/app/(main)/chat/@session/default.tsx | 23 ++++++ .../features/SessionHydration.tsx} | 1 + .../CollapseGroup/Actions.tsx | 0 .../CollapseGroup/index.tsx | 0 .../SessionListContent/DefaultMode.tsx | 6 +- .../SessionListContent/Inbox/index.tsx | 4 +- .../SessionListContent/List/AddButton.tsx | 0 .../SessionListContent/List/Item/Actions.tsx | 0 .../SessionListContent/List/Item/index.tsx | 0 .../SessionListContent/List/index.tsx | 7 +- .../SessionListContent/ListItem/index.tsx | 22 +++--- .../Modals/ConfigGroupModal/GroupItem.tsx | 0 .../Modals/ConfigGroupModal/index.tsx | 0 .../Modals/CreateGroupModal.tsx | 0 .../Modals/RenameGroupModal.tsx | 0 .../SessionListContent/SearchMode.tsx | 6 +- .../SessionListContent/SkeletonList.tsx | 0 .../features/SessionListContent/index.tsx | 2 + .../features/SessionSearchBar.tsx} | 8 +- .../chat/_layout/Desktop/SessionList.tsx | 39 --------- .../chat/_layout/Desktop/SessionPanel.tsx | 79 +++++++++++++++++++ src/app/(main)/chat/_layout/Desktop/index.tsx | 18 +++-- src/app/(main)/chat/_layout/Mobile.tsx | 52 ++++++++++++ src/app/(main)/chat/_layout/Mobile/index.tsx | 9 --- src/app/(main)/chat/_layout/type.ts | 1 + src/app/(main)/chat/error.tsx | 5 ++ .../(main)/chat/features/Migration/index.tsx | 11 +-- src/app/(main)/chat/not-found.tsx | 3 + src/app/(main)/chat/page.tsx | 25 ------ .../chat/settings/_layout/Mobile/Header.tsx | 7 +- .../chat/settings/features/HeaderContent.tsx | 4 +- .../features/SubmitAgentButton/index.tsx | 4 +- .../(main)/market/@detail/features/Header.tsx | 4 +- src/components/Cell/Divider.tsx | 4 +- src/components/Cell/index.tsx | 4 +- .../StoreHydration/ChatHydration/index.tsx | 4 +- src/const/session.ts | 2 + src/const/url.ts | 6 +- .../ChatInput/ActionBar/Tools/index.tsx | 5 +- .../components/InboxWelcome/AgentsSuggest.tsx | 2 +- .../components/InboxWelcome/index.tsx | 4 +- .../Conversation/components/SkeletonList.tsx | 29 +++++-- .../components/VirtualizedList/index.tsx | 36 +++++---- src/features/Conversation/index.tsx | 43 +++------- src/features/FolderPanel/index.tsx | 60 -------------- .../PluginStore/InstalledPluginList.tsx | 49 +++++++----- src/features/PluginStore/OnlineList.tsx | 14 +--- .../PluginStore/PluginItem/Action.tsx | 5 +- src/features/PluginStore/PluginItem/index.tsx | 2 + src/features/PluginStore/index.tsx | 6 +- src/features/User/UserAvatar.tsx | 3 +- src/features/User/UserPanel/useMenu.tsx | 2 +- src/layout/GlobalProvider/AppTheme.tsx | 25 ++---- src/store/global/action.ts | 2 + src/store/global/initialState.ts | 2 + src/styles/global.ts | 20 ++--- src/styles/mobileHeader.ts | 2 +- src/utils/screen.ts | 14 ---- 127 files changed, 767 insertions(+), 701 deletions(-) delete mode 100644 src/app/(main)/(mobile)/me/features/AvatarBanner.tsx delete mode 100644 src/app/(main)/(mobile)/me/features/style.ts delete mode 100644 src/app/(main)/chat/(desktop)/features/Conversation.tsx delete mode 100644 src/app/(main)/chat/(desktop)/index.tsx delete mode 100644 src/app/(main)/chat/(mobile)/features/SessionList.tsx delete mode 100644 src/app/(main)/chat/(mobile)/features/TopicList.tsx delete mode 100644 src/app/(main)/chat/(mobile)/index.tsx delete mode 100644 src/app/(main)/chat/(mobile)/mobile/ChatHeader/index.tsx delete mode 100644 src/app/(main)/chat/(mobile)/mobile/page.tsx create mode 100644 src/app/(main)/chat/(workspace)/@conversation/default.tsx rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/Footer/DragUpload.tsx (100%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/Footer/LocalFiles.tsx (100%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/Footer/SendMore.tsx (100%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/Footer/index.tsx (100%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/Header/index.tsx (100%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/TextArea.test.tsx (100%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/TextArea.tsx (100%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/__tests__/useAutoFocus.test.ts (100%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/index.tsx (91%) rename src/app/(main)/chat/{(desktop)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Desktop}/useAutoFocus.ts (100%) rename src/app/(main)/chat/{(mobile)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Mobile}/Files.tsx (100%) rename src/app/(main)/chat/{(mobile)/features/ChatInput => (workspace)/@conversation/features/ChatInput/Mobile}/index.tsx (83%) create mode 100644 src/app/(main)/chat/(workspace)/@topic/default.tsx rename src/app/(main)/chat/{(desktop)/features/SideBar/SystemRole/index.tsx => (workspace)/@topic/features/SystemRole/SystemRoleContent.tsx} (99%) create mode 100644 src/app/(main)/chat/(workspace)/@topic/features/SystemRole/index.tsx rename src/app/(main)/chat/{(desktop)/features/SideBar => (workspace)/@topic/features}/SystemRole/style.ts (100%) rename src/app/(main)/chat/{ => (workspace)/@topic}/features/TopicListContent/Header.tsx (99%) rename src/app/(main)/chat/{ => (workspace)/@topic}/features/TopicListContent/Topic/DefaultContent.tsx (100%) rename src/app/(main)/chat/{ => (workspace)/@topic}/features/TopicListContent/Topic/SkeletonList.tsx (100%) rename src/app/(main)/chat/{ => (workspace)/@topic}/features/TopicListContent/Topic/TopicContent.tsx (100%) rename src/app/(main)/chat/{ => (workspace)/@topic}/features/TopicListContent/Topic/TopicItem.tsx (100%) rename src/app/(main)/chat/{ => (workspace)/@topic}/features/TopicListContent/Topic/index.tsx (72%) rename src/app/(main)/chat/{ => (workspace)/@topic}/features/TopicListContent/TopicSearchBar/index.tsx (88%) rename src/app/(main)/chat/{ => (workspace)/@topic}/features/TopicListContent/index.tsx (79%) rename src/app/(main)/chat/{(desktop)/features => (workspace)/_layout/Desktop}/ChatHeader/HeaderAction.tsx (98%) rename src/app/(main)/chat/{(desktop)/features => (workspace)/_layout/Desktop}/ChatHeader/Main.tsx (96%) rename src/app/(main)/chat/{(desktop)/features => (workspace)/_layout/Desktop}/ChatHeader/Tags.tsx (95%) rename src/app/(main)/chat/{(desktop)/features => (workspace)/_layout/Desktop}/ChatHeader/index.tsx (54%) rename src/app/(main)/chat/{(desktop)/features => (workspace)/_layout/Desktop}/HotKeys.tsx (98%) rename src/app/(main)/chat/{(desktop)/features/SideBar/index.tsx => (workspace)/_layout/Desktop/TopicPanel.tsx} (57%) create mode 100644 src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx rename src/app/(main)/chat/{(mobile)/mobile => (workspace)/_layout/Mobile}/ChatHeader/ChatHeaderTitle.tsx (100%) create mode 100644 src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/index.tsx create mode 100644 src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx create mode 100644 src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx create mode 100644 src/app/(main)/chat/(workspace)/_layout/type.ts rename src/app/(main)/chat/{ => (workspace)}/features/PluginTag/PluginStatus.tsx (100%) rename src/app/(main)/chat/{ => (workspace)}/features/PluginTag/index.tsx (99%) rename src/app/(main)/chat/{ => (workspace)}/features/SettingButton.tsx (92%) rename src/app/(main)/chat/{ => (workspace)}/features/ShareButton/Preview.tsx (100%) rename src/app/(main)/chat/{ => (workspace)}/features/ShareButton/ShareModal.tsx (100%) rename src/app/(main)/chat/{ => (workspace)}/features/ShareButton/index.tsx (85%) rename src/app/(main)/chat/{ => (workspace)}/features/ShareButton/style.ts (100%) rename src/app/(main)/chat/{ => (workspace)}/features/ShareButton/type.ts (100%) rename src/app/(main)/chat/{ => (workspace)}/features/ShareButton/useScreenshot.ts (100%) rename src/app/(main)/chat/{features/TelemetryNotification/index.tsx => (workspace)/features/TelemetryNotification.tsx} (99%) create mode 100644 src/app/(main)/chat/(workspace)/features/useWorkspaceModal.tsx create mode 100644 src/app/(main)/chat/(workspace)/layout.ts create mode 100644 src/app/(main)/chat/(workspace)/page.tsx create mode 100644 src/app/(main)/chat/@session/_layout/Desktop/PanelBody.tsx rename src/app/(main)/chat/{ => @session}/_layout/Desktop/SessionHeader.tsx (99%) create mode 100644 src/app/(main)/chat/@session/_layout/Desktop/index.tsx rename src/app/(main)/chat/{(mobile)/features => @session/_layout/Mobile}/SessionHeader.tsx (60%) create mode 100644 src/app/(main)/chat/@session/_layout/Mobile/index.tsx create mode 100644 src/app/(main)/chat/@session/default.tsx rename src/app/(main)/chat/{components/SessionHydration/index.tsx => @session/features/SessionHydration.tsx} (91%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/CollapseGroup/Actions.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/CollapseGroup/index.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/DefaultMode.tsx (96%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/Inbox/index.tsx (86%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/List/AddButton.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/List/Item/Actions.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/List/Item/index.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/List/index.tsx (94%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/ListItem/index.tsx (75%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/Modals/ConfigGroupModal/index.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/Modals/CreateGroupModal.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/Modals/RenameGroupModal.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/SearchMode.tsx (81%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/SkeletonList.tsx (100%) rename src/app/(main)/chat/{ => @session}/features/SessionListContent/index.tsx (96%) rename src/app/(main)/chat/{features/SessionSearchBar/index.tsx => @session/features/SessionSearchBar.tsx} (79%) delete mode 100644 src/app/(main)/chat/_layout/Desktop/SessionList.tsx create mode 100644 src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx create mode 100644 src/app/(main)/chat/_layout/Mobile.tsx delete mode 100644 src/app/(main)/chat/_layout/Mobile/index.tsx create mode 100644 src/app/(main)/chat/error.tsx create mode 100644 src/app/(main)/chat/not-found.tsx delete mode 100644 src/app/(main)/chat/page.tsx delete mode 100644 src/features/FolderPanel/index.tsx delete mode 100644 src/utils/screen.ts diff --git a/src/app/(main)/(mobile)/me/features/AvatarBanner.tsx b/src/app/(main)/(mobile)/me/features/AvatarBanner.tsx deleted file mode 100644 index a0d733b300fb9..0000000000000 --- a/src/app/(main)/(mobile)/me/features/AvatarBanner.tsx +++ /dev/null @@ -1,27 +0,0 @@ -'use client'; - -import { PropsWithChildren, memo } from 'react'; -import { Flexbox } from 'react-layout-kit'; - -import UserAvatar from '@/features/User/UserAvatar'; - -import { useStyles } from './style'; - -export const AVATAR_SIZE = 80; - -const AvatarBanner = memo(({ children }) => { - const { styles } = useStyles(); - - return ( - <> - -
- -
-
- {children} - - ); -}); - -export default AvatarBanner; diff --git a/src/app/(main)/(mobile)/me/features/Header.tsx b/src/app/(main)/(mobile)/me/features/Header.tsx index 65e13b8308f4a..c37ede634f09c 100644 --- a/src/app/(main)/(mobile)/me/features/Header.tsx +++ b/src/app/(main)/(mobile)/me/features/Header.tsx @@ -1,35 +1,28 @@ 'use client'; import { ActionIcon, MobileNavBar } from '@lobehub/ui'; -import { useScroll } from 'ahooks'; import { useTheme } from 'antd-style'; import { Moon, Sun } from 'lucide-react'; import { memo } from 'react'; import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens'; import { useUserStore } from '@/store/user'; -import { mobileHeaderFixed } from '@/styles/mobileHeader'; +import { mobileHeaderSticky } from '@/styles/mobileHeader'; const Header = memo(() => { const theme = useTheme(); - const scroll = useScroll(() => document.querySelector('#lobe-mobile-scroll-container')); const switchThemeMode = useUserStore((s) => s.switchThemeMode); - const showBackground = (scroll as any)?.top > 44; return ( switchThemeMode(theme.isDarkMode ? 'light' : 'dark')} size={MOBILE_HEADER_ICON_SIZE} /> } - style={{ - ...mobileHeaderFixed, - background: showBackground ? undefined : 'transparent', - }} + style={mobileHeaderSticky} /> ); }); diff --git a/src/app/(main)/(mobile)/me/features/style.ts b/src/app/(main)/(mobile)/me/features/style.ts deleted file mode 100644 index b7bf3a9cc0909..0000000000000 --- a/src/app/(main)/(mobile)/me/features/style.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { createStyles } from 'antd-style'; - -export const useStyles = createStyles(({ css, token, isDarkMode }) => ({ - bannerBox: css` - position: relative; - - overflow: hidden; - flex: none; - - width: 100%; - height: 100px; - - background: ${token.colorFill}; - `, - bannerImg: css` - position: absolute; - scale: 8; - filter: blur(6px) saturate(2); - `, - info: css` - position: relative; - - margin-top: ${-token.borderRadiusLG}px; - - background: ${isDarkMode ? token.colorBgLayout : token.colorBgContainer}; - border-top-left-radius: ${token.borderRadiusLG}px; - border-top-right-radius: ${token.borderRadiusLG}px; - `, -})); diff --git a/src/app/(main)/(mobile)/me/loading.tsx b/src/app/(main)/(mobile)/me/loading.tsx index eacbe24611ed9..5db1a97cc46ea 100644 --- a/src/app/(main)/(mobile)/me/loading.tsx +++ b/src/app/(main)/(mobile)/me/loading.tsx @@ -7,21 +7,10 @@ import { Flexbox } from 'react-layout-kit'; import Divider from '@/components/Cell/Divider'; import SkeletonLoading from '@/components/SkeletonLoading'; -import { useStyles } from './features/style'; - const Loading = memo(() => { - const { styles } = useStyles(); return ( <> - - + diff --git a/src/app/(main)/(mobile)/me/page.tsx b/src/app/(main)/(mobile)/me/page.tsx index 74fc75fc6fa88..9fc3b4a3a143c 100644 --- a/src/app/(main)/(mobile)/me/page.tsx +++ b/src/app/(main)/(mobile)/me/page.tsx @@ -7,7 +7,6 @@ import DataStatistics from '@/features/User/DataStatistics'; import UserInfo from '@/features/User/UserInfo'; import { isMobileDevice } from '@/utils/responsive'; -import AvatarBanner from './features/AvatarBanner'; import Cate from './features/Cate'; import ExtraCate from './features/ExtraCate'; @@ -18,10 +17,8 @@ const Page = () => { return ( <> - - - - + + diff --git a/src/app/(main)/@nav/_layout/Mobile.tsx b/src/app/(main)/@nav/_layout/Mobile.tsx index d9c1959ab707d..8229347d09f7b 100644 --- a/src/app/(main)/@nav/_layout/Mobile.tsx +++ b/src/app/(main)/@nav/_layout/Mobile.tsx @@ -63,7 +63,7 @@ const Nav = memo(() => { [t], ); - return ; + return ; }); Nav.displayName = 'MobileNav'; diff --git a/src/app/(main)/_layout/Mobile.tsx b/src/app/(main)/_layout/Mobile.tsx index 0291a7e15522e..396902ce57af9 100644 --- a/src/app/(main)/_layout/Mobile.tsx +++ b/src/app/(main)/_layout/Mobile.tsx @@ -3,13 +3,17 @@ import { usePathname } from 'next/navigation'; import { memo } from 'react'; +import { useQuery } from '@/hooks/useQuery'; + import { LayoutProps } from './type'; const MOBILE_IGNORE_NAV_ROUTES = ['/settings/', '/chat/']; const Layout = memo(({ children, nav }: LayoutProps) => { + const { showMobileWorkspace } = useQuery(); const pathname = usePathname(); - const hideNav = MOBILE_IGNORE_NAV_ROUTES.some((path) => pathname.startsWith(path)); + const hideNav = + showMobileWorkspace || MOBILE_IGNORE_NAV_ROUTES.some((path) => pathname.startsWith(path)); return ( <> diff --git a/src/app/(main)/chat/(desktop)/features/Conversation.tsx b/src/app/(main)/chat/(desktop)/features/Conversation.tsx deleted file mode 100644 index d12850f729452..0000000000000 --- a/src/app/(main)/chat/(desktop)/features/Conversation.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { memo } from 'react'; - -import RawConversation from '@/features/Conversation'; - -import TelemetryNotification from '../../features/TelemetryNotification'; -import ChatInput from './ChatInput'; -import HotKeys from './HotKeys'; - -const Conversation = memo(() => { - return ( - <> - } /> - - - - ); -}); - -export default Conversation; diff --git a/src/app/(main)/chat/(desktop)/index.tsx b/src/app/(main)/chat/(desktop)/index.tsx deleted file mode 100644 index 2e4a5c2b3a254..0000000000000 --- a/src/app/(main)/chat/(desktop)/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -'use client'; - -import { memo } from 'react'; -import { Flexbox } from 'react-layout-kit'; - -import ClientResponsiveContent from '@/components/client/ClientResponsiveContent'; - -import ChatHeader from './features/ChatHeader'; -import Conversation from './features/Conversation'; -import SideBar from './features/SideBar'; - -const Desktop = memo(() => ( - <> - - - - - - -)); - -export default ClientResponsiveContent({ Desktop, Mobile: () => import('../(mobile)') }); diff --git a/src/app/(main)/chat/(mobile)/features/SessionList.tsx b/src/app/(main)/chat/(mobile)/features/SessionList.tsx deleted file mode 100644 index ec386c09dd142..0000000000000 --- a/src/app/(main)/chat/(mobile)/features/SessionList.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { memo } from 'react'; - -import SessionListContent from '../../features/SessionListContent'; -import SessionSearchBar from '../../features/SessionSearchBar'; - -const Sessions = memo(() => { - return ( - <> -
- -
- - - ); -}); - -export default Sessions; diff --git a/src/app/(main)/chat/(mobile)/features/TopicList.tsx b/src/app/(main)/chat/(mobile)/features/TopicList.tsx deleted file mode 100644 index abc4382e030e9..0000000000000 --- a/src/app/(main)/chat/(mobile)/features/TopicList.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Modal } from '@lobehub/ui'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { useGlobalStore } from '@/store/global'; - -import TopicListContent from '../../features/TopicListContent'; - -const Topics = memo(() => { - const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [ - s.preference.mobileShowTopic, - s.toggleMobileTopic, - ]); - - const { t } = useTranslation('chat'); - - return ( - toggleConfig(false)} - open={showAgentSettings} - title={t('topic.title')} - > - - - ); -}); - -export default Topics; diff --git a/src/app/(main)/chat/(mobile)/index.tsx b/src/app/(main)/chat/(mobile)/index.tsx deleted file mode 100644 index ac17bd995767a..0000000000000 --- a/src/app/(main)/chat/(mobile)/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -'use client'; - -import { useRouter } from 'next/navigation'; -import { memo, useEffect } from 'react'; - -import MobileContentLayout from '@/components/server/MobileNavLayout'; - -import SessionHeader from './features/SessionHeader'; -import SessionList from './features/SessionList'; - -const ChatMobilePage = memo(() => { - const router = useRouter(); - - useEffect(() => { - router.prefetch('/chat/mobile'); - router.prefetch('/settings'); - }, []); - - return ( - } withNav> - - - ); -}); - -export default ChatMobilePage; diff --git a/src/app/(main)/chat/(mobile)/mobile/ChatHeader/index.tsx b/src/app/(main)/chat/(mobile)/mobile/ChatHeader/index.tsx deleted file mode 100644 index 61f77bdf11067..0000000000000 --- a/src/app/(main)/chat/(mobile)/mobile/ChatHeader/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { MobileNavBar } from '@lobehub/ui'; -import { useRouter } from 'next/navigation'; -import { memo, useState } from 'react'; - -import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; - -import SettingButton from '../../../features/SettingButton'; -import ShareButton from '../../../features/ShareButton'; -import ChatHeaderTitle from './ChatHeaderTitle'; - -const MobileHeader = memo(() => { - const router = useRouter(); - const [open, setOpen] = useState(false); - - const { isAgentEditable } = useServerConfigStore(featureFlagsSelectors); - - // const items: MenuProps['items'] = [ - // { - // icon: , - // key: 'share', - // label: t('share', { ns: 'common' }), - // onClick: () => setOpen(true), - // }, - // !isInbox && { - // icon: , - // key: 'settings', - // label: t('header.session', { ns: 'setting' }), - // onClick: () => router.push(pathString('/chat/settings', { hash: location.hash })), - // }, - // ].filter(Boolean) as MenuProps['items']; - - return ( - } - onBackClick={() => router.push('/chat')} - right={ - <> - - {isAgentEditable && } - {/**/} - {/* */} - {/**/} - - } - showBackButton - style={{ width: '100%' }} - /> - ); -}); - -export default MobileHeader; diff --git a/src/app/(main)/chat/(mobile)/mobile/page.tsx b/src/app/(main)/chat/(mobile)/mobile/page.tsx deleted file mode 100644 index 3eed22d6e0aa4..0000000000000 --- a/src/app/(main)/chat/(mobile)/mobile/page.tsx +++ /dev/null @@ -1,26 +0,0 @@ -'use client'; - -import dynamic from 'next/dynamic'; -import { memo } from 'react'; - -import MobileContentLayout from '@/components/server/MobileNavLayout'; -import Conversation from '@/features/Conversation'; - -import SessionHydration from '../../components/SessionHydration'; -import TelemetryNotification from '../../features/TelemetryNotification'; -import ChatInput from '../features/ChatInput'; -import ChatHeader from './ChatHeader'; - -const TopicList = dynamic(() => import('../features/TopicList')); - -const Chat = memo(() => { - return ( - }> - } mobile /> - - - - - ); -}); -export default Chat; diff --git a/src/app/(main)/chat/(workspace)/@conversation/default.tsx b/src/app/(main)/chat/(workspace)/@conversation/default.tsx new file mode 100644 index 0000000000000..d1301f6ed9b8c --- /dev/null +++ b/src/app/(main)/chat/(workspace)/@conversation/default.tsx @@ -0,0 +1,23 @@ +import ChatHydration from '@/components/StoreHydration/ChatHydration'; +import Conversation from '@/features/Conversation'; +import { isMobileDevice } from '@/utils/responsive'; + +import DesktopChatInput from './features/ChatInput/Desktop'; +import MobileChatInput from './features/ChatInput/Mobile'; + +const ChatConversation = () => { + const mobile = isMobileDevice(); + const ChatInput = mobile ? MobileChatInput : DesktopChatInput; + + return ( + <> + + + + + ); +}; + +ChatConversation.displayName = 'ChatConversation'; + +export default ChatConversation; diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/Footer/DragUpload.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/DragUpload.tsx similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/Footer/DragUpload.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/DragUpload.tsx diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/Footer/LocalFiles.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/LocalFiles.tsx similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/Footer/LocalFiles.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/LocalFiles.tsx diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/Footer/SendMore.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/Footer/SendMore.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/Footer/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/Footer/index.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/Header/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Header/index.tsx similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/Header/index.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Header/index.tsx diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/TextArea.test.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/TextArea.test.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/TextArea.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/TextArea.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/__tests__/useAutoFocus.test.ts b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/__tests__/useAutoFocus.test.ts similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/__tests__/useAutoFocus.test.ts rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/__tests__/useAutoFocus.test.ts diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx similarity index 91% rename from src/app/(main)/chat/(desktop)/features/ChatInput/index.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx index fad04d9d8e2b5..705f0393681e7 100644 --- a/src/app/(main)/chat/(desktop)/features/ChatInput/index.tsx +++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx @@ -1,3 +1,5 @@ +'use client'; + import { DraggablePanel } from '@lobehub/ui'; import { memo, useState } from 'react'; import { Flexbox } from 'react-layout-kit'; @@ -13,7 +15,7 @@ import Footer from './Footer'; import Head from './Header'; import TextArea from './TextArea'; -const ChatInput = memo(() => { +const DesktopChatInput = memo(() => { const [expand, setExpand] = useState(false); const [inputHeight, updatePreference] = useGlobalStore((s) => [ @@ -52,4 +54,6 @@ const ChatInput = memo(() => { ); }); -export default ChatInput; +DesktopChatInput.displayName = 'DesktopChatInput'; + +export default DesktopChatInput; diff --git a/src/app/(main)/chat/(desktop)/features/ChatInput/useAutoFocus.ts b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/useAutoFocus.ts similarity index 100% rename from src/app/(main)/chat/(desktop)/features/ChatInput/useAutoFocus.ts rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/useAutoFocus.ts diff --git a/src/app/(main)/chat/(mobile)/features/ChatInput/Files.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files.tsx similarity index 100% rename from src/app/(main)/chat/(mobile)/features/ChatInput/Files.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files.tsx diff --git a/src/app/(main)/chat/(mobile)/features/ChatInput/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx similarity index 83% rename from src/app/(main)/chat/(mobile)/features/ChatInput/index.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx index 2fa8b00d5b7b1..767dd69147554 100644 --- a/src/app/(main)/chat/(mobile)/features/ChatInput/index.tsx +++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx @@ -1,3 +1,5 @@ +'use client'; + import { MobileChatInputArea, MobileChatSendButton } from '@lobehub/ui'; import { useTheme } from 'antd-style'; import { memo } from 'react'; @@ -10,7 +12,7 @@ import { useChatInput } from '@/features/ChatInput/useChatInput'; import Files from './Files'; -const ChatInputMobileLayout = memo(() => { +const MobileChatInput = memo(() => { const { t } = useTranslation('chat'); const theme = useTheme(); const { ref, onSend, loading, value, onInput, onStop, expand, setExpand } = useChatInput(); @@ -23,11 +25,12 @@ const ChatInputMobileLayout = memo(() => { onSend={onSend} placeholder={t('sendPlaceholder')} ref={ref} - safeArea setExpand={setExpand} style={{ - background: `linear-gradient(to bottom, ${theme.colorFillQuaternary}, transparent)`, + background: theme.colorBgLayout, + top: expand ? 0 : undefined, width: '100%', + zIndex: 101, }} textAreaLeftAddons={} textAreaRightAddons={ @@ -44,4 +47,6 @@ const ChatInputMobileLayout = memo(() => { ); }); -export default ChatInputMobileLayout; +MobileChatInput.displayName = 'MobileChatInput'; + +export default MobileChatInput; diff --git a/src/app/(main)/chat/(workspace)/@topic/default.tsx b/src/app/(main)/chat/(workspace)/@topic/default.tsx new file mode 100644 index 0000000000000..6e99bfc39ed4f --- /dev/null +++ b/src/app/(main)/chat/(workspace)/@topic/default.tsx @@ -0,0 +1,19 @@ +import { isMobileDevice } from '@/utils/responsive'; + +import SystemRole from './features/SystemRole'; +import TopicListContent from './features/TopicListContent'; + +const Topic = () => { + const mobile = isMobileDevice(); + + return ( + <> + {!mobile && } + + + ); +}; + +Topic.displayName = 'ChatTopic'; + +export default Topic; diff --git a/src/app/(main)/chat/(desktop)/features/SideBar/SystemRole/index.tsx b/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/SystemRoleContent.tsx similarity index 99% rename from src/app/(main)/chat/(desktop)/features/SideBar/SystemRole/index.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/SystemRole/SystemRoleContent.tsx index 05959b9dc6adc..1e26e496b467c 100644 --- a/src/app/(main)/chat/(desktop)/features/SideBar/SystemRole/index.tsx +++ b/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/SystemRoleContent.tsx @@ -1,3 +1,5 @@ +'use client'; + import { ActionIcon, EditableMessage } from '@lobehub/ui'; import { Skeleton } from 'antd'; import { Edit } from 'lucide-react'; diff --git a/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/index.tsx b/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/index.tsx new file mode 100644 index 0000000000000..e80a29316fad6 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/index.tsx @@ -0,0 +1,18 @@ +'use client'; + +import { memo } from 'react'; + +import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; +import { useSessionStore } from '@/store/session'; +import { sessionSelectors } from '@/store/session/selectors'; + +import SystemRoleContent from './SystemRoleContent'; + +const SystemRole = memo(() => { + const { isAgentEditable: showSystemRole } = useServerConfigStore(featureFlagsSelectors); + const isInbox = useSessionStore(sessionSelectors.isInboxSession); + + return showSystemRole && !isInbox && ; +}); + +export default SystemRole; diff --git a/src/app/(main)/chat/(desktop)/features/SideBar/SystemRole/style.ts b/src/app/(main)/chat/(workspace)/@topic/features/SystemRole/style.ts similarity index 100% rename from src/app/(main)/chat/(desktop)/features/SideBar/SystemRole/style.ts rename to src/app/(main)/chat/(workspace)/@topic/features/SystemRole/style.ts diff --git a/src/app/(main)/chat/features/TopicListContent/Header.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Header.tsx similarity index 99% rename from src/app/(main)/chat/features/TopicListContent/Header.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Header.tsx index 5816fb1823268..f1c5cbff25c9c 100644 --- a/src/app/(main)/chat/features/TopicListContent/Header.tsx +++ b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Header.tsx @@ -1,3 +1,5 @@ +'use client'; + import { ActionIcon, Icon } from '@lobehub/ui'; import { App, Dropdown, MenuProps } from 'antd'; import { MoreHorizontal, Search, Trash } from 'lucide-react'; diff --git a/src/app/(main)/chat/features/TopicListContent/Topic/DefaultContent.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/DefaultContent.tsx similarity index 100% rename from src/app/(main)/chat/features/TopicListContent/Topic/DefaultContent.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/DefaultContent.tsx diff --git a/src/app/(main)/chat/features/TopicListContent/Topic/SkeletonList.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/SkeletonList.tsx similarity index 100% rename from src/app/(main)/chat/features/TopicListContent/Topic/SkeletonList.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/SkeletonList.tsx diff --git a/src/app/(main)/chat/features/TopicListContent/Topic/TopicContent.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/TopicContent.tsx similarity index 100% rename from src/app/(main)/chat/features/TopicListContent/Topic/TopicContent.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/TopicContent.tsx diff --git a/src/app/(main)/chat/features/TopicListContent/Topic/TopicItem.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/TopicItem.tsx similarity index 100% rename from src/app/(main)/chat/features/TopicListContent/Topic/TopicItem.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/TopicItem.tsx diff --git a/src/app/(main)/chat/features/TopicListContent/Topic/index.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/index.tsx similarity index 72% rename from src/app/(main)/chat/features/TopicListContent/Topic/index.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/index.tsx index 8c9f591008e4a..89f221c3d7438 100644 --- a/src/app/(main)/chat/features/TopicListContent/Topic/index.tsx +++ b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/Topic/index.tsx @@ -1,5 +1,7 @@ +'use client'; + import { EmptyCard } from '@lobehub/ui'; -import { css, cx, useThemeMode } from 'antd-style'; +import { useThemeMode } from 'antd-style'; import isEqual from 'fast-deep-equal'; import React, { memo, useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; @@ -15,13 +17,7 @@ import { ChatTopic } from '@/types/topic'; import { Placeholder, SkeletonList } from './SkeletonList'; import TopicItem from './TopicItem'; -const container = css` - > div { - padding-inline: 8px; - } -`; - -export const Topic = memo(() => { +export const Topic = memo<{ mobile?: boolean }>(({ mobile }) => { const { t } = useTranslation('chat'); const virtuosoRef = useRef(null); const { isDarkMode } = useThemeMode(); @@ -62,29 +58,31 @@ export const Topic = memo(() => { return !topicsInit ? ( ) : ( - + {topicLength === 0 && ( - - { - updateGuideState({ topic: visible }); - }} - style={{ marginBottom: 6 }} - title={t('topic.guide.title')} - visible={visible} - width={200} - /> - + { + updateGuideState({ topic: visible }); + }} + style={{ flex: 'none', marginBottom: 12 }} + title={t('topic.guide.title')} + visible={visible} + width={200} + /> )} item.id} data={topics} diff --git a/src/app/(main)/chat/features/TopicListContent/TopicSearchBar/index.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicSearchBar/index.tsx similarity index 88% rename from src/app/(main)/chat/features/TopicListContent/TopicSearchBar/index.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicSearchBar/index.tsx index cac34c72c07b6..762f7f628f2b7 100644 --- a/src/app/(main)/chat/features/TopicListContent/TopicSearchBar/index.tsx +++ b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicSearchBar/index.tsx @@ -1,16 +1,18 @@ +'use client'; + import { SearchBar } from '@lobehub/ui'; import { useUnmount } from 'ahooks'; -import { useResponsive } from 'antd-style'; import { memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useChatStore } from '@/store/chat'; +import { useServerConfigStore } from '@/store/serverConfig'; const TopicSearchBar = memo<{ onClear?: () => void }>(({ onClear }) => { const { t } = useTranslation('chat'); const [keywords, setKeywords] = useState(''); - const { mobile } = useResponsive(); + const mobile = useServerConfigStore((s) => s.isMobile); const [activeSessionId, useSearchTopics] = useChatStore((s) => [s.activeId, s.useSearchTopics]); useSearchTopics(keywords, activeSessionId); diff --git a/src/app/(main)/chat/features/TopicListContent/index.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/index.tsx similarity index 79% rename from src/app/(main)/chat/features/TopicListContent/index.tsx rename to src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/index.tsx index 1a789d52890f3..381aae2203dad 100644 --- a/src/app/(main)/chat/features/TopicListContent/index.tsx +++ b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/index.tsx @@ -1,19 +1,18 @@ -import { memo } from 'react'; import { Flexbox } from 'react-layout-kit'; import Header from './Header'; import { Topic } from './Topic'; import TopicSearchBar from './TopicSearchBar'; -const TopicListContent = memo<{ mobile?: boolean }>(({ mobile }) => { +const TopicListContent = ({ mobile }: { mobile?: boolean }) => { return ( {mobile ? :
} - + ); -}); +}; export default TopicListContent; diff --git a/src/app/(main)/chat/(desktop)/features/ChatHeader/HeaderAction.tsx b/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx similarity index 98% rename from src/app/(main)/chat/(desktop)/features/ChatHeader/HeaderAction.tsx rename to src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx index 05db819f2ac29..296eba13ee802 100644 --- a/src/app/(main)/chat/(desktop)/features/ChatHeader/HeaderAction.tsx +++ b/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx @@ -1,3 +1,5 @@ +'use client'; + import { ActionIcon } from '@lobehub/ui'; import { PanelRightClose, PanelRightOpen } from 'lucide-react'; import { memo } from 'react'; diff --git a/src/app/(main)/chat/(desktop)/features/ChatHeader/Main.tsx b/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx similarity index 96% rename from src/app/(main)/chat/(desktop)/features/ChatHeader/Main.tsx rename to src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx index 7fd29e4fd5a70..8eb250a93be88 100644 --- a/src/app/(main)/chat/(desktop)/features/ChatHeader/Main.tsx +++ b/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Main.tsx @@ -1,3 +1,5 @@ +'use client'; + import { Avatar, ChatHeaderTitle } from '@lobehub/ui'; import { Skeleton } from 'antd'; import { memo } from 'react'; @@ -41,7 +43,7 @@ const Main = memo(() => { openChatSettings()} size={40} title={title} /> diff --git a/src/app/(main)/chat/(desktop)/features/ChatHeader/Tags.tsx b/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags.tsx similarity index 95% rename from src/app/(main)/chat/(desktop)/features/ChatHeader/Tags.tsx rename to src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags.tsx index acc32e75ad241..8a387ecf08e88 100644 --- a/src/app/(main)/chat/(desktop)/features/ChatHeader/Tags.tsx +++ b/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags.tsx @@ -19,7 +19,7 @@ const TitleTags = memo(() => { const showPlugin = useUserStore(modelProviderSelectors.isModelEnabledFunctionCall(model)); return ( - + diff --git a/src/app/(main)/chat/(desktop)/features/ChatHeader/index.tsx b/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/index.tsx similarity index 54% rename from src/app/(main)/chat/(desktop)/features/ChatHeader/index.tsx rename to src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/index.tsx index 6ec17cea37939..c45c62ee4cb69 100644 --- a/src/app/(main)/chat/(desktop)/features/ChatHeader/index.tsx +++ b/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/index.tsx @@ -1,9 +1,8 @@ import { ChatHeader } from '@lobehub/ui'; -import { memo } from 'react'; import HeaderAction from './HeaderAction'; import Main from './Main'; -const Header = memo(() => } right={} />); +const Header = () => } right={} style={{ zIndex: 11 }} />; export default Header; diff --git a/src/app/(main)/chat/(desktop)/features/HotKeys.tsx b/src/app/(main)/chat/(workspace)/_layout/Desktop/HotKeys.tsx similarity index 98% rename from src/app/(main)/chat/(desktop)/features/HotKeys.tsx rename to src/app/(main)/chat/(workspace)/_layout/Desktop/HotKeys.tsx index d53610cfb2a9a..04fa9f99322a5 100644 --- a/src/app/(main)/chat/(desktop)/features/HotKeys.tsx +++ b/src/app/(main)/chat/(workspace)/_layout/Desktop/HotKeys.tsx @@ -1,3 +1,5 @@ +'use client'; + import isEqual from 'fast-deep-equal'; import { useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; diff --git a/src/app/(main)/chat/(desktop)/features/SideBar/index.tsx b/src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx similarity index 57% rename from src/app/(main)/chat/(desktop)/features/SideBar/index.tsx rename to src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx index 0455b40c499b3..f6731b6efd676 100644 --- a/src/app/(main)/chat/(desktop)/features/SideBar/index.tsx +++ b/src/app/(main)/chat/(workspace)/_layout/Desktop/TopicPanel.tsx @@ -1,18 +1,12 @@ +'use client'; + import { DraggablePanel, DraggablePanelContainer } from '@lobehub/ui'; -import { createStyles } from 'antd-style'; -import dynamic from 'next/dynamic'; -import { memo } from 'react'; +import { createStyles, useResponsive } from 'antd-style'; +import { PropsWithChildren, memo, useEffect, useLayoutEffect, useState } from 'react'; import SafeSpacing from '@/components/SafeSpacing'; import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens'; import { useGlobalStore } from '@/store/global'; -import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; -import { useSessionStore } from '@/store/session'; -import { sessionSelectors } from '@/store/session/selectors'; - -import TopicListContent from '../../../features/TopicListContent'; - -const SystemRole = dynamic(() => import('./SystemRole')); const useStyles = createStyles(({ css, token }) => ({ content: css` @@ -21,7 +15,7 @@ const useStyles = createStyles(({ css, token }) => ({ height: 100% !important; `, drawer: css` - z-index: 0; + z-index: 10; background: ${token.colorBgLayout}; `, header: css` @@ -29,15 +23,25 @@ const useStyles = createStyles(({ css, token }) => ({ `, })); -const Desktop = memo(() => { +const TopicPanel = memo(({ children }: PropsWithChildren) => { const { styles } = useStyles(); - const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [ + const { md = true, lg = true } = useResponsive(); + const [showAgentSettings, toggleConfig, isPreferenceInit] = useGlobalStore((s) => [ s.preference.showChatSideBar, s.toggleChatSideBar, + s.isPreferenceInit, ]); + const [expand, setExpand] = useState(showAgentSettings); + + useLayoutEffect(() => { + if (!isPreferenceInit) return; + setExpand(showAgentSettings); + }, [isPreferenceInit, showAgentSettings]); - const { isAgentEditable: showSystemRole } = useServerConfigStore(featureFlagsSelectors); - const isInbox = useSessionStore(sessionSelectors.isInboxSession); + useEffect(() => { + if (lg && showAgentSettings) setExpand(true); + if (!lg) setExpand(false); + }, [lg, showAgentSettings]); return ( { classNames={{ content: styles.content, }} - expand={showAgentSettings} + expand={expand} minWidth={CHAT_SIDEBAR_WIDTH} - mode={'fixed'} + mode={md ? 'fixed' : 'float'} onExpandChange={toggleConfig} placement={'right'} showHandlerWideArea={false} @@ -61,11 +65,10 @@ const Desktop = memo(() => { }} > - {showSystemRole && !isInbox && } - + {children} ); }); -export default Desktop; +export default TopicPanel; diff --git a/src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx b/src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx new file mode 100644 index 0000000000000..9a80f23077b80 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/_layout/Desktop/index.tsx @@ -0,0 +1,35 @@ +import { Flexbox } from 'react-layout-kit'; + +import { LayoutProps } from '../type'; +import ChatHeader from './ChatHeader'; +import HotKeys from './HotKeys'; +import TopicPanel from './TopicPanel'; + +const Layout = ({ children, topic, conversation }: LayoutProps) => { + return ( + <> + + + + {conversation} + + {children} + {topic} + + + + ); +}; + +Layout.displayName = 'DesktopConversationLayout'; + +export default Layout; diff --git a/src/app/(main)/chat/(mobile)/mobile/ChatHeader/ChatHeaderTitle.tsx b/src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/ChatHeaderTitle.tsx similarity index 100% rename from src/app/(main)/chat/(mobile)/mobile/ChatHeader/ChatHeaderTitle.tsx rename to src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/ChatHeaderTitle.tsx diff --git a/src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/index.tsx b/src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/index.tsx new file mode 100644 index 0000000000000..5bb1ab3ba9c44 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/_layout/Mobile/ChatHeader/index.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { MobileNavBar } from '@lobehub/ui'; +import { memo, useState } from 'react'; + +import { useQueryRoute } from '@/hooks/useQueryRoute'; +import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; + +import SettingButton from '../../../features/SettingButton'; +import ShareButton from '../../../features/ShareButton'; +import ChatHeaderTitle from './ChatHeaderTitle'; + +const MobileHeader = memo(() => { + const router = useQueryRoute(); + const [open, setOpen] = useState(false); + + const { isAgentEditable } = useServerConfigStore(featureFlagsSelectors); + + return ( + } + onBackClick={() => router.push('/chat', { query: { session: '' }, replace: true })} + right={ + <> + + {isAgentEditable && } + + } + showBackButton + style={{ width: '100%' }} + /> + ); +}); + +export default MobileHeader; diff --git a/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx b/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx new file mode 100644 index 0000000000000..a569f83d3eaf4 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/_layout/Mobile/TopicModal.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { Modal } from '@lobehub/ui'; +import { PropsWithChildren, memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useGlobalStore } from '@/store/global'; + +import { useWorkspaceModal } from '../../features/useWorkspaceModal'; + +const Topics = memo(({ children }: PropsWithChildren) => { + const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [ + s.preference.mobileShowTopic, + s.toggleMobileTopic, + ]); + const [open, setOpen] = useWorkspaceModal(showAgentSettings, toggleConfig); + const { t } = useTranslation('chat'); + + return ( + setOpen(false)} open={open} title={t('topic.title')}> + {children} + + ); +}); + +export default Topics; diff --git a/src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx b/src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx new file mode 100644 index 0000000000000..4911326adc1fa --- /dev/null +++ b/src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx @@ -0,0 +1,21 @@ +import MobileContentLayout from '@/components/server/MobileNavLayout'; + +import { LayoutProps } from '../type'; +import ChatHeader from './ChatHeader'; +import TopicModal from './TopicModal'; + +const Layout = ({ children, topic, conversation }: LayoutProps) => { + return ( + <> + } style={{ overflowY: 'hidden' }}> + {conversation} + {children} + + {topic} + + ); +}; + +Layout.displayName = 'MobileConversationLayout'; + +export default Layout; diff --git a/src/app/(main)/chat/(workspace)/_layout/type.ts b/src/app/(main)/chat/(workspace)/_layout/type.ts new file mode 100644 index 0000000000000..e7d5b6781e748 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/_layout/type.ts @@ -0,0 +1,7 @@ +import { ReactNode } from 'react'; + +export interface LayoutProps { + children: ReactNode; + conversation: ReactNode; + topic: ReactNode; +} diff --git a/src/app/(main)/chat/features/PluginTag/PluginStatus.tsx b/src/app/(main)/chat/(workspace)/features/PluginTag/PluginStatus.tsx similarity index 100% rename from src/app/(main)/chat/features/PluginTag/PluginStatus.tsx rename to src/app/(main)/chat/(workspace)/features/PluginTag/PluginStatus.tsx diff --git a/src/app/(main)/chat/features/PluginTag/index.tsx b/src/app/(main)/chat/(workspace)/features/PluginTag/index.tsx similarity index 99% rename from src/app/(main)/chat/features/PluginTag/index.tsx rename to src/app/(main)/chat/(workspace)/features/PluginTag/index.tsx index 5a775ffc5d8e4..9cd9d46db146d 100644 --- a/src/app/(main)/chat/features/PluginTag/index.tsx +++ b/src/app/(main)/chat/(workspace)/features/PluginTag/index.tsx @@ -1,3 +1,5 @@ +'use client'; + import { Avatar, Icon, Tag } from '@lobehub/ui'; import type { MenuProps } from 'antd'; import { Dropdown } from 'antd'; diff --git a/src/app/(main)/chat/features/SettingButton.tsx b/src/app/(main)/chat/(workspace)/features/SettingButton.tsx similarity index 92% rename from src/app/(main)/chat/features/SettingButton.tsx rename to src/app/(main)/chat/(workspace)/features/SettingButton.tsx index c82560540ff9c..e8092366746dc 100644 --- a/src/app/(main)/chat/features/SettingButton.tsx +++ b/src/app/(main)/chat/(workspace)/features/SettingButton.tsx @@ -1,3 +1,5 @@ +'use client'; + import { ActionIcon } from '@lobehub/ui'; import { AlignJustify } from 'lucide-react'; import { memo } from 'react'; @@ -13,7 +15,7 @@ const SettingButton = memo<{ mobile?: boolean }>(({ mobile }) => { return ( openChatSettings()} size={mobile ? MOBILE_HEADER_ICON_SIZE : DESKTOP_HEADER_ICON_SIZE} title={t('header.session', { ns: 'setting' })} /> diff --git a/src/app/(main)/chat/features/ShareButton/Preview.tsx b/src/app/(main)/chat/(workspace)/features/ShareButton/Preview.tsx similarity index 100% rename from src/app/(main)/chat/features/ShareButton/Preview.tsx rename to src/app/(main)/chat/(workspace)/features/ShareButton/Preview.tsx diff --git a/src/app/(main)/chat/features/ShareButton/ShareModal.tsx b/src/app/(main)/chat/(workspace)/features/ShareButton/ShareModal.tsx similarity index 100% rename from src/app/(main)/chat/features/ShareButton/ShareModal.tsx rename to src/app/(main)/chat/(workspace)/features/ShareButton/ShareModal.tsx diff --git a/src/app/(main)/chat/features/ShareButton/index.tsx b/src/app/(main)/chat/(workspace)/features/ShareButton/index.tsx similarity index 85% rename from src/app/(main)/chat/features/ShareButton/index.tsx rename to src/app/(main)/chat/(workspace)/features/ShareButton/index.tsx index 7bd94820c36d7..302789cd5b5ab 100644 --- a/src/app/(main)/chat/features/ShareButton/index.tsx +++ b/src/app/(main)/chat/(workspace)/features/ShareButton/index.tsx @@ -1,13 +1,16 @@ +'use client'; + import { ActionIcon } from '@lobehub/ui'; import { Share2 } from 'lucide-react'; import dynamic from 'next/dynamic'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import useMergeState from 'use-merge-value'; import { DESKTOP_HEADER_ICON_SIZE, MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens'; import { useChatStore } from '@/store/chat'; +import { useWorkspaceModal } from '../useWorkspaceModal'; + const ShareModal = dynamic(() => import('./ShareModal')); interface ShareButtonProps { mobile?: boolean; @@ -16,11 +19,7 @@ interface ShareButtonProps { } const ShareButton = memo(({ mobile, setOpen, open }) => { - const [isModalOpen, setIsModalOpen] = useMergeState(false, { - defaultValue: false, - onChange: setOpen, - value: open, - }); + const [isModalOpen, setIsModalOpen] = useWorkspaceModal(open, setOpen); const { t } = useTranslation('common'); const [shareLoading] = useChatStore((s) => [s.shareLoading]); diff --git a/src/app/(main)/chat/features/ShareButton/style.ts b/src/app/(main)/chat/(workspace)/features/ShareButton/style.ts similarity index 100% rename from src/app/(main)/chat/features/ShareButton/style.ts rename to src/app/(main)/chat/(workspace)/features/ShareButton/style.ts diff --git a/src/app/(main)/chat/features/ShareButton/type.ts b/src/app/(main)/chat/(workspace)/features/ShareButton/type.ts similarity index 100% rename from src/app/(main)/chat/features/ShareButton/type.ts rename to src/app/(main)/chat/(workspace)/features/ShareButton/type.ts diff --git a/src/app/(main)/chat/features/ShareButton/useScreenshot.ts b/src/app/(main)/chat/(workspace)/features/ShareButton/useScreenshot.ts similarity index 100% rename from src/app/(main)/chat/features/ShareButton/useScreenshot.ts rename to src/app/(main)/chat/(workspace)/features/ShareButton/useScreenshot.ts diff --git a/src/app/(main)/chat/features/TelemetryNotification/index.tsx b/src/app/(main)/chat/(workspace)/features/TelemetryNotification.tsx similarity index 99% rename from src/app/(main)/chat/features/TelemetryNotification/index.tsx rename to src/app/(main)/chat/(workspace)/features/TelemetryNotification.tsx index 6cb9b90bcf49c..23e93339f97b6 100644 --- a/src/app/(main)/chat/features/TelemetryNotification/index.tsx +++ b/src/app/(main)/chat/(workspace)/features/TelemetryNotification.tsx @@ -1,3 +1,5 @@ +'use client'; + import { Avatar, Icon } from '@lobehub/ui'; import { Button } from 'antd'; import { createStyles } from 'antd-style'; diff --git a/src/app/(main)/chat/(workspace)/features/useWorkspaceModal.tsx b/src/app/(main)/chat/(workspace)/features/useWorkspaceModal.tsx new file mode 100644 index 0000000000000..56be274e4a321 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/features/useWorkspaceModal.tsx @@ -0,0 +1,27 @@ +import { useEffect } from 'react'; +import useMergeState from 'use-merge-value'; + +import { useQuery } from '@/hooks/useQuery'; +import { useServerConfigStore } from '@/store/serverConfig'; + +export const useWorkspaceModal = ( + value?: boolean, + onChange?: (v: boolean) => void, +): [boolean, (v: boolean) => void] => { + const mobile = useServerConfigStore((s) => s.isMobile); + const { showMobileWorkspace } = useQuery(); + const [isModalOpen, setIsModalOpen] = useMergeState(false, { + defaultValue: false, + onChange, + value, + }); + + useEffect(() => { + if (!mobile) return; + if (!showMobileWorkspace) { + setIsModalOpen(false); + } + }, [mobile, showMobileWorkspace]); + + return [isModalOpen, setIsModalOpen]; +}; diff --git a/src/app/(main)/chat/(workspace)/layout.ts b/src/app/(main)/chat/(workspace)/layout.ts new file mode 100644 index 0000000000000..e8112e9befaad --- /dev/null +++ b/src/app/(main)/chat/(workspace)/layout.ts @@ -0,0 +1,11 @@ +import ServerLayout from '@/components/server/ServerLayout'; + +import Desktop from './_layout/Desktop'; +import Mobile from './_layout/Mobile'; +import { LayoutProps } from './_layout/type'; + +const Layout = ServerLayout({ Desktop, Mobile }); + +Layout.displayName = 'ChatConversationLayout'; + +export default Layout; diff --git a/src/app/(main)/chat/(workspace)/page.tsx b/src/app/(main)/chat/(workspace)/page.tsx new file mode 100644 index 0000000000000..df67a3148d980 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/page.tsx @@ -0,0 +1,19 @@ +import { isMobileDevice } from '@/utils/responsive'; + +import PageTitle from '../features/PageTitle'; +import TelemetryNotification from './features/TelemetryNotification'; + +const Page = () => { + const mobile = isMobileDevice(); + + return ( + <> + + + + ); +}; + +Page.displayName = 'Chat'; + +export default Page; diff --git a/src/app/(main)/chat/@session/_layout/Desktop/PanelBody.tsx b/src/app/(main)/chat/@session/_layout/Desktop/PanelBody.tsx new file mode 100644 index 0000000000000..27547db693be2 --- /dev/null +++ b/src/app/(main)/chat/@session/_layout/Desktop/PanelBody.tsx @@ -0,0 +1,22 @@ +'use client'; + +import { DraggablePanelBody } from '@lobehub/ui'; +import { createStyles } from 'antd-style'; +import { PropsWithChildren, memo } from 'react'; + +const useStyles = createStyles( + ({ css }) => css` + display: flex; + flex-direction: column; + gap: 2px; + padding: 8px 8px 0; + `, +); + +const PanelBody = memo(({ children }) => { + const { styles } = useStyles(); + + return {children}; +}); + +export default PanelBody; diff --git a/src/app/(main)/chat/_layout/Desktop/SessionHeader.tsx b/src/app/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx similarity index 99% rename from src/app/(main)/chat/_layout/Desktop/SessionHeader.tsx rename to src/app/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx index 082d3768aff0e..cae90f877eba8 100644 --- a/src/app/(main)/chat/_layout/Desktop/SessionHeader.tsx +++ b/src/app/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx @@ -1,3 +1,5 @@ +'use client'; + import { ActionIcon, Logo } from '@lobehub/ui'; import { createStyles } from 'antd-style'; import { MessageSquarePlus } from 'lucide-react'; diff --git a/src/app/(main)/chat/@session/_layout/Desktop/index.tsx b/src/app/(main)/chat/@session/_layout/Desktop/index.tsx new file mode 100644 index 0000000000000..953e4621371b0 --- /dev/null +++ b/src/app/(main)/chat/@session/_layout/Desktop/index.tsx @@ -0,0 +1,15 @@ +import { PropsWithChildren } from 'react'; + +import PanelBody from './PanelBody'; +import Header from './SessionHeader'; + +const DesktopLayout = ({ children }: PropsWithChildren) => { + return ( + <> +
+ {children} + + ); +}; + +export default DesktopLayout; diff --git a/src/app/(main)/chat/(mobile)/features/SessionHeader.tsx b/src/app/(main)/chat/@session/_layout/Mobile/SessionHeader.tsx similarity index 60% rename from src/app/(main)/chat/(mobile)/features/SessionHeader.tsx rename to src/app/(main)/chat/@session/_layout/Mobile/SessionHeader.tsx index 42fd68338cb62..b08f4e4034e66 100644 --- a/src/app/(main)/chat/(mobile)/features/SessionHeader.tsx +++ b/src/app/(main)/chat/@session/_layout/Mobile/SessionHeader.tsx @@ -1,5 +1,6 @@ -import { ActionIcon, Avatar, Logo, MobileNavBar } from '@lobehub/ui'; -import { createStyles } from 'antd-style'; +'use client'; + +import { ActionIcon, Logo, MobileNavBar } from '@lobehub/ui'; import { MessageSquarePlus } from 'lucide-react'; import { useRouter } from 'next/navigation'; import { memo } from 'react'; @@ -7,37 +8,23 @@ import { Flexbox } from 'react-layout-kit'; import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens'; import SyncStatusInspector from '@/features/SyncStatusInspector'; +import UserAvatar from '@/features/User/UserAvatar'; import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig'; import { useSessionStore } from '@/store/session'; -import { useUserStore } from '@/store/user'; -import { userProfileSelectors } from '@/store/user/selectors'; import { mobileHeaderSticky } from '@/styles/mobileHeader'; -export const useStyles = createStyles(({ css, token }) => ({ - logo: css` - fill: ${token.colorText}; - `, - top: css` - position: sticky; - top: 0; - `, -})); - const Header = memo(() => { const [createSession] = useSessionStore((s) => [s.createSession]); const router = useRouter(); - const avatar = useUserStore(userProfileSelectors.userAvatar); - const { showCreateSession } = useServerConfigStore(featureFlagsSelectors); + const { enableWebrtc, showCreateSession } = useServerConfigStore(featureFlagsSelectors); return ( -
router.push('/me')}> - {avatar ? : } -
+ router.push('/me')} size={32} /> - + {enableWebrtc && } } right={ diff --git a/src/app/(main)/chat/@session/_layout/Mobile/index.tsx b/src/app/(main)/chat/@session/_layout/Mobile/index.tsx new file mode 100644 index 0000000000000..80c2106c6353b --- /dev/null +++ b/src/app/(main)/chat/@session/_layout/Mobile/index.tsx @@ -0,0 +1,19 @@ +import { PropsWithChildren } from 'react'; + +import MobileContentLayout from '@/components/server/MobileNavLayout'; + +import SessionSearchBar from '../../features/SessionSearchBar'; +import SessionHeader from './SessionHeader'; + +const MobileLayout = ({ children }: PropsWithChildren) => { + return ( + } withNav> +
+ +
+ {children} +
+ ); +}; + +export default MobileLayout; diff --git a/src/app/(main)/chat/@session/default.tsx b/src/app/(main)/chat/@session/default.tsx new file mode 100644 index 0000000000000..3248f907bb89b --- /dev/null +++ b/src/app/(main)/chat/@session/default.tsx @@ -0,0 +1,23 @@ +import ServerLayout from '@/components/server/ServerLayout'; + +import Desktop from './_layout/Desktop'; +import Mobile from './_layout/Mobile'; +import SessionHydration from './features/SessionHydration'; +import SessionListContent from './features/SessionListContent'; + +const Layout = ServerLayout({ Desktop, Mobile }); + +const Session = () => { + return ( + <> + + + + + + ); +}; + +Session.displayName = 'Session'; + +export default Session; diff --git a/src/app/(main)/chat/components/SessionHydration/index.tsx b/src/app/(main)/chat/@session/features/SessionHydration.tsx similarity index 91% rename from src/app/(main)/chat/components/SessionHydration/index.tsx rename to src/app/(main)/chat/@session/features/SessionHydration.tsx index 57ef0e9da25f3..37b716e04a4a3 100644 --- a/src/app/(main)/chat/components/SessionHydration/index.tsx +++ b/src/app/(main)/chat/@session/features/SessionHydration.tsx @@ -12,6 +12,7 @@ import { useSessionStore } from '@/store/session'; const SessionHydration = memo(() => { const useStoreUpdater = createStoreUpdater(useSessionStore); + // TODO: ๅŽ้ข่€ƒ่™‘ๅฏไปฅๅˆ ้™คไบ†๏ผŒ็”จ useServerConfigStore ๅฐฑไธ้œ€่ฆๅ†่ฟ™้‡Œๆณจๅ…ฅไบ† const { mobile } = useResponsive(); useStoreUpdater('isMobile', mobile); diff --git a/src/app/(main)/chat/features/SessionListContent/CollapseGroup/Actions.tsx b/src/app/(main)/chat/@session/features/SessionListContent/CollapseGroup/Actions.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/CollapseGroup/Actions.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/CollapseGroup/Actions.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/CollapseGroup/index.tsx b/src/app/(main)/chat/@session/features/SessionListContent/CollapseGroup/index.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/CollapseGroup/index.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/CollapseGroup/index.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/DefaultMode.tsx b/src/app/(main)/chat/@session/features/SessionListContent/DefaultMode.tsx similarity index 96% rename from src/app/(main)/chat/features/SessionListContent/DefaultMode.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/DefaultMode.tsx index 911163c8eef3a..c65553bf835f6 100644 --- a/src/app/(main)/chat/features/SessionListContent/DefaultMode.tsx +++ b/src/app/(main)/chat/@session/features/SessionListContent/DefaultMode.tsx @@ -16,7 +16,7 @@ import SessionList from './List'; import ConfigGroupModal from './Modals/ConfigGroupModal'; import RenameGroupModal from './Modals/RenameGroupModal'; -const SessionDefaultMode = memo(() => { +const DefaultMode = memo(() => { const { t } = useTranslation('chat'); const [activeGroupId, setActiveGroupId] = useState(); @@ -98,6 +98,6 @@ const SessionDefaultMode = memo(() => { ); }); -SessionDefaultMode.displayName = 'SessionDefaultMode'; +DefaultMode.displayName = 'SessionDefaultMode'; -export default SessionDefaultMode; +export default DefaultMode; diff --git a/src/app/(main)/chat/features/SessionListContent/Inbox/index.tsx b/src/app/(main)/chat/@session/features/SessionListContent/Inbox/index.tsx similarity index 86% rename from src/app/(main)/chat/features/SessionListContent/Inbox/index.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/Inbox/index.tsx index fc325b6fccfe3..18b9fdccc492c 100644 --- a/src/app/(main)/chat/features/SessionListContent/Inbox/index.tsx +++ b/src/app/(main)/chat/@session/features/SessionListContent/Inbox/index.tsx @@ -5,14 +5,14 @@ import { useTranslation } from 'react-i18next'; import { DEFAULT_INBOX_AVATAR } from '@/const/meta'; import { INBOX_SESSION_ID } from '@/const/session'; import { SESSION_CHAT_URL } from '@/const/url'; -import { useIsMobile } from '@/hooks/useIsMobile'; +import { useServerConfigStore } from '@/store/serverConfig'; import { useSessionStore } from '@/store/session'; import ListItem from '../ListItem'; const Inbox = memo(() => { const { t } = useTranslation('chat'); - const mobile = useIsMobile(); + const mobile = useServerConfigStore((s) => s.isMobile); const activeId = useSessionStore((s) => s.activeId); return ( diff --git a/src/app/(main)/chat/features/SessionListContent/List/AddButton.tsx b/src/app/(main)/chat/@session/features/SessionListContent/List/AddButton.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/List/AddButton.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/List/AddButton.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/List/Item/Actions.tsx b/src/app/(main)/chat/@session/features/SessionListContent/List/Item/Actions.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/List/Item/Actions.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/List/Item/Actions.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/List/Item/index.tsx b/src/app/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/List/Item/index.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/List/index.tsx b/src/app/(main)/chat/@session/features/SessionListContent/List/index.tsx similarity index 94% rename from src/app/(main)/chat/features/SessionListContent/List/index.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/List/index.tsx index 40c953ae96bb2..e924f62aa88a7 100644 --- a/src/app/(main)/chat/features/SessionListContent/List/index.tsx +++ b/src/app/(main)/chat/@session/features/SessionListContent/List/index.tsx @@ -1,5 +1,5 @@ import { Empty } from 'antd'; -import { createStyles, useResponsive } from 'antd-style'; +import { createStyles } from 'antd-style'; import Link from 'next/link'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -21,7 +21,6 @@ const useStyles = createStyles( min-height: 70px; `, ); - interface SessionListProps { dataSource?: LobeAgentSession[]; groupId?: string; @@ -31,10 +30,10 @@ const SessionList = memo(({ dataSource, groupId, showAddButton const { t } = useTranslation('chat'); const isInit = useSessionStore((s) => sessionSelectors.isSessionListInit(s)); const { showCreateSession } = useServerConfigStore(featureFlagsSelectors); - const { styles } = useStyles(); - const { mobile } = useResponsive(); + const { styles } = useStyles(); + const mobile = useServerConfigStore((s) => s.isMobile); const isEmpty = !dataSource || dataSource.length === 0; return !isInit ? ( diff --git a/src/app/(main)/chat/features/SessionListContent/ListItem/index.tsx b/src/app/(main)/chat/@session/features/SessionListContent/ListItem/index.tsx similarity index 75% rename from src/app/(main)/chat/features/SessionListContent/ListItem/index.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/ListItem/index.tsx index 1528279539d28..0c04cc505a373 100644 --- a/src/app/(main)/chat/features/SessionListContent/ListItem/index.tsx +++ b/src/app/(main)/chat/@session/features/SessionListContent/ListItem/index.tsx @@ -1,11 +1,13 @@ import { Avatar, List, ListItemProps } from '@lobehub/ui'; import { useHover } from 'ahooks'; -import { createStyles, useResponsive } from 'antd-style'; +import { createStyles } from 'antd-style'; import { memo, useMemo, useRef } from 'react'; +import { useServerConfigStore } from '@/store/serverConfig'; + const { Item } = List; -const useStyles = createStyles(({ css, token, responsive }) => { +const useStyles = createStyles(({ css, token }) => { return { container: css` position: relative; @@ -15,11 +17,11 @@ const useStyles = createStyles(({ css, token, responsive }) => { padding-left: 8px; border-radius: ${token.borderRadius}px; - ${responsive.mobile} { - margin-block: 0; - padding-left: 12px; - border-radius: 0; - } + `, + mobile: css` + margin-block: 0; + padding-left: 12px; + border-radius: 0; `, title: css` line-height: 1.2; @@ -31,8 +33,8 @@ const ListItem = memo { const ref = useRef(null); const isHovering = useHover(ref); - const { mobile } = useResponsive(); - const { styles } = useStyles(); + const mobile = useServerConfigStore((s) => s.isMobile); + const { cx, styles } = useStyles(); const avatarRender = useMemo( () => ( @@ -52,7 +54,7 @@ const ListItem = memo{title}} diff --git a/src/app/(main)/chat/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx b/src/app/(main)/chat/@session/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/Modals/ConfigGroupModal/index.tsx b/src/app/(main)/chat/@session/features/SessionListContent/Modals/ConfigGroupModal/index.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/Modals/ConfigGroupModal/index.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/Modals/ConfigGroupModal/index.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/Modals/CreateGroupModal.tsx b/src/app/(main)/chat/@session/features/SessionListContent/Modals/CreateGroupModal.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/Modals/CreateGroupModal.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/Modals/CreateGroupModal.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/Modals/RenameGroupModal.tsx b/src/app/(main)/chat/@session/features/SessionListContent/Modals/RenameGroupModal.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/Modals/RenameGroupModal.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/Modals/RenameGroupModal.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/SearchMode.tsx b/src/app/(main)/chat/@session/features/SessionListContent/SearchMode.tsx similarity index 81% rename from src/app/(main)/chat/features/SessionListContent/SearchMode.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/SearchMode.tsx index 3682e3e1cee76..eecd3c2fdf210 100644 --- a/src/app/(main)/chat/features/SessionListContent/SearchMode.tsx +++ b/src/app/(main)/chat/@session/features/SessionListContent/SearchMode.tsx @@ -5,7 +5,7 @@ import { useSessionStore } from '@/store/session'; import SessionList from './List'; import SkeletonList from './SkeletonList'; -const SessionListContent = memo(() => { +const SearchMode = memo(() => { const [sessionSearchKeywords, useSearchSessions] = useSessionStore((s) => [ s.sessionSearchKeywords, s.useSearchSessions, @@ -16,4 +16,6 @@ const SessionListContent = memo(() => { return isLoading ? : ; }); -export default SessionListContent; +SearchMode.displayName = 'SessionSearchMode'; + +export default SearchMode; diff --git a/src/app/(main)/chat/features/SessionListContent/SkeletonList.tsx b/src/app/(main)/chat/@session/features/SessionListContent/SkeletonList.tsx similarity index 100% rename from src/app/(main)/chat/features/SessionListContent/SkeletonList.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/SkeletonList.tsx diff --git a/src/app/(main)/chat/features/SessionListContent/index.tsx b/src/app/(main)/chat/@session/features/SessionListContent/index.tsx similarity index 96% rename from src/app/(main)/chat/features/SessionListContent/index.tsx rename to src/app/(main)/chat/@session/features/SessionListContent/index.tsx index 4a4fa6ac7b773..97f658ac4e7c1 100644 --- a/src/app/(main)/chat/features/SessionListContent/index.tsx +++ b/src/app/(main)/chat/@session/features/SessionListContent/index.tsx @@ -1,3 +1,5 @@ +'use client'; + import { memo } from 'react'; import { useSessionStore } from '@/store/session'; diff --git a/src/app/(main)/chat/features/SessionSearchBar/index.tsx b/src/app/(main)/chat/@session/features/SessionSearchBar.tsx similarity index 79% rename from src/app/(main)/chat/features/SessionSearchBar/index.tsx rename to src/app/(main)/chat/@session/features/SessionSearchBar.tsx index 2cd60b2058d87..22eddf0fc551a 100644 --- a/src/app/(main)/chat/features/SessionSearchBar/index.tsx +++ b/src/app/(main)/chat/@session/features/SessionSearchBar.tsx @@ -1,11 +1,12 @@ +'use client'; + import { SearchBar } from '@lobehub/ui'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useIsMobile } from '@/hooks/useIsMobile'; import { useSessionStore } from '@/store/session'; -const SessionSearchBar = memo<{ mobile?: boolean }>(({ mobile: controlledMobile }) => { +const SessionSearchBar = memo<{ mobile?: boolean }>(({ mobile }) => { const { t } = useTranslation('chat'); const [keywords, useSearchSessions, updateSearchKeywords] = useSessionStore((s) => [ @@ -16,9 +17,6 @@ const SessionSearchBar = memo<{ mobile?: boolean }>(({ mobile: controlledMobile const { isValidating } = useSearchSessions(keywords); - const isMobile = useIsMobile(); - const mobile = controlledMobile ?? isMobile; - return ( - cx( - stylish.noScrollbar, - css` - display: flex; - flex-direction: column; - gap: 2px; - padding: 8px 8px 0; - `, - ), -); - -const Sessions = memo(() => { - const { styles } = useStyles(); - - return ( - -
- - - - - ); -}); - -Sessions.displayName = 'SessionsList'; - -export default Sessions; diff --git a/src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx b/src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx new file mode 100644 index 0000000000000..8696aa6e04866 --- /dev/null +++ b/src/app/(main)/chat/_layout/Desktop/SessionPanel.tsx @@ -0,0 +1,79 @@ +'use client'; + +import { DraggablePanel, DraggablePanelContainer, type DraggablePanelProps } from '@lobehub/ui'; +import { createStyles, useResponsive } from 'antd-style'; +import isEqual from 'fast-deep-equal'; +import { PropsWithChildren, memo, useEffect, useLayoutEffect, useState } from 'react'; + +import { FOLDER_WIDTH } from '@/const/layoutTokens'; +import { useGlobalStore } from '@/store/global'; + +export const useStyles = createStyles(({ css, token }) => ({ + panel: css` + height: 100%; + color: ${token.colorTextSecondary}; + background: ${token.colorBgContainer}; + `, +})); + +const SessionPanel = memo(({ children }) => { + const { md = true } = useResponsive(); + const { styles } = useStyles(); + const [sessionsWidth, sessionExpandable, updatePreference, isPreferenceInit] = useGlobalStore( + (s) => [ + s.preference.sessionsWidth, + s.preference.showSessionPanel, + s.updatePreference, + s.isPreferenceInit, + ], + ); + const [expand, setExpand] = useState(sessionExpandable); + const [tmpWidth, setWidth] = useState(sessionsWidth); + if (tmpWidth !== sessionsWidth) setWidth(sessionsWidth); + + const handleExpand: DraggablePanelProps['onExpandChange'] = (expand) => { + updatePreference({ + sessionsWidth: expand ? 320 : 0, + showSessionPanel: expand, + }); + }; + + const handleSizeChange: DraggablePanelProps['onSizeChange'] = (_, size) => { + if (!size) return; + const nextWidth = typeof size.width === 'string' ? Number.parseInt(size.width) : size.width; + if (isEqual(nextWidth, sessionsWidth)) return; + setWidth(nextWidth); + updatePreference({ sessionsWidth: nextWidth }); + }; + + useLayoutEffect(() => { + if (!isPreferenceInit) return; + setExpand(sessionExpandable); + }, [isPreferenceInit, sessionExpandable]); + + useEffect(() => { + if (md && sessionExpandable) setExpand(true); + if (!md) setExpand(false); + }, [md, sessionExpandable]); + + return ( + + + {children} + + + ); +}); + +export default SessionPanel; diff --git a/src/app/(main)/chat/_layout/Desktop/index.tsx b/src/app/(main)/chat/_layout/Desktop/index.tsx index 4487be9c1622c..73f7b23daff1d 100644 --- a/src/app/(main)/chat/_layout/Desktop/index.tsx +++ b/src/app/(main)/chat/_layout/Desktop/index.tsx @@ -1,20 +1,24 @@ import { Flexbox } from 'react-layout-kit'; +import Migration from '../../features/Migration'; import { LayoutProps } from '../type'; -import ResponsiveSessionList from './SessionList'; +import SessionPanel from './SessionPanel'; -const Layout = ({ children }: LayoutProps) => { +const Layout = ({ children, session }: LayoutProps) => { return ( <> - - {children} + {session} + + {children} + + ); }; diff --git a/src/app/(main)/chat/_layout/Mobile.tsx b/src/app/(main)/chat/_layout/Mobile.tsx new file mode 100644 index 0000000000000..22b794d5d5fd8 --- /dev/null +++ b/src/app/(main)/chat/_layout/Mobile.tsx @@ -0,0 +1,52 @@ +'use client'; + +import { createStyles } from 'antd-style'; +import { memo } from 'react'; +import { Flexbox } from 'react-layout-kit'; + +import Migration from '@/app/(main)/chat/features/Migration'; +import { useQuery } from '@/hooks/useQuery'; + +import { LayoutProps } from './type'; + +const useStyles = createStyles(({ css, token }) => ({ + active: css` + display: unset; + `, + main: css` + position: relative; + overflow: hidden; + display: none; + background: ${token.colorBgLayout}; + `, +})); + +const Layout = memo(({ children, session }) => { + const { showMobileWorkspace } = useQuery(); + const { cx, styles } = useStyles(); + + return ( + <> + + {session} + + + {children} + + + + ); +}); + +Layout.displayName = 'MobileChatLayout'; + +export default Layout; diff --git a/src/app/(main)/chat/_layout/Mobile/index.tsx b/src/app/(main)/chat/_layout/Mobile/index.tsx deleted file mode 100644 index c56a8e065ee3b..0000000000000 --- a/src/app/(main)/chat/_layout/Mobile/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { LayoutProps } from '../type'; - -const Layout = ({ children }: LayoutProps) => { - return children; -}; - -Layout.displayName = 'MobileChatLayout'; - -export default Layout; diff --git a/src/app/(main)/chat/_layout/type.ts b/src/app/(main)/chat/_layout/type.ts index 4d063a09f7fa5..a4fd37b3a48d9 100644 --- a/src/app/(main)/chat/_layout/type.ts +++ b/src/app/(main)/chat/_layout/type.ts @@ -2,4 +2,5 @@ import { ReactNode } from 'react'; export interface LayoutProps { children: ReactNode; + session: ReactNode; } diff --git a/src/app/(main)/chat/error.tsx b/src/app/(main)/chat/error.tsx new file mode 100644 index 0000000000000..071491038c704 --- /dev/null +++ b/src/app/(main)/chat/error.tsx @@ -0,0 +1,5 @@ +'use client'; + +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('@/components/Error')); diff --git a/src/app/(main)/chat/features/Migration/index.tsx b/src/app/(main)/chat/features/Migration/index.tsx index 3927ddda06b79..788b21472563c 100644 --- a/src/app/(main)/chat/features/Migration/index.tsx +++ b/src/app/(main)/chat/features/Migration/index.tsx @@ -3,13 +3,13 @@ import { Spin } from 'antd'; import { createStore, getMany } from 'idb-keyval'; import dynamic from 'next/dynamic'; -import { PropsWithChildren, memo, useEffect, useState } from 'react'; +import { memo, useEffect, useState } from 'react'; import { MIGRATE_KEY, V1DB_NAME, V1DB_TABLE_NAME } from './const'; const Modal = dynamic(() => import('./Modal'), { loading: () => , ssr: false }); -const Migration = memo(({ children }) => { +const Migration = memo(() => { const [dbState, setDbState] = useState(null); const [open, setOpen] = useState(false); @@ -33,12 +33,7 @@ const Migration = memo(({ children }) => { checkMigration(); }, []); - return ( - <> - {open && } - {children} - - ); + return open && ; }); export default Migration; diff --git a/src/app/(main)/chat/not-found.tsx b/src/app/(main)/chat/not-found.tsx new file mode 100644 index 0000000000000..02503bc7fa461 --- /dev/null +++ b/src/app/(main)/chat/not-found.tsx @@ -0,0 +1,3 @@ +import dynamic from 'next/dynamic'; + +export default dynamic(() => import('@/components/404')); diff --git a/src/app/(main)/chat/page.tsx b/src/app/(main)/chat/page.tsx deleted file mode 100644 index 4b3521c886ef3..0000000000000 --- a/src/app/(main)/chat/page.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { isMobileDevice } from '@/utils/responsive'; - -import DesktopPage from './(desktop)'; -import MobilePage from './(mobile)'; -import SessionHydration from './components/SessionHydration'; -import Migration from './features/Migration'; -import PageTitle from './features/PageTitle'; - -const Page = () => { - const mobile = isMobileDevice(); - - const Page = mobile ? MobilePage : DesktopPage; - - return ( - <> - - - - - - - ); -}; - -export default Page; diff --git a/src/app/(main)/chat/settings/_layout/Mobile/Header.tsx b/src/app/(main)/chat/settings/_layout/Mobile/Header.tsx index beb425acdaf07..0adc952c6e5b3 100644 --- a/src/app/(main)/chat/settings/_layout/Mobile/Header.tsx +++ b/src/app/(main)/chat/settings/_layout/Mobile/Header.tsx @@ -1,23 +1,22 @@ 'use client'; import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui'; -import { useRouter } from 'next/navigation'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; +import { useQueryRoute } from '@/hooks/useQueryRoute'; import { mobileHeaderSticky } from '@/styles/mobileHeader'; -import { pathString } from '@/utils/url'; import HeaderContent from '../../features/HeaderContent'; const Header = memo(() => { const { t } = useTranslation('setting'); - const router = useRouter(); + const router = useQueryRoute(); return ( } - onBackClick={() => router.push(pathString('/chat/mobile', { search: location.search }))} + onBackClick={() => router.push('/chat')} right={} showBackButton style={mobileHeaderSticky} diff --git a/src/app/(main)/chat/settings/features/HeaderContent.tsx b/src/app/(main)/chat/settings/features/HeaderContent.tsx index 620ea96291904..8b2ddbb80ae74 100644 --- a/src/app/(main)/chat/settings/features/HeaderContent.tsx +++ b/src/app/(main)/chat/settings/features/HeaderContent.tsx @@ -1,12 +1,12 @@ import { ActionIcon, Icon } from '@lobehub/ui'; import { Button, Dropdown, MenuProps } from 'antd'; -import { useResponsive } from 'antd-style'; import { HardDriveDownload } from 'lucide-react'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { HEADER_ICON_SIZE } from '@/const/layoutTokens'; import { configService } from '@/services/config'; +import { useServerConfigStore } from '@/store/serverConfig'; import { useSessionStore } from '@/store/session'; import SubmitAgentButton from './SubmitAgentButton'; @@ -15,7 +15,7 @@ export const HeaderContent = memo<{ mobile?: boolean; modal?: boolean }>(({ moda const { t } = useTranslation('setting'); const id = useSessionStore((s) => s.activeId); - const { mobile } = useResponsive(); + const mobile = useServerConfigStore((s) => s.isMobile); const items = useMemo( () => [ diff --git a/src/app/(main)/chat/settings/features/SubmitAgentButton/index.tsx b/src/app/(main)/chat/settings/features/SubmitAgentButton/index.tsx index b27e4c6034d30..c584641b91c4e 100644 --- a/src/app/(main)/chat/settings/features/SubmitAgentButton/index.tsx +++ b/src/app/(main)/chat/settings/features/SubmitAgentButton/index.tsx @@ -1,17 +1,17 @@ import { ActionIcon, Icon } from '@lobehub/ui'; import { Button } from 'antd'; -import { useResponsive } from 'antd-style'; import { Share2 } from 'lucide-react'; import { memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { HEADER_ICON_SIZE } from '@/const/layoutTokens'; +import { useServerConfigStore } from '@/store/serverConfig'; import SubmitAgentModal from './SubmitAgentModal'; const SubmitAgentButton = memo<{ modal?: boolean }>(({ modal }) => { const { t } = useTranslation('setting'); - const { mobile } = useResponsive(); + const mobile = useServerConfigStore((s) => s.isMobile); const [isModalOpen, setIsModalOpen] = useState(false); return ( diff --git a/src/app/(main)/market/@detail/features/Header.tsx b/src/app/(main)/market/@detail/features/Header.tsx index e85d028ef2245..cac436f3dfeca 100644 --- a/src/app/(main)/market/@detail/features/Header.tsx +++ b/src/app/(main)/market/@detail/features/Header.tsx @@ -8,8 +8,8 @@ import { useTranslation } from 'react-i18next'; import { Center, Flexbox } from 'react-layout-kit'; import { SESSION_CHAT_URL } from '@/const/url'; -import { useIsMobile } from '@/hooks/useIsMobile'; import { agentMarketSelectors, useMarketStore } from '@/store/market'; +import { useServerConfigStore } from '@/store/serverConfig'; import { useSessionStore } from '@/store/session'; import { useStyles } from './style'; @@ -29,7 +29,7 @@ const Header = memo(() => { const { meta, createAt, author, homepage, config } = agentItem; const { title, description, tags } = meta; - const isMobile = useIsMobile(); + const isMobile = useServerConfigStore((s) => s.isMobile); const handleAddAgentAndConverse = async () => { if (!agentItem) return; diff --git a/src/components/Cell/Divider.tsx b/src/components/Cell/Divider.tsx index ef7a0d8fdf963..fc26c4ee4f51d 100644 --- a/src/components/Cell/Divider.tsx +++ b/src/components/Cell/Divider.tsx @@ -4,11 +4,11 @@ import { createStyles } from 'antd-style'; import { memo } from 'react'; const useStyles = createStyles( - ({ css, token, isDarkMode }) => css` + ({ css, token }) => css` flex: none; width: 100%; height: 6px; - background: ${isDarkMode ? token.colorBgContainer : token.colorBgLayout}; + background: ${token.colorFillTertiary}; `, ); diff --git a/src/components/Cell/index.tsx b/src/components/Cell/index.tsx index 0a043ccccd0b3..941a2f9ed0a83 100644 --- a/src/components/Cell/index.tsx +++ b/src/components/Cell/index.tsx @@ -5,7 +5,7 @@ import { ReactNode, memo } from 'react'; const { Item } = List; -const useStyles = createStyles(({ css, token, isDarkMode }) => ({ +const useStyles = createStyles(({ css, token }) => ({ container: css` position: relative; @@ -13,7 +13,7 @@ const useStyles = createStyles(({ css, token, isDarkMode }) => ({ padding: 16px !important; - background: ${isDarkMode ? token.colorBgLayout : token.colorBgContainer}; + background: ${token.colorBgLayout}; border-radius: 0; `, })); diff --git a/src/components/StoreHydration/ChatHydration/index.tsx b/src/components/StoreHydration/ChatHydration/index.tsx index b054eb5be8114..23f755cfd4b3b 100644 --- a/src/components/StoreHydration/ChatHydration/index.tsx +++ b/src/components/StoreHydration/ChatHydration/index.tsx @@ -1,7 +1,7 @@ 'use client'; import { useQueryState } from 'nuqs'; -import { memo, useEffect } from 'react'; +import { memo, useLayoutEffect } from 'react'; import { createStoreUpdater } from 'zustand-utils'; import { useChatStore } from '@/store/chat'; @@ -14,7 +14,7 @@ const ChatHydration = memo(() => { const [topic, setTopic] = useQueryState('topic', { history: 'replace', throttleMs: 500 }); useStoreUpdater('activeTopicId', topic); - useEffect(() => { + useLayoutEffect(() => { const unsubscribe = useChatStore.subscribe( (s) => s.activeTopicId, (state) => { diff --git a/src/const/session.ts b/src/const/session.ts index 3c0729469d7f3..e1ce9b35e20a0 100644 --- a/src/const/session.ts +++ b/src/const/session.ts @@ -5,6 +5,8 @@ import { merge } from '@/utils/merge'; export const INBOX_SESSION_ID = 'inbox'; +export const WELCOME_GUIDE_CHAT_ID = 'welcome'; + export const DEFAULT_AGENT_LOBE_SESSION: LobeAgentSession = { config: DEFAULT_AGENT_CONFIG, createdAt: Date.now(), diff --git a/src/const/url.ts b/src/const/url.ts index a9ab4508c23fe..dc9d603edf742 100644 --- a/src/const/url.ts +++ b/src/const/url.ts @@ -1,3 +1,4 @@ +import qs from 'query-string'; import urlJoin from 'url-join'; import { withBasePath } from '@/utils/basePath'; @@ -40,7 +41,10 @@ export const AGENTS_INDEX_GITHUB = 'https://github.com/lobehub/lobe-chat-agents' export const AGENTS_INDEX_GITHUB_ISSUE = urlJoin(AGENTS_INDEX_GITHUB, 'issues/new'); export const SESSION_CHAT_URL = (id: string = INBOX_SESSION_ID, mobile?: boolean) => - mobile ? `/chat/mobile?session=${id}` : `/chat?session=${id}`; + qs.stringifyUrl({ + query: mobile ? { session: id, showMobileWorkspace: mobile } : { session: id }, + url: '/chat', + }); export const imageUrl = (filename: string) => withBasePath(`/images/${filename}`); diff --git a/src/features/ChatInput/ActionBar/Tools/index.tsx b/src/features/ChatInput/ActionBar/Tools/index.tsx index 0f39687ecbd1e..f48794740cee1 100644 --- a/src/features/ChatInput/ActionBar/Tools/index.tsx +++ b/src/features/ChatInput/ActionBar/Tools/index.tsx @@ -4,10 +4,11 @@ import { createStyles } from 'antd-style'; import type { ItemType } from 'antd/es/menu/hooks/useItems'; import isEqual from 'fast-deep-equal'; import { ArrowRight, Blocks, Store, ToyBrick } from 'lucide-react'; -import { memo, useState } from 'react'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; +import { useWorkspaceModal } from '@/app/(main)/chat/(workspace)/features/useWorkspaceModal'; import PluginStore from '@/features/PluginStore'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/selectors'; @@ -45,7 +46,7 @@ const Tools = memo(() => { .filter((i) => !builtinList.some((b) => b.identifier === i)).length, ); - const [open, setOpen] = useState(false); + const [open, setOpen] = useWorkspaceModal(); const { styles } = useStyles(); const model = useAgentStore(agentSelectors.currentAgentModel); diff --git a/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx b/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx index 7ce89f41ed18a..98d6d1f55f3bd 100644 --- a/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx +++ b/src/features/Conversation/components/InboxWelcome/AgentsSuggest.tsx @@ -63,7 +63,7 @@ const AgentsSuggest = memo<{ mobile?: boolean }>(({ mobile }) => { const agentList = useMarketStore((s) => s.agentList, isEqual); const { styles } = useStyles(); - const agentLength = mobile ? 3 : 4; + const agentLength = mobile ? 2 : 4; const loadingCards = Array.from({ length: agentLength }).map((_, index) => ( diff --git a/src/features/Conversation/components/InboxWelcome/index.tsx b/src/features/Conversation/components/InboxWelcome/index.tsx index a61666769361c..9b3fcb2228c75 100644 --- a/src/features/Conversation/components/InboxWelcome/index.tsx +++ b/src/features/Conversation/components/InboxWelcome/index.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { Center, Flexbox } from 'react-layout-kit'; import { useGreeting } from '@/hooks/useGreeting'; -import { useIsMobile } from '@/hooks/useIsMobile'; +import { useServerConfigStore } from '@/store/serverConfig'; import AgentsSuggest from './AgentsSuggest'; import QuestionSuggest from './QuestionSuggest'; @@ -42,7 +42,7 @@ const useStyles = createStyles(({ css, responsive }) => ({ const InboxWelcome = memo(() => { const { t } = useTranslation('welcome'); const { styles } = useStyles(); - const mobile = useIsMobile(); + const mobile = useServerConfigStore((s) => s.isMobile); const greeting = useGreeting(); return ( diff --git a/src/features/Conversation/components/SkeletonList.tsx b/src/features/Conversation/components/SkeletonList.tsx index 4dc8ae1910eff..bf6058e9a39b1 100644 --- a/src/features/Conversation/components/SkeletonList.tsx +++ b/src/features/Conversation/components/SkeletonList.tsx @@ -1,13 +1,20 @@ +'use client'; + import { Skeleton } from 'antd'; import { createStyles } from 'antd-style'; import { memo } from 'react'; import { Flexbox } from 'react-layout-kit'; const useStyles = createStyles(({ css, prefixCls }) => ({ - user: css` + message: css` display: flex; + gap: 12px; + .${prefixCls}-skeleton-header { + padding: 0; + } + `, + user: css` flex-direction: row-reverse; - gap: 16px; .${prefixCls}-skeleton-paragraph { display: flex; @@ -20,18 +27,24 @@ interface SkeletonListProps { mobile?: boolean; } const SkeletonList = memo(({ mobile }) => { - const { styles } = useStyles(); + const { cx, styles } = useStyles(); return ( - + + - ); }); diff --git a/src/features/Conversation/components/VirtualizedList/index.tsx b/src/features/Conversation/components/VirtualizedList/index.tsx index de3c63d5ced6a..1eb45a375538a 100644 --- a/src/features/Conversation/components/VirtualizedList/index.tsx +++ b/src/features/Conversation/components/VirtualizedList/index.tsx @@ -1,11 +1,13 @@ +'use client'; + import isEqual from 'fast-deep-equal'; import React, { memo, useCallback, useEffect, useRef, useState } from 'react'; import { Flexbox } from 'react-layout-kit'; import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'; +import { WELCOME_GUIDE_CHAT_ID } from '@/const/session'; import { useChatStore } from '@/store/chat'; import { chatSelectors } from '@/store/chat/selectors'; -import { isMobileScreen } from '@/utils/screen'; import { useInitConversation } from '../../hooks/useInitConversation'; import AutoScroll from '../AutoScroll'; @@ -13,26 +15,11 @@ import Item from '../ChatItem'; import InboxWelcome from '../InboxWelcome'; import SkeletonList from '../SkeletonList'; -const WELCOME_ID = 'welcome'; - -const itemContent = (index: number, id: string) => { - const isMobile = isMobileScreen(); - - if (id === WELCOME_ID) return ; - - return index === 0 ? ( -
- ) : ( - - ); -}; - interface VirtualizedListProps { mobile?: boolean; } const VirtualizedList = memo(({ mobile }) => { useInitConversation(); - const virtuosoRef = useRef(null); const [atBottom, setAtBottom] = useState(true); const [isScrolling, setIsScrolling] = useState(false); @@ -44,7 +31,9 @@ const VirtualizedList = memo(({ mobile }) => { const data = useChatStore((s) => { const showInboxWelcome = chatSelectors.showInboxWelcome(s); - const ids = showInboxWelcome ? [WELCOME_ID] : chatSelectors.currentChatIDsWithGuideMessage(s); + const ids = showInboxWelcome + ? [WELCOME_GUIDE_CHAT_ID] + : chatSelectors.currentChatIDsWithGuideMessage(s); return ['empty', ...ids]; }, isEqual); @@ -65,6 +54,19 @@ const VirtualizedList = memo(({ mobile }) => { // overscan should be 1.5 times the height of the window const overscan = typeof window !== 'undefined' ? window.innerHeight * 1.5 : 0; + const itemContent = useCallback( + (index: number, id: string) => { + if (id === WELCOME_GUIDE_CHAT_ID) return ; + + return index === 0 ? ( +
+ ) : ( + + ); + }, + [mobile], + ); + return chatLoading ? ( ) : ( diff --git a/src/features/Conversation/index.tsx b/src/features/Conversation/index.tsx index 4d6a9e705db47..09d2c265ede37 100644 --- a/src/features/Conversation/index.tsx +++ b/src/features/Conversation/index.tsx @@ -1,49 +1,30 @@ -import { createStyles } from 'antd-style'; -import { ReactNode, Suspense, lazy, memo } from 'react'; +import { Suspense, lazy } from 'react'; import { Flexbox } from 'react-layout-kit'; -import ChatHydration from '@/components/StoreHydration/ChatHydration'; - import SkeletonList from './components/SkeletonList'; const ChatList = lazy(() => import('./components/VirtualizedList')); -const useStyles = createStyles( - ({ css, responsive }) => css` - position: relative; - overflow-y: auto; - height: 100%; - - ${responsive.mobile} { - width: 100%; - } - `, -); - interface ConversationProps { - chatInput: ReactNode; mobile?: boolean; } -const Conversation = memo(({ chatInput, mobile }) => { - const { styles } = useStyles(); - +const Conversation = ({ mobile }: ConversationProps) => { return ( -
- }> - - -
- {chatInput} - + }> + +
); -}); +}; export default Conversation; diff --git a/src/features/FolderPanel/index.tsx b/src/features/FolderPanel/index.tsx deleted file mode 100644 index 6b9d6c3cb5cb9..0000000000000 --- a/src/features/FolderPanel/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { DraggablePanel, DraggablePanelContainer } from '@lobehub/ui'; -import { createStyles } from 'antd-style'; -import isEqual from 'fast-deep-equal'; -import { PropsWithChildren, memo, useState } from 'react'; - -import { FOLDER_WIDTH } from '@/const/layoutTokens'; -import { useGlobalStore } from '@/store/global'; - -export const useStyles = createStyles(({ css, token }) => ({ - panel: css` - height: 100%; - color: ${token.colorTextSecondary}; - background: ${token.colorBgContainer}; - `, -})); - -const FolderPanel = memo(({ children }) => { - const { styles } = useStyles(); - const [sessionsWidth, sessionExpandable, updatePreference] = useGlobalStore((s) => [ - s.preference.sessionsWidth, - s.preference.showSessionPanel, - s.updatePreference, - ]); - const [tmpWidth, setWidth] = useState(sessionsWidth); - if (tmpWidth !== sessionsWidth) setWidth(sessionsWidth); - - return ( - { - updatePreference({ - sessionsWidth: expand ? 320 : 0, - showSessionPanel: expand, - }); - }} - onSizeChange={(_, size) => { - if (!size) return; - - const nextWidth = typeof size.width === 'string' ? Number.parseInt(size.width) : size.width; - - if (isEqual(nextWidth, sessionsWidth)) return; - - setWidth(nextWidth); - updatePreference({ sessionsWidth: nextWidth }); - }} - placement="left" - size={{ height: '100%', width: sessionsWidth }} - > - - {children} - - - ); -}); - -export default FolderPanel; diff --git a/src/features/PluginStore/InstalledPluginList.tsx b/src/features/PluginStore/InstalledPluginList.tsx index 094178b2aec6a..cb6238366b0a6 100644 --- a/src/features/PluginStore/InstalledPluginList.tsx +++ b/src/features/PluginStore/InstalledPluginList.tsx @@ -1,10 +1,11 @@ import { SearchBar } from '@lobehub/ui'; -import { useResponsive } from 'antd-style'; import isEqual from 'fast-deep-equal'; -import { memo, useState } from 'react'; +import { memo, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; +import { Virtuoso } from 'react-virtuoso'; +import { useServerConfigStore } from '@/store/serverConfig'; import { useToolStore } from '@/store/tool'; import { pluginSelectors } from '@/store/tool/selectors'; @@ -14,39 +15,45 @@ import PluginItem from './PluginItem'; export const InstalledPluginList = memo(() => { const { t } = useTranslation('plugin'); const [keywords, setKeywords] = useState(); - const { mobile } = useResponsive(); + const mobile = useServerConfigStore((s) => s.isMobile); const installedPlugins = useToolStore(pluginSelectors.installedPluginMetaList, isEqual); + const filteredPluginList = useMemo( + () => + installedPlugins.filter((item) => + [item.meta?.title, item.meta?.description, item.author, ...(item.meta?.tags || [])] + .filter(Boolean) + .join('') + .toLowerCase() + .includes((keywords || '')?.toLowerCase()), + ), + [installedPlugins, keywords], + ); + return ( <> - + setKeywords(e.target.value)} placeholder={t('store.placeholder')} + style={{ flex: 1, width: '100%' }} type={mobile ? 'block' : 'ghost'} value={keywords} /> - - - - - - - {installedPlugins - .filter((item) => - [item.meta?.title, item.meta?.description, item.author, ...(item.meta?.tags || [])] - .filter(Boolean) - .join('') - .toLowerCase() - .includes((keywords || '')?.toLowerCase()), - ) - .map((i) => ( - - ))} + + { + const item = filteredPluginList[index]; + return ; + }} + overscan={400} + style={{ height: 500, marginInline: -16 }} + totalCount={filteredPluginList.length} + /> ); }); diff --git a/src/features/PluginStore/OnlineList.tsx b/src/features/PluginStore/OnlineList.tsx index 53bed0a3c413e..8b96e50ab0223 100644 --- a/src/features/PluginStore/OnlineList.tsx +++ b/src/features/PluginStore/OnlineList.tsx @@ -1,6 +1,5 @@ import { Icon, SearchBar } from '@lobehub/ui'; import { Empty } from 'antd'; -import { useResponsive } from 'antd-style'; import isEqual from 'fast-deep-equal'; import { ServerCrash } from 'lucide-react'; import { memo, useMemo, useState } from 'react'; @@ -9,6 +8,7 @@ import { Center, Flexbox } from 'react-layout-kit'; import { Virtuoso } from 'react-virtuoso'; import AddPluginButton from '@/features/PluginStore/AddPluginButton'; +import { useServerConfigStore } from '@/store/serverConfig'; import { useToolStore } from '@/store/tool'; import { pluginSelectors, pluginStoreSelectors } from '@/store/tool/selectors'; @@ -18,7 +18,7 @@ import PluginItem from './PluginItem'; export const OnlineList = memo(() => { const { t } = useTranslation('plugin'); const [keywords, setKeywords] = useState(); - const { mobile } = useResponsive(); + const mobile = useServerConfigStore((s) => s.isMobile); const pluginStoreList = useToolStore((s) => { const custom = pluginSelectors.installedCustomPluginMetaList(s); const store = pluginStoreSelectors.onlinePluginStore(s); @@ -58,7 +58,6 @@ export const OnlineList = memo(() => { - {isLoading ? ( ) : isEmpty ? ( @@ -76,15 +75,10 @@ export const OnlineList = memo(() => { { const item = filteredPluginList[index]; - - return ( - - - - ); + return ; }} overscan={400} - style={{ height: 500 }} + style={{ height: 500, marginInline: -16 }} totalCount={filteredPluginList.length} /> )} diff --git a/src/features/PluginStore/PluginItem/Action.tsx b/src/features/PluginStore/PluginItem/Action.tsx index 8cf8f71ffccd3..537c190f8dca1 100644 --- a/src/features/PluginStore/PluginItem/Action.tsx +++ b/src/features/PluginStore/PluginItem/Action.tsx @@ -1,12 +1,12 @@ import { ActionIcon, Icon } from '@lobehub/ui'; import { Button, Dropdown, Popconfirm } from 'antd'; -import { useResponsive } from 'antd-style'; import { InfoIcon, MoreVerticalIcon, Settings, Trash2 } from 'lucide-react'; import { memo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Flexbox } from 'react-layout-kit'; import PluginDetailModal from '@/features/PluginDetailModal'; +import { useServerConfigStore } from '@/store/serverConfig'; import { pluginHelpers, useToolStore } from '@/store/tool'; import { pluginSelectors, pluginStoreSelectors } from '@/store/tool/selectors'; import { LobeToolType } from '@/types/tool/tool'; @@ -19,13 +19,14 @@ interface ActionsProps { } const Actions = memo(({ identifier, type }) => { + const mobile = useServerConfigStore((s) => s.isMobile); const [installed, installing, installPlugin, unInstallPlugin] = useToolStore((s) => [ pluginSelectors.isPluginInstalled(identifier)(s), pluginStoreSelectors.isPluginInstallLoading(identifier)(s), s.installPlugin, s.uninstallPlugin, ]); - const { mobile } = useResponsive(); + const isCustomPlugin = type === 'customPlugin'; const { t } = useTranslation('plugin'); const [open, setOpen] = useState(false); diff --git a/src/features/PluginStore/PluginItem/index.tsx b/src/features/PluginStore/PluginItem/index.tsx index 871f498fe07d6..4ea8b5851488c 100644 --- a/src/features/PluginStore/PluginItem/index.tsx +++ b/src/features/PluginStore/PluginItem/index.tsx @@ -40,6 +40,8 @@ const PluginItem = memo(({ identifier, homepage, author, type gap={8} horizontal justify={'space-between'} + paddingBlock={12} + paddingInline={16} style={{ position: 'relative' }} > (({ setOpen, open }) => { const { t } = useTranslation('plugin'); - + const mobile = useServerConfigStore((s) => s.isMobile); const [listType] = useToolStore((s) => [s.listType]); return ( @@ -26,10 +27,11 @@ export const PluginStore = memo(({ setOpen, open }) => { setOpen(false); }} open={open} + styles={{ body: { overflow: 'hidden' } }} title={t('store.title')} width={800} > - + { diff --git a/src/features/User/UserAvatar.tsx b/src/features/User/UserAvatar.tsx index 85c0c74db696b..62c5769b6c496 100644 --- a/src/features/User/UserAvatar.tsx +++ b/src/features/User/UserAvatar.tsx @@ -45,7 +45,7 @@ export interface UserAvatarProps extends AvatarProps { } const UserAvatar = memo( - ({ size = 40, background, clickable, className, ...rest }) => { + ({ size = 40, background, clickable, className, style, ...rest }) => { const { styles, cx } = useStyles(); const [avatar, username] = useUserStore((s) => [ userProfileSelectors.userAvatar(s), @@ -61,6 +61,7 @@ const UserAvatar = memo( background={isSignedIn && avatar ? background : undefined} className={cx(clickable && styles.clickable, className)} size={size} + style={{ flex: 'none', ...style }} unoptimized {...rest} /> diff --git a/src/features/User/UserPanel/useMenu.tsx b/src/features/User/UserPanel/useMenu.tsx index bc27399f1cd23..4c46e2610b7e2 100644 --- a/src/features/User/UserPanel/useMenu.tsx +++ b/src/features/User/UserPanel/useMenu.tsx @@ -207,4 +207,4 @@ export const useMenu = () => { ]; return { logoutItems, mainItems }; -}; +}; \ No newline at end of file diff --git a/src/layout/GlobalProvider/AppTheme.tsx b/src/layout/GlobalProvider/AppTheme.tsx index 27922ab8d9860..138876282ce5c 100644 --- a/src/layout/GlobalProvider/AppTheme.tsx +++ b/src/layout/GlobalProvider/AppTheme.tsx @@ -1,12 +1,11 @@ 'use client'; import { ConfigProvider, NeutralColors, PrimaryColors, ThemeProvider } from '@lobehub/ui'; -import { App } from 'antd'; import { ThemeAppearance, createStyles } from 'antd-style'; import 'antd/dist/reset.css'; import Image from 'next/image'; import Link from 'next/link'; -import { PropsWithChildren, ReactNode, memo, useEffect } from 'react'; +import { ReactNode, memo, useEffect } from 'react'; import AntdStaticMethods from '@/components/AntdStaticMethods'; import { @@ -20,19 +19,16 @@ import { GlobalStyle } from '@/styles'; import { setCookie } from '@/utils/cookie'; const useStyles = createStyles(({ css, token }) => ({ - bg: css` + app: css` position: relative; - overflow-y: hidden; overscroll-behavior: none; display: flex; flex-direction: column; align-items: center; - height: 100%; - max-height: 100dvh !important; - - background: ${token.colorBgLayout}; + min-height: 100dvh; + max-height: 100dvh; `, // scrollbar-width and scrollbar-color are supported from Chrome 121 // https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color @@ -64,14 +60,6 @@ const useStyles = createStyles(({ css, token }) => ({ `, })); -const Container = memo(({ children }) => { - const { styles, cx } = useStyles(); - - return ( - {children} - ); -}); - export interface AppThemeProps { children?: ReactNode; defaultAppearance?: ThemeAppearance; @@ -85,7 +73,7 @@ const AppTheme = memo( // console.debug('server:primaryColor', defaultPrimaryColor); // console.debug('server:neutralColor', defaultNeutralColor); const themeMode = useUserStore(settingsSelectors.currentThemeMode); - + const { styles, cx } = useStyles(); const [primaryColor, neutralColor] = useUserStore((s) => [ settingsSelectors.currentSettings(s).primaryColor, settingsSelectors.currentSettings(s).neutralColor, @@ -101,6 +89,7 @@ const AppTheme = memo( return ( ( - {children} + {children} ); diff --git a/src/store/global/action.ts b/src/store/global/action.ts index ee6f49f2d4ea2..e98ab224e8c08 100644 --- a/src/store/global/action.ts +++ b/src/store/global/action.ts @@ -97,6 +97,8 @@ export const globalActionSlice: StateCreator< onSuccess: (preference) => { const nextPreference = merge(get().preference, preference); + set({ isPreferenceInit: true }); + if (isEqual(get().preference, nextPreference)) return; set({ preference: nextPreference }, false, n('initPreference')); diff --git a/src/store/global/initialState.ts b/src/store/global/initialState.ts index 3db05d9b22072..c8c3e6829c414 100644 --- a/src/store/global/initialState.ts +++ b/src/store/global/initialState.ts @@ -50,6 +50,7 @@ export interface GlobalPreferenceState { export interface GlobalCommonState { hasNewVersion?: boolean; isMobile?: boolean; + isPreferenceInit?: boolean; latestVersion?: string; router?: AppRouterInstance; sidebarKey: SidebarTabKey; @@ -59,6 +60,7 @@ export type GlobalState = GlobalCommonState & GlobalPreferenceState; export const initialState: GlobalState = { isMobile: false, + isPreferenceInit: false, preference: { expandSessionGroupKeys: [SessionDefaultGroup.Pinned, SessionDefaultGroup.Default], inputHeight: 200, diff --git a/src/styles/global.ts b/src/styles/global.ts index 36bfa038c28f6..0c2f710807815 100644 --- a/src/styles/global.ts +++ b/src/styles/global.ts @@ -1,22 +1,24 @@ import { Theme, css } from 'antd-style'; -export default ({ prefixCls, token }: { prefixCls: string; token: Theme }) => css` +// fix ios input keyboard +// overflow: hidden; +// ref: https://zhuanlan.zhihu.com/p/113855026 +export default ({ token }: { prefixCls: string; token: Theme }) => css` html, body, - #__next, - .${prefixCls}-app { + #__next { position: relative; + overscroll-behavior: none; - height: 100% !important; - max-height: 100dvh !important; + + height: 100%; + min-height: 100dvh; + max-height: 100dvh; + background: ${token.colorBgLayout}; } * { scrollbar-color: ${token.colorFill} transparent; scrollbar-width: thin; } - - p { - margin-bottom: 0; - } `; diff --git a/src/styles/mobileHeader.ts b/src/styles/mobileHeader.ts index 541af34d25087..c9c26344310b6 100644 --- a/src/styles/mobileHeader.ts +++ b/src/styles/mobileHeader.ts @@ -8,7 +8,7 @@ export const mobileHeaderSticky: CSSProperties = { }; export const mobileHeaderFixed: CSSProperties = { - position: 'fixed', + position: 'absolute', top: 0, width: '100%', zIndex: 100, diff --git a/src/utils/screen.ts b/src/utils/screen.ts deleted file mode 100644 index f502ade313fe2..0000000000000 --- a/src/utils/screen.ts +++ /dev/null @@ -1,14 +0,0 @@ -// mobile ่ฎพๅค‡ๅฎฝๅบฆ -// https://github.com/ant-design/ant-design/blob/master/components/theme/util/alias.ts#L28 -export const screenSM = 576; - -/** - * check mobile device in browser - */ -export const isMobileScreen = () => { - if (typeof window === 'undefined') { - return false; - } - - return window.innerWidth <= screenSM; -};