From 4484f39525e17a2678afcbbf39f9f255d49e4f99 Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 13:56:09 +0800 Subject: [PATCH] feat(agent): enhance agent settings with accessible paths management - Add UI for managing accessible paths in agent settings - Improve error handling and loading states in agent components - Update type definitions for better type safety - Remove outdated comments and fix styling issues --- src/preload/index.ts | 3 +- .../components/Popups/agent/AgentModal.tsx | 1 - .../src/pages/home/Tabs/components/Agents.tsx | 6 +- .../AgentSettings/AgentEssentialSettings.tsx | 91 +++++++++++++++++-- .../pages/settings/AgentSettings/index.tsx | 33 ++++--- .../pages/settings/AgentSettings/shared.tsx | 15 ++- src/renderer/src/types/agent.ts | 2 +- 7 files changed, 123 insertions(+), 28 deletions(-) diff --git a/src/preload/index.ts b/src/preload/index.ts index af1cac21a1..d302b08441 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -166,7 +166,8 @@ const api = { openPath: (path: string) => ipcRenderer.invoke(IpcChannel.File_OpenPath, path), save: (path: string, content: string | NodeJS.ArrayBufferView, options?: any) => ipcRenderer.invoke(IpcChannel.File_Save, path, content, options), - selectFolder: (options?: OpenDialogOptions) => ipcRenderer.invoke(IpcChannel.File_SelectFolder, options), + selectFolder: (options?: OpenDialogOptions): Promise => + ipcRenderer.invoke(IpcChannel.File_SelectFolder, options), saveImage: (name: string, data: string) => ipcRenderer.invoke(IpcChannel.File_SaveImage, name, data), binaryImage: (fileId: string) => ipcRenderer.invoke(IpcChannel.File_BinaryImage, fileId), base64Image: (fileId: string): Promise<{ mime: string; base64: string; data: string }> => diff --git a/src/renderer/src/components/Popups/agent/AgentModal.tsx b/src/renderer/src/components/Popups/agent/AgentModal.tsx index 72945681da..76ddfa2ee9 100644 --- a/src/renderer/src/components/Popups/agent/AgentModal.tsx +++ b/src/renderer/src/components/Popups/agent/AgentModal.tsx @@ -327,7 +327,6 @@ export const AgentModal: React.FC = ({ agent, trigger, isOpen: _isOpen, o )} - {/* FIXME: Model type definition is string. It cannot be related to provider. Just mock a model now. */} { - setModel(value) - onUpdate() + updateModel(value) }} className="max-w-80 flex-1" placeholder={t('common.placeholders.select.model')} /> {/* TODO: Add accessible_paths and description */} + + + + + ))} + + ) } diff --git a/src/renderer/src/pages/settings/AgentSettings/index.tsx b/src/renderer/src/pages/settings/AgentSettings/index.tsx index 0de7c1996b..d6ff4e1a90 100644 --- a/src/renderer/src/pages/settings/AgentSettings/index.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/index.tsx @@ -1,5 +1,4 @@ -import { Spinner } from '@heroui/react' -import { HStack } from '@renderer/components/Layout' +import { Alert, Spinner } from '@heroui/react' import { TopView } from '@renderer/components/TopView' import { useAgent } from '@renderer/hooks/agents/useAgent' import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' @@ -28,7 +27,7 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag const { t } = useTranslation() const [menu, setMenu] = useState(tab || 'essential') - const { agent } = useAgent(agentId) + const { agent, isLoading, error } = useAgent(agentId) const updateAgent = useUpdateAgent() const onOk = () => { @@ -57,15 +56,24 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag ).filter(Boolean) const ModalContent = () => { - if (!agent) { + if (isLoading) { + // TODO: use skeleton for better ux return } + if (error) { + return ( +
+ +
+ ) + } return ( - +
setMenu(key as AgentSettingPopupTab)} /> @@ -74,7 +82,7 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag {menu === 'essential' && } {menu === 'prompt' && } - +
) } @@ -98,15 +106,19 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag styles={{ content: { padding: 0, - overflow: 'hidden' + overflow: 'hidden', + height: '80vh', + display: 'flex', + flexDirection: 'column' }, header: { padding: '10px 15px', borderBottom: '0.5px solid var(--color-border)', margin: 0, borderRadius: 0 }, body: { - padding: 0 + padding: 0, + display: 'flex', + flex: 1 } }} width="min(800px, 70vw)" - height="80vh" centered> @@ -114,7 +126,7 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag } const LeftMenu = styled.div` - height: calc(80vh - 20px); + height: 100%; border-right: 0.5px solid var(--color-border); ` @@ -123,7 +135,6 @@ const Settings = styled.div` flex-direction: column; flex: 1; padding: 16px 16px; - height: calc(80vh - 16px); overflow-y: scroll; ` diff --git a/src/renderer/src/pages/settings/AgentSettings/shared.tsx b/src/renderer/src/pages/settings/AgentSettings/shared.tsx index 1d2c9c5811..46bd122dcf 100644 --- a/src/renderer/src/pages/settings/AgentSettings/shared.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/shared.tsx @@ -2,12 +2,21 @@ import { Avatar, AvatarProps, cn } from '@heroui/react' import { getAgentAvatar } from '@renderer/config/agent' import { getAgentTypeLabel } from '@renderer/i18n/label' import { AgentType } from '@renderer/types' -import React from 'react' +import React, { ReactNode } from 'react' import { SettingDivider } from '..' -export const SettingsTitle: React.FC = ({ children }) => { - return
{children}
+export interface SettingsTitleProps extends React.ComponentPropsWithRef<'div'> { + actions?: ReactNode +} + +export const SettingsTitle: React.FC = ({ children, actions }) => { + return ( +
+ {children} + {actions !== undefined && actions} +
+ ) } export type AgentLabelProps = { diff --git a/src/renderer/src/types/agent.ts b/src/renderer/src/types/agent.ts index 0c7b8c0675..d3e4298425 100644 --- a/src/renderer/src/types/agent.ts +++ b/src/renderer/src/types/agent.ts @@ -57,7 +57,7 @@ export const AgentBaseSchema = z.object({ // Basic info name: z.string().optional(), description: z.string().optional(), - accessible_paths: z.array(z.string()), // Array of directory paths the agent can access + accessible_paths: z.array(z.string()).nonempty(), // Array of directory paths the agent can access // Instructions for the agent instructions: z.string().optional(), // System prompt