diff --git a/apps/desktop/src/renderer/src/components/SettingsModal.tsx b/apps/desktop/src/renderer/src/components/SettingsModal.tsx index e3339b2..6a67bf2 100644 --- a/apps/desktop/src/renderer/src/components/SettingsModal.tsx +++ b/apps/desktop/src/renderer/src/components/SettingsModal.tsx @@ -17,10 +17,20 @@ import { LuX } from 'react-icons/lu' import type { IconType } from 'react-icons' -import type { CustomProviderConfig, ModelCatalog, ModelMeta, ProviderCatalogEntry, ResourceDetails, SessionState, ThemeMode } from '@shared/types' +import type { + CustomProviderConfig, + ModelCatalog, + ModelMeta, + PluginInfo, + ProviderCatalogEntry, + ResourceDetails, + SessionState, + ThemeMode +} from '@shared/types' import { THEME_MODES, THEME_PRESETS, applyThemeColor } from '../lib/theme' +import { usePluginList } from '../plugins/store' -type Tab = 'general' | 'model' | 'prompt' | 'plugins' | 'skills' | 'commands' +type Tab = 'general' | 'corePlugins' | 'model' | 'prompt' | 'plugins' | 'skills' | 'commands' /** 左侧分组导航(参考 ZCode 设置页:基础设置 / Agent 能力) */ const NAV: Array<{ group: string; items: Array<{ id: Tab; label: string; icon: IconType }> }> = [ @@ -29,7 +39,8 @@ const NAV: Array<{ group: string; items: Array<{ id: Tab; label: string; icon: I items: [ { id: 'general', label: '常规', icon: LuSettings2 }, { id: 'model', label: '模型设置', icon: LuCpu }, - { id: 'prompt', label: '系统提示词', icon: LuTextQuote } + { id: 'prompt', label: '系统提示词', icon: LuTextQuote }, + { id: 'corePlugins', label: '核心插件', icon: LuPuzzle } ] }, { @@ -48,6 +59,7 @@ const PLACEHOLDER: Record = { const TAB_META: Record = { general: { title: '常规', desc: '主题色等外观偏好。' }, + corePlugins: { title: '核心插件', desc: '内置的第一方功能插件,按需开启或关闭;变更会重建 Agent 运行时,进行中的回合会被中断。' }, model: { title: '模型设置', desc: '' }, prompt: { title: '系统提示词', desc: '当前生效的 pi 原生系统提示词(含技能清单注入),只读。' }, plugins: { title: '插件', desc: PLACEHOLDER.plugins }, @@ -587,6 +599,59 @@ function ModelSettings({ ) } +/** 核心插件页:内置插件列表 + 启停开关(单一事实源在主进程,listChanged 事件驱动刷新) */ +function CorePluginPanel({ + togglingId, + onToggle +}: { + /** 正在切换中的插件 id(切换期间全部开关禁用,避免并发重建运行时) */ + togglingId: string | null + onToggle: (plugin: PluginInfo, next: boolean) => void +}): ReactNode { + // 本页只管核心(内置)插件;外置插件留给「Agent 能力 → 插件」 + const core = usePluginList().filter((p) => p.source === 'builtin') + if (core.length === 0) { + return ( +
+
暂无核心插件
+
+ ) + } + return ( +
+ {core + .slice() + .sort((a, b) => (a.manifest.order ?? 100) - (b.manifest.order ?? 100) || a.manifest.id.localeCompare(b.manifest.id)) + .map((p) => ( +
+
+ {p.manifest.name} + 内置 v{p.manifest.version} + {p.enabled ? '已启用' : '已停用'} + +
+ {p.manifest.description &&
{p.manifest.description}
} + {p.error &&
装载失败:{p.error}
} +
+ ))} +
+ ) +} + export function SettingsModal({ open, sessionState, @@ -614,6 +679,7 @@ export function SettingsModal({ const [resources, setResources] = useState(null) const [resourcesLoading, setResourcesLoading] = useState(false) const [customProviders, setCustomProviders] = useState([]) + const [togglingId, setTogglingId] = useState(null) const scrollRef = useRef(null) const refreshCustoms = (): void => { @@ -663,6 +729,22 @@ export function SettingsModal({ .finally(() => setBusy(false)) } + /** 切换核心插件启停:主进程持久化后重建运行时;列表经 plugins:listChanged 自动刷新 */ + const toggleCorePlugin = (plugin: PluginInfo, next: boolean): void => { + if (streaming) { + setNotice({ kind: 'err', text: 'Agent 正在回复中,请先停止或等待完成后再切换(切换会重建运行时)' }) + return + } + if (togglingId) return + setTogglingId(plugin.manifest.id) + setNotice(null) + window.api + .pluginsSetEnabled(plugin.manifest.id, next) + .then(() => setNotice({ kind: 'ok', text: `已${next ? '启用' : '停用'}「${plugin.manifest.name}」,运行时已重建` })) + .catch((err: unknown) => setNotice({ kind: 'err', text: err instanceof Error ? err.message : String(err) })) + .finally(() => setTogglingId(null)) + } + const refreshResources = (): void => { setResourcesLoading(true) window.api @@ -836,6 +918,15 @@ export function SettingsModal({ )} + {tab === 'corePlugins' && ( +
+ +
+ 开关立即生效(重建 Agent 运行时)。例如停用「浏览器」后,侧栏浏览器面板入口与全部 browser_* 工具将一并停用;重新开启即恢复。 +
+
+ )} + {tab === 'prompt' && (
@@ -937,7 +1028,7 @@ export function SettingsModal({ /> )} - {tab !== 'general' && tab !== 'model' && tab !== 'prompt' && tab !== 'skills' && tab !== 'commands' && ( + {tab !== 'general' && tab !== 'corePlugins' && tab !== 'model' && tab !== 'prompt' && tab !== 'skills' && tab !== 'commands' && ( i.id === tab)?.icon ?? LuPuzzle} label={meta.title} /> )}