mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-02 10:29:02 +08:00
feat(agent): implement add agent popup functionality
add new AddAgentPopup component with type selection move agent-related translations to dedicated section update UI to use new popup instead of placeholder toast
This commit is contained in:
parent
943fccf655
commit
dc9fb381f7
175
src/renderer/src/components/Popups/AddAgentPopup.tsx
Normal file
175
src/renderer/src/components/Popups/AddAgentPopup.tsx
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
Form,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalContent,
|
||||||
|
ModalFooter,
|
||||||
|
ModalHeader,
|
||||||
|
Select,
|
||||||
|
SelectedItemProps,
|
||||||
|
SelectedItems,
|
||||||
|
SelectItem
|
||||||
|
} from '@heroui/react'
|
||||||
|
import ClaudeCodeIcon from '@renderer/assets/images/models/claude.png'
|
||||||
|
import { TopView } from '@renderer/components/TopView'
|
||||||
|
import { useAgents } from '@renderer/hooks/useAgents'
|
||||||
|
import { useTimer } from '@renderer/hooks/useTimer'
|
||||||
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
|
import { AgentEntity } from '@renderer/types'
|
||||||
|
import { uuid } from '@renderer/utils'
|
||||||
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
resolve: (value: AgentEntity | undefined) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentTypeOption = {
|
||||||
|
key: AgentEntity['type']
|
||||||
|
name: AgentEntity['name']
|
||||||
|
avatar: AgentEntity['avatar']
|
||||||
|
}
|
||||||
|
|
||||||
|
const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||||
|
const [open, setOpen] = useState(true)
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const loadingRef = useRef(false)
|
||||||
|
const { setTimeoutTimer } = useTimer()
|
||||||
|
const { addAgent } = useAgents()
|
||||||
|
|
||||||
|
const Option = useCallback(
|
||||||
|
({ option }: { option?: AgentTypeOption | null }) => {
|
||||||
|
if (!option) {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Avatar name="?" className="h-5 w-5" />
|
||||||
|
{t('common.invalid_value')}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Avatar src={option.avatar} className="h-5 w-5" />
|
||||||
|
{option.name}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[t]
|
||||||
|
)
|
||||||
|
|
||||||
|
const Item = useCallback(
|
||||||
|
({ item }: { item: SelectedItemProps<AgentTypeOption> }) => <Option option={item.data} />,
|
||||||
|
[Option]
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderValue = useCallback(
|
||||||
|
(items: SelectedItems<AgentTypeOption>) => items.map((item) => <Item key={item.key} item={item} />),
|
||||||
|
[Item]
|
||||||
|
)
|
||||||
|
|
||||||
|
// add supported agents type here
|
||||||
|
const agentConfig = useMemo(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: 'claude-code',
|
||||||
|
name: 'Claude Code',
|
||||||
|
avatar: ClaudeCodeIcon
|
||||||
|
}
|
||||||
|
] as const satisfies AgentTypeOption[],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const agentOptions: AgentTypeOption[] = useMemo(
|
||||||
|
() =>
|
||||||
|
agentConfig.map(
|
||||||
|
(option) =>
|
||||||
|
({
|
||||||
|
...option,
|
||||||
|
rendered: <Option option={option} />
|
||||||
|
}) as const satisfies SelectedItemProps
|
||||||
|
),
|
||||||
|
[Option, agentConfig]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onCreateAgent = useCallback(
|
||||||
|
async (option: AgentTypeOption) => {
|
||||||
|
if (loadingRef.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingRef.current = true
|
||||||
|
// TODO: update redux state
|
||||||
|
const agent = {
|
||||||
|
id: uuid(),
|
||||||
|
type: option.key,
|
||||||
|
name: option.name,
|
||||||
|
created_at: '',
|
||||||
|
updated_at: '',
|
||||||
|
model: ''
|
||||||
|
} satisfies AgentEntity
|
||||||
|
|
||||||
|
setTimeoutTimer('onCreateAgent', () => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0)
|
||||||
|
resolve(agent)
|
||||||
|
setOpen(false)
|
||||||
|
},
|
||||||
|
[setTimeoutTimer, resolve]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onClose = async () => {
|
||||||
|
setOpen(false)
|
||||||
|
AddAgentPopup.hide()
|
||||||
|
resolve(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
window.toast.info('not implemented :(')
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={open} onClose={onClose}>
|
||||||
|
<ModalContent>
|
||||||
|
{(onClose) => (
|
||||||
|
<>
|
||||||
|
<ModalHeader>{t('agent.add.title')}</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<Form>
|
||||||
|
<Select
|
||||||
|
items={agentOptions}
|
||||||
|
label={t('agent.add.label')}
|
||||||
|
placeholder={t('agent.add.placeholder')}
|
||||||
|
renderValue={renderValue}>
|
||||||
|
{(option) => (
|
||||||
|
<SelectItem key={option.key}>
|
||||||
|
<Option option={option} />
|
||||||
|
</SelectItem>
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
</Form>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={onClose}>{t('common.close')}</Button>
|
||||||
|
<Button color="primary" onPress={onSubmit}>
|
||||||
|
{t('common.add')}
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AddAgentPopup {
|
||||||
|
static topviewId = 0
|
||||||
|
static hide() {
|
||||||
|
TopView.hide('AddAgentPopup')
|
||||||
|
}
|
||||||
|
static show() {
|
||||||
|
return new Promise<AgentEntity | undefined>((resolve) => {
|
||||||
|
TopView.show(<PopupContainer resolve={resolve} />, 'AddAgentPopup')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,11 @@
|
|||||||
{
|
{
|
||||||
|
"agent": {
|
||||||
|
"add": {
|
||||||
|
"label": "Agent 类型",
|
||||||
|
"placeholder": "选择 Agent 类型",
|
||||||
|
"title": "添加 Agent"
|
||||||
|
}
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"add": {
|
"add": {
|
||||||
"button": "添加到助手",
|
"button": "添加到助手",
|
||||||
@ -262,9 +269,6 @@
|
|||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"add": {
|
"add": {
|
||||||
"agent": {
|
|
||||||
"title": "添加 Agent"
|
|
||||||
},
|
|
||||||
"assistant": {
|
"assistant": {
|
||||||
"title": "添加助手"
|
"title": "添加助手"
|
||||||
},
|
},
|
||||||
@ -772,6 +776,7 @@
|
|||||||
"go_to_settings": "前往设置",
|
"go_to_settings": "前往设置",
|
||||||
"i_know": "我知道了",
|
"i_know": "我知道了",
|
||||||
"inspect": "检查",
|
"inspect": "检查",
|
||||||
|
"invalid_value": "无效值",
|
||||||
"knowledge_base": "知识库",
|
"knowledge_base": "知识库",
|
||||||
"language": "语言",
|
"language": "语言",
|
||||||
"loading": "加载中...",
|
"loading": "加载中...",
|
||||||
|
|||||||
@ -90,8 +90,8 @@ const Assistants: FC<AssistantsTabProps> = ({
|
|||||||
<AssistantAddItem onClick={onCreateAgent}>
|
<AssistantAddItem onClick={onCreateAgent}>
|
||||||
<AddItemWrapper>
|
<AddItemWrapper>
|
||||||
<Plus size={16} style={{ marginRight: 4, flexShrink: 0 }} />
|
<Plus size={16} style={{ marginRight: 4, flexShrink: 0 }} />
|
||||||
<Typography.Text style={{ color: 'inherit' }} ellipsis={{ tooltip: t('chat.add.agent.title') }}>
|
<Typography.Text style={{ color: 'inherit' }} ellipsis={{ tooltip: t('agent.add.title') }}>
|
||||||
{t('chat.add.agent.title')}
|
{t('agent.add.title')}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</AddItemWrapper>
|
</AddItemWrapper>
|
||||||
</AssistantAddItem>
|
</AssistantAddItem>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import AddAgentPopup from '@renderer/components/Popups/AddAgentPopup'
|
||||||
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
|
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
|
||||||
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
|
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
|
||||||
@ -63,7 +64,7 @@ const HomeTabs: FC<Props> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onCreateAgent = async () => {
|
const onCreateAgent = async () => {
|
||||||
window.toast.info('Not implemented')
|
await AddAgentPopup.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCreateDefaultAssistant = () => {
|
const onCreateDefaultAssistant = () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user