mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-22 17:00:14 +08:00
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 7f205bf241.
* 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 <kangfenmao@qq.com>
This commit is contained in:
parent
a290ee7f39
commit
1c2ce7e0aa
@ -74,7 +74,7 @@ const NavbarContainer = styled.div<{ $isFullScreen: boolean }>`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const NavbarLeftContainer = styled.div`
|
const NavbarLeftContainer = styled.div`
|
||||||
min-width: var(--assistants-width);
|
min-width: ${isMac ? 'calc(var(--assistants-width) - 20px)' : 'var(--assistants-width)'};
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@ -1,34 +1,24 @@
|
|||||||
import { BreadcrumbItem, Breadcrumbs, Chip, cn } from '@heroui/react'
|
|
||||||
import { NavbarHeader } from '@renderer/components/app/Navbar'
|
import { NavbarHeader } from '@renderer/components/app/Navbar'
|
||||||
import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer'
|
|
||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import SearchPopup from '@renderer/components/Popups/SearchPopup'
|
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 { 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 { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useShortcut } from '@renderer/hooks/useShortcuts'
|
import { useShortcut } from '@renderer/hooks/useShortcuts'
|
||||||
import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore'
|
import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
import { useAppDispatch } from '@renderer/store'
|
import { useAppDispatch } from '@renderer/store'
|
||||||
import { setNarrowMode } from '@renderer/store/settings'
|
import { setNarrowMode } from '@renderer/store/settings'
|
||||||
import { ApiModel, Assistant, PermissionMode, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { formatErrorMessageWithPrefix } from '@renderer/utils/error'
|
|
||||||
import { Tooltip } from 'antd'
|
import { Tooltip } from 'antd'
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react'
|
import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react'
|
||||||
import { AnimatePresence, motion } from 'motion/react'
|
import { AnimatePresence, motion } from 'motion/react'
|
||||||
import React, { FC, ReactNode, useCallback } from 'react'
|
import { FC } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { AgentSettingsPopup } from '../settings/AgentSettings'
|
|
||||||
import { AgentLabel } from '../settings/AgentSettings/shared'
|
|
||||||
import AssistantsDrawer from './components/AssistantsDrawer'
|
import AssistantsDrawer from './components/AssistantsDrawer'
|
||||||
import SelectAgentModelButton from './components/SelectAgentModelButton'
|
import ChatNavbarContent from './components/ChatNavbarContent'
|
||||||
import SelectModelButton from './components/SelectModelButton'
|
|
||||||
import UpdateAppButton from './components/UpdateAppButton'
|
import UpdateAppButton from './components/UpdateAppButton'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -45,11 +35,6 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
|
|||||||
const { topicPosition, narrowMode } = useSettings()
|
const { topicPosition, narrowMode } = useSettings()
|
||||||
const { showTopics, toggleShowTopics } = useShowTopics()
|
const { showTopics, toggleShowTopics } = useShowTopics()
|
||||||
const dispatch = useAppDispatch()
|
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)
|
useShortcut('toggle_show_assistants', toggleShowAssistants)
|
||||||
|
|
||||||
@ -79,14 +64,6 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUpdateModel = useCallback(
|
|
||||||
async (model: ApiModel) => {
|
|
||||||
if (!agent) return
|
|
||||||
return updateModel(agent.id, model.id, { showSuccessToast: false })
|
|
||||||
},
|
|
||||||
[agent, updateModel]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavbarHeader className="home-navbar">
|
<NavbarHeader className="home-navbar">
|
||||||
<div className="flex min-w-0 flex-1 shrink items-center overflow-auto">
|
<div className="flex min-w-0 flex-1 shrink items-center overflow-auto">
|
||||||
@ -117,38 +94,7 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
{activeTopicOrSession === 'topic' && <SelectModelButton assistant={assistant} />}
|
<ChatNavbarContent assistant={assistant} />
|
||||||
{activeTopicOrSession === 'session' && agent && (
|
|
||||||
<HorizontalScrollContainer>
|
|
||||||
<Breadcrumbs
|
|
||||||
classNames={{
|
|
||||||
base: 'flex',
|
|
||||||
list: 'flex-nowrap'
|
|
||||||
}}>
|
|
||||||
<BreadcrumbItem
|
|
||||||
onPress={() => AgentSettingsPopup.show({ agentId: agent.id })}
|
|
||||||
classNames={{
|
|
||||||
base: 'self-stretch',
|
|
||||||
item: 'h-full'
|
|
||||||
}}>
|
|
||||||
<Chip size="md" variant="light" className="h-full transition-background hover:bg-foreground-100">
|
|
||||||
<AgentLabel
|
|
||||||
agent={agent}
|
|
||||||
classNames={{ name: 'max-w-50 font-bold text-xs', avatar: 'h-4.5 w-4.5', container: 'gap-1.5' }}
|
|
||||||
/>
|
|
||||||
</Chip>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
<BreadcrumbItem>
|
|
||||||
<SelectAgentModelButton agent={agent} onSelect={handleUpdateModel} />
|
|
||||||
</BreadcrumbItem>
|
|
||||||
{activeAgentId && sessionId && (
|
|
||||||
<BreadcrumbItem>
|
|
||||||
<SessionWorkspaceMeta agentId={activeAgentId} sessionId={sessionId} />
|
|
||||||
</BreadcrumbItem>
|
|
||||||
)}
|
|
||||||
</Breadcrumbs>
|
|
||||||
</HorizontalScrollContainer>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<HStack alignItems="center" gap={8}>
|
<HStack alignItems="center" gap={8}>
|
||||||
<UpdateAppButton />
|
<UpdateAppButton />
|
||||||
@ -181,74 +127,6 @@ const HeaderNavbar: FC<Props> = ({ 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
|
|
||||||
}) => (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
'rounded-medium border border-default-200 px-2 py-1 text-foreground-500 text-xs dark:text-foreground-400',
|
|
||||||
onClick !== undefined ? 'cursor-pointer' : undefined,
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
title={text}
|
|
||||||
onClick={onClick}>
|
|
||||||
<span className="block truncate">{text}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
// infoItems.push(<InfoTag key="name" text={agent.name ?? ''} className="max-w-60" />)
|
|
||||||
|
|
||||||
if (firstAccessiblePath) {
|
|
||||||
infoItems.push(
|
|
||||||
<InfoTag
|
|
||||||
key="path"
|
|
||||||
text={firstAccessiblePath}
|
|
||||||
className="max-w-60 transition-colors hover:border-primary hover:text-primary"
|
|
||||||
onClick={() => {
|
|
||||||
window.api.file
|
|
||||||
.openPath(firstAccessiblePath)
|
|
||||||
.catch((e) =>
|
|
||||||
window.toast.error(
|
|
||||||
formatErrorMessageWithPrefix(e, t('files.error.open_path', { path: firstAccessiblePath }))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
infoItems.push(<InfoTag key="permission-mode" text={permissionModeLabel} className="max-w-50" />)
|
|
||||||
|
|
||||||
if (infoItems.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="ml-2 flex items-center gap-2">{infoItems}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const NavbarIcon = styled.div`
|
export const NavbarIcon = styled.div`
|
||||||
-webkit-app-region: none;
|
-webkit-app-region: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@ -130,6 +130,7 @@ const HomePage: FC = () => {
|
|||||||
setActiveTopic={setActiveTopic}
|
setActiveTopic={setActiveTopic}
|
||||||
setActiveAssistant={setActiveAssistant}
|
setActiveAssistant={setActiveAssistant}
|
||||||
position="left"
|
position="left"
|
||||||
|
activeTopicOrSession={activeTopicOrSession}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ContentContainer id={isLeftNavbar ? 'content-container' : undefined}>
|
<ContentContainer id={isLeftNavbar ? 'content-container' : undefined}>
|
||||||
|
|||||||
@ -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 { HStack } from '@renderer/components/Layout'
|
||||||
import SearchPopup from '@renderer/components/Popups/SearchPopup'
|
import SearchPopup from '@renderer/components/Popups/SearchPopup'
|
||||||
import { isLinux, isMac, isWin } from '@renderer/config/constant'
|
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 { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useShortcut } from '@renderer/hooks/useShortcuts'
|
import { useShortcut } from '@renderer/hooks/useShortcuts'
|
||||||
import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore'
|
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 { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
import { useAppDispatch } from '@renderer/store'
|
import { useAppDispatch } from '@renderer/store'
|
||||||
import { setNarrowMode } from '@renderer/store/settings'
|
import { setNarrowMode } from '@renderer/store/settings'
|
||||||
@ -15,7 +17,7 @@ import { Tooltip } from 'antd'
|
|||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react'
|
import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react'
|
||||||
import { AnimatePresence, motion } from 'motion/react'
|
import { AnimatePresence, motion } from 'motion/react'
|
||||||
import { FC } from 'react'
|
import React, { FC } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import AssistantsDrawer from './components/AssistantsDrawer'
|
import AssistantsDrawer from './components/AssistantsDrawer'
|
||||||
@ -28,13 +30,21 @@ interface Props {
|
|||||||
setActiveTopic: (topic: Topic) => void
|
setActiveTopic: (topic: Topic) => void
|
||||||
setActiveAssistant: (assistant: Assistant) => void
|
setActiveAssistant: (assistant: Assistant) => void
|
||||||
position: 'left' | 'right'
|
position: 'left' | 'right'
|
||||||
|
activeTopicOrSession?: 'topic' | 'session'
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTopic, setActiveTopic }) => {
|
const HeaderNavbar: FC<Props> = ({
|
||||||
|
activeAssistant,
|
||||||
|
setActiveAssistant,
|
||||||
|
activeTopic,
|
||||||
|
setActiveTopic,
|
||||||
|
activeTopicOrSession
|
||||||
|
}) => {
|
||||||
const { assistant } = useAssistant(activeAssistant.id)
|
const { assistant } = useAssistant(activeAssistant.id)
|
||||||
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
||||||
const { topicPosition, narrowMode } = useSettings()
|
const { topicPosition, narrowMode } = useSettings()
|
||||||
const { showTopics, toggleShowTopics } = useShowTopics()
|
const { showTopics, toggleShowTopics } = useShowTopics()
|
||||||
|
const chatMaxWidth = useChatMaxWidth()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
useShortcut('toggle_show_assistants', toggleShowAssistants)
|
useShortcut('toggle_show_assistants', toggleShowAssistants)
|
||||||
@ -113,15 +123,29 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
|
|||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</NavbarLeft>
|
</NavbarLeft>
|
||||||
)}
|
)}
|
||||||
|
<NavbarCenter>
|
||||||
|
{activeTopicOrSession === 'topic' ? (
|
||||||
<HStack alignItems="center" gap={6} ml={!isMac ? 16 : 0}>
|
<HStack alignItems="center" gap={6} ml={!isMac ? 16 : 0}>
|
||||||
<SelectModelButton assistant={assistant} />
|
<SelectModelButton assistant={assistant} />
|
||||||
</HStack>
|
</HStack>
|
||||||
|
) : (
|
||||||
|
<ChatNavbarContainer
|
||||||
|
style={{
|
||||||
|
maxWidth: chatMaxWidth,
|
||||||
|
marginLeft: !isMac ? 16 : 0
|
||||||
|
}}>
|
||||||
|
<ChatNavbarContent assistant={assistant} />
|
||||||
|
</ChatNavbarContainer>
|
||||||
|
)}
|
||||||
|
</NavbarCenter>
|
||||||
|
|
||||||
<NavbarRight
|
<NavbarRight
|
||||||
style={{
|
style={{
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
flex: 1,
|
flex: activeTopicOrSession === 'topic' ? 1 : 'none',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
paddingRight: isWin || isLinux ? '144px' : '15px'
|
paddingRight: isWin || isLinux ? '144px' : '15px',
|
||||||
|
minWidth: activeTopicOrSession === 'topic' ? '' : 'auto'
|
||||||
}}
|
}}
|
||||||
className="home-navbar-right">
|
className="home-navbar-right">
|
||||||
<HStack alignItems="center" gap={6}>
|
<HStack alignItems="center" gap={6}>
|
||||||
@ -196,4 +220,15 @@ const NarrowIcon = styled(NavbarIcon)`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ChatNavbarContainer: React.FC<{ children: React.ReactNode; style?: React.CSSProperties }> = ({
|
||||||
|
children,
|
||||||
|
style
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="nodrag flex min-w-0 flex-1 items-center justify-start gap-1.5 overflow-hidden" style={style}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default HeaderNavbar
|
export default HeaderNavbar
|
||||||
|
|||||||
136
src/renderer/src/pages/home/components/ChatNavbarContent.tsx
Normal file
136
src/renderer/src/pages/home/components/ChatNavbarContent.tsx
Normal file
@ -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<Props> = ({ 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' && <SelectModelButton assistant={assistant} />}
|
||||||
|
{activeTopicOrSession === 'session' && agent && (
|
||||||
|
<HorizontalScrollContainer>
|
||||||
|
<Breadcrumbs classNames={{ base: 'flex', list: 'flex-nowrap' }}>
|
||||||
|
<BreadcrumbItem
|
||||||
|
onPress={() => AgentSettingsPopup.show({ agentId: agent.id })}
|
||||||
|
classNames={{ base: 'self-stretch', item: 'h-full' }}>
|
||||||
|
<Chip size="md" variant="light" className="h-full transition-background hover:bg-foreground-100">
|
||||||
|
<AgentLabel
|
||||||
|
agent={agent}
|
||||||
|
classNames={{ name: 'max-w-50 font-bold text-xs', avatar: 'h-2 w-2 ml-[-4px]', container: 'gap-1.5' }}
|
||||||
|
/>
|
||||||
|
</Chip>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<SelectAgentModelButton agent={agent} onSelect={handleUpdateModel} />
|
||||||
|
</BreadcrumbItem>
|
||||||
|
{activeAgentId && sessionId && (
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<SessionWorkspaceMeta agentId={activeAgentId} sessionId={sessionId} />
|
||||||
|
</BreadcrumbItem>
|
||||||
|
)}
|
||||||
|
</Breadcrumbs>
|
||||||
|
</HorizontalScrollContainer>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'rounded-medium border border-default-200 px-2 py-1 text-foreground-500 text-xs dark:text-foreground-400',
|
||||||
|
onClick !== undefined ? 'cursor-pointer' : undefined,
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
title={text}
|
||||||
|
onClick={onClick}>
|
||||||
|
<span className="block truncate">{text}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
// infoItems.push(<InfoTag key="name" text={agent.name ?? ''} className="max-w-60" />)
|
||||||
|
|
||||||
|
if (firstAccessiblePath) {
|
||||||
|
infoItems.push(
|
||||||
|
<InfoTag
|
||||||
|
key="path"
|
||||||
|
text={firstAccessiblePath}
|
||||||
|
className="max-w-60 transition-colors hover:border-primary hover:text-primary"
|
||||||
|
onClick={() => {
|
||||||
|
window.api.file
|
||||||
|
.openPath(firstAccessiblePath)
|
||||||
|
.catch((e) =>
|
||||||
|
window.toast.error(
|
||||||
|
formatErrorMessageWithPrefix(e, t('files.error.open_path', { path: firstAccessiblePath }))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
infoItems.push(<InfoTag key="permission-mode" text={permissionModeLabel} className="max-w-50" />)
|
||||||
|
|
||||||
|
if (infoItems.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="ml-2 flex items-center gap-2">{infoItems}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChatNavbarContent
|
||||||
Loading…
Reference in New Issue
Block a user