From 46d98c2b224ce73415c2e759c1baa1f4a319def7 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Sun, 27 Jul 2025 11:17:45 +0800 Subject: [PATCH] feat(assistants): place copied assistant after the original one (#8557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(assistants): 复制助手功能插入到原助手后 添加 insertAssistant 方法用于在指定位置插入助手 实现 copyAssistant 功能用于复制助手并插入到原助手后面 更新相关组件以使用新的复制功能 * fix(useAssistant): 修复助手索引检查逻辑错误 原条件判断错误导致未找到索引时仍执行更新操作,现修正为仅在索引存在时更新 * fix(useAssistant): 添加错误处理及国际化支持 捕获插入助手时的异常并显示错误提示 添加react-i18next的翻译功能用于错误消息 --- src/renderer/src/hooks/useAssistant.ts | 28 +++++++++++++++++++ .../src/pages/home/Tabs/AssistantsTab.tsx | 6 ++-- .../home/Tabs/components/AssistantItem.tsx | 19 +++++++------ src/renderer/src/store/assistants.ts | 10 +++++++ 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/renderer/src/hooks/useAssistant.ts b/src/renderer/src/hooks/useAssistant.ts index ee375ec28d..375a41e37b 100644 --- a/src/renderer/src/hooks/useAssistant.ts +++ b/src/renderer/src/hooks/useAssistant.ts @@ -1,9 +1,11 @@ import { db } from '@renderer/databases' import { getDefaultTopic } from '@renderer/services/AssistantService' +import { loggerService } from '@renderer/services/LoggerService' import { useAppDispatch, useAppSelector } from '@renderer/store' import { addAssistant, addTopic, + insertAssistant, removeAllTopics, removeAssistant, removeTopic, @@ -17,18 +19,44 @@ import { } from '@renderer/store/assistants' import { setDefaultModel, setTopicNamingModel, setTranslateModel } from '@renderer/store/llm' import { Assistant, AssistantSettings, Model, Topic } from '@renderer/types' +import { uuid } from '@renderer/utils' import { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' import { TopicManager } from './useTopic' export function useAssistants() { + const { t } = useTranslation() const { assistants } = useAppSelector((state) => state.assistants) const dispatch = useAppDispatch() + const logger = loggerService.withContext('useAssistants') return { assistants, updateAssistants: (assistants: Assistant[]) => dispatch(updateAssistants(assistants)), addAssistant: (assistant: Assistant) => dispatch(addAssistant(assistant)), + insertAssistant: (index: number, assistant: Assistant) => dispatch(insertAssistant({ index, assistant })), + copyAssistant: (assistant: Assistant): Assistant | undefined => { + if (!assistant) { + logger.error("assistant doesn't exists.") + return + } + const index = assistants.findIndex((_assistant) => _assistant.id === assistant.id) + const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic(assistant.id)] } + if (index === -1) { + logger.warn("Origin assistant's id not found. Fallback to addAssistant.") + dispatch(addAssistant(_assistant)) + } else { + // 插入到后面 + try { + dispatch(insertAssistant({ index: index + 1, assistant: _assistant })) + } catch (e) { + logger.error('Failed to insert assistant', e as Error) + window.message.error(t('message.error.copy')) + } + } + return _assistant + }, removeAssistant: (id: string) => { dispatch(removeAssistant({ id })) const assistant = assistants.find((a) => a.id === id) diff --git a/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx b/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx index b42c6fbafe..e2c7589243 100644 --- a/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx @@ -25,7 +25,7 @@ const Assistants: FC = ({ onCreateAssistant, onCreateDefaultAssistant }) => { - const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants() + const { assistants, removeAssistant, copyAssistant, updateAssistants } = useAssistants() const [dragging, setDragging] = useState(false) const { addAgent } = useAgents() const { t } = useTranslation() @@ -106,7 +106,7 @@ const Assistants: FC = ({ onSwitch={setActiveAssistant} onDelete={onDelete} addAgent={addAgent} - addAssistant={addAssistant} + copyAssistant={copyAssistant} onCreateDefaultAssistant={onCreateDefaultAssistant} handleSortByChange={handleSortByChange} /> @@ -143,7 +143,7 @@ const Assistants: FC = ({ onSwitch={setActiveAssistant} onDelete={onDelete} addAgent={addAgent} - addAssistant={addAssistant} + copyAssistant={copyAssistant} onCreateDefaultAssistant={onCreateDefaultAssistant} handleSortByChange={handleSortByChange} /> diff --git a/src/renderer/src/pages/home/Tabs/components/AssistantItem.tsx b/src/renderer/src/pages/home/Tabs/components/AssistantItem.tsx index 0c169a7c24..d33c26c613 100644 --- a/src/renderer/src/pages/home/Tabs/components/AssistantItem.tsx +++ b/src/renderer/src/pages/home/Tabs/components/AssistantItem.tsx @@ -17,7 +17,7 @@ import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' import { useTags } from '@renderer/hooks/useTags' import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings' -import { getDefaultModel, getDefaultTopic } from '@renderer/services/AssistantService' +import { getDefaultModel } from '@renderer/services/AssistantService' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { Assistant, AssistantsSortType } from '@renderer/types' import { getLeadingEmoji, uuid } from '@renderer/utils' @@ -40,7 +40,7 @@ interface AssistantItemProps { onDelete: (assistant: Assistant) => void onCreateDefaultAssistant: () => void addAgent: (agent: any) => void - addAssistant: (assistant: Assistant) => void + copyAssistant: (assistant: Assistant) => void onTagClick?: (tag: string) => void handleSortByChange?: (sortType: AssistantsSortType) => void } @@ -52,7 +52,7 @@ const AssistantItem: FC = ({ onSwitch, onDelete, addAgent, - addAssistant, + copyAssistant, handleSortByChange }) => { const { t } = useTranslation() @@ -91,7 +91,7 @@ const AssistantItem: FC = ({ assistants, updateAssistants, addAgent, - addAssistant, + copyAssistant, onSwitch, onDelete, removeAllTopics, @@ -108,7 +108,7 @@ const AssistantItem: FC = ({ assistants, updateAssistants, addAgent, - addAssistant, + copyAssistant, onSwitch, onDelete, removeAllTopics, @@ -246,7 +246,7 @@ function getMenuItems({ assistants, updateAssistants, addAgent, - addAssistant, + copyAssistant, onSwitch, onDelete, removeAllTopics, @@ -268,9 +268,10 @@ function getMenuItems({ key: 'duplicate', icon: , onClick: async () => { - const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic(assistant.id)] } - addAssistant(_assistant) - onSwitch(_assistant) + const _assistant = copyAssistant(assistant) + if (_assistant) { + onSwitch(_assistant) + } } }, { diff --git a/src/renderer/src/store/assistants.ts b/src/renderer/src/store/assistants.ts index 70dbfa9841..238a5b44fd 100644 --- a/src/renderer/src/store/assistants.ts +++ b/src/renderer/src/store/assistants.ts @@ -32,6 +32,15 @@ const assistantsSlice = createSlice({ addAssistant: (state, action: PayloadAction) => { state.assistants.push(action.payload) }, + insertAssistant: (state, action: PayloadAction<{ index: number; assistant: Assistant }>) => { + const { index, assistant } = action.payload + + if (index < 0 || index > state.assistants.length) { + throw new Error(`InsertAssistant: index ${index} is out of bounds [0, ${state.assistants.length}]`) + } + + state.assistants.splice(index, 0, assistant) + }, removeAssistant: (state, action: PayloadAction<{ id: string }>) => { state.assistants = state.assistants.filter((c) => c.id !== action.payload.id) }, @@ -170,6 +179,7 @@ export const { updateDefaultAssistant, updateAssistants, addAssistant, + insertAssistant, removeAssistant, updateAssistant, addTopic,