refactor(assistant-presets): rename agents to assistant presets and update related components

- Rename agents to assistant presets across the codebase
- Update components, hooks, and pages to reflect the new naming
- Add new components for managing assistant presets
- Improve localization and grouping of presets
- Maintain existing functionality while updating the UI
This commit is contained in:
icarus 2025-09-13 23:16:22 +08:00
parent 3e04c9493f
commit 751e391db6
17 changed files with 214 additions and 209 deletions

View File

@ -8,7 +8,7 @@ import { ErrorBoundary } from './components/ErrorBoundary'
import TabsContainer from './components/Tab/TabContainer'
import NavigationHandler from './handler/NavigationHandler'
import { useNavbarPosition } from './hooks/useSettings'
import AgentsPage from './pages/agents/AgentsPage'
import AssistantPresetsPage from './pages/assistantPresets/AssistantPresetsPage'
import CodeToolsPage from './pages/code/CodeToolsPage'
import FilesPage from './pages/files/FilesPage'
import HomePage from './pages/home/HomePage'
@ -29,7 +29,7 @@ const Router: FC = () => {
<ErrorBoundary>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/agents" element={<AgentsPage />} />
<Route path="/assistantPresets" element={<AssistantPresetsPage />} />
<Route path="/paintings/*" element={<PaintingsRoutePage />} />
<Route path="/translate" element={<TranslatePage />} />
<Route path="/files" element={<FilesPage />} />

View File

@ -1,8 +1,8 @@
import { TopView } from '@renderer/components/TopView'
import { useAgents } from '@renderer/hooks/useAgents'
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
import { useTimer } from '@renderer/hooks/useTimer'
import { useSystemAgents } from '@renderer/pages/agents'
import { useSystemAssistantPresets } from '@renderer/pages/assistantPresets'
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { Assistant, AssistantPreset } from '@renderer/types'
@ -25,25 +25,25 @@ interface Props {
const PopupContainer: React.FC<Props> = ({ resolve }) => {
const [open, setOpen] = useState(true)
const { t } = useTranslation()
const { agents: userAgents } = useAgents()
const { presets: userPresets } = useAssistantPresets()
const [searchText, setSearchText] = useState('')
const { defaultAssistant } = useDefaultAssistant()
const { assistants, addAssistant } = useAssistants()
const inputRef = useRef<InputRef>(null)
const systemAgents = useSystemAgents()
const systemPresets = useSystemAssistantPresets()
const loadingRef = useRef(false)
const [selectedIndex, setSelectedIndex] = useState(0)
const containerRef = useRef<HTMLDivElement>(null)
const { setTimeoutTimer } = useTimer()
const agents = useMemo(() => {
const allAgents = [...userAgents, ...systemAgents] as AssistantPreset[]
const list = [defaultAssistant, ...allAgents.filter((agent) => !assistants.map((a) => a.id).includes(agent.id))]
const presets = useMemo(() => {
const allPresets = [...userPresets, ...systemPresets] as AssistantPreset[]
const list = [defaultAssistant, ...allPresets.filter((preset) => !assistants.map((a) => a.id).includes(preset.id))]
const filtered = searchText
? list.filter(
(agent) =>
agent.name.toLowerCase().includes(searchText.trim().toLocaleLowerCase()) ||
agent.description?.toLowerCase().includes(searchText.trim().toLocaleLowerCase())
(preset) =>
preset.name.toLowerCase().includes(searchText.trim().toLocaleLowerCase()) ||
preset.description?.toLowerCase().includes(searchText.trim().toLocaleLowerCase())
)
: list
@ -59,15 +59,15 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
return [newAgent, ...filtered]
}
return filtered
}, [assistants, defaultAssistant, searchText, systemAgents, userAgents])
}, [assistants, defaultAssistant, searchText, systemPresets, userPresets])
// 重置选中索引当搜索或列表内容变更时
useEffect(() => {
setSelectedIndex(0)
}, [agents.length, searchText])
}, [presets.length, searchText])
const onCreateAssistant = useCallback(
async (agent: AssistantPreset) => {
async (preset: AssistantPreset) => {
if (loadingRef.current) {
return
}
@ -75,11 +75,11 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
loadingRef.current = true
let assistant: Assistant
if (agent.id === 'default') {
assistant = { ...agent, id: uuid() }
if (preset.id === 'default') {
assistant = { ...preset, id: uuid() }
addAssistant(assistant)
} else {
assistant = await createAssistantFromAgent(agent)
assistant = await createAssistantFromAgent(preset)
}
setTimeoutTimer('onCreateAssistant', () => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0)
@ -93,28 +93,28 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
if (!open) return
const handleKeyDown = (e: KeyboardEvent) => {
const displayedAgents = take(agents, 100)
const displayedPresets = take(presets, 100)
switch (e.key) {
case 'ArrowDown':
e.preventDefault()
setSelectedIndex((prev) => (prev >= displayedAgents.length - 1 ? 0 : prev + 1))
setSelectedIndex((prev) => (prev >= displayedPresets.length - 1 ? 0 : prev + 1))
break
case 'ArrowUp':
e.preventDefault()
setSelectedIndex((prev) => (prev <= 0 ? displayedAgents.length - 1 : prev - 1))
setSelectedIndex((prev) => (prev <= 0 ? displayedPresets.length - 1 : prev - 1))
break
case 'Enter':
case 'NumpadEnter':
// 如果焦点在输入框且有搜索内容,则默认选择第一项
if (document.activeElement === inputRef.current?.input && searchText.trim()) {
e.preventDefault()
onCreateAssistant(displayedAgents[selectedIndex])
onCreateAssistant(displayedPresets[selectedIndex])
}
// 否则选择当前选中项
else if (selectedIndex >= 0 && selectedIndex < displayedAgents.length) {
else if (selectedIndex >= 0 && selectedIndex < displayedPresets.length) {
e.preventDefault()
onCreateAssistant(displayedAgents[selectedIndex])
onCreateAssistant(displayedPresets[selectedIndex])
}
break
}
@ -122,14 +122,14 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [open, selectedIndex, agents, searchText, onCreateAssistant])
}, [open, selectedIndex, presets, searchText, onCreateAssistant])
// 确保选中项在可视区域
useEffect(() => {
if (containerRef.current) {
const agentItems = containerRef.current.querySelectorAll('.agent-item')
if (agentItems[selectedIndex]) {
agentItems[selectedIndex].scrollIntoView({
const presetItems = containerRef.current.querySelectorAll('.agent-item')
if (presetItems[selectedIndex]) {
presetItems[selectedIndex].scrollIntoView({
behavior: 'smooth',
block: 'nearest'
})
@ -193,19 +193,19 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
</HStack>
<Divider style={{ margin: 0, marginTop: 4, borderBlockStartWidth: 0.5 }} />
<Container ref={containerRef}>
{take(agents, 100).map((agent, index) => (
{take(presets, 100).map((preset, index) => (
<AgentItem
key={agent.id}
onClick={() => onCreateAssistant(agent)}
className={`agent-item ${agent.id === 'default' ? 'default' : ''} ${index === selectedIndex ? 'keyboard-selected' : ''}`}
key={preset.id}
onClick={() => onCreateAssistant(preset)}
className={`agent-item ${preset.id === 'default' ? 'default' : ''} ${index === selectedIndex ? 'keyboard-selected' : ''}`}
onMouseEnter={() => setSelectedIndex(index)}>
<HStack alignItems="center" gap={5} style={{ overflow: 'hidden', maxWidth: '100%' }}>
<EmojiIcon emoji={agent.emoji || ''} />
<span className="text-nowrap">{agent.name}</span>
<EmojiIcon emoji={preset.emoji || ''} />
<span className="text-nowrap">{preset.name}</span>
</HStack>
{agent.id === 'default' && <Tag color="green">{t('agents.tag.system')}</Tag>}
{agent.type === 'agent' && <Tag color="orange">{t('agents.tag.agent')}</Tag>}
{agent.id === 'new' && <Tag color="green">{t('agents.tag.new')}</Tag>}
{preset.id === 'default' && <Tag color="green">{t('agents.tag.system')}</Tag>}
{preset.type === 'agent' && <Tag color="orange">{t('agents.tag.agent')}</Tag>}
{preset.id === 'new' && <Tag color="green">{t('agents.tag.new')}</Tag>}
</AgentItem>
))}
</Container>

View File

@ -1,34 +0,0 @@
import { useAppDispatch, useAppSelector } from '@renderer/store'
import {
addAssistantPreset,
removeAssistantPreset,
setAssistantPresets,
updateAssistantPreset,
updateAssistantPresetSettings
} from '@renderer/store/agents'
import { AssistantPreset, AssistantSettings } from '@renderer/types'
export function useAgents() {
const agents = useAppSelector((state) => state.agents.agents)
const dispatch = useAppDispatch()
return {
agents,
setAgents: (agents: AssistantPreset[]) => dispatch(setAssistantPresets(agents)),
addAgent: (agent: AssistantPreset) => dispatch(addAssistantPreset(agent)),
removeAgent: (id: string) => dispatch(removeAssistantPreset({ id }))
}
}
export function useAgent(id: string) {
const agent = useAppSelector((state) => state.agents.agents.find((a) => a.id === id) as AssistantPreset)
const dispatch = useAppDispatch()
return {
agent,
updateAgent: (agent: AssistantPreset) => dispatch(updateAssistantPreset(agent)),
updateAgentSettings: (settings: Partial<AssistantSettings>) => {
dispatch(updateAssistantPresetSettings({ assistantId: agent.id, settings }))
}
}
}

View File

@ -0,0 +1,35 @@
import { useAppDispatch, useAppSelector } from '@renderer/store'
import {
addAssistantPreset,
removeAssistantPreset,
setAssistantPresets,
updateAssistantPreset,
updateAssistantPresetSettings
} from '@renderer/store/agents'
import { AssistantPreset, AssistantSettings } from '@renderer/types'
export function useAssistantPresets() {
const presets = useAppSelector((state) => state.agents.agents)
const dispatch = useAppDispatch()
return {
presets,
setAssistantPresets: (presets: AssistantPreset[]) => dispatch(setAssistantPresets(presets)),
addAssistantPreset: (preset: AssistantPreset) => dispatch(addAssistantPreset(preset)),
removeAssistantPreset: (id: string) => dispatch(removeAssistantPreset({ id }))
}
}
export function useAssistantPreset(id: string) {
// FIXME: undefined is not handled
const preset = useAppSelector((state) => state.agents.agents.find((a) => a.id === id) as AssistantPreset)
const dispatch = useAppDispatch()
return {
preset,
updateAssistantPreset: (preset: AssistantPreset) => dispatch(updateAssistantPreset(preset)),
updateAssistantPresetSettings: (settings: Partial<AssistantSettings>) => {
dispatch(updateAssistantPresetSettings({ assistantId: preset.id, settings }))
}
}
}

View File

@ -33,8 +33,8 @@ import { cloneDeep } from 'lodash'
import { useCallback, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useAgents } from './useAgents'
import { useAssistants } from './useAssistant'
import { useAssistantPresets } from './useAssistantPresets'
import { useTimer } from './useTimer'
export const useKnowledge = (baseId: string) => {
@ -352,7 +352,7 @@ export const useKnowledgeBases = () => {
const dispatch = useDispatch()
const bases = useSelector((state: RootState) => state.knowledge.bases)
const { assistants, updateAssistants } = useAssistants()
const { agents, setAgents } = useAgents()
const { presets, setAssistantPresets } = useAssistantPresets()
const addKnowledgeBase = (base: KnowledgeBase) => {
dispatch(addBase(base))
@ -379,7 +379,7 @@ export const useKnowledgeBases = () => {
})
// remove agent knowledge_base
const _agents = agents.map((agent) => {
const _presets = presets.map((agent) => {
if (agent.knowledge_bases?.find((kb) => kb.id === baseId)) {
return {
...agent,
@ -390,7 +390,7 @@ export const useKnowledgeBases = () => {
})
updateAssistants(_assistants)
setAgents(_agents)
setAssistantPresets(_presets)
}
const updateKnowledgeBases = (bases: KnowledgeBase[]) => {

View File

@ -4,7 +4,7 @@ import { HStack } from '@renderer/components/Layout'
import ListItem from '@renderer/components/ListItem'
import Scrollbar from '@renderer/components/Scrollbar'
import CustomTag from '@renderer/components/Tags/CustomTag'
import { useAgents } from '@renderer/hooks/useAgents'
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
import { useNavbarPosition } from '@renderer/hooks/useSettings'
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
import { AssistantPreset } from '@renderer/types'
@ -17,65 +17,65 @@ import { useTranslation } from 'react-i18next'
import ReactMarkdown from 'react-markdown'
import styled from 'styled-components'
import { groupByCategories, useSystemAgents } from '.'
import { groupTranslations } from './agentGroupTranslations'
import AddAgentPopup from './components/AddAgentPopup'
import AgentCard from './components/AgentCard'
import { AgentGroupIcon } from './components/AgentGroupIcon'
import ImportAgentPopup from './components/ImportAgentPopup'
import { groupByCategories, useSystemAssistantPresets } from '.'
import { groupTranslations } from './assistantPresetGroupTranslations'
import AddAssistantPresetPopup from './components/AddAssistantPresetPopup'
import AssistantPresetCard from './components/AssistantPresetCard'
import { AssistantPresetGroupIcon } from './components/AssistantPresetGroupIcon'
import ImportAssistantPresetPopup from './components/ImportAssistantPresetPopup'
const AgentsPage: FC = () => {
const AssistantPresetsPage: FC = () => {
const [search, setSearch] = useState('')
const [searchInput, setSearchInput] = useState('')
const [activeGroup, setActiveGroup] = useState('我的')
const [agentGroups, setAgentGroups] = useState<Record<string, AssistantPreset[]>>({})
const [isSearchExpanded, setIsSearchExpanded] = useState(false)
const systemAgents = useSystemAgents()
const { agents: userAgents } = useAgents()
const systemPresets = useSystemAssistantPresets()
const { presets: userPresets } = useAssistantPresets()
const { isTopNavbar } = useNavbarPosition()
useEffect(() => {
const systemAgentsGroupList = groupByCategories(systemAgents)
const systemAgentsGroupList = groupByCategories(systemPresets)
const agentsGroupList = {
我的: userAgents,
我的: userPresets,
: [],
...systemAgentsGroupList
} as Record<string, AssistantPreset[]>
setAgentGroups(agentsGroupList)
}, [systemAgents, userAgents])
}, [systemPresets, userPresets])
const filteredAgents = useMemo(() => {
const filteredPresets = useMemo(() => {
// 搜索框为空直接返回「我的」分组下的 agent
if (!search.trim()) {
return agentGroups[activeGroup] || []
}
const uniqueAgents = new Map<string, AssistantPreset>()
const uniquePresets = new Map<string, AssistantPreset>()
Object.entries(agentGroups).forEach(([, agents]) => {
agents.forEach((agent) => {
if (
agent.name.toLowerCase().includes(search.toLowerCase()) ||
agent.description?.toLowerCase().includes(search.toLowerCase())
) {
uniqueAgents.set(agent.id, agent)
uniquePresets.set(agent.id, agent)
}
})
})
return Array.from(uniqueAgents.values())
return Array.from(uniquePresets.values())
}, [agentGroups, activeGroup, search])
const { t, i18n } = useTranslation()
const onAddAgentConfirm = useCallback(
(agent: AssistantPreset) => {
const onAddPresetConfirm = useCallback(
(preset: AssistantPreset) => {
window.modal.confirm({
title: agent.name,
title: preset.name,
content: (
<Flex gap={16} vertical style={{ width: 'calc(100% + 12px)' }}>
{agent.description && <AgentDescription>{agent.description}</AgentDescription>}
{preset.description && <AgentDescription>{preset.description}</AgentDescription>}
{agent.prompt && (
{preset.prompt && (
<AgentPrompt className="markdown">
<ReactMarkdown>{agent.prompt}</ReactMarkdown>
<ReactMarkdown>{preset.prompt}</ReactMarkdown>
</AgentPrompt>
)}
</Flex>
@ -87,16 +87,16 @@ const AgentsPage: FC = () => {
centered: true,
okButtonProps: { type: 'primary' },
okText: t('agents.add.button'),
onOk: () => createAssistantFromAgent(agent)
onOk: () => createAssistantFromAgent(preset)
})
},
[t]
)
const getAgentFromSystemAgent = useCallback((agent: (typeof systemAgents)[number]) => {
const getPresetFromSystemPreset = useCallback((preset: (typeof systemPresets)[number]) => {
return {
...omit(agent, 'group'),
name: agent.name,
...omit(preset, 'group'),
name: preset.name,
id: uuid(),
topics: [],
type: 'agent'
@ -161,14 +161,14 @@ const AgentsPage: FC = () => {
}
const handleAddAgent = () => {
AddAgentPopup.show().then(() => {
AddAssistantPresetPopup.show().then(() => {
handleSearchClear()
})
}
const handleImportAgent = async () => {
try {
await ImportAgentPopup.show()
await ImportAssistantPresetPopup.show()
} catch (error) {
window.toast.error(error instanceof Error ? error.message : t('message.agents.import.error'))
}
@ -207,7 +207,7 @@ const AgentsPage: FC = () => {
title={
<Flex gap={16} align="center" justify="space-between">
<Flex gap={10} align="center">
<AgentGroupIcon groupName={group} />
<AssistantPresetGroupIcon groupName={group} />
{getLocalizedGroupName(group)}
</Flex>
{
@ -229,19 +229,19 @@ const AgentsPage: FC = () => {
<AgentsListTitle>
{search.trim() ? (
<>
<AgentGroupIcon groupName="搜索" size={24} />
<AssistantPresetGroupIcon groupName="搜索" size={24} />
{search.trim()}{' '}
</>
) : (
<>
<AgentGroupIcon groupName={activeGroup} size={24} />
<AssistantPresetGroupIcon groupName={activeGroup} size={24} />
{getLocalizedGroupName(activeGroup)}
</>
)}
{
<CustomTag color="#A0A0A0" size={10}>
{filteredAgents.length}
{filteredPresets.length}
</CustomTag>
}
</AgentsListTitle>
@ -282,13 +282,13 @@ const AgentsPage: FC = () => {
</Flex>
</AgentsListHeader>
{filteredAgents.length > 0 ? (
{filteredPresets.length > 0 ? (
<AgentsList>
{filteredAgents.map((agent, index) => (
<AgentCard
{filteredPresets.map((agent, index) => (
<AssistantPresetCard
key={agent.id || index}
onClick={() => onAddAgentConfirm(getAgentFromSystemAgent(agent))}
agent={agent}
onClick={() => onAddPresetConfirm(getPresetFromSystemPreset(agent))}
preset={agent}
activegroup={activeGroup}
getLocalizedGroupName={getLocalizedGroupName}
/>
@ -390,4 +390,4 @@ const EmptyView = styled.div`
color: var(--color-text-secondary);
`
export default AgentsPage
export default AssistantPresetsPage

View File

@ -1,3 +1,4 @@
// FIXME: Just use i18next!
export type GroupTranslations = {
[key: string]: {
'el-GR': string

View File

@ -5,7 +5,7 @@ import { loggerService } from '@logger'
import EmojiPicker from '@renderer/components/EmojiPicker'
import { TopView } from '@renderer/components/TopView'
import { AGENT_PROMPT } from '@renderer/config/prompts'
import { useAgents } from '@renderer/hooks/useAgents'
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
import { useSidebarIconShow } from '@renderer/hooks/useSidebarIcon'
import { fetchGenerate } from '@renderer/services/ApiService'
import { getDefaultModel } from '@renderer/services/AssistantService'
@ -31,13 +31,13 @@ type FieldType = {
knowledge_base_ids: string[]
}
const logger = loggerService.withContext('AddAgentPopup')
const logger = loggerService.withContext('AddAssistantPresetPopup')
const PopupContainer: React.FC<Props> = ({ resolve }) => {
const [open, setOpen] = useState(true)
const [form] = Form.useForm()
const { t } = useTranslation()
const { addAgent } = useAgents()
const { addAssistantPreset } = useAssistantPresets()
const formRef = useRef<FormInstance>(null)
const [emoji, setEmoji] = useState('')
const [loading, setLoading] = useState(false)
@ -91,7 +91,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
messages: []
}
addAgent(_agent)
addAssistantPreset(_agent)
resolve(_agent)
setOpen(false)
}
@ -266,10 +266,10 @@ const TokenCount = styled.div`
user-select: none;
`
export default class AddAgentPopup {
export default class AddAssistantPresetPopup {
static topviewId = 0
static hide() {
TopView.hide('AddAgentPopup')
TopView.hide('AddAssistantPresetPopup')
}
static show() {
return new Promise<AssistantPreset | null>((resolve) => {
@ -280,7 +280,7 @@ export default class AddAgentPopup {
this.hide()
}}
/>,
'AddAgentPopup'
'AddAssistantPresetPopup'
)
})
}

View File

@ -1,6 +1,6 @@
import { DeleteIcon, EditIcon } from '@renderer/components/Icons'
import CustomTag from '@renderer/components/Tags/CustomTag'
import { useAgents } from '@renderer/hooks/useAgents'
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
import type { AssistantPreset } from '@renderer/types'
@ -11,50 +11,50 @@ import { ArrowDownAZ, Ellipsis, PlusIcon, SquareArrowOutUpRight } from 'lucide-r
import { type FC, memo, useCallback, useEffect, useRef, useState } from 'react'
import styled from 'styled-components'
import ManageAgentsPopup from './ManageAgentsPopup'
import ManageAssistantPresetsPopup from './ManageAssistantPresetsPopup'
interface Props {
agent: AssistantPreset
preset: AssistantPreset
activegroup?: string
onClick: () => void
getLocalizedGroupName: (group: string) => string
}
const AgentCard: FC<Props> = ({ agent, onClick, activegroup, getLocalizedGroupName }) => {
const { removeAgent } = useAgents()
const AssistantPresetCard: FC<Props> = ({ preset, onClick, activegroup, getLocalizedGroupName }) => {
const { removeAssistantPreset } = useAssistantPresets()
const [isVisible, setIsVisible] = useState(false)
const cardRef = useRef<HTMLDivElement>(null)
const handleDelete = useCallback(
(agent: AssistantPreset) => {
(preset: AssistantPreset) => {
window.modal.confirm({
centered: true,
content: t('agents.delete.popup.content'),
onOk: () => removeAgent(agent.id)
onOk: () => removeAssistantPreset(preset.id)
})
},
[removeAgent]
[removeAssistantPreset]
)
const exportAgent = useCallback(async () => {
const exportPreset = useCallback(async () => {
const result = [
{
name: agent.name,
emoji: agent.emoji,
group: agent.group,
prompt: agent.prompt,
description: agent.description,
regularPhrases: agent.regularPhrases,
name: preset.name,
emoji: preset.emoji,
group: preset.group,
prompt: preset.prompt,
description: preset.description,
regularPhrases: preset.regularPhrases,
type: 'agent'
}
]
const resultStr = JSON.stringify(result, null, 2)
await window.api.file.save(`${agent.name}.json`, new TextEncoder().encode(resultStr), {
await window.api.file.save(`${preset.name}.json`, new TextEncoder().encode(resultStr), {
filters: [{ name: t('agents.import.file_filter'), extensions: ['json'] }]
})
}, [agent])
}, [preset])
const menuItems = [
{
@ -63,7 +63,7 @@ const AgentCard: FC<Props> = ({ agent, onClick, activegroup, getLocalizedGroupNa
icon: <EditIcon size={14} />,
onClick: (e: any) => {
e.domEvent.stopPropagation()
AssistantSettingsPopup.show({ assistant: agent })
AssistantSettingsPopup.show({ assistant: preset })
}
},
{
@ -72,7 +72,7 @@ const AgentCard: FC<Props> = ({ agent, onClick, activegroup, getLocalizedGroupNa
icon: <PlusIcon size={14} />,
onClick: (e: any) => {
e.domEvent.stopPropagation()
createAssistantFromAgent(agent)
createAssistantFromAgent(preset)
}
},
{
@ -81,7 +81,7 @@ const AgentCard: FC<Props> = ({ agent, onClick, activegroup, getLocalizedGroupNa
icon: <ArrowDownAZ size={14} />,
onClick: (e: any) => {
e.domEvent.stopPropagation()
ManageAgentsPopup.show()
ManageAssistantPresetsPopup.show()
}
},
{
@ -90,7 +90,7 @@ const AgentCard: FC<Props> = ({ agent, onClick, activegroup, getLocalizedGroupNa
icon: <SquareArrowOutUpRight size={14} />,
onClick: (e: any) => {
e.domEvent.stopPropagation()
exportAgent()
exportPreset()
}
},
{
@ -100,7 +100,7 @@ const AgentCard: FC<Props> = ({ agent, onClick, activegroup, getLocalizedGroupNa
danger: true,
onClick: (e: any) => {
e.domEvent.stopPropagation()
handleDelete(agent)
handleDelete(preset)
}
}
]
@ -125,8 +125,8 @@ const AgentCard: FC<Props> = ({ agent, onClick, activegroup, getLocalizedGroupNa
}
}, [])
const emoji = agent.emoji || getLeadingEmoji(agent.name)
const prompt = (agent.description || agent.prompt).substring(0, 200).replace(/\\n/g, '')
const emoji = preset.emoji || getLeadingEmoji(preset.name)
const prompt = (preset.description || preset.prompt).substring(0, 200).replace(/\\n/g, '')
const content = (
<AgentCardContainer onClick={onClick} ref={cardRef}>
@ -135,15 +135,15 @@ const AgentCard: FC<Props> = ({ agent, onClick, activegroup, getLocalizedGroupNa
<AgentCardBackground>{emoji}</AgentCardBackground>
<AgentCardHeader>
<AgentCardHeaderInfo>
<AgentCardHeaderInfoTitle>{agent.name}</AgentCardHeaderInfoTitle>
<AgentCardHeaderInfoTitle>{preset.name}</AgentCardHeaderInfoTitle>
<AgentCardHeaderInfoTags>
{activegroup === '我的' && (
<CustomTag color="#A0A0A0" size={11}>
{getLocalizedGroupName('我的')}
</CustomTag>
)}
{!!agent.group?.length &&
agent.group.map((group) => (
{!!preset.group?.length &&
preset.group.map((group) => (
<CustomTag key={group} color="#A0A0A0" size={11}>
{getLocalizedGroupName(group)}
</CustomTag>
@ -346,4 +346,4 @@ const AgentPrompt = styled.div`
color: var(--color-text-2);
`
export default memo(AgentCard)
export default memo(AssistantPresetCard)

View File

@ -1,4 +1,4 @@
import { groupTranslations } from '@renderer/pages/agents/agentGroupTranslations'
import { groupTranslations } from '@renderer/pages/assistantPresets/assistantPresetGroupTranslations'
import { DynamicIcon, IconName } from 'lucide-react/dynamic'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
@ -9,7 +9,7 @@ interface Props {
strokeWidth?: number
}
export const AgentGroupIcon: FC<Props> = ({ groupName, size = 20, strokeWidth = 1.2 }) => {
export const AssistantPresetGroupIcon: FC<Props> = ({ groupName, size = 20, strokeWidth = 1.2 }) => {
const { i18n } = useTranslation()
const currentLanguage = i18n.language as keyof (typeof groupTranslations)[string]

View File

@ -1,5 +1,5 @@
import { TopView } from '@renderer/components/TopView'
import { useAgents } from '@renderer/hooks/useAgents'
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
import { useTimer } from '@renderer/hooks/useTimer'
import { getDefaultModel } from '@renderer/services/AssistantService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
@ -17,7 +17,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
const [open, setOpen] = useState(true)
const [form] = Form.useForm()
const { t } = useTranslation()
const { addAgent } = useAgents()
const { addAssistantPreset } = useAssistantPresets()
const [importType, setImportType] = useState<'url' | 'file'>('url')
const [loading, setLoading] = useState(false)
const { setTimeoutTimer } = useTimer()
@ -25,7 +25,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
const onFinish = async (values: { url?: string }) => {
setLoading(true)
try {
let agents: AssistantPreset[] = []
let presets: AssistantPreset[] = []
if (importType === 'url') {
if (!values.url) {
@ -36,16 +36,16 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
throw new Error(t('agents.import.error.fetch_failed'))
}
const data = await response.json()
agents = Array.isArray(data) ? data : [data]
presets = Array.isArray(data) ? data : [data]
} else {
const result = await window.api.file.open({
filters: [{ name: t('agents.import.file_filter'), extensions: ['json'] }]
})
if (result) {
agents = JSON.parse(new TextDecoder('utf-8').decode(result.content))
if (!Array.isArray(agents)) {
agents = [agents]
presets = JSON.parse(new TextDecoder('utf-8').decode(result.content))
if (!Array.isArray(presets)) {
presets = [presets]
}
} else {
return
@ -53,32 +53,32 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
}
// Validate and process agents
for (const agent of agents) {
if (!agent.name || !agent.prompt) {
for (const preset of presets) {
if (!preset.name || !preset.prompt) {
throw new Error(t('agents.import.error.invalid_format'))
}
const newAgent: AssistantPreset = {
const newPreset: AssistantPreset = {
id: uuid(),
name: agent.name,
emoji: agent.emoji || '🤖',
group: agent.group || [],
prompt: agent.prompt,
description: agent.description || '',
name: preset.name,
emoji: preset.emoji || '🤖',
group: preset.group || [],
prompt: preset.prompt,
description: preset.description || '',
type: 'agent',
topics: [],
messages: [],
defaultModel: getDefaultModel(),
regularPhrases: agent.regularPhrases || []
regularPhrases: preset.regularPhrases || []
}
addAgent(newAgent)
addAssistantPreset(newPreset)
}
window.toast.success(t('message.agents.imported'))
setTimeoutTimer('onFinish', () => EventEmitter.emit(EVENT_NAMES.SHOW_ASSISTANTS), 0)
setOpen(false)
resolve(agents)
resolve(presets)
} catch (error) {
window.toast.error(error instanceof Error ? error.message : t('message.agents.import.error'))
} finally {
@ -131,7 +131,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
)
}
export default class ImportAgentPopup {
export default class ImportAssistantPresetPopup {
static show() {
return new Promise<AssistantPreset[] | null>((resolve) => {
TopView.show(
@ -141,12 +141,12 @@ export default class ImportAgentPopup {
this.hide()
}}
/>,
'ImportAgentPopup'
'ImportAssistantPresetPopup'
)
})
}
static hide() {
TopView.hide('ImportAgentPopup')
TopView.hide('ImportAssistantPresetPopup')
}
}

View File

@ -2,7 +2,7 @@ import { MenuOutlined } from '@ant-design/icons'
import { DraggableList } from '@renderer/components/DraggableList'
import { Box, HStack } from '@renderer/components/Layout'
import { TopView } from '@renderer/components/TopView'
import { useAgents } from '@renderer/hooks/useAgents'
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
import { Empty, Modal } from 'antd'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -11,7 +11,7 @@ import styled from 'styled-components'
const PopupContainer: React.FC = () => {
const [open, setOpen] = useState(true)
const { t } = useTranslation()
const { agents, setAgents } = useAgents()
const { presets, setAssistantPresets } = useAssistantPresets()
const onOk = () => {
setOpen(false)
@ -22,14 +22,14 @@ const PopupContainer: React.FC = () => {
}
const onClose = async () => {
ManageAgentsPopup.hide()
ManageAssistantPresetsPopup.hide()
}
useEffect(() => {
if (agents.length === 0) {
if (presets.length === 0) {
setOpen(false)
}
}, [agents])
}, [presets])
return (
<Modal
@ -42,8 +42,8 @@ const PopupContainer: React.FC = () => {
transitionName="animation-move-down"
centered>
<Container>
{agents.length > 0 && (
<DraggableList list={agents} onUpdate={setAgents}>
{presets.length > 0 && (
<DraggableList list={presets} onUpdate={setAssistantPresets}>
{(item) => (
<AgentItem>
<Box mr={8}>
@ -56,7 +56,7 @@ const PopupContainer: React.FC = () => {
)}
</DraggableList>
)}
{agents.length === 0 && <Empty description="" />}
{presets.length === 0 && <Empty description="" />}
</Container>
</Modal>
)
@ -90,12 +90,12 @@ const AgentItem = styled.div`
}
`
export default class ManageAgentsPopup {
export default class ManageAssistantPresetsPopup {
static topviewId = 0
static hide() {
TopView.hide('ManageAgentsPopup')
TopView.hide('ManageAssistantPresetsPopup')
}
static show() {
TopView.show(<PopupContainer />, 'ManageAgentsPopup')
TopView.show(<PopupContainer />, 'ManageAssistantPresetsPopup')
}
}

View File

@ -26,9 +26,9 @@ export const getAgentsFromSystemAgents = (systemAgents: any) => {
return agents
}
export function useSystemAgents() {
const { defaultAgent } = useSettings()
const [agents, setAgents] = useState<AssistantPreset[]>([])
export function useSystemAssistantPresets() {
const { defaultAgent: defaultPreset } = useSettings()
const [presets, setPresets] = useState<AssistantPreset[]>([])
const { resourcesPath } = useRuntime()
const { agentssubscribeUrl } = store.getState().settings
const { i18n } = useTranslation()
@ -46,7 +46,7 @@ export function useSystemAgents() {
throw new Error(`HTTP error! Status: ${response.status}`)
}
const agentsData = (await response.json()) as AssistantPreset[]
setAgents(agentsData)
setPresets(agentsData)
return
} catch (error) {
logger.error('Failed to load remote agents:', error as Error)
@ -65,18 +65,18 @@ export function useSystemAgents() {
}
}
setAgents(_agents)
setPresets(_agents)
} catch (error) {
logger.error('Failed to load agents:', error as Error)
// 发生错误时使用已加载的本地 agents
setAgents(_agents)
setPresets(_agents)
}
}
loadAgents()
}, [defaultAgent, resourcesPath, agentssubscribeUrl, currentLanguage])
}, [defaultPreset, resourcesPath, agentssubscribeUrl, currentLanguage])
return agents
return presets
}
export function groupByCategories(data: AssistantPreset[]) {

View File

@ -1,8 +1,8 @@
import { DownOutlined, RightOutlined } from '@ant-design/icons'
import { DraggableList } from '@renderer/components/DraggableList'
import Scrollbar from '@renderer/components/Scrollbar'
import { useAgents } from '@renderer/hooks/useAgents'
import { useAssistants } from '@renderer/hooks/useAssistant'
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
import { useAssistantsTabSortType } from '@renderer/hooks/useStore'
import { useTags } from '@renderer/hooks/useTags'
import { Assistant, AssistantsSortType } from '@renderer/types'
@ -30,7 +30,7 @@ const Assistants: FC<AssistantsTabProps> = ({
}) => {
const { assistants, removeAssistant, copyAssistant, updateAssistants } = useAssistants()
const [dragging, setDragging] = useState(false)
const { addAgent } = useAgents()
const { addAssistantPreset } = useAssistantPresets()
const { t } = useTranslation()
const { getGroupedAssistants, collapsedTags, toggleTagCollapse } = useTags()
const { assistantsTabSortType = 'list', setAssistantsTabSortType } = useAssistantsTabSortType()
@ -134,7 +134,7 @@ const Assistants: FC<AssistantsTabProps> = ({
sortBy={assistantsTabSortType}
onSwitch={setActiveAssistant}
onDelete={onDelete}
addAgent={addAgent}
addPreset={addAssistantPreset}
copyAssistant={copyAssistant}
onCreateDefaultAssistant={onCreateDefaultAssistant}
handleSortByChange={handleSortByChange}
@ -167,7 +167,7 @@ const Assistants: FC<AssistantsTabProps> = ({
sortBy={assistantsTabSortType}
onSwitch={setActiveAssistant}
onDelete={onDelete}
addAgent={addAgent}
addPreset={addAssistantPreset}
copyAssistant={copyAssistant}
onCreateDefaultAssistant={onCreateDefaultAssistant}
handleSortByChange={handleSortByChange}

View File

@ -40,7 +40,7 @@ interface AssistantItemProps {
onSwitch: (assistant: Assistant) => void
onDelete: (assistant: Assistant) => void
onCreateDefaultAssistant: () => void
addAgent: (agent: any) => void
addPreset: (agent: any) => void
copyAssistant: (assistant: Assistant) => void
onTagClick?: (tag: string) => void
handleSortByChange?: (sortType: AssistantsSortType) => void
@ -52,7 +52,7 @@ const AssistantItem: FC<AssistantItemProps> = ({
sortBy,
onSwitch,
onDelete,
addAgent,
addPreset,
copyAssistant,
handleSortByChange
}) => {
@ -91,7 +91,7 @@ const AssistantItem: FC<AssistantItemProps> = ({
allTags,
assistants,
updateAssistants,
addAgent,
addPreset,
copyAssistant,
onSwitch,
onDelete,
@ -108,7 +108,7 @@ const AssistantItem: FC<AssistantItemProps> = ({
allTags,
assistants,
updateAssistants,
addAgent,
addPreset,
copyAssistant,
onSwitch,
onDelete,
@ -249,7 +249,7 @@ function getMenuItems({
allTags,
assistants,
updateAssistants,
addAgent,
addPreset,
copyAssistant,
onSwitch,
onDelete,
@ -297,10 +297,10 @@ function getMenuItems({
key: 'save-to-agent',
icon: <Save size={14} />,
onClick: async () => {
const agent = omit(assistant, ['model', 'emoji'])
agent.id = uuid()
agent.type = 'agent'
addAgent(agent)
const preset = omit(assistant, ['model', 'emoji'])
preset.id = uuid()
preset.type = 'agent'
addPreset(preset)
window.toast.success(t('assistants.save.success'))
}
},

View File

@ -1,7 +1,7 @@
import { HStack } from '@renderer/components/Layout'
import { TopView } from '@renderer/components/TopView'
import { useAgent } from '@renderer/hooks/useAgents'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useAssistantPreset } from '@renderer/hooks/useAssistantPresets'
import { useSidebarIconShow } from '@renderer/hooks/useSidebarIcon'
import { Assistant } from '@renderer/types'
import { Menu, Modal } from 'antd'
@ -40,12 +40,14 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
const [menu, setMenu] = useState<AssistantSettingPopupTab>(tab || 'prompt')
const _useAssistant = useAssistant(props.assistant.id)
const _useAgent = useAgent(props.assistant.id)
const _useAgent = useAssistantPreset(props.assistant.id)
const isAgent = props.assistant.type === 'agent'
const assistant = isAgent ? _useAgent.agent : _useAssistant.assistant
const updateAssistant = isAgent ? _useAgent.updateAgent : _useAssistant.updateAssistant
const updateAssistantSettings = isAgent ? _useAgent.updateAgentSettings : _useAssistant.updateAssistantSettings
const assistant = isAgent ? _useAgent.preset : _useAssistant.assistant
const updateAssistant = isAgent ? _useAgent.updateAssistantPreset : _useAssistant.updateAssistant
const updateAssistantSettings = isAgent
? _useAgent.updateAssistantPresetSettings
: _useAssistant.updateAssistantSettings
const showKnowledgeIcon = useSidebarIconShow('knowledge')

View File

@ -156,6 +156,7 @@ export interface SettingsState {
joplinUrl: string | null
joplinExportReasoning: boolean
defaultObsidianVault: string | null
/** This state is actaully default assistant preset */
defaultAgent: string | null
// 思源笔记配置
siyuanApiUrl: string | null