diff --git a/web/src/components/Settings/ConfigPage.tsx b/web/src/components/Settings/ConfigPage.tsx index cccad71..19d7070 100644 --- a/web/src/components/Settings/ConfigPage.tsx +++ b/web/src/components/Settings/ConfigPage.tsx @@ -200,6 +200,23 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage return () => document.removeEventListener('keydown', h) }, [handleClose]) + // 子模态框打开时,ESC 键仅关闭子模态框(阻止冒泡到 ConfigPage 全局 ESC,避免关闭整个配置页) + // 必须放在所有条件 return 之前,否则 loading 首次渲染时不执行此 hook, + // config 加载后重新渲染才执行 → hooks 数量不一致 → React 崩溃 + useEffect(() => { + if (!editingExpert && !editingSubagent) return + const handler = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + e.stopPropagation() + e.preventDefault() + setEditingExpert(null) + setEditingSubagent(null) + } + } + window.addEventListener('keydown', handler, true) + return () => window.removeEventListener('keydown', handler, true) + }, [editingExpert, editingSubagent]) + const update = useCallback((key: K, value: AppConfig[K]) => { setConfig(prev => prev ? { ...prev, [key]: value } : prev) setDirty(true) @@ -904,21 +921,6 @@ export function ConfigPage({ onClose, onSaveConnection, initialTab }: ConfigPage const toolEmptyHint = toolListLoading ? '加载中...' : '未发现任何工具' const subagentEmptyHint = subagentListLoading ? '加载中...' : '未发现任何子代理' - // 子模态框打开时,ESC 键仅关闭子模态框(阻止冒泡到 ConfigPage 全局 ESC,避免关闭整个配置页) - useEffect(() => { - if (!editingExpert && !editingSubagent) return - const handler = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - e.stopPropagation() - e.preventDefault() - setEditingExpert(null) - setEditingSubagent(null) - } - } - window.addEventListener('keydown', handler, true) - return () => window.removeEventListener('keydown', handler, true) - }, [editingExpert, editingSubagent]) - const renderExpertModal = () => { if (!editingExpert) return null const isEdit = editingExpert.mode === 'edit'