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 TopViewContainer from './components/TopView'
|
||||||
import AntdProvider from './context/AntdProvider'
|
import AntdProvider from './context/AntdProvider'
|
||||||
import { ThemeProvider } from './context/ThemeProvider'
|
import { ThemeProvider } from './context/ThemeProvider'
|
||||||
|
import AgentEditPage from './pages/agents/AgentEditPage'
|
||||||
import AgentsPage from './pages/agents/AgentsPage'
|
import AgentsPage from './pages/agents/AgentsPage'
|
||||||
import AppsPage from './pages/apps/AppsPage'
|
import AppsPage from './pages/apps/AppsPage'
|
||||||
import FilesPage from './pages/files/FilesPage'
|
import FilesPage from './pages/files/FilesPage'
|
||||||
@ -30,6 +31,7 @@ function App(): JSX.Element {
|
|||||||
<Route path="/" element={<HomePage />} />
|
<Route path="/" element={<HomePage />} />
|
||||||
<Route path="/files" element={<FilesPage />} />
|
<Route path="/files" element={<FilesPage />} />
|
||||||
<Route path="/agents" element={<AgentsPage />} />
|
<Route path="/agents" element={<AgentsPage />} />
|
||||||
|
<Route path="/agents/:id" element={<AgentEditPage />} />
|
||||||
<Route path="/translate" element={<TranslatePage />} />
|
<Route path="/translate" element={<TranslatePage />} />
|
||||||
<Route path="/apps" element={<AppsPage />} />
|
<Route path="/apps" element={<AppsPage />} />
|
||||||
<Route path="/messages/*" element={<HistoryPage />} />
|
<Route path="/messages/*" element={<HistoryPage />} />
|
||||||
|
|||||||
@ -54,7 +54,7 @@ const Sidebar: FC = () => {
|
|||||||
</Icon>
|
</Icon>
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
<StyledLink onClick={() => to('/agents')}>
|
<StyledLink onClick={() => to('/agents')}>
|
||||||
<Icon className={isRoute('/agents')}>
|
<Icon className={isRoutes('/agents')}>
|
||||||
<i className="iconfont icon-business-smart-assistant" />
|
<i className="iconfont icon-business-smart-assistant" />
|
||||||
</Icon>
|
</Icon>
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
|
|||||||
@ -15,3 +15,14 @@ export function useAgents() {
|
|||||||
updateAgents: (agents: Agent[]) => dispatch(updateAgents(agents))
|
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",
|
"warning": "Warning",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
"close": "Close"
|
"close": "Close",
|
||||||
|
"cancel": "Cancel"
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
|
|||||||
@ -28,7 +28,8 @@
|
|||||||
"warning": "警告",
|
"warning": "警告",
|
||||||
"back": "返回",
|
"back": "返回",
|
||||||
"chat": "聊天",
|
"chat": "聊天",
|
||||||
"close": "关闭"
|
"close": "关闭",
|
||||||
|
"cancel": "取消"
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"add": "添加",
|
"add": "添加",
|
||||||
|
|||||||
@ -28,7 +28,8 @@
|
|||||||
"warning": "警告",
|
"warning": "警告",
|
||||||
"back": "返回",
|
"back": "返回",
|
||||||
"chat": "聊天",
|
"chat": "聊天",
|
||||||
"close": "關閉"
|
"close": "關閉",
|
||||||
|
"cancel": "取消"
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"add": "添加",
|
"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 { 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 Agents from '@renderer/config/agents.json'
|
||||||
import { useAgents } from '@renderer/hooks/useAgents'
|
|
||||||
import { useAssistants } from '@renderer/hooks/useAssistant'
|
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
import { covertAgentToAssistant } from '@renderer/services/assistant'
|
import { covertAgentToAssistant } from '@renderer/services/assistant'
|
||||||
import { Agent } from '@renderer/types'
|
import { Agent } from '@renderer/types'
|
||||||
@ -13,14 +11,12 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import AgentCard from './components/AgentCard'
|
import AgentCard from './components/AgentCard'
|
||||||
import ManageAgentsPopup from './components/ManageAgentsPopup'
|
import MyAgents from './components/MyAgents'
|
||||||
import UserAgents from './components/UserAgents'
|
|
||||||
|
|
||||||
const { Title } = Typography
|
const { Title } = Typography
|
||||||
|
|
||||||
const AppsPage: FC = () => {
|
const AppsPage: FC = () => {
|
||||||
const { assistants, addAssistant } = useAssistants()
|
const { assistants, addAssistant } = useAssistants()
|
||||||
const { agents } = useAgents()
|
|
||||||
const agentGroups = groupBy(Agents, 'group')
|
const agentGroups = groupBy(Agents, 'group')
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
@ -55,31 +51,29 @@ const AppsPage: FC = () => {
|
|||||||
<NavbarCenter style={{ borderRight: 'none' }}>{t('agents.title')}</NavbarCenter>
|
<NavbarCenter style={{ borderRight: 'none' }}>{t('agents.title')}</NavbarCenter>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
<ContentContainer id="content-container">
|
<ContentContainer id="content-container">
|
||||||
|
<MyAgents onClick={onAddAgentConfirm} />
|
||||||
<AssistantsContainer>
|
<AssistantsContainer>
|
||||||
<HStack alignItems="center" style={{ marginBottom: 16 }}>
|
<VStack style={{ flex: 1 }}>
|
||||||
<Title level={4}>{t('agents.my_agents')}</Title>
|
{Object.keys(agentGroups)
|
||||||
{agents.length > 0 && <ManageIcon onClick={ManageAgentsPopup.show} />}
|
.reverse()
|
||||||
</HStack>
|
.map((group) => (
|
||||||
<UserAgents onAdd={onAddAgentConfirm} />
|
<div key={group}>
|
||||||
{Object.keys(agentGroups)
|
<Title level={5} key={group} style={{ marginBottom: 16 }}>
|
||||||
.reverse()
|
{group}
|
||||||
.map((group) => (
|
</Title>
|
||||||
<div key={group}>
|
<Row gutter={16}>
|
||||||
<Title level={4} key={group} style={{ marginBottom: 16 }}>
|
{agentGroups[group].map((agent, index) => {
|
||||||
{group}
|
return (
|
||||||
</Title>
|
<Col span={8} key={group + index}>
|
||||||
<Row gutter={16}>
|
<AgentCard onClick={() => onAddAgentConfirm(agent)} agent={agent as any} />
|
||||||
{agentGroups[group].map((agent, index) => {
|
</Col>
|
||||||
return (
|
)
|
||||||
<Col span={8} key={group + index}>
|
})}
|
||||||
<AgentCard onClick={() => onAddAgentConfirm(agent)} agent={agent as any} />
|
</Row>
|
||||||
</Col>
|
</div>
|
||||||
)
|
))}
|
||||||
})}
|
<div style={{ minHeight: 20 }} />
|
||||||
</Row>
|
</VStack>
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ minHeight: 20 }} />
|
|
||||||
</AssistantsContainer>
|
</AssistantsContainer>
|
||||||
</ContentContainer>
|
</ContentContainer>
|
||||||
</Container>
|
</Container>
|
||||||
@ -99,24 +93,15 @@ const ContentContainer = styled.div`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: scroll;
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const AssistantsContainer = styled.div`
|
const AssistantsContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
height: calc(100vh - var(--navbar-height));
|
height: calc(100vh - var(--navbar-height));
|
||||||
padding: 20px;
|
padding: 15px 20px;
|
||||||
max-width: 1000px;
|
overflow-y: scroll;
|
||||||
`
|
|
||||||
|
|
||||||
const ManageIcon = styled(UnorderedListOutlined)`
|
|
||||||
font-size: 18px;
|
|
||||||
color: var(--color-icon);
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
margin-left: 0.5em;
|
|
||||||
`
|
`
|
||||||
|
|
||||||
export default AppsPage
|
export default AppsPage
|
||||||
|
|||||||
@ -5,16 +5,14 @@ import EmojiPicker from '@renderer/components/EmojiPicker'
|
|||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { useAgents } from '@renderer/hooks/useAgents'
|
import { useAgents } from '@renderer/hooks/useAgents'
|
||||||
import { fetchGenerate } from '@renderer/services/api'
|
import { fetchGenerate } from '@renderer/services/api'
|
||||||
import { syncAgentToAssistant } from '@renderer/services/assistant'
|
|
||||||
import { Agent } from '@renderer/types'
|
import { Agent } from '@renderer/types'
|
||||||
import { getLeadingEmoji, uuid } from '@renderer/utils'
|
import { getLeadingEmoji, uuid } from '@renderer/utils'
|
||||||
import { Button, Form, FormInstance, Input, Modal, Popover } from 'antd'
|
import { Button, Form, FormInstance, Input, Modal, Popover } from 'antd'
|
||||||
import TextArea from 'antd/es/input/TextArea'
|
import TextArea from 'antd/es/input/TextArea'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
agent?: Agent
|
|
||||||
resolve: (data: Agent | null) => void
|
resolve: (data: Agent | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,13 +22,13 @@ type FieldType = {
|
|||||||
prompt: string
|
prompt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const PopupContainer: React.FC<Props> = ({ agent, resolve }) => {
|
const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
const [form] = Form.useForm()
|
const [form] = Form.useForm()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { addAgent, updateAgent } = useAgents()
|
const { addAgent } = useAgents()
|
||||||
const formRef = useRef<FormInstance>(null)
|
const formRef = useRef<FormInstance>(null)
|
||||||
const [emoji, setEmoji] = useState(agent?.emoji)
|
const [emoji, setEmoji] = useState('')
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const onFinish = (values: FieldType) => {
|
const onFinish = (values: FieldType) => {
|
||||||
@ -40,20 +38,6 @@ const PopupContainer: React.FC<Props> = ({ agent, resolve }) => {
|
|||||||
return
|
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 = {
|
const _agent = {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: values.name,
|
name: values.name,
|
||||||
@ -75,15 +59,6 @@ const PopupContainer: React.FC<Props> = ({ agent, resolve }) => {
|
|||||||
resolve(null)
|
resolve(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (agent) {
|
|
||||||
form.setFieldsValue({
|
|
||||||
name: agent.name,
|
|
||||||
prompt: agent.prompt
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [agent, form])
|
|
||||||
|
|
||||||
const handleButtonClick = async () => {
|
const handleButtonClick = async () => {
|
||||||
const prompt = `你是一个专业的 prompt 优化助手,我会给你一段prompt,你需要帮我优化它,仅回复优化后的 prompt 不要添加任何解释,使用 [CRISPE提示框架] 回复。`
|
const prompt = `你是一个专业的 prompt 优化助手,我会给你一段prompt,你需要帮我优化它,仅回复优化后的 prompt 不要添加任何解释,使用 [CRISPE提示框架] 回复。`
|
||||||
|
|
||||||
@ -114,13 +89,13 @@ const PopupContainer: React.FC<Props> = ({ agent, resolve }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={agent ? t('agents.edit.title') : t('agents.add.title')}
|
title={t('agents.add.title')}
|
||||||
open={open}
|
open={open}
|
||||||
onOk={() => formRef.current?.submit()}
|
onOk={() => formRef.current?.submit()}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
afterClose={onClose}
|
afterClose={onClose}
|
||||||
okText={agent ? t('common.save') : t('agents.add.button')}
|
okText={t('agents.add.button')}
|
||||||
centered>
|
centered>
|
||||||
<Form
|
<Form
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
@ -163,11 +138,10 @@ export default class AddAgentPopup {
|
|||||||
static hide() {
|
static hide() {
|
||||||
TopView.hide('AddAgentPopup')
|
TopView.hide('AddAgentPopup')
|
||||||
}
|
}
|
||||||
static show(agent?: Agent) {
|
static show() {
|
||||||
return new Promise<Agent | null>((resolve) => {
|
return new Promise<Agent | null>((resolve) => {
|
||||||
TopView.show(
|
TopView.show(
|
||||||
<PopupContainer
|
<PopupContainer
|
||||||
agent={agent}
|
|
||||||
resolve={(v) => {
|
resolve={(v) => {
|
||||||
resolve(v)
|
resolve(v)
|
||||||
this.hide()
|
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