From bfe2e87f59577c99b31be6d2a28ef549730ad9be Mon Sep 17 00:00:00 2001 From: icarus Date: Sun, 21 Sep 2025 21:27:04 +0800 Subject: [PATCH 01/21] feat(agent-settings): add prompt settings tab and refactor essential settings - Introduce new AgentPromptSettings component for managing agent prompts - Move prompt-related functionality from AgentEssentialSettings to new component - Add avatar display to essential settings - Improve layout structure and styling for both settings components --- .../AgentSettings/AgentEssentialSettings.tsx | 146 +--------------- .../AgentSettings/AgentPromptSettings.tsx | 160 ++++++++++++++++++ .../pages/settings/AgentSettings/index.tsx | 12 +- 3 files changed, 180 insertions(+), 138 deletions(-) create mode 100644 src/renderer/src/pages/settings/AgentSettings/AgentPromptSettings.tsx diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx index 621b84e424..90eb278295 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx @@ -1,18 +1,11 @@ -import CodeEditor from '@renderer/components/CodeEditor' -import { Box, HSpaceBetweenStack, HStack } from '@renderer/components/Layout' -import { RichEditorRef } from '@renderer/components/RichEditor/types' +import { Avatar } from '@heroui/react' +import { Box, HStack } from '@renderer/components/Layout' +import { getAgentAvatar } from '@renderer/config/agent' import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' -import { usePromptProcessor } from '@renderer/hooks/usePromptProcessor' -import { estimateTextTokens } from '@renderer/services/TokenService' import { AgentEntity, UpdateAgentForm } from '@renderer/types' -import { Button, Input, Popover } from 'antd' -import { Edit, HelpCircle, Save } from 'lucide-react' -import { FC, useEffect, useRef, useState } from 'react' +import { Input } from 'antd' +import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' -import ReactMarkdown from 'react-markdown' -import styled from 'styled-components' - -import { SettingDivider } from '..' interface AgentEssentialSettingsProps { agent: AgentEntity | undefined | null @@ -22,41 +15,22 @@ interface AgentEssentialSettingsProps { const AgentEssentialSettings: FC = ({ agent, update }) => { const { t } = useTranslation() const [name, setName] = useState((agent?.name ?? '').trim()) - const [instructions, setInstructions] = useState(agent?.instructions ?? '') - const [showPreview, setShowPreview] = useState(!!agent?.instructions?.length) - const [tokenCount, setTokenCount] = useState(0) - - useEffect(() => { - const updateTokenCount = async () => { - const count = estimateTextTokens(instructions) - setTokenCount(count) - } - updateTokenCount() - }, [instructions]) - - const editorRef = useRef(null) - - const processedPrompt = usePromptProcessor({ - prompt: instructions, - modelName: agent?.model - }) const onUpdate = () => { if (!agent) return - const _agent = { ...agent, type: undefined, name: name.trim(), instructions } satisfies UpdateAgentForm + const _agent = { ...agent, type: undefined, name: name.trim() } satisfies UpdateAgentForm update(_agent) } - const promptVarsContent =
{t('agents.add.prompt.variables.tip.content')}
- if (!agent) return null return ( - +
{t('common.name')} + = ({ agent, update style={{ flex: 1 }} /> - - - {t('common.prompt')} - - - - - - - {showPreview ? ( - { - const currentScrollTop = editorRef.current?.getScrollTop?.() || 0 - setShowPreview(false) - requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop)) - }}> - {processedPrompt || instructions} - - ) : ( - - )} - - - - Tokens: {tokenCount} - - - +
) } -const Container = styled.div` - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; -` - -const TextAreaContainer = styled.div` - position: relative; - width: 100%; -` - -const TokenCount = styled.div` - padding: 2px 2px; - border-radius: 4px; - font-size: 14px; - color: var(--color-text-2); - user-select: none; -` - -const RichEditorContainer = styled.div` - height: calc(80vh - 202px); - border: 0.5px solid var(--color-border); - border-radius: 5px; - overflow: hidden; - - .prompt-rich-editor { - border: none; - height: 100%; - - .rich-editor-wrapper { - height: 100%; - display: flex; - flex-direction: column; - } - - .rich-editor-content { - flex: 1; - overflow: auto; - } - } -` - -const MarkdownContainer = styled.div.attrs({ className: 'markdown' })` - height: 100%; - padding: 0.5em; - overflow: auto; -` - export default AgentEssentialSettings diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentPromptSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentPromptSettings.tsx new file mode 100644 index 0000000000..80f9931901 --- /dev/null +++ b/src/renderer/src/pages/settings/AgentSettings/AgentPromptSettings.tsx @@ -0,0 +1,160 @@ +import CodeEditor from '@renderer/components/CodeEditor' +import { Box, HSpaceBetweenStack, HStack } from '@renderer/components/Layout' +import { RichEditorRef } from '@renderer/components/RichEditor/types' +import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' +import { usePromptProcessor } from '@renderer/hooks/usePromptProcessor' +import { estimateTextTokens } from '@renderer/services/TokenService' +import { AgentEntity, UpdateAgentForm } from '@renderer/types' +import { Button, Popover } from 'antd' +import { Edit, HelpCircle, Save } from 'lucide-react' +import { FC, useEffect, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import ReactMarkdown from 'react-markdown' +import styled from 'styled-components' + +interface AgentPromptSettingsProps { + agent: AgentEntity | undefined | null + update: ReturnType +} + +const AgentPromptSettings: FC = ({ agent, update }) => { + const { t } = useTranslation() + const [instructions, setInstructions] = useState(agent?.instructions ?? '') + const [showPreview, setShowPreview] = useState(!!agent?.instructions?.length) + const [tokenCount, setTokenCount] = useState(0) + + useEffect(() => { + const updateTokenCount = async () => { + const count = estimateTextTokens(instructions) + setTokenCount(count) + } + updateTokenCount() + }, [instructions]) + + const editorRef = useRef(null) + + const processedPrompt = usePromptProcessor({ + prompt: instructions, + modelName: agent?.model + }) + + const onUpdate = () => { + if (!agent) return + const _agent = { ...agent, type: undefined, instructions } satisfies UpdateAgentForm + update(_agent) + } + + const promptVarsContent =
{t('agents.add.prompt.variables.tip.content')}
+ + if (!agent) return null + + return ( + + + {t('common.prompt')} + + + + + + + {showPreview ? ( + { + const currentScrollTop = editorRef.current?.getScrollTop?.() || 0 + setShowPreview(false) + requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop)) + }}> + {processedPrompt || instructions} + + ) : ( + + )} + + + + Tokens: {tokenCount} + + + + ) +} + +const Container = styled.div` + display: flex; + flex: 1; + flex-direction: column; + overflow: hidden; +` + +const TextAreaContainer = styled.div` + position: relative; + width: 100%; + flex: 1; +` + +const TokenCount = styled.div` + padding: 2px 2px; + border-radius: 4px; + font-size: 14px; + color: var(--color-text-2); + user-select: none; +` + +const RichEditorContainer = styled.div` + height: 100%; + flex: 1; + border: 0.5px solid var(--color-border); + border-radius: 5px; + overflow: hidden; + + .prompt-rich-editor { + border: none; + height: 100%; + + .rich-editor-wrapper { + height: 100%; + display: flex; + flex-direction: column; + } + + .rich-editor-content { + flex: 1; + overflow: auto; + } + } +` + +const MarkdownContainer = styled.div.attrs({ className: 'markdown' })` + height: 100%; + padding: 0.5em; + overflow: auto; +` + +export default AgentPromptSettings diff --git a/src/renderer/src/pages/settings/AgentSettings/index.tsx b/src/renderer/src/pages/settings/AgentSettings/index.tsx index 9de26a617b..9a2cbc39c8 100644 --- a/src/renderer/src/pages/settings/AgentSettings/index.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/index.tsx @@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next' import styled from 'styled-components' import AgentEssentialSettings from './AgentEssentialSettings' +import AgentPromptSettings from './AgentPromptSettings' interface AgentSettingPopupShowParams { agentId: string @@ -47,6 +48,10 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag { key: 'essential', label: t('agent.settings.essential') + }, + { + key: 'prompt', + label: t('agent.settings.prompt') } ] satisfies { key: AgentSettingPopupTab; label: string }[] ).filter(Boolean) as { key: string; label: string }[] @@ -88,7 +93,10 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag onSelect={({ key }) => setMenu(key as AgentSettingPopupTab)} /> - {menu === 'essential' && } + + {menu === 'essential' && } + {menu === 'prompt' && } + ) @@ -100,6 +108,8 @@ const LeftMenu = styled.div` ` const Settings = styled.div` + display: flex; + flex-direction: column; flex: 1; padding: 16px 16px; height: calc(80vh - 16px); From ea62294bd83cbbcfb959e0ce457bedc83a657f85 Mon Sep 17 00:00:00 2001 From: icarus Date: Sun, 21 Sep 2025 22:16:15 +0800 Subject: [PATCH 02/21] refactor(agent-settings): extract shared components and improve styling - Move common components (AgentLabel, SettingsTitle, SettingsInline) to shared file - Update CSS to use @layer base for better organization - Fix agent type label translation key in AgentModal - Add agent type label utility function --- src/renderer/src/assets/styles/index.css | 14 ++++---- .../components/Popups/agent/AgentModal.tsx | 2 +- src/renderer/src/i18n/label.ts | 16 ++++++++- .../AgentSettings/AgentEssentialSettings.tsx | 17 ++++++---- .../AgentSettings/AgentPromptSettings.tsx | 10 +++--- .../pages/settings/AgentSettings/index.tsx | 13 ++++---- .../pages/settings/AgentSettings/shared.tsx | 33 +++++++++++++++++++ 7 files changed, 80 insertions(+), 25 deletions(-) create mode 100644 src/renderer/src/pages/settings/AgentSettings/shared.tsx diff --git a/src/renderer/src/assets/styles/index.css b/src/renderer/src/assets/styles/index.css index 0c6bee69d2..960d28061d 100644 --- a/src/renderer/src/assets/styles/index.css +++ b/src/renderer/src/assets/styles/index.css @@ -11,12 +11,14 @@ @import '../fonts/ubuntu/ubuntu.css'; @import '../fonts/country-flag-fonts/flag.css'; -*, -*::before, -*::after { - box-sizing: border-box; - /* margin: 0; */ - font-weight: normal; +@layer base { + *, + *::before, + *::after { + box-sizing: border-box; + /* margin: 0; */ + font-weight: normal; + } } *:focus { diff --git a/src/renderer/src/components/Popups/agent/AgentModal.tsx b/src/renderer/src/components/Popups/agent/AgentModal.tsx index c19640c12d..ef5ac31785 100644 --- a/src/renderer/src/components/Popups/agent/AgentModal.tsx +++ b/src/renderer/src/components/Popups/agent/AgentModal.tsx @@ -317,7 +317,7 @@ export const AgentModal: React.FC = ({ agent, trigger, isOpen: _isOpen, o selectedKeys={[form.type]} onChange={onAgentTypeChange} items={agentOptions} - label={t('agent.add.type.label')} + label={t('agent.type.label')} placeholder={t('agent.add.type.placeholder')} renderValue={renderOption}> {(option) => ( diff --git a/src/renderer/src/i18n/label.ts b/src/renderer/src/i18n/label.ts index 9c858c3baa..db29d3f5e1 100644 --- a/src/renderer/src/i18n/label.ts +++ b/src/renderer/src/i18n/label.ts @@ -5,7 +5,13 @@ */ import { loggerService } from '@logger' -import { BuiltinMCPServerName, BuiltinMCPServerNames, BuiltinOcrProviderId, ThinkingOption } from '@renderer/types' +import { + AgentType, + BuiltinMCPServerName, + BuiltinMCPServerNames, + BuiltinOcrProviderId, + ThinkingOption +} from '@renderer/types' import i18n from './index' @@ -339,3 +345,11 @@ export const getBuiltinOcrProviderLabel = (key: BuiltinOcrProviderId) => { else if (key == 'paddleocr') return 'PaddleOCR' else return getLabel(builtinOcrProviderKeyMap, key) } + +const agentTypeKeyMap = { + 'claude-code': 'Claude Code' +} as const satisfies Record + +export const getAgentTypeLabel = (key: AgentType) => { + return getLabel(agentTypeKeyMap, key, t('agent.type.unknown')) +} diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx index 90eb278295..e06b5a5ac8 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx @@ -1,12 +1,13 @@ -import { Avatar } from '@heroui/react' -import { Box, HStack } from '@renderer/components/Layout' -import { getAgentAvatar } from '@renderer/config/agent' +import { HStack } from '@renderer/components/Layout' import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' import { AgentEntity, UpdateAgentForm } from '@renderer/types' import { Input } from 'antd' import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' +import { SettingDivider } from '..' +import { AgentLabel, SettingsInline, SettingsTitle } from './shared' + interface AgentEssentialSettingsProps { agent: AgentEntity | undefined | null update: ReturnType @@ -26,11 +27,13 @@ const AgentEssentialSettings: FC = ({ agent, update return (
- - {t('common.name')} - + + {t('agent.type.label')} + + + + {t('common.name')} - @@ -50,12 +52,12 @@ const AgentPromptSettings: FC = ({ agent, update }) => return ( - - {t('common.prompt')} + + {t('common.prompt')} - + {showPreview ? ( diff --git a/src/renderer/src/pages/settings/AgentSettings/index.tsx b/src/renderer/src/pages/settings/AgentSettings/index.tsx index 9a2cbc39c8..e1801d3954 100644 --- a/src/renderer/src/pages/settings/AgentSettings/index.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/index.tsx @@ -1,7 +1,5 @@ -import { Avatar } from '@heroui/react' import { HStack } from '@renderer/components/Layout' import { TopView } from '@renderer/components/TopView' -import { getAgentAvatar } from '@renderer/config/agent' import { useAgent } from '@renderer/hooks/agents/useAgent' import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' import { Menu, Modal } from 'antd' @@ -11,6 +9,7 @@ import styled from 'styled-components' import AgentEssentialSettings from './AgentEssentialSettings' import AgentPromptSettings from './AgentPromptSettings' +import { AgentLabel } from './shared' interface AgentSettingPopupShowParams { agentId: string @@ -65,10 +64,12 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag maskClosable={false} footer={null} title={ -
- - {agent?.name ?? ''} -
+ } transitionName="animation-move-down" styles={{ diff --git a/src/renderer/src/pages/settings/AgentSettings/shared.tsx b/src/renderer/src/pages/settings/AgentSettings/shared.tsx new file mode 100644 index 0000000000..6ca583db65 --- /dev/null +++ b/src/renderer/src/pages/settings/AgentSettings/shared.tsx @@ -0,0 +1,33 @@ +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' + +export const SettingsTitle: React.FC = ({ children }) => { + return
{children}
+} + +export const SettingsInline: React.FC = ({ children }) => { + return
{children}
+} + +export type AgentLabelProps = { + type: AgentType + name?: string + classNames?: { + container?: string + avatar?: string + name?: string + } + avatarProps?: AvatarProps +} + +export const AgentLabel: React.FC = ({ type, name, classNames, avatarProps }) => { + return ( +
+ + {name ?? getAgentTypeLabel(type)} +
+ ) +} From f49d3791b6639fbaedad7305f5c3b81d48b0787f Mon Sep 17 00:00:00 2001 From: icarus Date: Sun, 21 Sep 2025 22:41:09 +0800 Subject: [PATCH 03/21] refactor(AgentSettings): restructure settings components for better reusability - Replace SettingsInline with more flexible SettingsItem component - Add SettingsContainer for consistent layout - Remove redundant styled components in favor of shared components --- .../AgentSettings/AgentEssentialSettings.tsx | 42 +++---- .../AgentSettings/AgentPromptSettings.tsx | 115 +++++++++--------- .../pages/settings/AgentSettings/shared.tsx | 40 +++++- 3 files changed, 112 insertions(+), 85 deletions(-) diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx index e06b5a5ac8..ba18763d7c 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx @@ -5,8 +5,7 @@ import { Input } from 'antd' import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' -import { SettingDivider } from '..' -import { AgentLabel, SettingsInline, SettingsTitle } from './shared' +import { AgentLabel, SettingsContainer, SettingsItem, SettingsTitle } from './shared' interface AgentEssentialSettingsProps { agent: AgentEntity | undefined | null @@ -26,27 +25,28 @@ const AgentEssentialSettings: FC = ({ agent, update if (!agent) return null return ( -
- + + {t('agent.type.label')} - - - {t('common.name')} - - setName(e.target.value)} - onBlur={() => { - if (name !== agent.name) { - onUpdate() - } - }} - style={{ flex: 1 }} - /> - -
+ + + {t('common.name')} + + setName(e.target.value)} + onBlur={() => { + if (name !== agent.name) { + onUpdate() + } + }} + style={{ flex: 1 }} + /> + + + ) } diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentPromptSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentPromptSettings.tsx index db58e6ba6c..9cf6478f0d 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentPromptSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentPromptSettings.tsx @@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next' import ReactMarkdown from 'react-markdown' import styled from 'styled-components' -import { SettingsTitle } from './shared' +import { SettingsContainer, SettingsItem, SettingsTitle } from './shared' interface AgentPromptSettingsProps { agent: AgentEntity | undefined | null @@ -51,70 +51,65 @@ const AgentPromptSettings: FC = ({ agent, update }) => if (!agent) return null return ( - - - {t('common.prompt')} - - - - - - - {showPreview ? ( - { - const currentScrollTop = editorRef.current?.getScrollTop?.() || 0 + + + + {t('common.prompt')} + + + + + + + {showPreview ? ( + { + const currentScrollTop = editorRef.current?.getScrollTop?.() || 0 + setShowPreview(false) + requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop)) + }}> + {processedPrompt || instructions} + + ) : ( + + )} + + + + Tokens: {tokenCount} + - - + } else { + onUpdate() + requestAnimationFrame(() => { + setShowPreview(true) + requestAnimationFrame(() => editorRef.current?.setScrollTop?.(currentScrollTop)) + }) + } + }}> + {showPreview ? t('common.edit') : t('common.save')} + + + + ) } -const Container = styled.div` - display: flex; - flex: 1; - flex-direction: column; - overflow: hidden; -` - const TextAreaContainer = styled.div` position: relative; width: 100%; diff --git a/src/renderer/src/pages/settings/AgentSettings/shared.tsx b/src/renderer/src/pages/settings/AgentSettings/shared.tsx index 6ca583db65..a570c82036 100644 --- a/src/renderer/src/pages/settings/AgentSettings/shared.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/shared.tsx @@ -4,14 +4,12 @@ import { getAgentTypeLabel } from '@renderer/i18n/label' import { AgentType } from '@renderer/types' import React from 'react' +import { SettingDivider } from '..' + export const SettingsTitle: React.FC = ({ children }) => { return
{children}
} -export const SettingsInline: React.FC = ({ children }) => { - return
{children}
-} - export type AgentLabelProps = { type: AgentType name?: string @@ -31,3 +29,37 @@ export const AgentLabel: React.FC = ({ type, name, classNames,
) } + +export interface SettingsItemProps extends React.ComponentPropsWithRef<'div'> { + /** Add a divider beneath the item if true, defaults to true. */ + divider?: boolean + /** Apply row direction flex or not, defaults to false. */ + inline?: boolean +} + +export const SettingsItem: React.FC = ({ + children, + divider = true, + inline = false, + className, + ...props +}) => { + return ( + <> +
+ {children} +
+ {divider && } + + ) +} + +export const SettingsContainer: React.FC> = ({ children, className, ...props }) => { + return ( +
+ {children} +
+ ) +} From f45b744318124973ae42faed768c00f4ef4c31fe Mon Sep 17 00:00:00 2001 From: icarus Date: Sun, 21 Sep 2025 23:31:13 +0800 Subject: [PATCH 04/21] feat(AgentSettings): add model selection to essential settings - Make model prop optional in ModelAvatar component - Ensure models array always returns an array in useModels hook - Export SelectorProps type for reuse - Add getProviderNameById utility function - Introduce ModelLabel component for displaying model info - Update AgentEssentialSettings to include model selection dropdown --- .../src/components/Avatar/ModelAvatar.tsx | 2 +- src/renderer/src/components/Selector.tsx | 2 +- src/renderer/src/hooks/agents/useModels.ts | 2 +- .../AgentSettings/AgentEssentialSettings.tsx | 51 ++++++++++++------- .../pages/settings/AgentSettings/shared.tsx | 20 +++++++- src/renderer/src/services/ProviderService.ts | 9 ++++ 6 files changed, 63 insertions(+), 23 deletions(-) diff --git a/src/renderer/src/components/Avatar/ModelAvatar.tsx b/src/renderer/src/components/Avatar/ModelAvatar.tsx index d1a6f98be9..22fafcd98f 100644 --- a/src/renderer/src/components/Avatar/ModelAvatar.tsx +++ b/src/renderer/src/components/Avatar/ModelAvatar.tsx @@ -5,7 +5,7 @@ import { first } from 'lodash' import { FC } from 'react' interface Props { - model: Model + model?: Model size: number props?: AvatarProps className?: string diff --git a/src/renderer/src/components/Selector.tsx b/src/renderer/src/components/Selector.tsx index d18c76dbff..bffe2b2eaf 100644 --- a/src/renderer/src/components/Selector.tsx +++ b/src/renderer/src/components/Selector.tsx @@ -34,7 +34,7 @@ interface MultipleSelectorProps extends BaseSelectorProps { onChange: (value: V[]) => void } -type SelectorProps = SingleSelectorProps | MultipleSelectorProps +export type SelectorProps = SingleSelectorProps | MultipleSelectorProps const Selector = ({ options, diff --git a/src/renderer/src/hooks/agents/useModels.ts b/src/renderer/src/hooks/agents/useModels.ts index 1834ed819d..442f42d4c4 100644 --- a/src/renderer/src/hooks/agents/useModels.ts +++ b/src/renderer/src/hooks/agents/useModels.ts @@ -12,7 +12,7 @@ export const useModels = (filter?: ApiModelsFilter) => { }, [client, filter]) const { data, error, isLoading } = useSWR(path, fetcher) return { - models: data?.data, + models: data?.data ?? [], error, isLoading } diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx index ba18763d7c..e22dfa7931 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx @@ -1,11 +1,12 @@ -import { HStack } from '@renderer/components/Layout' +import { useModels } from '@renderer/hooks/agents/useModels' import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' import { AgentEntity, UpdateAgentForm } from '@renderer/types' -import { Input } from 'antd' -import { FC, useState } from 'react' +import { Input, Select } from 'antd' +import { DefaultOptionType } from 'antd/es/select' +import { FC, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { AgentLabel, SettingsContainer, SettingsItem, SettingsTitle } from './shared' +import { AgentLabel, ModelLabel, SettingsContainer, SettingsItem, SettingsTitle } from './shared' interface AgentEssentialSettingsProps { agent: AgentEntity | undefined | null @@ -15,6 +16,7 @@ interface AgentEssentialSettingsProps { const AgentEssentialSettings: FC = ({ agent, update }) => { const { t } = useTranslation() const [name, setName] = useState((agent?.name ?? '').trim()) + const { models } = useModels({ providerType: 'anthropic' }) const onUpdate = () => { if (!agent) return @@ -22,6 +24,13 @@ const AgentEssentialSettings: FC = ({ agent, update update(_agent) } + const modelOptions = useMemo(() => { + return models.map((model) => ({ + value: model.id, + label: + })) satisfies DefaultOptionType[] + }, [models]) + if (!agent) return null return ( @@ -30,21 +39,27 @@ const AgentEssentialSettings: FC = ({ agent, update {t('agent.type.label')} - + {t('common.name')} - - setName(e.target.value)} - onBlur={() => { - if (name !== agent.name) { - onUpdate() - } - }} - style={{ flex: 1 }} - /> - + setName(e.target.value)} + onBlur={() => { + if (name !== agent.name) { + onUpdate() + } + }} + className="max-w-80 flex-1" + /> + + + {t('common.model')} + { + setModel(value) + onUpdate() + }} className="max-w-80 flex-1" placeholder={t('common.placeholders.select.model')} /> From 710592b0536560be06234f7abb5e20598312c4f5 Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 00:04:15 +0800 Subject: [PATCH 06/21] refactor(agents): rename useModels to useApiModels and extract model label component Extract ModelLabel component into standalone ApiModelLabel and rename useModels hook to useApiModels for better clarity. Update all references to use the new names. This improves code organization and maintainability. --- src/renderer/src/components/ApiModelLabel.tsx | 19 +++++++++++++++++++ .../components/Popups/agent/AgentModal.tsx | 4 ++-- .../components/Popups/agent/SessionModal.tsx | 4 ++-- src/renderer/src/hooks/agents/useModel.ts | 10 ++++++++++ src/renderer/src/hooks/agents/useModels.ts | 2 +- src/renderer/src/pages/home/ChatNavbar.tsx | 14 ++++++++++++-- .../AgentSettings/AgentEssentialSettings.tsx | 9 +++++---- .../pages/settings/AgentSettings/shared.tsx | 18 +----------------- 8 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 src/renderer/src/components/ApiModelLabel.tsx create mode 100644 src/renderer/src/hooks/agents/useModel.ts diff --git a/src/renderer/src/components/ApiModelLabel.tsx b/src/renderer/src/components/ApiModelLabel.tsx new file mode 100644 index 0000000000..e9c78deb48 --- /dev/null +++ b/src/renderer/src/components/ApiModelLabel.tsx @@ -0,0 +1,19 @@ +import { Avatar, cn } from '@heroui/react' +import { getModelLogo } from '@renderer/config/models' +import { ApiModel } from '@renderer/types' +import React from 'react' + +export interface ModelLabelProps extends Omit, 'children'> { + model?: ApiModel +} + +export const ApiModelLabel: React.FC = ({ model, className, ...props }) => { + return ( +
+ + + {model?.name} | {model?.provider_name} + +
+ ) +} diff --git a/src/renderer/src/components/Popups/agent/AgentModal.tsx b/src/renderer/src/components/Popups/agent/AgentModal.tsx index ef5ac31785..72945681da 100644 --- a/src/renderer/src/components/Popups/agent/AgentModal.tsx +++ b/src/renderer/src/components/Popups/agent/AgentModal.tsx @@ -18,7 +18,7 @@ import { loggerService } from '@logger' import ClaudeIcon from '@renderer/assets/images/models/claude.png' import { getModelLogo } from '@renderer/config/models' import { useAgents } from '@renderer/hooks/agents/useAgents' -import { useModels } from '@renderer/hooks/agents/useModels' +import { useApiModels } from '@renderer/hooks/agents/useModels' import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' import { AddAgentForm, AgentEntity, AgentType, BaseAgentForm, isAgentType, UpdateAgentForm } from '@renderer/types' import { ChangeEvent, FormEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -82,7 +82,7 @@ export const AgentModal: React.FC = ({ agent, trigger, isOpen: _isOpen, o const { addAgent } = useAgents() const updateAgent = useUpdateAgent() // hard-coded. We only support anthropic for now. - const { models } = useModels({ providerType: 'anthropic' }) + const { models } = useApiModels({ providerType: 'anthropic' }) const isEditing = (agent?: AgentEntity) => agent !== undefined const [form, setForm] = useState(() => buildAgentForm(agent)) diff --git a/src/renderer/src/components/Popups/agent/SessionModal.tsx b/src/renderer/src/components/Popups/agent/SessionModal.tsx index 1735c73d55..6e64e4a1aa 100644 --- a/src/renderer/src/components/Popups/agent/SessionModal.tsx +++ b/src/renderer/src/components/Popups/agent/SessionModal.tsx @@ -18,7 +18,7 @@ import { import { loggerService } from '@logger' import { getModelLogo } from '@renderer/config/models' import { useAgent } from '@renderer/hooks/agents/useAgent' -import { useModels } from '@renderer/hooks/agents/useModels' +import { useApiModels } from '@renderer/hooks/agents/useModels' import { useSessions } from '@renderer/hooks/agents/useSessions' import { AgentEntity, AgentSessionEntity, BaseSessionForm, CreateSessionForm, UpdateSessionForm } from '@renderer/types' import { ChangeEvent, FormEvent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -80,7 +80,7 @@ export const SessionModal: React.FC = ({ agentId, session, trigger, isOpe // const { setTimeoutTimer } = useTimer() const { createSession, updateSession } = useSessions(agentId) // Only support claude code for now - const { models } = useModels({ providerType: 'anthropic' }) + const { models } = useApiModels({ providerType: 'anthropic' }) const { agent } = useAgent(agentId) const isEditing = (session?: AgentSessionEntity) => session !== undefined diff --git a/src/renderer/src/hooks/agents/useModel.ts b/src/renderer/src/hooks/agents/useModel.ts new file mode 100644 index 0000000000..dd7f9cc1d6 --- /dev/null +++ b/src/renderer/src/hooks/agents/useModel.ts @@ -0,0 +1,10 @@ +import { useApiModels } from './useModels' + +export type UseModelProps = { + id: string +} + +export const useApiModel = (id?: string) => { + const { models } = useApiModels() + return models.find((model) => model.id === id) +} diff --git a/src/renderer/src/hooks/agents/useModels.ts b/src/renderer/src/hooks/agents/useModels.ts index 442f42d4c4..f05cb777b9 100644 --- a/src/renderer/src/hooks/agents/useModels.ts +++ b/src/renderer/src/hooks/agents/useModels.ts @@ -4,7 +4,7 @@ import useSWR from 'swr' import { useAgentClient } from './useAgentClient' -export const useModels = (filter?: ApiModelsFilter) => { +export const useApiModels = (filter?: ApiModelsFilter) => { const client = useAgentClient() const path = client.getModelsPath(filter) const fetcher = useCallback(() => { diff --git a/src/renderer/src/pages/home/ChatNavbar.tsx b/src/renderer/src/pages/home/ChatNavbar.tsx index 63ed16f989..f437f0bb49 100644 --- a/src/renderer/src/pages/home/ChatNavbar.tsx +++ b/src/renderer/src/pages/home/ChatNavbar.tsx @@ -1,8 +1,11 @@ +import { ApiModelLabel } from '@renderer/components/ApiModelLabel' import { NavbarHeader } from '@renderer/components/app/Navbar' import { HStack } from '@renderer/components/Layout' import SearchPopup from '@renderer/components/Popups/SearchPopup' +import { useAgent } from '@renderer/hooks/agents/useAgent' +import { useApiModel } from '@renderer/hooks/agents/useModel' import { useAssistant } from '@renderer/hooks/useAssistant' -import { modelGenerating } from '@renderer/hooks/useRuntime' +import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime' import { useSettings } from '@renderer/hooks/useSettings' import { useShortcut } from '@renderer/hooks/useShortcuts' import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore' @@ -35,6 +38,10 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo const { topicPosition, narrowMode } = useSettings() const { showTopics, toggleShowTopics } = useShowTopics() const dispatch = useAppDispatch() + const { chat } = useRuntime() + const { activeTopicOrSession, activeAgentId } = chat + const { agent } = useAgent(activeAgentId) + const agentModel = useApiModel(agent?.model) useShortcut('toggle_show_assistants', toggleShowAssistants) @@ -94,7 +101,10 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo )} - + {activeTopicOrSession === 'topic' && } + {/* TODO: Show a select model button for agent. */} + {/* FIXME: models endpoint doesn't return all models, so cannot found. */} + {activeTopicOrSession === 'session' && } diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx index c3ea9f8b24..b278da6dca 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx @@ -1,4 +1,5 @@ -import { useModels } from '@renderer/hooks/agents/useModels' +import { ApiModelLabel } from '@renderer/components/ApiModelLabel' +import { useApiModels } from '@renderer/hooks/agents/useModels' import { useUpdateAgent } from '@renderer/hooks/agents/useUpdateAgent' import { AgentEntity, UpdateAgentForm } from '@renderer/types' import { Input, Select } from 'antd' @@ -6,7 +7,7 @@ import { DefaultOptionType } from 'antd/es/select' import { FC, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { AgentLabel, ModelLabel, SettingsContainer, SettingsItem, SettingsTitle } from './shared' +import { AgentLabel, SettingsContainer, SettingsItem, SettingsTitle } from './shared' interface AgentEssentialSettingsProps { agent: AgentEntity | undefined | null @@ -16,7 +17,7 @@ interface AgentEssentialSettingsProps { const AgentEssentialSettings: FC = ({ agent, update }) => { const { t } = useTranslation() const [name, setName] = useState((agent?.name ?? '').trim()) - const { models } = useModels({ providerType: 'anthropic' }) + const { models } = useApiModels({ providerType: 'anthropic' }) const agentModel = models.find((model) => model.id === agent?.model) const [model, setModel] = useState(agentModel?.id) @@ -29,7 +30,7 @@ const AgentEssentialSettings: FC = ({ agent, update const modelOptions = useMemo(() => { return models.map((model) => ({ value: model.id, - label: + label: })) satisfies DefaultOptionType[] }, [models]) diff --git a/src/renderer/src/pages/settings/AgentSettings/shared.tsx b/src/renderer/src/pages/settings/AgentSettings/shared.tsx index 4e6d36b3af..1d2c9c5811 100644 --- a/src/renderer/src/pages/settings/AgentSettings/shared.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/shared.tsx @@ -1,8 +1,7 @@ import { Avatar, AvatarProps, cn } from '@heroui/react' import { getAgentAvatar } from '@renderer/config/agent' -import { getModelLogo } from '@renderer/config/models' import { getAgentTypeLabel } from '@renderer/i18n/label' -import { AgentType, ApiModel } from '@renderer/types' +import { AgentType } from '@renderer/types' import React from 'react' import { SettingDivider } from '..' @@ -64,18 +63,3 @@ export const SettingsContainer: React.FC> = ( ) } - -export interface ModelLabelProps extends Omit, 'children'> { - model: ApiModel -} - -export const ModelLabel: React.FC = ({ model, className, ...props }) => { - return ( -
- - - {model.name} | {model.provider_name} - -
- ) -} From 3816076464f25f0adf70386d863c3cf5345d5853 Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 00:06:18 +0800 Subject: [PATCH 07/21] refactor(i18n): reorganize translation keys and add new entries - Move 'label' from agent type to root type section - Add new prompt settings and type-related translations - Update Chinese translations for consistency --- src/renderer/src/i18n/locales/en-us.json | 8 ++++++-- src/renderer/src/i18n/locales/zh-cn.json | 16 ++++++++++------ src/renderer/src/i18n/locales/zh-tw.json | 8 ++++++-- src/renderer/src/i18n/translate/el-gr.json | 8 ++++++-- src/renderer/src/i18n/translate/es-es.json | 8 ++++++-- src/renderer/src/i18n/translate/fr-fr.json | 8 ++++++-- src/renderer/src/i18n/translate/ja-jp.json | 8 ++++++-- src/renderer/src/i18n/translate/pt-pt.json | 8 ++++++-- src/renderer/src/i18n/translate/ru-ru.json | 8 ++++++-- 9 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index af2cdccc39..1eea1834a9 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -7,7 +7,6 @@ }, "title": "Add Agent", "type": { - "label": "Agent Type", "placeholder": "Select an agent type" } }, @@ -62,7 +61,12 @@ } }, "settings": { - "essential": "Essential Settings" + "essential": "Essential Settings", + "prompt": "Prompt Settings" + }, + "type": { + "label": "Agent Type", + "unknown": "Unknown Type" }, "update": { "error": { diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 51abaf7337..d36cbb3173 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -7,7 +7,6 @@ }, "title": "添加 Agent", "type": { - "label": "Agent 类型", "placeholder": "选择 Agent 类型" } }, @@ -62,11 +61,16 @@ } }, "settings": { - "essential": "基础设置" + "essential": "基础设置", + "prompt": "提示词设置" + }, + "type": { + "label": "智能体类型", + "unknown": "未知类型" }, "update": { "error": { - "failed": "更新 Agent 失败" + "failed": "更新智能体失败" } } }, @@ -817,10 +821,10 @@ "add": "添加", "add_success": "添加成功", "advanced_settings": "高级设置", - "agent_one": "Agent", - "agent_other": "Agents", + "agent_one": "智能体", + "agent_other": "智能体", "and": "和", - "assistant": "智能体", + "assistant": "助手", "assistant_one": "助手", "assistant_other": "助手", "avatar": "头像", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index faea8607d1..34ed3216a6 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -7,7 +7,6 @@ }, "title": "新增代理", "type": { - "label": "代理類型", "placeholder": "選擇 Agent 類型" } }, @@ -62,7 +61,12 @@ } }, "settings": { - "essential": "必要設定" + "essential": "必要設定", + "prompt": "[to be translated]:Prompt Settings" + }, + "type": { + "label": "[to be translated]:Agent Type", + "unknown": "[to be translated]:Unknown Type" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 9bb69da292..64b0e683ce 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -7,7 +7,6 @@ }, "title": "Προσθήκη Agent", "type": { - "label": "Τύπος πράκτορα", "placeholder": "Επιλέξτε τύπο Agent" } }, @@ -62,7 +61,12 @@ } }, "settings": { - "essential": "Βασικές Ρυθμίσεις" + "essential": "Βασικές Ρυθμίσεις", + "prompt": "[to be translated]:Prompt Settings" + }, + "type": { + "label": "[to be translated]:Agent Type", + "unknown": "[to be translated]:Unknown Type" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index a1004d1211..9f331dae58 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -7,7 +7,6 @@ }, "title": "Agregar Agente", "type": { - "label": "Tipo de agente", "placeholder": "Seleccionar tipo de Agente" } }, @@ -62,7 +61,12 @@ } }, "settings": { - "essential": "Configuraciones esenciales" + "essential": "Configuraciones esenciales", + "prompt": "[to be translated]:Prompt Settings" + }, + "type": { + "label": "[to be translated]:Agent Type", + "unknown": "[to be translated]:Unknown Type" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 068914b64b..b32da4140d 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -7,7 +7,6 @@ }, "title": "Ajouter un agent", "type": { - "label": "Type d'agent", "placeholder": "Sélectionner le type d'Agent" } }, @@ -62,7 +61,12 @@ } }, "settings": { - "essential": "Paramètres essentiels" + "essential": "Paramètres essentiels", + "prompt": "[to be translated]:Prompt Settings" + }, + "type": { + "label": "[to be translated]:Agent Type", + "unknown": "[to be translated]:Unknown Type" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 5f47bf3fad..cbc4e2251c 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -7,7 +7,6 @@ }, "title": "エージェントを追加", "type": { - "label": "エージェントタイプ", "placeholder": "エージェントタイプを選択" } }, @@ -62,7 +61,12 @@ } }, "settings": { - "essential": "必須設定" + "essential": "必須設定", + "prompt": "[to be translated]:Prompt Settings" + }, + "type": { + "label": "[to be translated]:Agent Type", + "unknown": "[to be translated]:Unknown Type" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 598eca1e4e..084e42beaa 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -7,7 +7,6 @@ }, "title": "Adicionar Agente", "type": { - "label": "Tipo de Agente", "placeholder": "Selecionar tipo de Agente" } }, @@ -62,7 +61,12 @@ } }, "settings": { - "essential": "Configurações Essenciais" + "essential": "Configurações Essenciais", + "prompt": "[to be translated]:Prompt Settings" + }, + "type": { + "label": "[to be translated]:Agent Type", + "unknown": "[to be translated]:Unknown Type" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index e2b3f37141..0a89f37c6e 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -7,7 +7,6 @@ }, "title": "Добавить агента", "type": { - "label": "Тип агента", "placeholder": "Выбор типа агента" } }, @@ -62,7 +61,12 @@ } }, "settings": { - "essential": "Основные настройки" + "essential": "Основные настройки", + "prompt": "[to be translated]:Prompt Settings" + }, + "type": { + "label": "[to be translated]:Agent Type", + "unknown": "[to be translated]:Unknown Type" }, "update": { "error": { From 00717126e5314033b595844a7996e927c2566df7 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 21 Sep 2025 16:07:26 +0000 Subject: [PATCH 08/21] fix(i18n): Auto update translations for PR #10096 --- src/renderer/src/i18n/locales/zh-tw.json | 6 +++--- src/renderer/src/i18n/translate/el-gr.json | 6 +++--- src/renderer/src/i18n/translate/es-es.json | 6 +++--- src/renderer/src/i18n/translate/fr-fr.json | 6 +++--- src/renderer/src/i18n/translate/ja-jp.json | 6 +++--- src/renderer/src/i18n/translate/pt-pt.json | 6 +++--- src/renderer/src/i18n/translate/ru-ru.json | 6 +++--- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 34ed3216a6..4851d61e60 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -62,11 +62,11 @@ }, "settings": { "essential": "必要設定", - "prompt": "[to be translated]:Prompt Settings" + "prompt": "提示設定" }, "type": { - "label": "[to be translated]:Agent Type", - "unknown": "[to be translated]:Unknown Type" + "label": "代理類型", + "unknown": "未知類型" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 64b0e683ce..5b02b04304 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -62,11 +62,11 @@ }, "settings": { "essential": "Βασικές Ρυθμίσεις", - "prompt": "[to be translated]:Prompt Settings" + "prompt": "Ρυθμίσεις Προτροπής" }, "type": { - "label": "[to be translated]:Agent Type", - "unknown": "[to be translated]:Unknown Type" + "label": "Τύπος Πράκτορα", + "unknown": "Άγνωστος Τύπος" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 9f331dae58..0fca9b6175 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -62,11 +62,11 @@ }, "settings": { "essential": "Configuraciones esenciales", - "prompt": "[to be translated]:Prompt Settings" + "prompt": "Configuración de indicaciones" }, "type": { - "label": "[to be translated]:Agent Type", - "unknown": "[to be translated]:Unknown Type" + "label": "Tipo de Agente", + "unknown": "Tipo desconocido" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index b32da4140d..56782ab944 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -62,11 +62,11 @@ }, "settings": { "essential": "Paramètres essentiels", - "prompt": "[to be translated]:Prompt Settings" + "prompt": "Paramètres de l'invite" }, "type": { - "label": "[to be translated]:Agent Type", - "unknown": "[to be translated]:Unknown Type" + "label": "Type d'agent", + "unknown": "Type inconnu" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index cbc4e2251c..268b4302f7 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -62,11 +62,11 @@ }, "settings": { "essential": "必須設定", - "prompt": "[to be translated]:Prompt Settings" + "prompt": "プロンプト設定" }, "type": { - "label": "[to be translated]:Agent Type", - "unknown": "[to be translated]:Unknown Type" + "label": "エージェントタイプ", + "unknown": "不明なタイプ" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 084e42beaa..562e07d6aa 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -62,11 +62,11 @@ }, "settings": { "essential": "Configurações Essenciais", - "prompt": "[to be translated]:Prompt Settings" + "prompt": "Configurações de Prompt" }, "type": { - "label": "[to be translated]:Agent Type", - "unknown": "[to be translated]:Unknown Type" + "label": "Tipo de Agente", + "unknown": "Tipo Desconhecido" }, "update": { "error": { diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index 0a89f37c6e..cf6f22847c 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -62,11 +62,11 @@ }, "settings": { "essential": "Основные настройки", - "prompt": "[to be translated]:Prompt Settings" + "prompt": "Настройки подсказки" }, "type": { - "label": "[to be translated]:Agent Type", - "unknown": "[to be translated]:Unknown Type" + "label": "Тип агента", + "unknown": "Неизвестный тип" }, "update": { "error": { From 18330929986a81445839022584f84e039e15e0c9 Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 00:16:19 +0800 Subject: [PATCH 09/21] fix(useAgent): handle fake agent id to prevent unnecessary API calls --- src/renderer/src/hooks/agents/useAgent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/hooks/agents/useAgent.ts b/src/renderer/src/hooks/agents/useAgent.ts index 8ca0c865b5..62d2393618 100644 --- a/src/renderer/src/hooks/agents/useAgent.ts +++ b/src/renderer/src/hooks/agents/useAgent.ts @@ -7,7 +7,7 @@ export const useAgent = (id: string | null) => { const client = useAgentClient() const key = id ? client.agentPaths.withId(id) : null const fetcher = useCallback(async () => { - if (!id) { + if (!id || id === 'fake') { return null } const result = await client.getAgent(id) From b24667625724899d883d882c5338c8608d4f1c7d Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 00:19:25 +0800 Subject: [PATCH 10/21] chore(AgentEssentialSettings): add todo comment for future enhancements --- .../src/pages/settings/AgentSettings/AgentEssentialSettings.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx index b278da6dca..2836a18a82 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx @@ -69,6 +69,7 @@ const AgentEssentialSettings: FC = ({ agent, update placeholder={t('common.placeholders.select.model')} />
+ {/* TODO: Add accessible_paths and description */} ) } From 6aaef9b7beaa5986bc306ada94d282d3d296f52a Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 00:21:45 +0800 Subject: [PATCH 11/21] docs: remove completed TODO comment from Sessions component --- src/renderer/src/pages/home/Tabs/components/Sessions.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/src/pages/home/Tabs/components/Sessions.tsx b/src/renderer/src/pages/home/Tabs/components/Sessions.tsx index 73ceff87f0..2964e761e6 100644 --- a/src/renderer/src/pages/home/Tabs/components/Sessions.tsx +++ b/src/renderer/src/pages/home/Tabs/components/Sessions.tsx @@ -60,7 +60,6 @@ const Sessions: React.FC = ({ agentId }) => { animate={{ opacity: 1 }} transition={{ duration: 0.3 }} className="agents-tab h-full w-full p-2"> - {/* TODO: Add session button */} Date: Mon, 22 Sep 2025 00:34:29 +0800 Subject: [PATCH 12/21] refactor(agents): remove unused agent entities and related code Clean up agents store by removing deprecated AgentEntity-related code that is no longer used. This simplifies the store structure as we're moving away from redux. --- src/renderer/src/store/agents.ts | 41 ++++---------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/src/renderer/src/store/agents.ts b/src/renderer/src/store/agents.ts index a869374301..261da0df7f 100644 --- a/src/renderer/src/store/agents.ts +++ b/src/renderer/src/store/agents.ts @@ -1,24 +1,17 @@ -import { loggerService } from '@logger' import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { DEFAULT_CONTEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' -import { AgentEntity, AssistantPreset, AssistantSettings } from '@renderer/types' -import { cloneDeep, mergeWith } from 'lodash' +import { AssistantPreset, AssistantSettings } from '@renderer/types' -const logger = loggerService.withContext('Agents') +// const logger = loggerService.withContext('Agents') export interface AgentsState { /** They are actually assistant presets. * They should not be in this slice. However, since redux will be removed * in the future, I just don't care where should they are. */ agents: AssistantPreset[] - /** For new autonomous agent feature. They are actual agent entities. - * They won't be used anymore when sqlite api is ready. - */ - agentsNew: AgentEntity[] } const initialState: AgentsState = { - agents: [], - agentsNew: [] + agents: [] } const assistantsSlice = createSlice({ @@ -58,28 +51,6 @@ const assistantsSlice = createSlice({ } } } - }, - setAgents: (state, action: PayloadAction) => { - state.agentsNew = action.payload - }, - addAgent: (state, action: PayloadAction) => { - state.agentsNew.push(action.payload) - }, - removeAgent: (state, action: PayloadAction<{ id: string }>) => { - state.agentsNew = state.agentsNew.filter((agent) => agent.id !== action.payload.id) - }, - updateAgent: (state, action: PayloadAction & { id: string }>) => { - const { id, ...update } = action.payload - const agent = state.agentsNew.find((agent) => agent.id === id) - if (agent) { - mergeWith(agent, update, (_, srcVal) => { - // cut reference - if (Array.isArray(srcVal)) return cloneDeep(srcVal) - else return undefined - }) - } else { - logger.warn('Agent not found when trying to update') - } } } }) @@ -89,11 +60,7 @@ export const { addAssistantPreset, removeAssistantPreset, updateAssistantPreset, - updateAssistantPresetSettings, - setAgents, - addAgent, - removeAgent, - updateAgent + updateAssistantPresetSettings } = assistantsSlice.actions export default assistantsSlice.reducer From d7960140dc3736de61e49fe4e29fd67652e5c31b Mon Sep 17 00:00:00 2001 From: Vaayne Date: Mon, 22 Sep 2025 09:55:26 +0800 Subject: [PATCH 13/21] feat(claudecode): add allowedTools to session configuration in invoke method --- src/main/services/agents/services/claudecode/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/services/agents/services/claudecode/index.ts b/src/main/services/agents/services/claudecode/index.ts index 32509acb61..99000d9b33 100644 --- a/src/main/services/agents/services/claudecode/index.ts +++ b/src/main/services/agents/services/claudecode/index.ts @@ -86,7 +86,8 @@ class ClaudeCodeService implements AgentServiceInterface { }, appendSystemPrompt: session.instructions, permissionMode: session.configuration?.permission_mode, - maxTurns: session.configuration?.max_turns + maxTurns: session.configuration?.max_turns, + allowedTools: session.allowed_tools } if (session.accessible_paths.length > 1) { From bd6428d473cee31aff918a1467ad375567f06159 Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 11:22:57 +0800 Subject: [PATCH 14/21] refactor(AgentSettings): extract modal content into separate component for better readability --- .../pages/settings/AgentSettings/index.tsx | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/renderer/src/pages/settings/AgentSettings/index.tsx b/src/renderer/src/pages/settings/AgentSettings/index.tsx index e1801d3954..05413115a7 100644 --- a/src/renderer/src/pages/settings/AgentSettings/index.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/index.tsx @@ -1,3 +1,4 @@ +import { Spinner } from '@heroui/react' import { HStack } from '@renderer/components/Layout' import { TopView } from '@renderer/components/TopView' import { useAgent } from '@renderer/hooks/agents/useAgent' @@ -55,6 +56,28 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag ] satisfies { key: AgentSettingPopupTab; label: string }[] ).filter(Boolean) as { key: string; label: string }[] + const ModalContent = () => { + if (!agent) { + return + } + return ( + + + setMenu(key as AgentSettingPopupTab)} + /> + + + {menu === 'essential' && } + {menu === 'prompt' && } + + + ) + } + return ( = ({ tab, ag width="min(800px, 70vw)" height="80vh" centered> - - - setMenu(key as AgentSettingPopupTab)} - /> - - - {menu === 'essential' && } - {menu === 'prompt' && } - - + ) } From 8cd40a471e6eb8f08819971816852b1762dcf11a Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 12:14:45 +0800 Subject: [PATCH 15/21] refactor(agent): replace agent type label map with switch statement Simplify agent type label handling by replacing the map with a direct switch statement. Also update related type references and array type assertions for consistency. --- src/renderer/src/config/agent.ts | 4 ++-- src/renderer/src/i18n/label.ts | 11 ++++++----- .../src/pages/settings/AgentSettings/index.tsx | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/renderer/src/config/agent.ts b/src/renderer/src/config/agent.ts index ec0b4a11da..af5435d718 100644 --- a/src/renderer/src/config/agent.ts +++ b/src/renderer/src/config/agent.ts @@ -1,5 +1,5 @@ import ClaudeAvatar from '@renderer/assets/images/models/claude.png' -import { AgentBase, AgentEntity } from '@renderer/types' +import { AgentBase, AgentType } from '@renderer/types' // base agent config. no default config for now. const DEFAULT_AGENT_CONFIG: Omit = { @@ -11,7 +11,7 @@ export const DEFAULT_CLAUDE_CODE_CONFIG: Omit = { ...DEFAULT_AGENT_CONFIG } as const -export const getAgentAvatar = (type: AgentEntity['type']): string => { +export const getAgentAvatar = (type: AgentType): string => { switch (type) { case 'claude-code': return ClaudeAvatar diff --git a/src/renderer/src/i18n/label.ts b/src/renderer/src/i18n/label.ts index db29d3f5e1..c0cc436de8 100644 --- a/src/renderer/src/i18n/label.ts +++ b/src/renderer/src/i18n/label.ts @@ -346,10 +346,11 @@ export const getBuiltinOcrProviderLabel = (key: BuiltinOcrProviderId) => { else return getLabel(builtinOcrProviderKeyMap, key) } -const agentTypeKeyMap = { - 'claude-code': 'Claude Code' -} as const satisfies Record - export const getAgentTypeLabel = (key: AgentType) => { - return getLabel(agentTypeKeyMap, key, t('agent.type.unknown')) + switch (key) { + case 'claude-code': + return 'Claude Code' + default: + return 'Unknown Type' + } } diff --git a/src/renderer/src/pages/settings/AgentSettings/index.tsx b/src/renderer/src/pages/settings/AgentSettings/index.tsx index 05413115a7..0de7c1996b 100644 --- a/src/renderer/src/pages/settings/AgentSettings/index.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/index.tsx @@ -53,8 +53,8 @@ const AgentSettingPopupContainer: React.FC = ({ tab, ag key: 'prompt', label: t('agent.settings.prompt') } - ] satisfies { key: AgentSettingPopupTab; label: string }[] - ).filter(Boolean) as { key: string; label: string }[] + ] as const satisfies { key: AgentSettingPopupTab; label: string }[] + ).filter(Boolean) const ModalContent = () => { if (!agent) { From 82c08128b6ef6d8ba5c8bb7f0c3d3d20aef92c66 Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 12:28:03 +0800 Subject: [PATCH 16/21] refactor(useModels): merge default filter with provided filter Use lodash merge to combine provided filter with default values --- src/renderer/src/hooks/agents/useModels.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/hooks/agents/useModels.ts b/src/renderer/src/hooks/agents/useModels.ts index f05cb777b9..e35d58dde1 100644 --- a/src/renderer/src/hooks/agents/useModels.ts +++ b/src/renderer/src/hooks/agents/useModels.ts @@ -1,4 +1,5 @@ import { ApiModelsFilter } from '@renderer/types' +import { merge } from 'lodash' import { useCallback } from 'react' import useSWR from 'swr' @@ -6,10 +7,13 @@ import { useAgentClient } from './useAgentClient' export const useApiModels = (filter?: ApiModelsFilter) => { const client = useAgentClient() - const path = client.getModelsPath(filter) + // const defaultFilter = { limit: -1 } satisfies ApiModelsFilter + const defaultFilter = {} satisfies ApiModelsFilter + const finalFilter = merge(filter, defaultFilter) + const path = client.getModelsPath(finalFilter) const fetcher = useCallback(() => { - return client.getModels(filter) - }, [client, filter]) + return client.getModels(finalFilter) + }, [client, finalFilter]) const { data, error, isLoading } = useSWR(path, fetcher) return { models: data?.data ?? [], From 4484f39525e17a2678afcbbf39f9f255d49e4f99 Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 13:56:09 +0800 Subject: [PATCH 17/21] 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 From 381397ed316a18784caa9f9fe6de9772e34f098a Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 13:59:05 +0800 Subject: [PATCH 18/21] style(AgentSettings): remove overflow-y scroll and adjust overflow settings Update overflow behavior in settings components to use auto instead of scroll and add right padding to prevent content clipping --- src/renderer/src/pages/settings/AgentSettings/index.tsx | 1 - src/renderer/src/pages/settings/AgentSettings/shared.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/renderer/src/pages/settings/AgentSettings/index.tsx b/src/renderer/src/pages/settings/AgentSettings/index.tsx index d6ff4e1a90..525eb94675 100644 --- a/src/renderer/src/pages/settings/AgentSettings/index.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/index.tsx @@ -135,7 +135,6 @@ const Settings = styled.div` flex-direction: column; flex: 1; padding: 16px 16px; - overflow-y: scroll; ` const StyledModal = styled(Modal)` diff --git a/src/renderer/src/pages/settings/AgentSettings/shared.tsx b/src/renderer/src/pages/settings/AgentSettings/shared.tsx index 46bd122dcf..3a311eabe2 100644 --- a/src/renderer/src/pages/settings/AgentSettings/shared.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/shared.tsx @@ -67,7 +67,7 @@ export const SettingsItem: React.FC = ({ export const SettingsContainer: React.FC> = ({ children, className, ...props }) => { return ( -
+
{children}
) From d682045655fc26833107153f675ee786f58ae357 Mon Sep 17 00:00:00 2001 From: icarus Date: Mon, 22 Sep 2025 14:02:44 +0800 Subject: [PATCH 19/21] refactor(AgentSettings): remove unused agentModel variable and TODO comment --- .../src/pages/settings/AgentSettings/AgentEssentialSettings.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx index fc75a04994..6506be9cbd 100644 --- a/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx +++ b/src/renderer/src/pages/settings/AgentSettings/AgentEssentialSettings.tsx @@ -23,7 +23,6 @@ const AgentEssentialSettings: FC = ({ agent, update const { t } = useTranslation() const [name, setName] = useState((agent?.name ?? '').trim()) const { models } = useApiModels({ providerType: 'anthropic' }) - const agentModel = models.find((model) => model.id === agent?.model) const updateName = (name: string) => { if (!agent) return @@ -118,7 +117,6 @@ const AgentEssentialSettings: FC = ({ agent, update placeholder={t('common.placeholders.select.model')} /> - {/* TODO: Add accessible_paths and description */} Date: Mon, 22 Sep 2025 14:04:37 +0800 Subject: [PATCH 20/21] feat(i18n): add error messages for agent operations Add error messages for agent get/list operations and restructure accessible paths validation messages --- src/renderer/src/i18n/locales/en-us.json | 14 +++++++++++++- src/renderer/src/i18n/locales/zh-cn.json | 14 +++++++++++++- src/renderer/src/i18n/locales/zh-tw.json | 14 +++++++++++++- src/renderer/src/i18n/translate/el-gr.json | 14 +++++++++++++- src/renderer/src/i18n/translate/es-es.json | 14 +++++++++++++- src/renderer/src/i18n/translate/fr-fr.json | 14 +++++++++++++- src/renderer/src/i18n/translate/ja-jp.json | 14 +++++++++++++- src/renderer/src/i18n/translate/pt-pt.json | 14 +++++++++++++- src/renderer/src/i18n/translate/ru-ru.json | 14 +++++++++++++- 9 files changed, 117 insertions(+), 9 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 1eea1834a9..8af40a56e4 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -20,13 +20,25 @@ "edit": { "title": "Edit Agent" }, + "get": { + "error": { + "failed": "Failed to get the agent." + } + }, + "list": { + "error": { + "failed": "Failed to list agents." + } + }, "session": { "accessible_paths": { "add": "Add directory", "duplicate": "This directory is already included.", "empty": "Select at least one directory that the agent can access.", + "error": { + "at_least_one": "Please select at least one accessible directory." + }, "label": "Accessible directories", - "required": "Please select at least one accessible directory.", "select_failed": "Failed to select directory." }, "add": { diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index d36cbb3173..592a31612a 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -20,13 +20,25 @@ "edit": { "title": "编辑 Agent" }, + "get": { + "error": { + "failed": "获取智能体失败" + } + }, + "list": { + "error": { + "failed": "获取智能体列表失败" + } + }, "session": { "accessible_paths": { "add": "添加目录", "duplicate": "该目录已添加。", "empty": "请选择至少一个智能体可访问的目录。", + "error": { + "at_least_one": "请至少选择一个可访问的目录" + }, "label": "工作目录", - "required": "请至少选择一个可访问的目录。", "select_failed": "选择目录失败" }, "add": { diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 4851d61e60..f4993bc54d 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -20,13 +20,25 @@ "edit": { "title": "編輯 Agent" }, + "get": { + "error": { + "failed": "[to be translated]:Failed to get the agent." + } + }, + "list": { + "error": { + "failed": "[to be translated]:Failed to list agents." + } + }, "session": { "accessible_paths": { "add": "新增目錄", "duplicate": "此目錄已包含在內。", "empty": "選擇至少一個代理可以存取的目錄。", + "error": { + "at_least_one": "[to be translated]:Please select at least one accessible directory." + }, "label": "可存取的目錄", - "required": "請至少選擇一個可存取的目錄。", "select_failed": "無法選擇目錄。" }, "add": { diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 5b02b04304..21a4bd88ff 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -20,13 +20,25 @@ "edit": { "title": "Επεξεργαστής Agent" }, + "get": { + "error": { + "failed": "[to be translated]:Failed to get the agent." + } + }, + "list": { + "error": { + "failed": "[to be translated]:Failed to list agents." + } + }, "session": { "accessible_paths": { "add": "Προσθήκη καταλόγου", "duplicate": "Αυτός ο κατάλογος έχει ήδη συμπεριληφθεί.", "empty": "Επιλέξτε τουλάχιστον έναν κατάλογο στον οποίο ο πράκτορας μπορεί να έχει πρόσβαση.", + "error": { + "at_least_one": "[to be translated]:Please select at least one accessible directory." + }, "label": "Προσβάσιμοι κατάλογοι", - "required": "Παρακαλώ επιλέξτε τουλάχιστον έναν προσβάσιμο κατάλογο.", "select_failed": "Αποτυχία επιλογής καταλόγου." }, "add": { diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 0fca9b6175..5d7a6a2c75 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -20,13 +20,25 @@ "edit": { "title": "Agent de edición" }, + "get": { + "error": { + "failed": "[to be translated]:Failed to get the agent." + } + }, + "list": { + "error": { + "failed": "[to be translated]:Failed to list agents." + } + }, "session": { "accessible_paths": { "add": "Agregar directorio", "duplicate": "Este directorio ya está incluido.", "empty": "Selecciona al menos un directorio al que el agente pueda acceder.", + "error": { + "at_least_one": "[to be translated]:Please select at least one accessible directory." + }, "label": "Directorios accesibles", - "required": "Por favor, seleccione al menos un directorio accesible.", "select_failed": "Error al seleccionar el directorio." }, "add": { diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 56782ab944..c3adbedc2c 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -20,13 +20,25 @@ "edit": { "title": "Éditer Agent" }, + "get": { + "error": { + "failed": "[to be translated]:Failed to get the agent." + } + }, + "list": { + "error": { + "failed": "[to be translated]:Failed to list agents." + } + }, "session": { "accessible_paths": { "add": "Ajouter un répertoire", "duplicate": "Ce répertoire est déjà inclus.", "empty": "Sélectionnez au moins un répertoire auquel l'agent peut accéder.", + "error": { + "at_least_one": "[to be translated]:Please select at least one accessible directory." + }, "label": "Répertoires accessibles", - "required": "Veuillez sélectionner au moins un répertoire accessible.", "select_failed": "Échec de la sélection du répertoire." }, "add": { diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 268b4302f7..6a561008f5 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -20,13 +20,25 @@ "edit": { "title": "編集エージェント" }, + "get": { + "error": { + "failed": "[to be translated]:Failed to get the agent." + } + }, + "list": { + "error": { + "failed": "[to be translated]:Failed to list agents." + } + }, "session": { "accessible_paths": { "add": "ディレクトリを追加", "duplicate": "このディレクトリは既に含まれています。", "empty": "エージェントがアクセスできるディレクトリを少なくとも1つ選択してください。", + "error": { + "at_least_one": "[to be translated]:Please select at least one accessible directory." + }, "label": "アクセス可能なディレクトリ", - "required": "アクセス可能なディレクトリを少なくとも1つ選択してください。", "select_failed": "ディレクトリの選択に失敗しました。" }, "add": { diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 562e07d6aa..c4ce25087b 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -20,13 +20,25 @@ "edit": { "title": "Agent Editor" }, + "get": { + "error": { + "failed": "[to be translated]:Failed to get the agent." + } + }, + "list": { + "error": { + "failed": "[to be translated]:Failed to list agents." + } + }, "session": { "accessible_paths": { "add": "Adicionar diretório", "duplicate": "Este diretório já está incluído.", "empty": "Selecione pelo menos um diretório ao qual o agente possa acessar.", + "error": { + "at_least_one": "[to be translated]:Please select at least one accessible directory." + }, "label": "Diretórios acessíveis", - "required": "Por favor, selecione pelo menos um diretório acessível.", "select_failed": "Falha ao selecionar o diretório." }, "add": { diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index cf6f22847c..81cdb590ac 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -20,13 +20,25 @@ "edit": { "title": "Редактировать агент" }, + "get": { + "error": { + "failed": "[to be translated]:Failed to get the agent." + } + }, + "list": { + "error": { + "failed": "[to be translated]:Failed to list agents." + } + }, "session": { "accessible_paths": { "add": "Добавить каталог", "duplicate": "Этот каталог уже включён.", "empty": "Выберите хотя бы один каталог, к которому агент имеет доступ.", + "error": { + "at_least_one": "[to be translated]:Please select at least one accessible directory." + }, "label": "Доступные директории", - "required": "Пожалуйста, выберите хотя бы один доступный каталог.", "select_failed": "Не удалось выбрать каталог." }, "add": { From 8d3dbcb5f858728cce39a074076fb02ba9e93c72 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 22 Sep 2025 06:08:24 +0000 Subject: [PATCH 21/21] fix(i18n): Auto update translations for PR #10096 --- src/renderer/src/i18n/locales/zh-tw.json | 6 +++--- src/renderer/src/i18n/translate/el-gr.json | 4 ++-- src/renderer/src/i18n/translate/es-es.json | 6 +++--- src/renderer/src/i18n/translate/fr-fr.json | 4 ++-- src/renderer/src/i18n/translate/ja-jp.json | 6 +++--- src/renderer/src/i18n/translate/pt-pt.json | 4 ++-- src/renderer/src/i18n/translate/ru-ru.json | 6 +++--- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index f4993bc54d..3e2670ac45 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -22,12 +22,12 @@ }, "get": { "error": { - "failed": "[to be translated]:Failed to get the agent." + "failed": "無法取得代理程式。" } }, "list": { "error": { - "failed": "[to be translated]:Failed to list agents." + "failed": "無法列出代理程式。" } }, "session": { @@ -36,7 +36,7 @@ "duplicate": "此目錄已包含在內。", "empty": "選擇至少一個代理可以存取的目錄。", "error": { - "at_least_one": "[to be translated]:Please select at least one accessible directory." + "at_least_one": "請至少選取一個可存取的目錄。" }, "label": "可存取的目錄", "select_failed": "無法選擇目錄。" diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 21a4bd88ff..d4f1db430f 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -27,7 +27,7 @@ }, "list": { "error": { - "failed": "[to be translated]:Failed to list agents." + "failed": "Αποτυχία καταχώρησης πρακτόρων." } }, "session": { @@ -36,7 +36,7 @@ "duplicate": "Αυτός ο κατάλογος έχει ήδη συμπεριληφθεί.", "empty": "Επιλέξτε τουλάχιστον έναν κατάλογο στον οποίο ο πράκτορας μπορεί να έχει πρόσβαση.", "error": { - "at_least_one": "[to be translated]:Please select at least one accessible directory." + "at_least_one": "Παρακαλώ επιλέξτε τουλάχιστον έναν προσβάσιμο κατάλογο." }, "label": "Προσβάσιμοι κατάλογοι", "select_failed": "Αποτυχία επιλογής καταλόγου." diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 5d7a6a2c75..a0b3e0b911 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -22,12 +22,12 @@ }, "get": { "error": { - "failed": "[to be translated]:Failed to get the agent." + "failed": "No se pudo obtener el agente." } }, "list": { "error": { - "failed": "[to be translated]:Failed to list agents." + "failed": "Error al listar agentes." } }, "session": { @@ -36,7 +36,7 @@ "duplicate": "Este directorio ya está incluido.", "empty": "Selecciona al menos un directorio al que el agente pueda acceder.", "error": { - "at_least_one": "[to be translated]:Please select at least one accessible directory." + "at_least_one": "Por favor, seleccione al menos un directorio accesible." }, "label": "Directorios accesibles", "select_failed": "Error al seleccionar el directorio." diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index c3adbedc2c..5c6e146f52 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -22,7 +22,7 @@ }, "get": { "error": { - "failed": "[to be translated]:Failed to get the agent." + "failed": "Échec de l'obtention de l'agent." } }, "list": { @@ -36,7 +36,7 @@ "duplicate": "Ce répertoire est déjà inclus.", "empty": "Sélectionnez au moins un répertoire auquel l'agent peut accéder.", "error": { - "at_least_one": "[to be translated]:Please select at least one accessible directory." + "at_least_one": "Veuillez sélectionner au moins un répertoire accessible." }, "label": "Répertoires accessibles", "select_failed": "Échec de la sélection du répertoire." diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 6a561008f5..fc36f737c3 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -22,12 +22,12 @@ }, "get": { "error": { - "failed": "[to be translated]:Failed to get the agent." + "failed": "エージェントの取得に失敗しました。" } }, "list": { "error": { - "failed": "[to be translated]:Failed to list agents." + "failed": "エージェントの一覧取得に失敗しました。" } }, "session": { @@ -36,7 +36,7 @@ "duplicate": "このディレクトリは既に含まれています。", "empty": "エージェントがアクセスできるディレクトリを少なくとも1つ選択してください。", "error": { - "at_least_one": "[to be translated]:Please select at least one accessible directory." + "at_least_one": "アクセス可能なディレクトリを少なくとも1つ選択してください。" }, "label": "アクセス可能なディレクトリ", "select_failed": "ディレクトリの選択に失敗しました。" diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index c4ce25087b..b41060e582 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -22,7 +22,7 @@ }, "get": { "error": { - "failed": "[to be translated]:Failed to get the agent." + "failed": "Falha ao obter o agente." } }, "list": { @@ -36,7 +36,7 @@ "duplicate": "Este diretório já está incluído.", "empty": "Selecione pelo menos um diretório ao qual o agente possa acessar.", "error": { - "at_least_one": "[to be translated]:Please select at least one accessible directory." + "at_least_one": "Por favor, selecione pelo menos um diretório acessível." }, "label": "Diretórios acessíveis", "select_failed": "Falha ao selecionar o diretório." diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index 81cdb590ac..7c98010d35 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -22,12 +22,12 @@ }, "get": { "error": { - "failed": "[to be translated]:Failed to get the agent." + "failed": "Не удалось получить агента." } }, "list": { "error": { - "failed": "[to be translated]:Failed to list agents." + "failed": "Не удалось получить список агентов." } }, "session": { @@ -36,7 +36,7 @@ "duplicate": "Этот каталог уже включён.", "empty": "Выберите хотя бы один каталог, к которому агент имеет доступ.", "error": { - "at_least_one": "[to be translated]:Please select at least one accessible directory." + "at_least_one": "Пожалуйста, выберите хотя бы один доступный каталог." }, "label": "Доступные директории", "select_failed": "Не удалось выбрать каталог."