feat: Agents 页面改版 #198
This commit is contained in:
parent
a3a005b946
commit
a8ccaf6847
@ -9,6 +9,7 @@ import Sidebar from './components/app/Sidebar'
|
||||
import TopViewContainer from './components/TopView'
|
||||
import AntdProvider from './context/AntdProvider'
|
||||
import { ThemeProvider } from './context/ThemeProvider'
|
||||
import AgentEditPage from './pages/agents/AgentEditPage'
|
||||
import AgentsPage from './pages/agents/AgentsPage'
|
||||
import AppsPage from './pages/apps/AppsPage'
|
||||
import FilesPage from './pages/files/FilesPage'
|
||||
@ -30,6 +31,7 @@ function App(): JSX.Element {
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/files" element={<FilesPage />} />
|
||||
<Route path="/agents" element={<AgentsPage />} />
|
||||
<Route path="/agents/:id" element={<AgentEditPage />} />
|
||||
<Route path="/translate" element={<TranslatePage />} />
|
||||
<Route path="/apps" element={<AppsPage />} />
|
||||
<Route path="/messages/*" element={<HistoryPage />} />
|
||||
|
||||
@ -54,7 +54,7 @@ const Sidebar: FC = () => {
|
||||
</Icon>
|
||||
</StyledLink>
|
||||
<StyledLink onClick={() => to('/agents')}>
|
||||
<Icon className={isRoute('/agents')}>
|
||||
<Icon className={isRoutes('/agents')}>
|
||||
<i className="iconfont icon-business-smart-assistant" />
|
||||
</Icon>
|
||||
</StyledLink>
|
||||
|
||||
@ -15,3 +15,14 @@ export function useAgents() {
|
||||
updateAgents: (agents: Agent[]) => dispatch(updateAgents(agents))
|
||||
}
|
||||
}
|
||||
|
||||
export function useAgent(id: string) {
|
||||
const agents = useSelector((state: RootState) => state.agents.agents)
|
||||
const dispatch = useDispatch()
|
||||
const agent = agents.find((a) => a.id === id)
|
||||
|
||||
return {
|
||||
agent,
|
||||
updateAgent: (agent: Agent) => dispatch(updateAgent(agent))
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,8 @@
|
||||
"warning": "Warning",
|
||||
"back": "Back",
|
||||
"chat": "Chat",
|
||||
"close": "Close"
|
||||
"close": "Close",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"button": {
|
||||
"add": "Add",
|
||||
|
||||
@ -28,7 +28,8 @@
|
||||
"warning": "警告",
|
||||
"back": "返回",
|
||||
"chat": "聊天",
|
||||
"close": "关闭"
|
||||
"close": "关闭",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"button": {
|
||||
"add": "添加",
|
||||
|
||||
@ -28,7 +28,8 @@
|
||||
"warning": "警告",
|
||||
"back": "返回",
|
||||
"chat": "聊天",
|
||||
"close": "關閉"
|
||||
"close": "關閉",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"button": {
|
||||
"add": "添加",
|
||||
|
||||
157
src/renderer/src/pages/agents/AgentEditPage.tsx
Normal file
157
src/renderer/src/pages/agents/AgentEditPage.tsx
Normal file
@ -0,0 +1,157 @@
|
||||
import { LoadingOutlined, ThunderboltOutlined } from '@ant-design/icons'
|
||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||
import EmojiPicker from '@renderer/components/EmojiPicker'
|
||||
import { useAgent, useAgents } from '@renderer/hooks/useAgents'
|
||||
import { fetchGenerate } from '@renderer/services/api'
|
||||
import { syncAgentToAssistant } from '@renderer/services/assistant'
|
||||
import { Agent } from '@renderer/types'
|
||||
import { getLeadingEmoji } from '@renderer/utils'
|
||||
import { Button, Form, FormInstance, Input, Popover } from 'antd'
|
||||
import TextArea from 'antd/es/input/TextArea'
|
||||
import { FC, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
import styled from 'styled-components'
|
||||
|
||||
type FieldType = {
|
||||
id: string
|
||||
name: string
|
||||
prompt: string
|
||||
}
|
||||
|
||||
const AgentEditPage: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { id } = useParams()
|
||||
const { agent } = useAgent(id!)
|
||||
const [form] = Form.useForm()
|
||||
const formRef = useRef<FormInstance>(null)
|
||||
const { addAgent, updateAgent } = useAgents()
|
||||
const [emoji, setEmoji] = useState(agent?.emoji)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const onFinish = (values: FieldType) => {
|
||||
const _emoji = emoji || getLeadingEmoji(values.name)
|
||||
|
||||
if (values.name.trim() === '' || values.prompt.trim() === '') {
|
||||
return
|
||||
}
|
||||
|
||||
const _agent = {
|
||||
...agent,
|
||||
name: values.name,
|
||||
emoji: _emoji,
|
||||
prompt: values.prompt
|
||||
} as Agent
|
||||
|
||||
updateAgent(_agent)
|
||||
syncAgentToAssistant(_agent)
|
||||
|
||||
navigate(-1)
|
||||
}
|
||||
|
||||
const handleButtonClick = async () => {
|
||||
const prompt = `你是一个专业的 prompt 优化助手,我会给你一段prompt,你需要帮我优化它,仅回复优化后的 prompt 不要添加任何解释,使用 [CRISPE提示框架] 回复。`
|
||||
|
||||
const name = formRef.current?.getFieldValue('name')
|
||||
const content = formRef.current?.getFieldValue('prompt')
|
||||
const promptText = content || name
|
||||
|
||||
if (!promptText) {
|
||||
return
|
||||
}
|
||||
|
||||
if (content) {
|
||||
navigator.clipboard.writeText(content)
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
const prefixedContent = `请帮我优化下面这段 prompt,使用 CRISPE 提示框架,请使用 Markdown 格式回复,不要使用 codeblock: ${promptText}`
|
||||
const generatedText = await fetchGenerate({ prompt, content: prefixedContent })
|
||||
formRef.current?.setFieldValue('prompt', generatedText)
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error)
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (agent) {
|
||||
form.setFieldsValue({
|
||||
name: agent.name,
|
||||
prompt: agent.prompt
|
||||
})
|
||||
}
|
||||
}, [agent, form])
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Navbar>
|
||||
<NavbarCenter style={{ borderRight: 'none' }}>{t('agents.edit.title')}</NavbarCenter>
|
||||
</Navbar>
|
||||
<ContentContainer id="content-container">
|
||||
<Form
|
||||
ref={formRef}
|
||||
layout="vertical"
|
||||
form={form}
|
||||
labelAlign="left"
|
||||
colon={false}
|
||||
style={{ width: '100%' }}
|
||||
onFinish={onFinish}>
|
||||
<Form.Item name="name" label="Emoji">
|
||||
<Popover content={<EmojiPicker onEmojiClick={setEmoji} />} arrow placement="rightBottom">
|
||||
<Button icon={emoji && <span style={{ fontSize: 20 }}>{emoji}</span>}>{t('common.select')}</Button>
|
||||
</Popover>
|
||||
</Form.Item>
|
||||
<Form.Item name="name" label={t('agents.add.name')} rules={[{ required: true }]}>
|
||||
<Input placeholder={t('agents.add.name.placeholder')} spellCheck={false} allowClear />
|
||||
</Form.Item>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<Form.Item
|
||||
name="prompt"
|
||||
label={t('agents.add.prompt')}
|
||||
rules={[{ required: true }]}
|
||||
style={{ position: 'relative' }}>
|
||||
<TextArea placeholder={t('agents.add.prompt.placeholder')} spellCheck={false} rows={10} />
|
||||
</Form.Item>
|
||||
<Button
|
||||
icon={loading ? <LoadingOutlined /> : <ThunderboltOutlined />}
|
||||
onClick={handleButtonClick}
|
||||
style={{ position: 'absolute', top: 8, right: 8 }}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
<Form.Item wrapperCol={{ span: 16 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
<Button type="link" onClick={() => navigate(-1)}>
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div style={{ minHeight: 50 }} />
|
||||
</ContentContainer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
`
|
||||
|
||||
const ContentContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
overflow-y: scroll;
|
||||
`
|
||||
|
||||
export default AgentEditPage
|
||||
@ -1,8 +1,6 @@
|
||||
import { UnorderedListOutlined } from '@ant-design/icons'
|
||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import { VStack } from '@renderer/components/Layout'
|
||||
import Agents from '@renderer/config/agents.json'
|
||||
import { useAgents } from '@renderer/hooks/useAgents'
|
||||
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||
import { covertAgentToAssistant } from '@renderer/services/assistant'
|
||||
import { Agent } from '@renderer/types'
|
||||
@ -13,14 +11,12 @@ import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AgentCard from './components/AgentCard'
|
||||
import ManageAgentsPopup from './components/ManageAgentsPopup'
|
||||
import UserAgents from './components/UserAgents'
|
||||
import MyAgents from './components/MyAgents'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
const AppsPage: FC = () => {
|
||||
const { assistants, addAssistant } = useAssistants()
|
||||
const { agents } = useAgents()
|
||||
const agentGroups = groupBy(Agents, 'group')
|
||||
const { t } = useTranslation()
|
||||
|
||||
@ -55,31 +51,29 @@ const AppsPage: FC = () => {
|
||||
<NavbarCenter style={{ borderRight: 'none' }}>{t('agents.title')}</NavbarCenter>
|
||||
</Navbar>
|
||||
<ContentContainer id="content-container">
|
||||
<MyAgents onClick={onAddAgentConfirm} />
|
||||
<AssistantsContainer>
|
||||
<HStack alignItems="center" style={{ marginBottom: 16 }}>
|
||||
<Title level={4}>{t('agents.my_agents')}</Title>
|
||||
{agents.length > 0 && <ManageIcon onClick={ManageAgentsPopup.show} />}
|
||||
</HStack>
|
||||
<UserAgents onAdd={onAddAgentConfirm} />
|
||||
{Object.keys(agentGroups)
|
||||
.reverse()
|
||||
.map((group) => (
|
||||
<div key={group}>
|
||||
<Title level={4} key={group} style={{ marginBottom: 16 }}>
|
||||
{group}
|
||||
</Title>
|
||||
<Row gutter={16}>
|
||||
{agentGroups[group].map((agent, index) => {
|
||||
return (
|
||||
<Col span={8} key={group + index}>
|
||||
<AgentCard onClick={() => onAddAgentConfirm(agent)} agent={agent as any} />
|
||||
</Col>
|
||||
)
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
))}
|
||||
<div style={{ minHeight: 20 }} />
|
||||
<VStack style={{ flex: 1 }}>
|
||||
{Object.keys(agentGroups)
|
||||
.reverse()
|
||||
.map((group) => (
|
||||
<div key={group}>
|
||||
<Title level={5} key={group} style={{ marginBottom: 16 }}>
|
||||
{group}
|
||||
</Title>
|
||||
<Row gutter={16}>
|
||||
{agentGroups[group].map((agent, index) => {
|
||||
return (
|
||||
<Col span={8} key={group + index}>
|
||||
<AgentCard onClick={() => onAddAgentConfirm(agent)} agent={agent as any} />
|
||||
</Col>
|
||||
)
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
))}
|
||||
<div style={{ minHeight: 20 }} />
|
||||
</VStack>
|
||||
</AssistantsContainer>
|
||||
</ContentContainer>
|
||||
</Container>
|
||||
@ -99,24 +93,15 @@ const ContentContainer = styled.div`
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
`
|
||||
|
||||
const AssistantsContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
height: calc(100vh - var(--navbar-height));
|
||||
padding: 20px;
|
||||
max-width: 1000px;
|
||||
`
|
||||
|
||||
const ManageIcon = styled(UnorderedListOutlined)`
|
||||
font-size: 18px;
|
||||
color: var(--color-icon);
|
||||
cursor: pointer;
|
||||
margin-bottom: 0.5em;
|
||||
margin-left: 0.5em;
|
||||
padding: 15px 20px;
|
||||
overflow-y: scroll;
|
||||
`
|
||||
|
||||
export default AppsPage
|
||||
|
||||
@ -5,16 +5,14 @@ import EmojiPicker from '@renderer/components/EmojiPicker'
|
||||
import { TopView } from '@renderer/components/TopView'
|
||||
import { useAgents } from '@renderer/hooks/useAgents'
|
||||
import { fetchGenerate } from '@renderer/services/api'
|
||||
import { syncAgentToAssistant } from '@renderer/services/assistant'
|
||||
import { Agent } from '@renderer/types'
|
||||
import { getLeadingEmoji, uuid } from '@renderer/utils'
|
||||
import { Button, Form, FormInstance, Input, Modal, Popover } from 'antd'
|
||||
import TextArea from 'antd/es/input/TextArea'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
interface Props {
|
||||
agent?: Agent
|
||||
resolve: (data: Agent | null) => void
|
||||
}
|
||||
|
||||
@ -24,13 +22,13 @@ type FieldType = {
|
||||
prompt: string
|
||||
}
|
||||
|
||||
const PopupContainer: React.FC<Props> = ({ agent, resolve }) => {
|
||||
const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||
const [open, setOpen] = useState(true)
|
||||
const [form] = Form.useForm()
|
||||
const { t } = useTranslation()
|
||||
const { addAgent, updateAgent } = useAgents()
|
||||
const { addAgent } = useAgents()
|
||||
const formRef = useRef<FormInstance>(null)
|
||||
const [emoji, setEmoji] = useState(agent?.emoji)
|
||||
const [emoji, setEmoji] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const onFinish = (values: FieldType) => {
|
||||
@ -40,20 +38,6 @@ const PopupContainer: React.FC<Props> = ({ agent, resolve }) => {
|
||||
return
|
||||
}
|
||||
|
||||
if (agent) {
|
||||
const _agent = {
|
||||
...agent,
|
||||
name: values.name,
|
||||
emoji: _emoji,
|
||||
prompt: values.prompt
|
||||
}
|
||||
updateAgent(_agent)
|
||||
syncAgentToAssistant(_agent)
|
||||
resolve(_agent)
|
||||
setOpen(false)
|
||||
return
|
||||
}
|
||||
|
||||
const _agent = {
|
||||
id: uuid(),
|
||||
name: values.name,
|
||||
@ -75,15 +59,6 @@ const PopupContainer: React.FC<Props> = ({ agent, resolve }) => {
|
||||
resolve(null)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (agent) {
|
||||
form.setFieldsValue({
|
||||
name: agent.name,
|
||||
prompt: agent.prompt
|
||||
})
|
||||
}
|
||||
}, [agent, form])
|
||||
|
||||
const handleButtonClick = async () => {
|
||||
const prompt = `你是一个专业的 prompt 优化助手,我会给你一段prompt,你需要帮我优化它,仅回复优化后的 prompt 不要添加任何解释,使用 [CRISPE提示框架] 回复。`
|
||||
|
||||
@ -114,13 +89,13 @@ const PopupContainer: React.FC<Props> = ({ agent, resolve }) => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={agent ? t('agents.edit.title') : t('agents.add.title')}
|
||||
title={t('agents.add.title')}
|
||||
open={open}
|
||||
onOk={() => formRef.current?.submit()}
|
||||
onCancel={onCancel}
|
||||
maskClosable={false}
|
||||
afterClose={onClose}
|
||||
okText={agent ? t('common.save') : t('agents.add.button')}
|
||||
okText={t('agents.add.button')}
|
||||
centered>
|
||||
<Form
|
||||
ref={formRef}
|
||||
@ -163,11 +138,10 @@ export default class AddAgentPopup {
|
||||
static hide() {
|
||||
TopView.hide('AddAgentPopup')
|
||||
}
|
||||
static show(agent?: Agent) {
|
||||
static show() {
|
||||
return new Promise<Agent | null>((resolve) => {
|
||||
TopView.show(
|
||||
<PopupContainer
|
||||
agent={agent}
|
||||
resolve={(v) => {
|
||||
resolve(v)
|
||||
this.hide()
|
||||
|
||||
@ -1,109 +0,0 @@
|
||||
import { DeleteOutlined, EditOutlined, MenuOutlined } from '@ant-design/icons'
|
||||
import DragableList from '@renderer/components/DragableList'
|
||||
import { Box, HStack } from '@renderer/components/Layout'
|
||||
import { TopView } from '@renderer/components/TopView'
|
||||
import { useAgents } from '@renderer/hooks/useAgents'
|
||||
import { Empty, Modal, Popconfirm } from 'antd'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AddAgentPopup from './AddAgentPopup'
|
||||
|
||||
const PopupContainer: React.FC = () => {
|
||||
const [open, setOpen] = useState(true)
|
||||
const { t } = useTranslation()
|
||||
const { agents, removeAgent, updateAgents } = useAgents()
|
||||
|
||||
const onOk = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const onClose = async () => {
|
||||
ManageAgentsPopup.hide()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (agents.length === 0) {
|
||||
setOpen(false)
|
||||
}
|
||||
}, [agents])
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t('agents.manage.title')}
|
||||
open={open}
|
||||
onOk={onOk}
|
||||
onCancel={onCancel}
|
||||
afterClose={onClose}
|
||||
footer={null}
|
||||
centered>
|
||||
<Container>
|
||||
{agents.length > 0 && (
|
||||
<DragableList list={agents} onUpdate={updateAgents}>
|
||||
{(item) => (
|
||||
<AgentItem>
|
||||
<Box mr={8}>
|
||||
{item.emoji} {item.name}
|
||||
</Box>
|
||||
<HStack gap="15px">
|
||||
<Popconfirm
|
||||
title={t('agents.delete.popup.content')}
|
||||
okButtonProps={{ danger: true }}
|
||||
onConfirm={() => removeAgent(item)}>
|
||||
<DeleteOutlined style={{ color: 'var(--color-error)' }} />
|
||||
</Popconfirm>
|
||||
<EditOutlined style={{ cursor: 'pointer' }} onClick={() => AddAgentPopup.show(item)} />
|
||||
<MenuOutlined style={{ cursor: 'move' }} />
|
||||
</HStack>
|
||||
</AgentItem>
|
||||
)}
|
||||
</DragableList>
|
||||
)}
|
||||
{agents.length === 0 && <Empty description="" />}
|
||||
</Container>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
padding: 12px 0;
|
||||
height: 50vh;
|
||||
overflow-y: auto;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
const AgentItem = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
user-select: none;
|
||||
background-color: var(--color-background-soft);
|
||||
margin-bottom: 8px;
|
||||
.anticon {
|
||||
font-size: 16px;
|
||||
color: var(--color-icon);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--color-background-mute);
|
||||
}
|
||||
`
|
||||
|
||||
export default class ManageAgentsPopup {
|
||||
static topviewId = 0
|
||||
static hide() {
|
||||
TopView.hide('ManageAgentsPopup')
|
||||
}
|
||||
static show() {
|
||||
TopView.show(<PopupContainer />, 'ManageAgentsPopup')
|
||||
}
|
||||
}
|
||||
98
src/renderer/src/pages/agents/components/MyAgents.tsx
Normal file
98
src/renderer/src/pages/agents/components/MyAgents.tsx
Normal file
@ -0,0 +1,98 @@
|
||||
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'
|
||||
import DragableList from '@renderer/components/DragableList'
|
||||
import { Box, HStack } from '@renderer/components/Layout'
|
||||
import { useAgents } from '@renderer/hooks/useAgents'
|
||||
import { Agent } from '@renderer/types'
|
||||
import { Button, Popconfirm, Typography } from 'antd'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNavigate } from 'react-router'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AddAgentPopup from './AddAgentPopup'
|
||||
|
||||
const { Title } = Typography
|
||||
|
||||
interface Props {
|
||||
onClick: (agent: Agent) => void
|
||||
}
|
||||
|
||||
const MyAssistants: React.FC<Props> = ({ onClick }) => {
|
||||
const { t } = useTranslation()
|
||||
const { agents, removeAgent, updateAgents } = useAgents()
|
||||
const [dragging, setDragging] = useState(false)
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<Container style={{ paddingBottom: dragging ? 30 : 0 }}>
|
||||
<Title level={5} style={{ marginLeft: 10 }}>
|
||||
{t('agents.my_agents')}
|
||||
</Title>
|
||||
{agents.length > 0 && (
|
||||
<DragableList
|
||||
list={agents}
|
||||
onUpdate={updateAgents}
|
||||
onDragStart={() => setDragging(true)}
|
||||
onDragEnd={() => setDragging(false)}>
|
||||
{(agent) => (
|
||||
<AgentItem onClick={() => onClick(agent)}>
|
||||
<Box mr={8}>
|
||||
{agent.emoji} {agent.name}
|
||||
</Box>
|
||||
<HStack gap="15px" onClick={(e) => e.stopPropagation()}>
|
||||
<Popconfirm
|
||||
title={t('agents.delete.popup.content')}
|
||||
placement="bottom"
|
||||
okButtonProps={{ danger: true }}
|
||||
onConfirm={() => removeAgent(agent)}>
|
||||
<DeleteOutlined style={{ color: 'var(--color-error)' }} />
|
||||
</Popconfirm>
|
||||
<EditOutlined style={{ cursor: 'pointer' }} onClick={() => navigate(`/agents/${agent.id}`)} />
|
||||
</HStack>
|
||||
</AgentItem>
|
||||
)}
|
||||
</DragableList>
|
||||
)}
|
||||
{!dragging && (
|
||||
<Button
|
||||
type="dashed"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => AddAgentPopup.show()}
|
||||
style={{ borderRadius: 20, height: 34 }}>
|
||||
{t('agents.add.title')}
|
||||
</Button>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
padding: 15px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: var(--assistants-width);
|
||||
height: calc(100vh - var(--navbar-height));
|
||||
border-right: 0.5px solid var(--color-border);
|
||||
overflow-y: scroll;
|
||||
`
|
||||
|
||||
const AgentItem = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
border-radius: 20px;
|
||||
user-select: none;
|
||||
background-color: var(--color-background-soft);
|
||||
margin-bottom: 8px;
|
||||
.anticon {
|
||||
font-size: 16px;
|
||||
color: var(--color-icon);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--color-background-mute);
|
||||
}
|
||||
`
|
||||
|
||||
export default MyAssistants
|
||||
@ -1,57 +0,0 @@
|
||||
import { PlusOutlined } from '@ant-design/icons'
|
||||
import { useAgents } from '@renderer/hooks/useAgents'
|
||||
import { Agent } from '@renderer/types'
|
||||
import { Col, Row } from 'antd'
|
||||
import { FC } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AddAssistantPopup from './AddAgentPopup'
|
||||
import AgentCard from './AgentCard'
|
||||
|
||||
interface Props {
|
||||
onAdd: (agent: Agent) => void
|
||||
}
|
||||
|
||||
const UserAgents: FC<Props> = ({ onAdd }) => {
|
||||
const { agents } = useAgents()
|
||||
|
||||
const onAddMyAgentClick = () => {
|
||||
AddAssistantPopup.show()
|
||||
}
|
||||
|
||||
return (
|
||||
<Row gutter={16} style={{ marginBottom: 16 }}>
|
||||
{agents.map((agent) => (
|
||||
<Col span={8} key={agent.id}>
|
||||
<AgentCard agent={agent} onClick={() => onAdd(agent)} />
|
||||
</Col>
|
||||
))}
|
||||
<Col span={8}>
|
||||
<AssistantCardContainer style={{ borderStyle: 'dashed' }} onClick={onAddMyAgentClick}>
|
||||
<PlusOutlined />
|
||||
</AssistantCardContainer>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
const AssistantCardContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
border: 1px dashed var(--color-border-soft);
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
min-height: 72px;
|
||||
.anticon {
|
||||
font-size: 16px;
|
||||
color: var(--color-icon);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--color-background-soft);
|
||||
}
|
||||
`
|
||||
|
||||
export default UserAgents
|
||||
Loading…
x
Reference in New Issue
Block a user