mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-22 00:13:09 +08:00
refactor: replace ContextMenu with Dropdown in AgentItem and SessionItem components for improved context menu handling
This commit is contained in:
parent
9df38c7e83
commit
4e01210df4
@ -5,12 +5,12 @@ import AgentSettingsPopup from '@renderer/pages/settings/AgentSettings/AgentSett
|
|||||||
import { AgentLabel } from '@renderer/pages/settings/AgentSettings/shared'
|
import { AgentLabel } from '@renderer/pages/settings/AgentSettings/shared'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
import type { AgentEntity } from '@renderer/types'
|
import type { AgentEntity } from '@renderer/types'
|
||||||
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '@renderer/ui/context-menu'
|
|
||||||
import { cn } from '@renderer/utils'
|
import { cn } from '@renderer/utils'
|
||||||
import { Tooltip } from 'antd'
|
import type { MenuProps } from 'antd'
|
||||||
|
import { Dropdown, Tooltip } from 'antd'
|
||||||
import { Bot } from 'lucide-react'
|
import { Bot } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { memo, useCallback } from 'react'
|
import { memo, useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
// const logger = loggerService.withContext('AgentItem')
|
// const logger = loggerService.withContext('AgentItem')
|
||||||
@ -37,45 +37,52 @@ const AgentItem: FC<AgentItemProps> = ({ agent, isActive, onDelete, onPress }) =
|
|||||||
onPress()
|
onPress()
|
||||||
}, [clickAssistantToShowTopic, topicPosition, onPress])
|
}, [clickAssistantToShowTopic, topicPosition, onPress])
|
||||||
|
|
||||||
|
const menuItems: MenuProps['items'] = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
label: t('common.edit'),
|
||||||
|
key: 'edit',
|
||||||
|
icon: <EditIcon size={14} />,
|
||||||
|
onClick: () => AgentSettingsPopup.show({ agentId: agent.id })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('common.delete'),
|
||||||
|
key: 'delete',
|
||||||
|
icon: <DeleteIcon size={14} className="lucide-custom" />,
|
||||||
|
danger: true,
|
||||||
|
onClick: () => {
|
||||||
|
window.modal.confirm({
|
||||||
|
title: t('agent.delete.title'),
|
||||||
|
content: t('agent.delete.content'),
|
||||||
|
centered: true,
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
onOk: () => onDelete(agent)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[t, agent, onDelete]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenu modal={false}>
|
<Dropdown
|
||||||
<ContextMenuTrigger>
|
menu={{ items: menuItems }}
|
||||||
<Container onClick={handlePress} isActive={isActive}>
|
trigger={['contextMenu']}
|
||||||
<AssistantNameRow className="name" title={agent.name ?? agent.id}>
|
popupRender={(menu) => <div onPointerDown={(e) => e.stopPropagation()}>{menu}</div>}>
|
||||||
<AgentNameWrapper>
|
<Container onClick={handlePress} isActive={isActive}>
|
||||||
<AgentLabel agent={agent} />
|
<AssistantNameRow className="name" title={agent.name ?? agent.id}>
|
||||||
</AgentNameWrapper>
|
<AgentNameWrapper>
|
||||||
{isActive && (
|
<AgentLabel agent={agent} />
|
||||||
<MenuButton>
|
</AgentNameWrapper>
|
||||||
<SessionCount>{sessions.length}</SessionCount>
|
{isActive && (
|
||||||
</MenuButton>
|
<MenuButton>
|
||||||
)}
|
<SessionCount>{sessions.length}</SessionCount>
|
||||||
{!isActive && <BotIcon />}
|
</MenuButton>
|
||||||
</AssistantNameRow>
|
)}
|
||||||
</Container>
|
{!isActive && <BotIcon />}
|
||||||
</ContextMenuTrigger>
|
</AssistantNameRow>
|
||||||
<ContextMenuContent>
|
</Container>
|
||||||
<ContextMenuItem key="edit" onClick={() => AgentSettingsPopup.show({ agentId: agent.id })}>
|
</Dropdown>
|
||||||
<EditIcon size={14} />
|
|
||||||
{t('common.edit')}
|
|
||||||
</ContextMenuItem>
|
|
||||||
<ContextMenuItem
|
|
||||||
key="delete"
|
|
||||||
className="text-danger"
|
|
||||||
onClick={() => {
|
|
||||||
window.modal.confirm({
|
|
||||||
title: t('agent.delete.title'),
|
|
||||||
content: t('agent.delete.content'),
|
|
||||||
centered: true,
|
|
||||||
okButtonProps: { danger: true },
|
|
||||||
onOk: () => onDelete(agent)
|
|
||||||
})
|
|
||||||
}}>
|
|
||||||
<DeleteIcon size={14} className="lucide-custom text-danger" />
|
|
||||||
<span className="text-danger">{t('common.delete')}</span>
|
|
||||||
</ContextMenuItem>
|
|
||||||
</ContextMenuContent>
|
|
||||||
</ContextMenu>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,18 +10,10 @@ import { SessionLabel } from '@renderer/pages/settings/AgentSettings/shared'
|
|||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import { newMessagesActions } from '@renderer/store/newMessage'
|
import { newMessagesActions } from '@renderer/store/newMessage'
|
||||||
import type { AgentSessionEntity } from '@renderer/types'
|
import type { AgentSessionEntity } from '@renderer/types'
|
||||||
import {
|
|
||||||
ContextMenu,
|
|
||||||
ContextMenuContent,
|
|
||||||
ContextMenuItem,
|
|
||||||
ContextMenuSub,
|
|
||||||
ContextMenuSubContent,
|
|
||||||
ContextMenuSubTrigger,
|
|
||||||
ContextMenuTrigger
|
|
||||||
} from '@renderer/ui/context-menu'
|
|
||||||
import { classNames } from '@renderer/utils'
|
import { classNames } from '@renderer/utils'
|
||||||
import { buildAgentSessionTopicId } from '@renderer/utils/agentSession'
|
import { buildAgentSessionTopicId } from '@renderer/utils/agentSession'
|
||||||
import { Tooltip } from 'antd'
|
import type { MenuProps } from 'antd'
|
||||||
|
import { Dropdown, Tooltip } from 'antd'
|
||||||
import { MenuIcon, XIcon } from 'lucide-react'
|
import { MenuIcon, XIcon } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React, { memo, startTransition, useEffect, useMemo, useState } from 'react'
|
import React, { memo, startTransition, useEffect, useMemo, useState } from 'react'
|
||||||
@ -111,80 +103,86 @@ const SessionItem: FC<SessionItemProps> = ({ session, agentId, onDelete, onPress
|
|||||||
const { topicPosition, setTopicPosition } = useSettings()
|
const { topicPosition, setTopicPosition } = useSettings()
|
||||||
const singlealone = topicPosition === 'right'
|
const singlealone = topicPosition === 'right'
|
||||||
|
|
||||||
|
const menuItems: MenuProps['items'] = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
label: t('common.edit'),
|
||||||
|
key: 'edit',
|
||||||
|
icon: <EditIcon size={14} />,
|
||||||
|
onClick: () => {
|
||||||
|
SessionSettingsPopup.show({
|
||||||
|
agentId,
|
||||||
|
sessionId: session.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('settings.topic.position.label'),
|
||||||
|
key: 'topic-position',
|
||||||
|
icon: <MenuIcon size={14} />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: t('settings.topic.position.left'),
|
||||||
|
key: 'left',
|
||||||
|
onClick: () => setTopicPosition('left')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('settings.topic.position.right'),
|
||||||
|
key: 'right',
|
||||||
|
onClick: () => setTopicPosition('right')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('common.delete'),
|
||||||
|
key: 'delete',
|
||||||
|
icon: <DeleteIcon size={14} className="lucide-custom" />,
|
||||||
|
danger: true,
|
||||||
|
onClick: () => {
|
||||||
|
onDelete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[t, agentId, session.id, setTopicPosition, onDelete]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Dropdown
|
||||||
<ContextMenu modal={false}>
|
menu={{ items: menuItems }}
|
||||||
<ContextMenuTrigger>
|
trigger={['contextMenu']}
|
||||||
<SessionListItem
|
popupRender={(menu) => <div onPointerDown={(e) => e.stopPropagation()}>{menu}</div>}>
|
||||||
className={classNames(isActive ? 'active' : '', singlealone ? 'singlealone' : '')}
|
<SessionListItem
|
||||||
onClick={isEditing ? undefined : onPress}
|
className={classNames(isActive ? 'active' : '', singlealone ? 'singlealone' : '')}
|
||||||
onDoubleClick={() => startEdit(session.name ?? '')}
|
onClick={isEditing ? undefined : onPress}
|
||||||
title={session.name ?? session.id}
|
onDoubleClick={() => startEdit(session.name ?? '')}
|
||||||
style={{
|
title={session.name ?? session.id}
|
||||||
borderRadius: 'var(--list-item-border-radius)',
|
style={{
|
||||||
cursor: isEditing ? 'default' : 'pointer'
|
borderRadius: 'var(--list-item-border-radius)',
|
||||||
}}>
|
cursor: isEditing ? 'default' : 'pointer'
|
||||||
{isPending && !isActive && <PendingIndicator />}
|
}}>
|
||||||
{isFulfilled && !isActive && <FulfilledIndicator />}
|
{isPending && !isActive && <PendingIndicator />}
|
||||||
<SessionNameContainer>
|
{isFulfilled && !isActive && <FulfilledIndicator />}
|
||||||
{isEditing ? (
|
<SessionNameContainer>
|
||||||
<SessionEditInput
|
{isEditing ? (
|
||||||
ref={inputRef}
|
<SessionEditInput
|
||||||
value={editValue}
|
ref={inputRef}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleValueChange(e.target.value)}
|
value={editValue}
|
||||||
onKeyDown={handleKeyDown}
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleValueChange(e.target.value)}
|
||||||
onClick={(e: React.MouseEvent) => e.stopPropagation()}
|
onKeyDown={handleKeyDown}
|
||||||
style={{ opacity: isSaving ? 0.5 : 1 }}
|
onClick={(e: React.MouseEvent) => e.stopPropagation()}
|
||||||
/>
|
style={{ opacity: isSaving ? 0.5 : 1 }}
|
||||||
) : (
|
/>
|
||||||
<>
|
) : (
|
||||||
<SessionName>
|
<>
|
||||||
<SessionLabel session={session} />
|
<SessionName>
|
||||||
</SessionName>
|
<SessionLabel session={session} />
|
||||||
<DeleteButton />
|
</SessionName>
|
||||||
</>
|
<DeleteButton />
|
||||||
)}
|
</>
|
||||||
</SessionNameContainer>
|
)}
|
||||||
</SessionListItem>
|
</SessionNameContainer>
|
||||||
</ContextMenuTrigger>
|
</SessionListItem>
|
||||||
<ContextMenuContent>
|
</Dropdown>
|
||||||
<ContextMenuItem
|
|
||||||
key="edit"
|
|
||||||
onClick={() => {
|
|
||||||
SessionSettingsPopup.show({
|
|
||||||
agentId,
|
|
||||||
sessionId: session.id
|
|
||||||
})
|
|
||||||
}}>
|
|
||||||
<EditIcon size={14} />
|
|
||||||
{t('common.edit')}
|
|
||||||
</ContextMenuItem>
|
|
||||||
<ContextMenuSub>
|
|
||||||
<ContextMenuSubTrigger className="gap-2">
|
|
||||||
<MenuIcon size={14} />
|
|
||||||
{t('settings.topic.position.label')}
|
|
||||||
</ContextMenuSubTrigger>
|
|
||||||
<ContextMenuSubContent>
|
|
||||||
<ContextMenuItem key="left" onClick={() => setTopicPosition('left')}>
|
|
||||||
{t('settings.topic.position.left')}
|
|
||||||
</ContextMenuItem>
|
|
||||||
<ContextMenuItem key="right" onClick={() => setTopicPosition('right')}>
|
|
||||||
{t('settings.topic.position.right')}
|
|
||||||
</ContextMenuItem>
|
|
||||||
</ContextMenuSubContent>
|
|
||||||
</ContextMenuSub>
|
|
||||||
<ContextMenuItem
|
|
||||||
key="delete"
|
|
||||||
className="text-danger"
|
|
||||||
onClick={() => {
|
|
||||||
onDelete()
|
|
||||||
}}>
|
|
||||||
<DeleteIcon size={14} className="lucide-custom text-danger" />
|
|
||||||
<span className="text-danger">{t('common.delete')}</span>
|
|
||||||
</ContextMenuItem>
|
|
||||||
</ContextMenuContent>
|
|
||||||
</ContextMenu>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -130,6 +130,7 @@ const Container = styled(Scrollbar)`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 12px 10px;
|
padding: 12px 10px;
|
||||||
|
overflow-x: hidden;
|
||||||
`
|
`
|
||||||
|
|
||||||
export default memo(Sessions)
|
export default memo(Sessions)
|
||||||
|
|||||||
@ -57,7 +57,7 @@ const UnifiedAddButton: FC<UnifiedAddButtonProps> = ({ onCreateAssistant, setAct
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-[6px]">
|
<div className="-mt-[4px] mb-[6px]">
|
||||||
<AddButton onClick={handleAddButtonClick}>{t('chat.add.assistant.title')}</AddButton>
|
<AddButton onClick={handleAddButtonClick}>{t('chat.add.assistant.title')}</AddButton>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user