From 1c2ce7e0aacc129ffbdd7590de19a12ca94a9370 Mon Sep 17 00:00:00 2001 From: defi-failure <159208748+defi-failure@users.noreply.github.com> Date: Fri, 17 Oct 2025 10:19:48 +0800 Subject: [PATCH] fix: agents show ChatNavbar in both LeftNavbar and TopNavbar layouts (#10718) * fix: show ChatNavbar in both LeftNavbar and TopNavbar layouts * Revert "fix: show ChatNavbar in both LeftNavbar and TopNavbar layouts" This reverts commit 7f205bf24122682cba78175b1152b85eafe03e96. * refactor: extract ChatNavBarContent from ChatNavBar * fix: add navbar content to top nav in left nav mode * fix: add nodrag to navbar container * fix: lint error * fix: ChatNavbarContainer layout * fix: adjust NavbarLeftContainer min-width for macOS compatibility --------- Co-authored-by: kangfenmao --- src/renderer/src/components/app/Navbar.tsx | 2 +- src/renderer/src/pages/home/ChatNavbar.tsx | 132 +---------------- src/renderer/src/pages/home/HomePage.tsx | 1 + src/renderer/src/pages/home/Navbar.tsx | 51 +++++-- .../home/components/ChatNavbarContent.tsx | 136 ++++++++++++++++++ 5 files changed, 186 insertions(+), 136 deletions(-) create mode 100644 src/renderer/src/pages/home/components/ChatNavbarContent.tsx diff --git a/src/renderer/src/components/app/Navbar.tsx b/src/renderer/src/components/app/Navbar.tsx index 1578e73a7e..5ab2c486b5 100644 --- a/src/renderer/src/components/app/Navbar.tsx +++ b/src/renderer/src/components/app/Navbar.tsx @@ -74,7 +74,7 @@ const NavbarContainer = styled.div<{ $isFullScreen: boolean }>` ` const NavbarLeftContainer = styled.div` - min-width: var(--assistants-width); + min-width: ${isMac ? 'calc(var(--assistants-width) - 20px)' : 'var(--assistants-width)'}; padding: 0 10px; display: flex; flex-direction: row; diff --git a/src/renderer/src/pages/home/ChatNavbar.tsx b/src/renderer/src/pages/home/ChatNavbar.tsx index f232aab91a..0934754139 100644 --- a/src/renderer/src/pages/home/ChatNavbar.tsx +++ b/src/renderer/src/pages/home/ChatNavbar.tsx @@ -1,34 +1,24 @@ -import { BreadcrumbItem, Breadcrumbs, Chip, cn } from '@heroui/react' import { NavbarHeader } from '@renderer/components/app/Navbar' -import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer' import { HStack } from '@renderer/components/Layout' import SearchPopup from '@renderer/components/Popups/SearchPopup' -import { permissionModeCards } from '@renderer/constants/permissionModes' -import { useAgent } from '@renderer/hooks/agents/useAgent' -import { useSession } from '@renderer/hooks/agents/useSession' -import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' import { useAssistant } from '@renderer/hooks/useAssistant' -import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime' +import { modelGenerating } from '@renderer/hooks/useRuntime' import { useSettings } from '@renderer/hooks/useSettings' import { useShortcut } from '@renderer/hooks/useShortcuts' import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { useAppDispatch } from '@renderer/store' import { setNarrowMode } from '@renderer/store/settings' -import { ApiModel, Assistant, PermissionMode, Topic } from '@renderer/types' -import { formatErrorMessageWithPrefix } from '@renderer/utils/error' +import { Assistant, Topic } from '@renderer/types' import { Tooltip } from 'antd' import { t } from 'i18next' import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react' import { AnimatePresence, motion } from 'motion/react' -import React, { FC, ReactNode, useCallback } from 'react' +import { FC } from 'react' import styled from 'styled-components' -import { AgentSettingsPopup } from '../settings/AgentSettings' -import { AgentLabel } from '../settings/AgentSettings/shared' import AssistantsDrawer from './components/AssistantsDrawer' -import SelectAgentModelButton from './components/SelectAgentModelButton' -import SelectModelButton from './components/SelectModelButton' +import ChatNavbarContent from './components/ChatNavbarContent' import UpdateAppButton from './components/UpdateAppButton' interface Props { @@ -45,11 +35,6 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo const { topicPosition, narrowMode } = useSettings() const { showTopics, toggleShowTopics } = useShowTopics() const dispatch = useAppDispatch() - const { chat } = useRuntime() - const { activeTopicOrSession, activeAgentId } = chat - const sessionId = activeAgentId ? (chat.activeSessionId[activeAgentId] ?? null) : null - const { agent } = useAgent(activeAgentId) - const { updateModel } = useUpdateAgent() useShortcut('toggle_show_assistants', toggleShowAssistants) @@ -79,14 +64,6 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo }) } - const handleUpdateModel = useCallback( - async (model: ApiModel) => { - if (!agent) return - return updateModel(agent.id, model.id, { showSuccessToast: false }) - }, - [agent, updateModel] - ) - return (
@@ -117,38 +94,7 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo )} - {activeTopicOrSession === 'topic' && } - {activeTopicOrSession === 'session' && agent && ( - - - AgentSettingsPopup.show({ agentId: agent.id })} - classNames={{ - base: 'self-stretch', - item: 'h-full' - }}> - - - - - - - - {activeAgentId && sessionId && ( - - - - )} - - - )} +
@@ -181,74 +127,6 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo ) } -const SessionWorkspaceMeta: FC<{ agentId: string; sessionId: string }> = ({ agentId, sessionId }) => { - const { agent } = useAgent(agentId) - const { session } = useSession(agentId, sessionId) - if (!session || !agent) { - return null - } - - const firstAccessiblePath = session.accessible_paths?.[0] - const permissionMode = (session.configuration?.permission_mode ?? 'default') as PermissionMode - const permissionModeCard = permissionModeCards.find((card) => card.mode === permissionMode) - const permissionModeLabel = permissionModeCard - ? t(permissionModeCard.titleKey, permissionModeCard.titleFallback) - : permissionMode - - const infoItems: ReactNode[] = [] - - const InfoTag = ({ - text, - className, - onClick - }: { - text: string - className?: string - classNames?: {} - onClick?: (e: React.MouseEvent) => void - }) => ( -
- {text} -
- ) - - // infoItems.push() - - if (firstAccessiblePath) { - infoItems.push( - { - window.api.file - .openPath(firstAccessiblePath) - .catch((e) => - window.toast.error( - formatErrorMessageWithPrefix(e, t('files.error.open_path', { path: firstAccessiblePath })) - ) - ) - }} - /> - ) - } - - infoItems.push() - - if (infoItems.length === 0) { - return null - } - - return
{infoItems}
-} - export const NavbarIcon = styled.div` -webkit-app-region: none; border-radius: 8px; diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index cd18552a3e..32f1ec3975 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -130,6 +130,7 @@ const HomePage: FC = () => { setActiveTopic={setActiveTopic} setActiveAssistant={setActiveAssistant} position="left" + activeTopicOrSession={activeTopicOrSession} /> )} diff --git a/src/renderer/src/pages/home/Navbar.tsx b/src/renderer/src/pages/home/Navbar.tsx index 43217d7ca3..6d9ccc2285 100644 --- a/src/renderer/src/pages/home/Navbar.tsx +++ b/src/renderer/src/pages/home/Navbar.tsx @@ -1,4 +1,4 @@ -import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar' +import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar' import { HStack } from '@renderer/components/Layout' import SearchPopup from '@renderer/components/Popups/SearchPopup' import { isLinux, isMac, isWin } from '@renderer/config/constant' @@ -7,6 +7,8 @@ import { modelGenerating } from '@renderer/hooks/useRuntime' import { useSettings } from '@renderer/hooks/useSettings' import { useShortcut } from '@renderer/hooks/useShortcuts' import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore' +import { useChatMaxWidth } from '@renderer/pages/home/Chat' +import ChatNavbarContent from '@renderer/pages/home/components/ChatNavbarContent' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { useAppDispatch } from '@renderer/store' import { setNarrowMode } from '@renderer/store/settings' @@ -15,7 +17,7 @@ import { Tooltip } from 'antd' import { t } from 'i18next' import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react' import { AnimatePresence, motion } from 'motion/react' -import { FC } from 'react' +import React, { FC } from 'react' import styled from 'styled-components' import AssistantsDrawer from './components/AssistantsDrawer' @@ -28,13 +30,21 @@ interface Props { setActiveTopic: (topic: Topic) => void setActiveAssistant: (assistant: Assistant) => void position: 'left' | 'right' + activeTopicOrSession?: 'topic' | 'session' } -const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTopic, setActiveTopic }) => { +const HeaderNavbar: FC = ({ + activeAssistant, + setActiveAssistant, + activeTopic, + setActiveTopic, + activeTopicOrSession +}) => { const { assistant } = useAssistant(activeAssistant.id) const { showAssistants, toggleShowAssistants } = useShowAssistants() const { topicPosition, narrowMode } = useSettings() const { showTopics, toggleShowTopics } = useShowTopics() + const chatMaxWidth = useChatMaxWidth() const dispatch = useAppDispatch() useShortcut('toggle_show_assistants', toggleShowAssistants) @@ -113,15 +123,29 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo )} - - - + + {activeTopicOrSession === 'topic' ? ( + + + + ) : ( + + + + )} + + @@ -196,4 +220,15 @@ const NarrowIcon = styled(NavbarIcon)` } ` +const ChatNavbarContainer: React.FC<{ children: React.ReactNode; style?: React.CSSProperties }> = ({ + children, + style +}) => { + return ( +
+ {children} +
+ ) +} + export default HeaderNavbar diff --git a/src/renderer/src/pages/home/components/ChatNavbarContent.tsx b/src/renderer/src/pages/home/components/ChatNavbarContent.tsx new file mode 100644 index 0000000000..5d89bfd94e --- /dev/null +++ b/src/renderer/src/pages/home/components/ChatNavbarContent.tsx @@ -0,0 +1,136 @@ +import { BreadcrumbItem, Breadcrumbs, Chip, cn } from '@heroui/react' +import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer' +import { permissionModeCards } from '@renderer/constants/permissionModes' +import { useAgent } from '@renderer/hooks/agents/useAgent' +import { useSession } from '@renderer/hooks/agents/useSession' +import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' +import { useRuntime } from '@renderer/hooks/useRuntime' +import { ApiModel, Assistant, PermissionMode } from '@renderer/types' +import { formatErrorMessageWithPrefix } from '@renderer/utils/error' +import { t } from 'i18next' +import { FC, ReactNode, useCallback } from 'react' + +import { AgentSettingsPopup } from '../../settings/AgentSettings' +import { AgentLabel } from '../../settings/AgentSettings/shared' +import SelectAgentModelButton from './SelectAgentModelButton' +import SelectModelButton from './SelectModelButton' + +interface Props { + assistant: Assistant +} + +const ChatNavbarContent: FC = ({ assistant }) => { + const { chat } = useRuntime() + const { activeTopicOrSession, activeAgentId } = chat + const sessionId = activeAgentId ? (chat.activeSessionId[activeAgentId] ?? null) : null + const { agent } = useAgent(activeAgentId) + const { updateModel } = useUpdateAgent() + + const handleUpdateModel = useCallback( + async (model: ApiModel) => { + if (!agent) return + return updateModel(agent.id, model.id, { showSuccessToast: false }) + }, + [agent, updateModel] + ) + + return ( + <> + {activeTopicOrSession === 'topic' && } + {activeTopicOrSession === 'session' && agent && ( + + + AgentSettingsPopup.show({ agentId: agent.id })} + classNames={{ base: 'self-stretch', item: 'h-full' }}> + + + + + + + + {activeAgentId && sessionId && ( + + + + )} + + + )} + + ) +} + +const SessionWorkspaceMeta: FC<{ agentId: string; sessionId: string }> = ({ agentId, sessionId }) => { + const { agent } = useAgent(agentId) + const { session } = useSession(agentId, sessionId) + if (!session || !agent) { + return null + } + + const firstAccessiblePath = session.accessible_paths?.[0] + const permissionMode = (session.configuration?.permission_mode ?? 'default') as PermissionMode + const permissionModeCard = permissionModeCards.find((card) => card.mode === permissionMode) + const permissionModeLabel = permissionModeCard + ? t(permissionModeCard.titleKey, permissionModeCard.titleFallback) + : permissionMode + + const infoItems: ReactNode[] = [] + + const InfoTag = ({ + text, + className, + onClick + }: { + text: string + className?: string + classNames?: {} + onClick?: (e: React.MouseEvent) => void + }) => ( +
+ {text} +
+ ) + + // infoItems.push() + + if (firstAccessiblePath) { + infoItems.push( + { + window.api.file + .openPath(firstAccessiblePath) + .catch((e) => + window.toast.error( + formatErrorMessageWithPrefix(e, t('files.error.open_path', { path: firstAccessiblePath })) + ) + ) + }} + /> + ) + } + + infoItems.push() + + if (infoItems.length === 0) { + return null + } + + return
{infoItems}
+} + +export default ChatNavbarContent