diff --git a/src/app/chat/(desktop)/features/ChatHeader.tsx b/src/app/chat/(desktop)/features/ChatHeader.tsx
deleted file mode 100644
index aed2842c..00000000
--- a/src/app/chat/(desktop)/features/ChatHeader.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { ActionIcon, Avatar, ChatHeader, ChatHeaderTitle } from '@lobehub/ui';
-import { Skeleton } from 'antd';
-import { PanelRightClose, PanelRightOpen } from 'lucide-react';
-import { useRouter } from 'next/navigation';
-import { memo } from 'react';
-import { useTranslation } from 'react-i18next';
-import { Flexbox } from 'react-layout-kit';
-
-import ModelTag from '@/components/ModelTag';
-import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
-import { useGlobalStore } from '@/store/global';
-import { modelProviderSelectors } from '@/store/global/slices/settings/selectors';
-import { useSessionStore } from '@/store/session';
-import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
-import { pathString } from '@/utils/url';
-
-import PluginTag from '../../features/ChatHeader/PluginTag';
-import SettingButton from '../../features/ChatHeader/SettingButton';
-import ShareButton from '../../features/ChatHeader/ShareButton';
-
-const Left = memo(() => {
- const { t } = useTranslation('chat');
-
- const router = useRouter();
-
- const [init, isInbox, title, description, avatar, backgroundColor, model, plugins] =
- useSessionStore((s) => [
- sessionSelectors.isSomeSessionActive(s),
- sessionSelectors.isInboxSession(s),
- agentSelectors.currentAgentTitle(s),
- agentSelectors.currentAgentDescription(s),
- agentSelectors.currentAgentAvatar(s),
- agentSelectors.currentAgentBackgroundColor(s),
- agentSelectors.currentAgentModel(s),
- agentSelectors.currentAgentPlugins(s),
- ]);
-
- const showPlugin = useGlobalStore(modelProviderSelectors.modelEnabledFunctionCall(model));
- const displayTitle = isInbox ? t('inbox.title') : title;
- const displayDesc = isInbox ? t('inbox.desc') : description;
-
- return !init ? (
-
-
-
- ) : (
-
-
- isInbox
- ? router.push('/settings/agent')
- : router.push(pathString('/chat/settings', { search: location.search }))
- }
- size={40}
- title={title}
- />
-
-
- {showPlugin && plugins?.length > 0 && }
- >
- }
- title={displayTitle}
- />
-
- );
-});
-
-const Right = memo(() => {
- const { t } = useTranslation('chat');
-
- const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
- s.preference.showChatSideBar,
- s.toggleChatSideBar,
- ]);
-
- return (
- <>
-
- toggleConfig()}
- size={DESKTOP_HEADER_ICON_SIZE}
- title={t('roleAndArchive')}
- />
-
- >
- );
-});
-
-const Header = memo(() => } right={} />);
-
-export default Header;
diff --git a/src/app/chat/(desktop)/features/ChatHeader/HeaderAction.tsx b/src/app/chat/(desktop)/features/ChatHeader/HeaderAction.tsx
new file mode 100644
index 00000000..8cc94083
--- /dev/null
+++ b/src/app/chat/(desktop)/features/ChatHeader/HeaderAction.tsx
@@ -0,0 +1,34 @@
+import { ActionIcon } from '@lobehub/ui';
+import { PanelRightClose, PanelRightOpen } from 'lucide-react';
+import { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
+import { useGlobalStore } from '@/store/global';
+
+import SettingButton from '../../../features/ChatHeader/SettingButton';
+import ShareButton from '../../../features/ChatHeader/ShareButton';
+
+const HeaderAction = memo(() => {
+ const { t } = useTranslation('chat');
+
+ const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
+ s.preference.showChatSideBar,
+ s.toggleChatSideBar,
+ ]);
+
+ return (
+ <>
+
+ toggleConfig()}
+ size={DESKTOP_HEADER_ICON_SIZE}
+ title={t('roleAndArchive')}
+ />
+
+ >
+ );
+});
+
+export default HeaderAction;
diff --git a/src/app/chat/(desktop)/features/ChatHeader/Main.tsx b/src/app/chat/(desktop)/features/ChatHeader/Main.tsx
new file mode 100644
index 00000000..48cd68e1
--- /dev/null
+++ b/src/app/chat/(desktop)/features/ChatHeader/Main.tsx
@@ -0,0 +1,58 @@
+import { Avatar, ChatHeaderTitle } from '@lobehub/ui';
+import { Skeleton } from 'antd';
+import { useRouter } from 'next/navigation';
+import { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Flexbox } from 'react-layout-kit';
+
+import { useSessionStore } from '@/store/session';
+import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
+import { pathString } from '@/utils/url';
+
+import Tags from './Tags';
+
+const Main = memo(() => {
+ const { t } = useTranslation('chat');
+
+ const router = useRouter();
+
+ const [init, isInbox, title, description, avatar, backgroundColor] = useSessionStore((s) => [
+ sessionSelectors.isSomeSessionActive(s),
+ sessionSelectors.isInboxSession(s),
+ agentSelectors.currentAgentTitle(s),
+ agentSelectors.currentAgentDescription(s),
+ agentSelectors.currentAgentAvatar(s),
+ agentSelectors.currentAgentBackgroundColor(s),
+ ]);
+
+ const displayTitle = isInbox ? t('inbox.title') : title;
+ const displayDesc = isInbox ? t('inbox.desc') : description;
+
+ return !init ? (
+
+
+
+ ) : (
+
+
+ isInbox
+ ? router.push('/settings/agent')
+ : router.push(pathString('/chat/settings', { search: location.search }))
+ }
+ size={40}
+ title={title}
+ />
+ } title={displayTitle} />
+
+ );
+});
+
+export default Main;
diff --git a/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx b/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx
new file mode 100644
index 00000000..d5b79690
--- /dev/null
+++ b/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx
@@ -0,0 +1,30 @@
+import { memo } from 'react';
+
+import ModelTag from '@/components/ModelTag';
+import ModelSwitchPanel from '@/features/ModelSwitchPanel';
+import { useGlobalStore } from '@/store/global';
+import { modelProviderSelectors } from '@/store/global/selectors';
+import { useSessionStore } from '@/store/session';
+import { agentSelectors } from '@/store/session/selectors';
+
+import PluginTag from '../../../features/PluginTag';
+
+const TitleTags = memo(() => {
+ const [model, plugins] = useSessionStore((s) => [
+ agentSelectors.currentAgentModel(s),
+ agentSelectors.currentAgentPlugins(s),
+ ]);
+
+ const showPlugin = useGlobalStore(modelProviderSelectors.modelEnabledFunctionCall(model));
+
+ return (
+ <>
+
+
+
+ {showPlugin && plugins?.length > 0 && }
+ >
+ );
+});
+
+export default TitleTags;
diff --git a/src/app/chat/(desktop)/features/ChatHeader/index.tsx b/src/app/chat/(desktop)/features/ChatHeader/index.tsx
new file mode 100644
index 00000000..6ec17cea
--- /dev/null
+++ b/src/app/chat/(desktop)/features/ChatHeader/index.tsx
@@ -0,0 +1,9 @@
+import { ChatHeader } from '@lobehub/ui';
+import { memo } from 'react';
+
+import HeaderAction from './HeaderAction';
+import Main from './Main';
+
+const Header = memo(() => } right={} />);
+
+export default Header;
diff --git a/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx b/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx
index 20f5edfb..2d231610 100644
--- a/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx
+++ b/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx
@@ -9,7 +9,7 @@ import ChatList from '@/features/Conversation/components/ChatList';
import { useSessionStore } from '@/store/session';
import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
-import PluginTag from '../../ChatHeader/PluginTag';
+import PluginTag from '../../PluginTag';
import { useStyles } from './style';
import { FieldType } from './type';
diff --git a/src/app/chat/features/ChatHeader/PluginTag/PluginStatus.tsx b/src/app/chat/features/PluginTag/PluginStatus.tsx
similarity index 100%
rename from src/app/chat/features/ChatHeader/PluginTag/PluginStatus.tsx
rename to src/app/chat/features/PluginTag/PluginStatus.tsx
diff --git a/src/app/chat/features/ChatHeader/PluginTag/index.tsx b/src/app/chat/features/PluginTag/index.tsx
similarity index 100%
rename from src/app/chat/features/ChatHeader/PluginTag/index.tsx
rename to src/app/chat/features/PluginTag/index.tsx
diff --git a/src/features/ChatInput/ActionBar/ModelSwitch.tsx b/src/features/ChatInput/ActionBar/ModelSwitch.tsx
index 4b3a7718..e23e2819 100644
--- a/src/features/ChatInput/ActionBar/ModelSwitch.tsx
+++ b/src/features/ChatInput/ActionBar/ModelSwitch.tsx
@@ -1,84 +1,17 @@
import { ActionIcon } from '@lobehub/ui';
-import { Dropdown } from 'antd';
-import { createStyles } from 'antd-style';
-import isEqual from 'fast-deep-equal';
import { BrainCog } from 'lucide-react';
-import { memo, useMemo } from 'react';
+import { memo } from 'react';
import { useTranslation } from 'react-i18next';
-import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
-import { useGlobalStore } from '@/store/global';
-import { modelProviderSelectors } from '@/store/global/selectors';
-import { useSessionStore } from '@/store/session';
-import { agentSelectors } from '@/store/session/selectors';
-import { ModelProviderCard } from '@/types/llm';
-
-const useStyles = createStyles(({ css, prefixCls }) => ({
- menu: css`
- .${prefixCls}-dropdown-menu-item {
- display: flex;
- gap: 8px;
- }
- .${prefixCls}-dropdown-menu {
- &-item-group-title {
- padding-inline: 8px;
- }
-
- &-item-group-list {
- margin: 0 !important;
- }
- }
- `,
-}));
+import ModelSwitchPanel from '@/features/ModelSwitchPanel';
const ModelSwitch = memo(() => {
const { t } = useTranslation('chat');
- const { styles } = useStyles();
- const model = useSessionStore(agentSelectors.currentAgentModel);
- const updateAgentConfig = useSessionStore((s) => s.updateAgentConfig);
-
- const select = useGlobalStore(modelProviderSelectors.modelSelectList, isEqual);
- const enabledList = select.filter((s) => s.enabled);
-
- const items = useMemo(() => {
- const getModelItems = (provider: ModelProviderCard) =>
- provider.chatModels
- .filter((c) => !c.hidden)
- .map((model) => ({
- key: model.id,
- label: ,
- onClick: () => {
- updateAgentConfig({ model: model.id, provider: provider.id });
- },
- }));
-
- if (enabledList.length === 1) {
- const provider = enabledList[0];
- return getModelItems(provider);
- }
- return enabledList.map((provider) => ({
- children: getModelItems(provider),
- key: provider.id,
- label: ,
- type: 'group',
- }));
- }, [enabledList]);
return (
-
+
-
+
);
});
diff --git a/src/features/ModelSwitchPanel/index.tsx b/src/features/ModelSwitchPanel/index.tsx
new file mode 100644
index 00000000..7ae84bb9
--- /dev/null
+++ b/src/features/ModelSwitchPanel/index.tsx
@@ -0,0 +1,85 @@
+import { Dropdown } from 'antd';
+import { createStyles } from 'antd-style';
+import isEqual from 'fast-deep-equal';
+import { PropsWithChildren, memo, useMemo } from 'react';
+
+import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
+import { useGlobalStore } from '@/store/global';
+import { modelProviderSelectors } from '@/store/global/selectors';
+import { useSessionStore } from '@/store/session';
+import { agentSelectors } from '@/store/session/selectors';
+import { ModelProviderCard } from '@/types/llm';
+
+const useStyles = createStyles(({ css, prefixCls }) => ({
+ menu: css`
+ .${prefixCls}-dropdown-menu-item {
+ display: flex;
+ gap: 8px;
+ }
+ .${prefixCls}-dropdown-menu {
+ &-item-group-title {
+ padding-inline: 8px;
+ }
+
+ &-item-group-list {
+ margin: 0 !important;
+ }
+ }
+ `,
+ tag: css`
+ cursor: pointer;
+ `,
+}));
+
+const ModelSwitchPanel = memo(({ children }) => {
+ const { styles } = useStyles();
+ const model = useSessionStore(agentSelectors.currentAgentModel);
+ const updateAgentConfig = useSessionStore((s) => s.updateAgentConfig);
+
+ const select = useGlobalStore(modelProviderSelectors.modelSelectList, isEqual);
+ const enabledList = select.filter((s) => s.enabled);
+
+ const items = useMemo(() => {
+ const getModelItems = (provider: ModelProviderCard) =>
+ provider.chatModels
+ .filter((c) => !c.hidden)
+ .map((model) => ({
+ key: model.id,
+ label: ,
+ onClick: () => {
+ updateAgentConfig({ model: model.id, provider: provider.id });
+ },
+ }));
+
+ if (enabledList.length === 1) {
+ const provider = enabledList[0];
+ return getModelItems(provider);
+ }
+
+ return enabledList.map((provider) => ({
+ children: getModelItems(provider),
+ key: provider.id,
+ label: ,
+ type: 'group',
+ }));
+ }, [enabledList]);
+
+ return (
+
+ {children}
+
+ );
+});
+
+export default ModelSwitchPanel;