ai-helper-agent 2.3.1 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -3
- package/dist/assets/__vite-browser-external-CM3Eme-n.js +1 -0
- package/dist/assets/agent-store-Ce3f4Ya3.js +1 -0
- package/dist/assets/background.js +104 -80
- package/dist/assets/chat-export-CWSUeOo4.js +37 -0
- package/dist/assets/chunk-CMxvf4Kt.js +1 -0
- package/dist/assets/content.js +159 -156
- package/dist/assets/{dist-BQOFgIxk.js → dist-DhUTa_WT.js} +5 -6
- package/dist/assets/dist-LQPh1k0y.js +24 -0
- package/dist/assets/i18n-ouMgDZ2t.js +49 -0
- package/dist/assets/jszip.min-Bm_16osY.js +2 -0
- package/dist/assets/lib-33nIefF6.js +220 -0
- package/dist/assets/{marked.min.js-08SGy6CO.js → marked.min.js-D2rXgLAQ.js} +7 -8
- package/dist/assets/{marked.min.js-loader-4kRZ9iCP.js → marked.min.js-loader-tKH5SQVQ.js} +1 -1
- package/dist/assets/model-profiles-6dzdrSS4.js +24 -0
- package/dist/assets/network-scope-CNRYXk74.js +1 -0
- package/dist/assets/options-Cp1UMkjK.css +1 -0
- package/dist/assets/options.js +108 -102
- package/dist/assets/{page-snapshot-renderer-CVL6r6Kf.js → page-snapshot-renderer-j72q92KH.js} +2 -3
- package/dist/assets/pdf-DnVqBaAN.js +6 -0
- package/dist/assets/session-store-CmGs_rhh.js +76 -0
- package/dist/assets/side_panel.css +1 -1
- package/dist/assets/side_panel.js +958 -1124
- package/dist/assets/theme-BRAX5X2F.css +1 -0
- package/dist/assets/{modulepreload-polyfill-P2Xu9kJm.js → theme-CLwYsx9o.js} +1 -1
- package/dist/assets/utils-9i9g8vW8.js +112 -0
- package/dist/assets/version-BrT5Y9A7.js +1 -0
- package/dist/assets/xlsx-DGkz7cnk.js +105 -0
- package/dist/manifest.json +2 -5
- package/dist/offscreen/offscreen.js +7 -7
- package/dist/options.html +151 -75
- package/dist/side_panel.html +167 -137
- package/package.json +1 -1
- package/src/config.js +3 -2
- package/src/executor.js +99 -18
- package/src/locales/en.json +16 -2
- package/src/locales/zh.json +16 -2
- package/src/memory/injection.js +12 -2
- package/src/memory/routes.js +6 -0
- package/src/memory/service.js +57 -2
- package/src/rag/manager.js +1 -1
- package/src/rag/url-guard.js +27 -12
- package/src/security.js +433 -31
- package/src/server.js +28 -8
- package/dist/assets/agent-store-DcAs6YyA.js +0 -2
- package/dist/assets/agent-store-DcAs6YyA.js.map +0 -1
- package/dist/assets/background.js.map +0 -1
- package/dist/assets/dist-BQOFgIxk.js.map +0 -1
- package/dist/assets/i18n-C4jSRoCH.js +0 -50
- package/dist/assets/i18n-C4jSRoCH.js.map +0 -1
- package/dist/assets/jszip.min-BXqW2-9v.js +0 -3
- package/dist/assets/jszip.min-BXqW2-9v.js.map +0 -1
- package/dist/assets/lib-Bi7BFuND.js +0 -221
- package/dist/assets/lib-Bi7BFuND.js.map +0 -1
- package/dist/assets/marked.min.js-08SGy6CO.js.map +0 -1
- package/dist/assets/model-profiles-Dl_MsXWa.js +0 -25
- package/dist/assets/model-profiles-Dl_MsXWa.js.map +0 -1
- package/dist/assets/options-8-nu3P5K.css +0 -1
- package/dist/assets/options.js.map +0 -1
- package/dist/assets/page-snapshot-renderer-CVL6r6Kf.js.map +0 -1
- package/dist/assets/pdf-HiJ0icvU.js +0 -7
- package/dist/assets/pdf-HiJ0icvU.js.map +0 -1
- package/dist/assets/rolldown-runtime-C0FnF6B9.js +0 -1
- package/dist/assets/session-store-CzVQCgQ_.js +0 -77
- package/dist/assets/session-store-CzVQCgQ_.js.map +0 -1
- package/dist/assets/side_panel.js.map +0 -1
- package/dist/assets/version-CNG2KGnJ.js +0 -2
- package/dist/assets/version-CNG2KGnJ.js.map +0 -1
- package/dist/assets/xlsx-BHKh7vjA.js +0 -106
- package/dist/assets/xlsx-BHKh7vjA.js.map +0 -1
- package/dist/icons/badges/license-MIT.svg +0 -21
- package/dist/icons/badges/platform.svg +0 -21
- package/dist/icons/badges/prs-welcome.svg +0 -21
- package/dist/icons/badges/version.svg +0 -21
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"options-Djm7emls.js","names":["_DEFAULT_REACT_CONFIG","_DEFAULT_CHAT_CONFIG","state","showToast","escapeHtml","showToast","renderInstallGuide","initialized","renderGate","renderList","initialized","$","renderStatus","versionMeta","showToast"],"sources":["../../src/options/constants.js","../../src/options/ctx-popover.js","../../src/options/config-manager.js","../../src/options/model-fetcher.js","../../src/options/toolbar-config.js","../../src/options/config-io.js","../../src/options/profile-manager.js","../../src/options/toolbox-shared.js","../../src/options/toolbox-mcp.js","../../src/options/toolbox-skills.js","../../src/options/toolbox-config.js","../../src/options/toolbox-rag.js","../../src/options/knowledge-panel.js","../../src/options/memory-entries.js","../../src/options/memory-panel.js","../../src/options/save-bar.js","../../src/options/install-guide.js","../../src/options/index.js"],"sourcesContent":["// options/constants.js - 常量定义\n\nimport { DEFAULT_REFLECTION_CONFIG, DEFAULT_REACT_CONFIG as _DEFAULT_REACT_CONFIG, DEFAULT_CHAT_CONFIG as _DEFAULT_CHAT_CONFIG } from '../background/constants.js';\nimport { t, registerTranslations } from '../shared/i18n.js';\n\n// 自注册本模块翻译(optionsConst 命名空间)\nregisterTranslations('zh', {\n optionsConst: {\n aiSearch: 'AI搜索',\n explain: '解释',\n translate: '翻译',\n summary: '总结',\n copy: '复制',\n defaultSystemPrompt: 'AI Helper:IT技术助手。\\n\\n## 能力\\n编程开发与调试(Java/Python/JavaScript/Go/C++)、架构优化、性能调优、代码审查、文档编写、浏览器工具调用\\n\\n## 要求\\n精准的技术术语、可运行的代码示例、Markdown格式、可操作的解决方案、不生成违反安全规定的代码',\n },\n});\n\nregisterTranslations('en', {\n optionsConst: {\n aiSearch: 'AI Search',\n explain: 'Explain',\n translate: 'Translate',\n summary: 'Summarize',\n copy: 'Copy',\n defaultSystemPrompt: 'AI Helper: IT Technical Assistant.\\n\\n## Capabilities\\nProgramming development and debugging (Java/Python/JavaScript/Go/C++), architecture optimization, performance tuning, code review, documentation writing, browser tool invocation\\n\\n## Requirements\\nPrecise technical terminology, runnable code examples, Markdown format, actionable solutions, no security-violating code',\n },\n});\n\n// 内置工具 systemPrompt 多语言文案(与 content/selection-toolbar.js 保持一致)\nregisterTranslations('zh', {\n selToolbar: {\n sysPromptAisearch: '你正在处理用户在网页上选中的内容。使用ReAct Agent模式,通过多轮思考、搜索和推理来回答选中的问题。',\n sysPromptExplain: '你正在处理用户在网页上选中的内容。用1-3句简洁解释选中内容,必要时补充一个简短示例。不要展开长篇论述。',\n sysPromptTranslate: '你正在处理用户在网页上选中的内容。自动检测语言:中文→英文,英文→中文,其他语言→同时给出中英文。只输出翻译结果,不添加额外说明。',\n sysPromptSummary: '你正在处理用户在网页上选中的内容。用3-5个要点总结选中内容,每条要点一句话,提炼核心信息即可。',\n sysPromptCopy: '将选中内容复制到剪贴板。',\n },\n});\n\nregisterTranslations('en', {\n selToolbar: {\n sysPromptAisearch: 'You are processing content selected by the user on a web page. Use ReAct Agent mode to answer selected questions through multiple rounds of thinking, searching, and reasoning.',\n sysPromptExplain: 'You are processing content selected by the user on a web page. Explain the selected content in 1-3 concise sentences, supplementing with a brief example if necessary. Do not expand into lengthy discussions.',\n sysPromptTranslate: 'You are processing content selected by the user on a web page. Auto-detect language: Chinese→English, English→Chinese, other languages→provide both Chinese and English. Output only the translation result, no additional explanations.',\n sysPromptSummary: 'You are processing content selected by the user on a web page. Summarize the selected content in 3-5 bullet points, one sentence per point, distilling the core information.',\n sysPromptCopy: 'Copy the selected content to the clipboard.',\n },\n});\n\nexport const PRESET_MODELS = [\n 'deepseek-v4-pro',\n 'deepseek-v4-flash'\n];\n\nexport const PRESET_IMAGE_MODELS = [];\n\nexport const PRESET_API_BASES = [\n // 国内主流\n 'https://api.deepseek.com', // DeepSeek\n 'https://api.hunyuan.cloud.tencent.com/v1', // 腾讯混元\n 'https://dashscope.aliyuncs.com/compatible-mode/v1', // 阿里云百炼(通义千问)\n 'https://qianfan.baidubce.com/v2', // 百度千帆(文心)\n 'https://ark.cn-beijing.volces.com/api/v3', // 火山方舟(豆包)\n 'https://open.bigmodel.cn/api/paas/v4', // 智谱 GLM\n 'https://api.moonshot.cn/v1', // Moonshot(Kimi)\n 'https://api.stepfun.com/v1', // 阶跃星辰\n 'https://api.minimaxi.com/v1', // MiniMax\n 'https://spark-api-open.xf-yun.com/v1', // 讯飞星火\n 'https://api.baichuan-ai.com/v1', // 百川智能\n 'https://api.siliconflow.cn/v1', // 硅基流动\n // 海外 & 聚合平台\n 'https://api.openai.com/v1', // OpenAI\n 'https://api.anthropic.com/v1', // Anthropic Claude\n 'https://generativelanguage.googleapis.com/v1beta/openai', // Google Gemini\n 'https://openrouter.ai/api/v1', // OpenRouter(聚合)\n 'https://api.groq.com/openai/v1', // Groq\n 'https://api.mistral.ai/v1', // Mistral\n];\n\nexport const DEFAULT_SYSTEM_PROMPT = `AI Helper: IT Technical Assistant.\n\n## Capabilities\nProgramming development and debugging (Java/Python/JavaScript/Go/C++), architecture optimization, performance tuning, code review, documentation writing, browser tool invocation\n\n## Requirements\nPrecise technical terminology, runnable code examples, Markdown format, actionable solutions, no security-violating code`;\n\nexport const DEFAULT_REACT_CONFIG = _DEFAULT_REACT_CONFIG;\n\nexport const DEFAULT_TOOLBAR_TOOLS = [\n { id: 'ai-search', name: t('optionsConst.aiSearch'), systemPrompt: 'You are processing content selected by the user on a web page. Use ReAct Agent mode to answer the selected question through multiple rounds of thinking, searching, and reasoning.', builtin: true, order: 0 },\n { id: 'explain', name: t('optionsConst.explain'), systemPrompt: 'You are processing content selected by the user on a web page. Explain the selected content in 1-3 concise sentences, adding a brief example if necessary. Do not elaborate at length.', builtin: true, order: 1 },\n { id: 'translate', name: t('optionsConst.translate'), systemPrompt: 'You are processing content selected by the user on a web page. Auto-detect language: Chinese to English, English to Chinese, other languages provide both Chinese and English. Output only the translation result without additional explanations.', builtin: true, order: 2 },\n { id: 'summary', name: t('optionsConst.summary'), systemPrompt: 'You are processing content selected by the user on a web page. Summarize the selected content in 3-5 key points, one sentence per point, distilling the core information.', builtin: true, order: 3 },\n { id: 'copy', name: t('optionsConst.copy'), systemPrompt: 'Copy the selected content to the clipboard.', builtin: true, order: 99 }\n];\n\n// 内置工具名称 i18n key 映射(用于运行时动态获取当前语言的名称)\nconst BUILTIN_TOOL_NAME_KEYS = {\n 'ai-search': 'optionsConst.aiSearch',\n 'explain': 'optionsConst.explain',\n 'translate': 'optionsConst.translate',\n 'summary': 'optionsConst.summary',\n 'copy': 'optionsConst.copy',\n};\n\n/**\n * 获取内置工具的当前语言名称(每次调用都从 i18n 字典实时查找)\n * @param {string} toolId - 工具 ID\n * @returns {string|undefined} 当前语言的工具名称\n */\nexport function getBuiltinToolName(toolId) {\n const key = BUILTIN_TOOL_NAME_KEYS[toolId];\n return key ? t(key) : undefined;\n}\n\n// 内置工具 systemPrompt i18n key 映射(用于运行时动态获取当前语言的提示词)\nconst BUILTIN_TOOL_PROMPT_KEYS = {\n 'ai-search': 'selToolbar.sysPromptAisearch',\n 'explain': 'selToolbar.sysPromptExplain',\n 'translate': 'selToolbar.sysPromptTranslate',\n 'summary': 'selToolbar.sysPromptSummary',\n 'copy': 'selToolbar.sysPromptCopy',\n};\n\n/**\n * 获取内置工具的当前语言 systemPrompt(每次调用都从 i18n 字典实时查找)\n * @param {string} toolId - 工具 ID\n * @returns {string|undefined} 当前语言的系统提示词\n */\nexport function getBuiltinToolPrompt(toolId) {\n const key = BUILTIN_TOOL_PROMPT_KEYS[toolId];\n return key ? t(key) : undefined;\n}\n\n/**\n * 获取当前语言对应的默认系统提示词\n * @returns {string}\n */\nexport function getDefaultSystemPrompt() {\n return t('optionsConst.defaultSystemPrompt');\n}\n\nexport const DEFAULT_TOOLBAR_MAX_VISIBLE = 5;\nexport const DEFAULT_TOOLBAR_ICON_ONLY = false;\nexport const DEFAULT_ENABLE_SELECTION_TOOLBAR = true;\n\nexport const DEFAULT_CHAT_CONFIG = {\n maxInputHistory: 20,\n maxHistoryMessages: 50,\n ..._DEFAULT_CHAT_CONFIG\n};\n\nexport { DEFAULT_REFLECTION_CONFIG };\n","// options/ctx-popover.js - 模型上下文窗口滑杆浮层(单例)\n//\n// 浮层挂到 document.body 并以 position:fixed 定位:\n// 模型下拉容器(.model-dropdown)限高 240px + overflow-y:auto,内部浮层会被裁切。\n//\n// 交互约定(由 index.js 接线):\n// - 拖拽 input 事件 → onPreview(tokens):实时预览徽标,不持久化\n// - change 事件(松手/键盘步进)→ onCommit(tokens):持久化\n// - 点击「自动」→ onReset():清除该模型的自定义值(0 = 自动推断)\n\nimport { t } from '../shared/i18n.js';\nimport { inferContextWindow, formatContextWindow } from '../shared/token-counter.js';\n\n// 标准档位(1000 进制,与「128K=128000, 1M=1000000」文案约定一致)\nexport const CTX_LADDER = [\n 1000, 2000, 4000, 8000, 16000, 32000,\n 64000, 128000, 200000, 256000, 512000, 1000000,\n];\n\nconst MAX_INDEX = CTX_LADDER.length - 1;\n\n/**\n * 找最接近 tokens 的档位索引(越界夹取到首尾档位)\n * @param {number} tokens\n * @returns {number}\n */\nexport function tokensToIndex(tokens) {\n const n = Number(tokens) || 0;\n let bestIndex = 0;\n let bestDist = Infinity;\n for (let i = 0; i < CTX_LADDER.length; i++) {\n const dist = Math.abs(CTX_LADDER[i] - n);\n if (dist < bestDist) {\n bestDist = dist;\n bestIndex = i;\n }\n }\n return bestIndex;\n}\n\n/**\n * 档位索引 → tokens(越界夹取到首尾档位)\n * @param {number} index\n * @returns {number}\n */\nexport function indexToTokens(index) {\n const i = Math.min(Math.max(Math.round(Number(index) || 0), 0), MAX_INDEX);\n return CTX_LADDER[i];\n}\n\n// 单例元素与状态\nlet popoverEl = null;\nlet sliderEl = null;\nlet valueEl = null;\nlet autoTagEl = null;\nlet autoBtnEl = null;\nlet scaleDots = [];\nlet state = null;\n\n// 自动推断值:统一复用 token-counter 的 inferContextWindow(与 getContextWindow 内置映射口径一致)\n\n// 构建浮层 DOM(每次打开重建,关闭时整体移除)\nfunction buildPopoverEl() {\n popoverEl = document.createElement('div');\n popoverEl.className = 'ctx-popover';\n popoverEl.setAttribute('role', 'dialog');\n popoverEl.setAttribute('aria-label', t('settings.ctxSliderIconTitle'));\n popoverEl.innerHTML = `\n <div class=\"ctx-popover-title-row\">\n <span class=\"ctx-popover-title\"></span>\n <button type=\"button\" class=\"ctx-popover-auto-btn\"></button>\n </div>\n <div class=\"ctx-popover-value-row\">\n <span class=\"ctx-popover-value\"></span>\n <span class=\"ctx-popover-auto-tag\"></span>\n </div>\n <input type=\"range\" class=\"ctx-popover-slider\" min=\"0\" max=\"${MAX_INDEX}\" step=\"1\">\n <div class=\"ctx-popover-scale\" aria-hidden=\"true\"></div>\n <div class=\"ctx-popover-scale-labels\" aria-hidden=\"true\">\n <span class=\"ctx-popover-scale-start\"></span>\n <span class=\"ctx-popover-scale-end\"></span>\n </div>\n `;\n\n sliderEl = popoverEl.querySelector('.ctx-popover-slider');\n valueEl = popoverEl.querySelector('.ctx-popover-value');\n autoTagEl = popoverEl.querySelector('.ctx-popover-auto-tag');\n autoBtnEl = popoverEl.querySelector('.ctx-popover-auto-btn');\n\n // 刻度:12 个点与档位一一对应(对齐由 CSS 的 thumb 半径内缩保证),起止文字取首末档位\n const scaleEl = popoverEl.querySelector('.ctx-popover-scale');\n for (let i = 0; i < CTX_LADDER.length; i++) {\n const dot = document.createElement('span');\n dot.className = 'ctx-popover-scale-dot';\n scaleEl.appendChild(dot);\n }\n scaleDots = [...scaleEl.children];\n popoverEl.querySelector('.ctx-popover-scale-start').textContent = formatContextWindow(CTX_LADDER[0]);\n popoverEl.querySelector('.ctx-popover-scale-end').textContent = formatContextWindow(CTX_LADDER[MAX_INDEX]);\n\n // 按钮文案与状态标签区分(「恢复自动」 vs 「自动」),避免两处同名造成“同一控件跳位”的误读\n autoBtnEl.textContent = t('settings.ctxSliderResetAuto');\n autoBtnEl.title = t('settings.ctxSliderAutoTitle');\n autoTagEl.textContent = t('settings.ctxSliderAuto');\n\n // 拖拽会话:pointerdown 记录起点(未松手期间只预览不落存储,收口逻辑见 pointerup)\n sliderEl.addEventListener('pointerdown', () => {\n if (!state) return;\n const originTokens = parseInt(state.optionEl.dataset.contextWindow, 10) || 0;\n state.drag = {\n originTokens,\n originIndex: tokensToIndex(originTokens || inferContextWindow(state.optionEl.dataset.value)),\n moved: false,\n };\n });\n\n // 拖拽实时预览:只更新徽标,不写存储\n sliderEl.addEventListener('input', () => {\n if (!state) return;\n if (state.drag) state.drag.moved = true;\n const tokens = indexToTokens(sliderEl.value);\n renderValue(tokens, false);\n state.callbacks.onPreview?.(tokens);\n });\n\n // 松手 / 键盘步进后提交并持久化\n sliderEl.addEventListener('change', () => {\n if (!state) return;\n state.callbacks.onCommit?.(indexToTokens(sliderEl.value));\n });\n\n // 恢复自动推断\n autoBtnEl.addEventListener('click', () => {\n if (!state) return;\n state.callbacks.onReset?.();\n renderValue(inferContextWindow(state.optionEl.dataset.value), true);\n });\n}\n\n// 值区渲染:显式值用品牌色;自动态灰色 + 「自动」标签(滑杆同步到最近档位并填充进度)\nfunction renderValue(tokens, isAuto) {\n const index = tokensToIndex(tokens);\n sliderEl.value = String(index);\n valueEl.textContent = formatContextWindow(tokens);\n valueEl.classList.toggle('is-auto', isAuto);\n autoTagEl.style.display = isAuto ? '' : 'none';\n autoBtnEl.style.display = isAuto ? 'none' : '';\n // 当前档位刻度点高亮\n scaleDots.forEach((dot, i) => dot.classList.toggle('is-active', i === index));\n const pct = (index / MAX_INDEX * 100).toFixed(1) + '%';\n sliderEl.style.setProperty('--ctx-fill', pct);\n}\n\n// 回滚未松手拖拽的预览副作用(中断/关闭时调用):恢复 dataset/徽标与浮层显示,不改存储\nfunction revertDragPreview(drag) {\n if (!drag || !drag.moved || !state) return;\n state.callbacks.onPreview?.(drag.originTokens);\n if (drag.originTokens > 0) {\n renderValue(drag.originTokens, false);\n } else {\n renderValue(inferContextWindow(state.optionEl.dataset.value), true);\n }\n}\n\n// 定位:水平右缘对齐图标并夹取在视口内;垂直默认下方,空间不足翻转上方\nfunction positionPopover() {\n const rect = state.anchorBtn.getBoundingClientRect();\n const popRect = popoverEl.getBoundingClientRect();\n const gap = 6;\n const margin = 8;\n\n let left = rect.right - popRect.width;\n left = Math.max(margin, Math.min(left, window.innerWidth - popRect.width - margin));\n\n let top = rect.bottom + gap;\n if (top + popRect.height > window.innerHeight - margin) {\n const above = rect.top - gap - popRect.height;\n top = above >= margin ? above : Math.max(margin, window.innerHeight - popRect.height - margin);\n }\n\n popoverEl.style.left = `${Math.round(left)}px`;\n popoverEl.style.top = `${Math.round(top)}px`;\n}\n\n/**\n * 打开上下文窗口滑杆浮层(再次点击同一锚点 = 收起)\n * @param {HTMLElement} anchorBtn - 触发图标(.ctx-set-btn)\n * @param {HTMLElement} optionEl - 对应的模型选项(.model-option)\n * @param {{onPreview?:Function,onCommit?:Function,onReset?:Function}} [callbacks]\n */\nexport function openCtxPopover(anchorBtn, optionEl, callbacks = {}) {\n if (!anchorBtn || !optionEl) return;\n if (state && state.anchorBtn === anchorBtn) {\n closeCtxPopover();\n return;\n }\n closeCtxPopover();\n\n const tokens = parseInt(optionEl.dataset.contextWindow, 10) || 0;\n buildPopoverEl();\n state = { anchorBtn, optionEl, callbacks };\n\n popoverEl.querySelector('.ctx-popover-title').textContent = optionEl.dataset.value || '';\n if (tokens > 0) {\n renderValue(tokens, false);\n } else {\n renderValue(inferContextWindow(optionEl.dataset.value), true);\n }\n\n document.body.appendChild(popoverEl);\n positionPopover();\n // preventScroll:聚焦滑杆不影响页面/列表滚动,避免误触 scroll 关闭\n sliderEl.focus({ preventScroll: true });\n\n // 关闭时机监听:\n // - click 用捕获阶段,不受各元素 stopPropagation 影响(如输入框的点击拦截)\n // - scroll 用捕获阶段,覆盖下拉列表滚动与页面滚动\n state.onDocClick = (e) => {\n if (popoverEl.contains(e.target) || anchorBtn.contains(e.target)) return;\n closeCtxPopover();\n };\n state.onKeyDown = (e) => {\n if (e.key === 'Escape') closeCtxPopover();\n };\n state.onScroll = () => closeCtxPopover();\n state.onResize = () => closeCtxPopover();\n // pointerup 收口拖拽会话:Chrome 在「终点档位 == 起点档位」时不派发 change,\n // 需在此补提交固化;终点不同则交给随后的 change 提交(避免重复)\n state.onDocPointerUp = () => {\n if (!state?.drag) return;\n const drag = state.drag;\n state.drag = null;\n if (!drag.moved) return;\n const currentIndex = Number(sliderEl.value);\n if (currentIndex === drag.originIndex) {\n state.callbacks.onCommit?.(indexToTokens(currentIndex));\n }\n };\n // 指针被系统中断(如触控被接管):视为操作未完成,回滚预览不提交\n state.onDocPointerCancel = () => {\n if (!state?.drag) return;\n const drag = state.drag;\n state.drag = null;\n revertDragPreview(drag);\n };\n\n document.addEventListener('click', state.onDocClick, true);\n document.addEventListener('keydown', state.onKeyDown);\n document.addEventListener('scroll', state.onScroll, true);\n document.addEventListener('pointerup', state.onDocPointerUp, true);\n document.addEventListener('pointercancel', state.onDocPointerCancel, true);\n window.addEventListener('resize', state.onResize);\n}\n\n/**\n * 关闭浮层(幂等)\n *\n * 若存在未收口的拖拽(未松手),视为操作未完成:回滚预览副作用且不提交。\n * remove() 会使拖拽中的 range 同步派发 change,故先置 state = null,\n * 让事件处理器中的 `if (!state) return` 拦截,避免以中途值误提交。\n */\nexport function closeCtxPopover() {\n if (!state) return;\n const s = state;\n revertDragPreview(s.drag);\n state = null;\n document.removeEventListener('click', s.onDocClick, true);\n document.removeEventListener('keydown', s.onKeyDown);\n document.removeEventListener('scroll', s.onScroll, true);\n document.removeEventListener('pointerup', s.onDocPointerUp, true);\n document.removeEventListener('pointercancel', s.onDocPointerCancel, true);\n window.removeEventListener('resize', s.onResize);\n popoverEl?.remove();\n popoverEl = null;\n sliderEl = null;\n valueEl = null;\n autoTagEl = null;\n autoBtnEl = null;\n scaleDots = [];\n}\n\n/**\n * 判断事件目标是否位于浮层内部(供下拉的文档级关闭逻辑排除浮层点击)\n * @param {EventTarget|null} el\n * @returns {boolean}\n */\nexport function containsCtxPopoverEl(el) {\n return !!(popoverEl && el && popoverEl.contains(el));\n}\n","// options/config-manager.js - 配置管理与状态\n\nimport { PRESET_MODELS, PRESET_IMAGE_MODELS, PRESET_API_BASES, DEFAULT_SYSTEM_PROMPT, DEFAULT_REACT_CONFIG, DEFAULT_CHAT_CONFIG, DEFAULT_REFLECTION_CONFIG } from './constants.js';\nimport logger from '../shared/logger.js';\nimport { t } from '../shared/i18n.js';\nimport { normalizeModels, syncActiveProfileModels, updateActiveProfile, compareByName } from '../shared/model-profiles.js';\nimport { formatContextWindow, inferContextWindow } from '../shared/token-counter.js';\nimport { closeCtxPopover } from './ctx-popover.js';\nimport { encodeSecret, decodeSecret } from '../shared/secret.js';\n\n\n// Re-export PRESET_MODELS so index.js can use it\nexport { PRESET_MODELS, PRESET_IMAGE_MODELS };\n\nlet currentModel = 'deepseek-v4-pro';\nexport { currentModel };\nexport function setCurrentModel(value) { currentModel = value; }\n\nlet currentImageModel = '';\nexport { currentImageModel };\nexport function setCurrentImageModel(value) { currentImageModel = value; }\n\n// 上下文窗口设置图标(滑杆图形,14x14)\nconst CTX_SET_ICON_SVG = '<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\"><g stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" fill=\"none\"><path d=\"M1.2 4h3.1M9.7 4h3.1M1.2 10h4.6M11.2 10h1.6\"/><circle cx=\"7\" cy=\"4\" r=\"1.6\"/><circle cx=\"8.5\" cy=\"10\" r=\"1.6\"/></g></svg>';\n\n/**\n * 构建上下文窗口设置按钮(选项行右侧入口,点击弹出滑杆浮层)\n */\nfunction buildCtxSetButton() {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'ctx-set-btn';\n btn.title = t('settings.ctxSliderIconTitle');\n btn.setAttribute('aria-label', t('settings.ctxSliderIconTitle'));\n btn.innerHTML = CTX_SET_ICON_SVG;\n return btn;\n}\n\n/**\n * 同步选项行上下文徽标(行内徽标唯一渲染入口):\n * 显式值 = 蓝色实底;自动态(0)= 灰色虚线(is-auto)+ 内置推断值 + tooltip\n * @param {HTMLElement} optionEl 需已含 .model-option-right\n * @param {number} tokens 0 表示自动推断\n */\nfunction syncCtxBadge(optionEl, tokens) {\n const rightSpan = optionEl.querySelector('.model-option-right');\n if (!rightSpan) return;\n const value = Number(tokens) || 0;\n let badge = rightSpan.querySelector('.model-ctx-badge');\n if (!badge) {\n badge = document.createElement('span');\n badge.className = 'model-ctx-badge';\n rightSpan.insertBefore(badge, rightSpan.firstChild);\n }\n if (value > 0) {\n badge.classList.remove('is-auto');\n badge.textContent = formatContextWindow(value);\n badge.removeAttribute('title');\n } else {\n badge.classList.add('is-auto');\n badge.textContent = formatContextWindow(inferContextWindow(optionEl.dataset.value));\n badge.title = t('settings.ctxBadgeAutoTip');\n }\n}\n\n// 说明:模型列表统一由\"当前厂商配置\"的 models 字段管理(见 shared/model-profiles.js)\n// 添加模型到下拉列表(支持重新加回已删除的预设模型)\n// modelName: 模型名称\n// contextWindow: 可选的上下文窗口大小,0 或不传表示使用内置映射自动推断\n/**\n * 将旧格式(文本节点直接放在 option 下)迁移为 model-option-left 包裹格式\n */\nfunction migrateOptionLeft(option) {\n const leftSpan = document.createElement('span');\n leftSpan.className = 'model-option-left';\n const textNodes = [];\n for (const child of [...option.childNodes]) {\n if (child.nodeType === Node.TEXT_NODE) {\n textNodes.push(child);\n }\n }\n textNodes.forEach(n => {\n leftSpan.appendChild(n);\n });\n // badge 现在属于右侧容器,不放入 leftSpan\n option.insertBefore(leftSpan, option.firstChild);\n}\n\nexport function addCustomModelToDropdown(modelName, contextWindow) {\n const modelDropdown = document.getElementById('modelDropdown');\n // 检查是否已存在\n const existingOption = modelDropdown.querySelector(`.model-option[data-value=\"${modelName}\"]`);\n if (existingOption) {\n // 如果已存在但传入了新的 contextWindow,更新右侧显示\n if (contextWindow && contextWindow > 0) {\n existingOption.dataset.contextWindow = contextWindow;\n existingOption.dataset.isCustom = 'true';\n\n // 确保模型名有 model-option-left 包裹\n let leftSpan = existingOption.querySelector('.model-option-left');\n if (!leftSpan) { migrateOptionLeft(existingOption); leftSpan = existingOption.querySelector('.model-option-left'); }\n\n // 确保有右侧容器(badge + 上下文滑杆按钮 + 可选删除按钮)\n let rightSpan = existingOption.querySelector('.model-option-right');\n if (!rightSpan) {\n rightSpan = document.createElement('span');\n rightSpan.className = 'model-option-right';\n // 把旧结构中散落的 badge 移入右侧容器\n const oldBadge = existingOption.querySelector(':scope > .model-ctx-badge');\n if (oldBadge) rightSpan.appendChild(oldBadge);\n rightSpan.appendChild(buildCtxSetButton());\n let deleteBtn = existingOption.querySelector(':scope > .delete-model-btn');\n if (!deleteBtn) {\n deleteBtn = document.createElement('button');\n deleteBtn.type = 'button';\n deleteBtn.className = 'delete-model-btn';\n deleteBtn.title = t('settings.deleteModel');\n deleteBtn.innerHTML = '×';\n }\n deleteBtn.style.display = 'inline-block';\n rightSpan.appendChild(deleteBtn);\n existingOption.appendChild(rightSpan);\n } else if (!rightSpan.querySelector('.ctx-set-btn')) {\n // 旧结构补齐:滑杆按钮插入在删除按钮之前\n const deleteBtn = rightSpan.querySelector('.delete-model-btn');\n if (deleteBtn) rightSpan.insertBefore(buildCtxSetButton(), deleteBtn);\n else rightSpan.appendChild(buildCtxSetButton());\n }\n\n // 更新 badge(统一入口:自动态/显式态样式口径一致)\n syncCtxBadge(existingOption, contextWindow);\n\n saveCustomModels();\n }\n return;\n }\n \n insertModelOptionSorted(modelDropdown, buildModelOption(modelName, contextWindow || 0), modelName);\n\n // 保存模型列表到存储(并同步到当前厂商配置)\n saveCustomModels();\n}\n\n/**\n * 构建单个模型选项 DOM(名称 + 上下文窗口 badge + 删除按钮)\n * @param {string} modelName\n * @param {number} [contextWindow]\n * @returns {HTMLDivElement}\n */\nexport function buildModelOption(modelName, contextWindow = 0) {\n const option = document.createElement('div');\n option.className = 'model-option';\n option.dataset.value = modelName;\n // 预设模型不加 isCustom 标记(除非用户配置了上下文窗口)\n const isPreset = PRESET_MODELS.includes(modelName);\n if (!isPreset || (contextWindow && contextWindow > 0)) {\n option.dataset.isCustom = 'true';\n }\n if (contextWindow && contextWindow > 0) {\n option.dataset.contextWindow = contextWindow;\n }\n\n // 模型名称(左侧)\n const nameSpan = document.createElement('span');\n nameSpan.className = 'model-option-left';\n nameSpan.textContent = modelName;\n option.appendChild(nameSpan);\n\n // 右侧容器:badge + 上下文滑杆按钮 + 删除按钮(badge 始终存在,自动态为灰色虚线推断值)\n const rightSpan = document.createElement('span');\n rightSpan.className = 'model-option-right';\n rightSpan.appendChild(buildCtxSetButton());\n const deleteBtn = document.createElement('button');\n deleteBtn.type = 'button';\n deleteBtn.className = 'delete-model-btn';\n deleteBtn.title = t('settings.deleteModel');\n deleteBtn.innerHTML = '×';\n deleteBtn.style.display = 'inline-block';\n rightSpan.appendChild(deleteBtn);\n option.appendChild(rightSpan);\n syncCtxBadge(option, contextWindow || 0);\n\n return option;\n}\n\n/**\n * 按名称顺序插入模型选项,保持下拉列表始终有序\n * (排序规则与 renderModelDropdownFromList 一致)\n * @param {HTMLElement} dropdown 目标下拉容器\n * @param {HTMLElement} option 已构建的选项元素\n * @param {string} modelName 新模型名(用于定位排序位置)\n */\nfunction insertModelOptionSorted(dropdown, option, modelName) {\n const next = [...dropdown.querySelectorAll('.model-option')]\n .find(o => compareByName(o.dataset.value, modelName) > 0);\n if (next) {\n dropdown.insertBefore(option, next);\n } else {\n dropdown.appendChild(option);\n }\n}\n\n/**\n * 过滤指定下拉中的模型选项(输入框即搜索框)。\n * 不区分大小写、包含匹配整项文本(模型名 + 上下文徽标);\n * 无匹配时创建\"无匹配项\"空态,空查询恢复全部选项。\n * @param {HTMLElement|null} dropdown 下拉容器(#modelDropdown / #imageModelDropdown)\n * @param {string} query 搜索关键字\n */\nexport function filterModelDropdown(dropdown, query) {\n if (!dropdown) return;\n const q = String(query || '').trim().toLowerCase();\n let visible = 0;\n dropdown.querySelectorAll('.model-option').forEach(option => {\n const hit = !q || option.textContent.toLowerCase().includes(q);\n option.style.display = hit ? '' : 'none';\n if (hit) visible += 1;\n });\n let empty = dropdown.querySelector('.model-dropdown-empty');\n if (q && visible === 0) {\n if (!empty) {\n empty = document.createElement('div');\n empty.className = 'model-dropdown-empty';\n dropdown.appendChild(empty);\n }\n empty.textContent = t('settings.noMatch');\n } else if (empty) {\n empty.remove();\n }\n}\n\n// 从下拉列表移除模型(删除后立即同步存储与当前厂商配置)\nexport function removeCustomModel(modelName) {\n closeCtxPopover();\n const modelDropdown = document.getElementById('modelDropdown');\n const option = modelDropdown.querySelector(`.model-option[data-value=\"${modelName}\"]`);\n if (option) {\n option.remove();\n }\n\n // 如果当前选中的是被删除的模型,回退为第一个剩余模型或空\n if (currentModel === modelName) {\n const firstOption = modelDropdown.querySelector('.model-option');\n const newModel = firstOption ? firstOption.dataset.value : '';\n setCurrentModel(newModel);\n const modelInput = document.getElementById('modelInput');\n if (modelInput) modelInput.value = newModel;\n updateModelSelection(newModel);\n }\n\n // 更新存储(并同步到当前厂商配置)\n saveCustomModels();\n}\n\n// 快照当前模型下拉中的完整模型列表\nexport function collectModelsFromDropdown() {\n const modelDropdown = document.getElementById('modelDropdown');\n const customModels = [];\n modelDropdown?.querySelectorAll('.model-option').forEach(option => {\n customModels.push({\n name: option.dataset.value,\n contextWindow: parseInt(option.dataset.contextWindow) || 0\n });\n });\n return customModels;\n}\n\n// 保存模型列表到存储(快照整个下拉,并同步到当前厂商配置)\nexport function saveCustomModels() {\n const customModels = collectModelsFromDropdown();\n chrome.storage.local.set({ customModels }, () => {\n logger.debug('[Options] customModel list saved:', customModels);\n syncActiveProfileModels(customModels);\n });\n}\n\n// 加载模型列表到下拉列表(全量重建:列表来源为当前厂商配置的完整模型列表)\nexport function loadCustomModels(callback) {\n chrome.storage.local.get(['customModels'], (result) => {\n renderModelDropdownFromList(normalizeModels(result.customModels));\n if (typeof callback === 'function') callback();\n });\n}\n\n/**\n * 全量重建模型下拉列表(清空后按列表渲染)\n * @param {Array<{name:string, contextWindow:number}>} models\n * @param {string} [selectedValue] - 需要标记为选中的模型名(默认取 currentModel)\n */\nexport function renderModelDropdownFromList(models, selectedValue) {\n const modelDropdown = document.getElementById('modelDropdown');\n if (!modelDropdown) return;\n // 列表重建后原锚点失效,防御性关闭浮层\n closeCtxPopover();\n modelDropdown.innerHTML = '';\n // 展示副本按名称排序(不影响存储顺序)\n const sorted = [...(models || [])].sort((a, b) => compareByName(a?.name, b?.name));\n for (const item of sorted) {\n modelDropdown.appendChild(buildModelOption(item.name, item.contextWindow || 0));\n }\n const selected = selectedValue !== undefined ? selectedValue : currentModel;\n modelDropdown.querySelectorAll('.model-option').forEach(option => {\n option.classList.toggle('selected', option.dataset.value === selected);\n });\n}\n\n// 更新模型选中状态\nexport function updateModelSelection(selectedValue) {\n document.querySelectorAll('.model-option').forEach(option => {\n if (option.dataset.value === selectedValue) {\n option.classList.add('selected');\n } else {\n option.classList.remove('selected');\n }\n });\n updateSelectedCtxBadge('modelInput', 'modelSelectedCtxBadge', 'modelDropdown');\n}\n\n/**\n * 更新选中模型输入框右侧的上下文窗口大小标签\n */\nexport function updateSelectedCtxBadge(inputId, badgeId, dropdownId) {\n const input = document.getElementById(inputId);\n const badge = document.getElementById(badgeId);\n const dropdown = document.getElementById(dropdownId);\n if (!badge || !dropdown) return;\n \n // 如果没有选中模型(输入为空),隐藏 badge\n if (input && !input.value) {\n badge.style.display = 'none';\n return;\n }\n \n const selectedOption = dropdown.querySelector('.model-option.selected') \n || (input ? dropdown.querySelector(`.model-option[data-value=\"${input.value}\"]`) : null);\n \n if (selectedOption) {\n const ctxWindow = parseInt(selectedOption.dataset.contextWindow);\n if (ctxWindow > 0) {\n badge.textContent = formatContextWindow(ctxWindow);\n badge.style.display = '';\n } else {\n badge.style.display = 'none';\n }\n } else {\n badge.style.display = 'none';\n }\n}\n\n/**\n * 应用上下文窗口到指定模型选项(仅更新 DOM 与 dataset,不持久化)\n * tokens = 0 时恢复自动推断:徽标切回灰色虚线推断值,清除 dataset(预设模型同时清除 isCustom 标记)\n * @param {HTMLElement} optionEl\n * @param {number} tokens\n */\nexport function applyModelContextWindow(optionEl, tokens) {\n if (!optionEl) return;\n const rightSpan = optionEl.querySelector('.model-option-right');\n if (!rightSpan) return;\n const value = Number(tokens) || 0;\n const isPreset = PRESET_MODELS.includes(optionEl.dataset.value);\n\n if (value > 0) {\n optionEl.dataset.contextWindow = String(value);\n if (isPreset) optionEl.dataset.isCustom = 'true';\n } else {\n delete optionEl.dataset.contextWindow;\n if (isPreset) delete optionEl.dataset.isCustom;\n }\n syncCtxBadge(optionEl, value);\n}\n\n/**\n * 提交上下文窗口变更:更新 DOM + 立即持久化(与增删模型一致),并刷新选中徽标\n * @param {HTMLElement} optionEl\n * @param {number} tokens\n */\nexport function commitModelContextWindow(optionEl, tokens) {\n applyModelContextWindow(optionEl, tokens);\n saveCustomModels();\n updateSelectedCtxBadge('modelInput', 'modelSelectedCtxBadge', 'modelDropdown');\n}\n\n// ============================================================\n// 图片识别模型管理(所有模型均可删除,包括预设)\n// ============================================================\n\n// 添加图片识别模型到下拉列表\n// modelName: 模型名称\n// contextWindow: 可选的上下文窗口大小,0 或不传表示使用内置映射自动推断\nexport function addCustomImageModelToDropdown(modelName, contextWindow) {\n const modelDropdown = document.getElementById('imageModelDropdown');\n if (!modelDropdown) return;\n const existingOption = modelDropdown.querySelector(`.model-option[data-value=\"${modelName}\"]`);\n if (existingOption) {\n // 如果已存在但传入了新的 contextWindow,更新右侧显示\n if (contextWindow && contextWindow > 0) {\n existingOption.dataset.contextWindow = contextWindow;\n\n let leftSpan = existingOption.querySelector('.model-option-left');\n if (!leftSpan) { migrateOptionLeft(existingOption); leftSpan = existingOption.querySelector('.model-option-left'); }\n\n // 右侧容器(badge + 上下文滑杆按钮 + 删除按钮)\n let rightSpan = existingOption.querySelector('.model-option-right');\n if (!rightSpan) {\n rightSpan = document.createElement('span');\n rightSpan.className = 'model-option-right';\n const oldBadge = existingOption.querySelector(':scope > .model-ctx-badge');\n if (oldBadge) rightSpan.appendChild(oldBadge);\n rightSpan.appendChild(buildCtxSetButton());\n // 图片模型都是自定义,保留已有删除按钮\n const oldDelete = existingOption.querySelector(':scope > .delete-model-btn');\n if (oldDelete) rightSpan.appendChild(oldDelete);\n existingOption.appendChild(rightSpan);\n } else if (!rightSpan.querySelector('.ctx-set-btn')) {\n // 旧结构补齐:滑杆按钮插入在删除按钮之前\n const deleteBtn = rightSpan.querySelector('.delete-model-btn');\n if (deleteBtn) rightSpan.insertBefore(buildCtxSetButton(), deleteBtn);\n else rightSpan.appendChild(buildCtxSetButton());\n }\n\n // 更新 badge(统一入口:自动态/显式态样式口径一致)\n syncCtxBadge(existingOption, contextWindow);\n // 持久化更新(与主模型 addCustomModelToDropdown 行为对齐)\n saveImageModels();\n }\n return;\n }\n\n const option = document.createElement('div');\n option.className = 'model-option';\n option.dataset.value = modelName;\n if (contextWindow && contextWindow > 0) {\n option.dataset.contextWindow = contextWindow;\n }\n\n const nameSpan = document.createElement('span');\n nameSpan.className = 'model-option-left';\n nameSpan.textContent = modelName;\n option.appendChild(nameSpan);\n\n // 右侧容器:badge + 上下文滑杆按钮 + 删除按钮(badge 始终存在,自动态为灰色虚线推断值)\n const rightSpan = document.createElement('span');\n rightSpan.className = 'model-option-right';\n rightSpan.appendChild(buildCtxSetButton());\n const deleteBtn = document.createElement('button');\n deleteBtn.type = 'button';\n deleteBtn.className = 'delete-model-btn';\n deleteBtn.title = t('settings.deleteModel');\n deleteBtn.innerHTML = '×';\n // 图片模型删除按钮始终可见\n deleteBtn.style.display = 'inline-block';\n rightSpan.appendChild(deleteBtn);\n option.appendChild(rightSpan);\n syncCtxBadge(option, contextWindow || 0);\n\n insertModelOptionSorted(modelDropdown, option, modelName);\n saveImageModels();\n}\n\n// 从下拉列表移除图片识别模型(包括预设)\nexport function removeImageModel(modelName) {\n // 删除后原锚点失效,防御性关闭浮层\n closeCtxPopover();\n const modelDropdown = document.getElementById('imageModelDropdown');\n if (!modelDropdown) return;\n const option = modelDropdown.querySelector(`.model-option[data-value=\"${modelName}\"]`);\n if (option) {\n option.remove();\n }\n\n // 如果当前选中的是被删除的模型,恢复为第一个剩余模型或空\n if (currentImageModel === modelName) {\n const firstOption = modelDropdown.querySelector('.model-option');\n const newModel = firstOption ? firstOption.dataset.value : '';\n setCurrentImageModel(newModel);\n const input = document.getElementById('imageModelInput');\n if (input) input.value = newModel;\n updateImageModelSelection(newModel);\n }\n\n saveImageModels();\n}\n\n// 保存图片识别模型列表到存储(新格式:对象数组)\nexport function saveImageModels() {\n const modelDropdown = document.getElementById('imageModelDropdown');\n if (!modelDropdown) return;\n const imageModels = [];\n modelDropdown.querySelectorAll('.model-option').forEach(option => {\n imageModels.push({\n name: option.dataset.value,\n contextWindow: parseInt(option.dataset.contextWindow) || 0\n });\n });\n chrome.storage.local.set({ imageModels }, () => {\n logger.debug('[Options] image recognitionmodel saved:', imageModels);\n });\n}\n\n/**\n * 提交图片模型上下文窗口变更:更新 DOM + 立即持久化(与增删图片模型一致),并刷新选中徽标\n * @param {HTMLElement} optionEl\n * @param {number} tokens\n */\nexport function commitImageModelContextWindow(optionEl, tokens) {\n applyModelContextWindow(optionEl, tokens);\n saveImageModels();\n updateSelectedCtxBadge('imageModelInput', 'imageModelSelectedCtxBadge', 'imageModelDropdown');\n}\n\n// 从存储加载图片识别模型到下拉列表\nexport function loadImageModels(callback) {\n chrome.storage.local.get(['imageModels'], (result) => {\n const modelDropdown = document.getElementById('imageModelDropdown');\n if (!modelDropdown) {\n if (typeof callback === 'function') callback();\n return;\n }\n const imageModels = result.imageModels || [];\n let needsMigration = false;\n\n // 列表重建后原锚点失效,防御性关闭浮层\n closeCtxPopover();\n // 清空现有选项\n modelDropdown.innerHTML = '';\n\n // 渲染前按名称排序(兼容字符串旧格式与对象格式)\n const itemName = (item) => (typeof item === 'string' ? item : (item && item.name) || '');\n [...imageModels].sort((a, b) => compareByName(itemName(a), itemName(b))).forEach(item => {\n // 向前兼容:旧格式为字符串,新格式为对象\n let modelName, contextWindow = 0;\n if (typeof item === 'string') {\n modelName = item;\n needsMigration = true;\n } else if (item && typeof item === 'object' && item.name) {\n modelName = item.name;\n contextWindow = item.contextWindow || 0;\n } else {\n return;\n }\n\n const option = document.createElement('div');\n option.className = 'model-option';\n option.dataset.value = modelName;\n if (contextWindow && contextWindow > 0) {\n option.dataset.contextWindow = contextWindow;\n }\n\n // 模型名称(左侧)\n const nameSpan = document.createElement('span');\n nameSpan.className = 'model-option-left';\n nameSpan.textContent = modelName;\n option.appendChild(nameSpan);\n\n // 右侧容器:badge + 上下文滑杆按钮 + 删除按钮(badge 始终存在,自动态为灰色虚线推断值)\n const rightSpan = document.createElement('span');\n rightSpan.className = 'model-option-right';\n rightSpan.appendChild(buildCtxSetButton());\n const deleteBtn = document.createElement('button');\n deleteBtn.type = 'button';\n deleteBtn.className = 'delete-model-btn';\n deleteBtn.title = t('settings.deleteModel');\n deleteBtn.innerHTML = '×';\n deleteBtn.style.display = 'inline-block';\n rightSpan.appendChild(deleteBtn);\n option.appendChild(rightSpan);\n syncCtxBadge(option, contextWindow || 0);\n\n modelDropdown.appendChild(option);\n });\n\n // 如果存在旧格式数据,自动迁移\n if (needsMigration) {\n saveImageModels();\n logger.debug('[Options] image recognitionmodelauto-migratedisnewformat');\n }\n\n if (typeof callback === 'function') {\n callback();\n }\n });\n}\n\n// 更新图片识别模型选中状态\nexport function updateImageModelSelection(selectedValue) {\n const modelDropdown = document.getElementById('imageModelDropdown');\n if (!modelDropdown) return;\n modelDropdown.querySelectorAll('.model-option').forEach(option => {\n if (option.dataset.value === selectedValue) {\n option.classList.add('selected');\n } else {\n option.classList.remove('selected');\n }\n });\n updateSelectedCtxBadge('imageModelInput', 'imageModelSelectedCtxBadge', 'imageModelDropdown');\n}\n\n// ============================================================\n// API Base URL 管理(预设 + 用户自定义,支持增删改)\n// ============================================================\n\n// 已被用户删除的预设 API Base URL 列表\nlet deletedPresetApiBases = [];\n\n/**\n * 构建单个 API Base 选项 DOM 元素\n */\nfunction buildApiBaseOption(url, label, isCustom) {\n const option = document.createElement('div');\n option.className = 'model-option';\n option.dataset.value = url;\n if (label) option.dataset.label = label;\n if (isCustom) option.dataset.isCustom = 'true';\n\n const leftSpan = document.createElement('span');\n leftSpan.className = 'model-option-left';\n leftSpan.textContent = label || url;\n option.appendChild(leftSpan);\n\n const rightSpan = document.createElement('span');\n rightSpan.className = 'model-option-right';\n\n // 如果有标签,显示简短 URL 作为辅助信息\n if (label) {\n const urlHint = document.createElement('span');\n urlHint.className = 'api-base-url-hint';\n urlHint.textContent = url.length > 50 ? url.substring(0, 47) + '...' : url;\n rightSpan.appendChild(urlHint);\n }\n\n const deleteBtn = document.createElement('button');\n deleteBtn.type = 'button';\n deleteBtn.className = 'delete-model-btn';\n deleteBtn.title = t('settings.deleteApiBase');\n deleteBtn.innerHTML = '×';\n deleteBtn.style.display = 'inline-block';\n rightSpan.appendChild(deleteBtn);\n option.appendChild(rightSpan);\n\n return option;\n}\n\n/**\n * 添加 API Base URL 到下拉列表\n */\nexport function addCustomApiBase(url, label) {\n const dropdown = document.getElementById('apiBaseDropdown');\n if (!dropdown) return;\n\n // 检查是否已存在\n const existing = dropdown.querySelector(`.model-option[data-value=\"${CSS.escape(url)}\"]`);\n if (existing) {\n // 更新 label\n if (label) {\n existing.dataset.label = label;\n const leftSpan = existing.querySelector('.model-option-left');\n if (leftSpan) leftSpan.textContent = label;\n }\n saveApiBases();\n return;\n }\n\n // 如果之前被删过,从删除列表移除\n if (PRESET_API_BASES.includes(url) && deletedPresetApiBases.includes(url)) {\n deletedPresetApiBases = deletedPresetApiBases.filter(u => u !== url);\n }\n\n const isCustom = !PRESET_API_BASES.includes(url);\n const option = buildApiBaseOption(url, label, isCustom);\n dropdown.appendChild(option);\n\n saveApiBases();\n}\n\n/**\n * 从下拉列表移除 API Base URL\n */\nexport function removeApiBase(url) {\n const dropdown = document.getElementById('apiBaseDropdown');\n if (!dropdown) return;\n\n const option = dropdown.querySelector(`.model-option[data-value=\"${CSS.escape(url)}\"]`);\n if (option) option.remove();\n\n // 如果是预设,记录到删除列表\n if (PRESET_API_BASES.includes(url) && !deletedPresetApiBases.includes(url)) {\n deletedPresetApiBases.push(url);\n }\n\n // 如果当前选中的是被删除的 URL,清空输入框\n const apiBaseInput = document.getElementById('apiBase');\n if (apiBaseInput && apiBaseInput.value.trim() === url) {\n apiBaseInput.value = '';\n }\n\n saveApiBases();\n}\n\n/**\n * 保存 API Base URL 列表到存储\n */\nexport function saveApiBases() {\n const dropdown = document.getElementById('apiBaseDropdown');\n if (!dropdown) return;\n\n const customBases = [];\n dropdown.querySelectorAll('.model-option[data-is-custom=\"true\"]').forEach(opt => {\n customBases.push({\n url: opt.dataset.value,\n label: opt.dataset.label || ''\n });\n });\n\n chrome.storage.local.set({\n customApiBases: customBases,\n deletedPresetApiBases\n }, () => {\n logger.debug('[Options] API Base URL column table saved:', customBases, 'deletedpreset:', deletedPresetApiBases);\n });\n}\n\n/**\n * 从存储加载 API Base URL 列表到下拉\n */\nexport function loadApiBases(callback) {\n chrome.storage.local.get(['customApiBases', 'deletedPresetApiBases'], (result) => {\n const dropdown = document.getElementById('apiBaseDropdown');\n if (!dropdown) {\n if (typeof callback === 'function') callback();\n return;\n }\n\n deletedPresetApiBases = result.deletedPresetApiBases || [];\n const customBases = result.customApiBases || [];\n\n // 清空现有选项\n dropdown.innerHTML = '';\n\n // 先渲染预设(未被删除的)\n PRESET_API_BASES.forEach(url => {\n if (!deletedPresetApiBases.includes(url)) {\n const option = buildApiBaseOption(url, null, false);\n dropdown.appendChild(option);\n }\n });\n\n // 再渲染用户自定义\n customBases.forEach(item => {\n if (typeof item === 'string') {\n // 旧格式兼容\n if (!PRESET_API_BASES.includes(item)) {\n const option = buildApiBaseOption(item, null, true);\n dropdown.appendChild(option);\n }\n } else if (item && item.url) {\n const option = buildApiBaseOption(item.url, item.label || null, true);\n dropdown.appendChild(option);\n }\n });\n\n if (typeof callback === 'function') callback();\n });\n}\n\n/**\n * 更新 API Base 下拉选中状态\n */\nexport function updateApiBaseSelection(url) {\n const dropdown = document.getElementById('apiBaseDropdown');\n if (!dropdown) return;\n dropdown.querySelectorAll('.model-option').forEach(opt => {\n if (opt.dataset.value === url) {\n opt.classList.add('selected');\n } else {\n opt.classList.remove('selected');\n }\n });\n}\n\n// ============================================================\n// 图片识别 API Base URL 管理(复用 PRESET_API_BASES,独立存储)\n// ============================================================\n\nlet deletedPresetImageApiBases = [];\n\nfunction buildImageApiBaseOption(url, label, isCustom) {\n const option = document.createElement('div');\n option.className = 'model-option';\n option.dataset.value = url;\n if (label) option.dataset.label = label;\n if (isCustom) option.dataset.isCustom = 'true';\n\n const leftSpan = document.createElement('span');\n leftSpan.className = 'model-option-left';\n leftSpan.textContent = label || url;\n option.appendChild(leftSpan);\n\n const rightSpan = document.createElement('span');\n rightSpan.className = 'model-option-right';\n\n if (label) {\n const urlHint = document.createElement('span');\n urlHint.className = 'api-base-url-hint';\n urlHint.textContent = url.length > 50 ? url.substring(0, 47) + '...' : url;\n rightSpan.appendChild(urlHint);\n }\n\n const deleteBtn = document.createElement('button');\n deleteBtn.type = 'button';\n deleteBtn.className = 'delete-model-btn';\n deleteBtn.title = t('settings.deleteApiBase');\n deleteBtn.innerHTML = '×';\n deleteBtn.style.display = 'inline-block';\n rightSpan.appendChild(deleteBtn);\n option.appendChild(rightSpan);\n\n return option;\n}\n\nexport function addCustomImageApiBase(url, label) {\n const dropdown = document.getElementById('imageApiBaseDropdown');\n if (!dropdown) return;\n\n const existing = dropdown.querySelector(`.model-option[data-value=\"${CSS.escape(url)}\"]`);\n if (existing) {\n if (label) {\n existing.dataset.label = label;\n const leftSpan = existing.querySelector('.model-option-left');\n if (leftSpan) leftSpan.textContent = label;\n }\n saveImageApiBases();\n return;\n }\n\n if (PRESET_API_BASES.includes(url) && deletedPresetImageApiBases.includes(url)) {\n deletedPresetImageApiBases = deletedPresetImageApiBases.filter(u => u !== url);\n }\n\n const isCustom = !PRESET_API_BASES.includes(url);\n const option = buildImageApiBaseOption(url, label, isCustom);\n dropdown.appendChild(option);\n\n saveImageApiBases();\n}\n\nexport function removeImageApiBase(url) {\n const dropdown = document.getElementById('imageApiBaseDropdown');\n if (!dropdown) return;\n\n const option = dropdown.querySelector(`.model-option[data-value=\"${CSS.escape(url)}\"]`);\n if (option) option.remove();\n\n if (PRESET_API_BASES.includes(url) && !deletedPresetImageApiBases.includes(url)) {\n deletedPresetImageApiBases.push(url);\n }\n\n const input = document.getElementById('imageApiBase');\n if (input && input.value.trim() === url) {\n input.value = '';\n }\n\n saveImageApiBases();\n}\n\nexport function saveImageApiBases() {\n const dropdown = document.getElementById('imageApiBaseDropdown');\n if (!dropdown) return;\n\n const customBases = [];\n dropdown.querySelectorAll('.model-option[data-is-custom=\"true\"]').forEach(opt => {\n customBases.push({\n url: opt.dataset.value,\n label: opt.dataset.label || ''\n });\n });\n\n chrome.storage.local.set({\n customImageApiBases: customBases,\n deletedPresetImageApiBases\n }, () => {\n logger.debug('[Options] image API Base URL column table saved:', customBases);\n });\n}\n\nexport function loadImageApiBases(callback) {\n chrome.storage.local.get(['customImageApiBases', 'deletedPresetImageApiBases'], (result) => {\n const dropdown = document.getElementById('imageApiBaseDropdown');\n if (!dropdown) {\n if (typeof callback === 'function') callback();\n return;\n }\n\n deletedPresetImageApiBases = result.deletedPresetImageApiBases || [];\n const customBases = result.customImageApiBases || [];\n\n dropdown.innerHTML = '';\n\n PRESET_API_BASES.forEach(url => {\n if (!deletedPresetImageApiBases.includes(url)) {\n const option = buildImageApiBaseOption(url, null, false);\n dropdown.appendChild(option);\n }\n });\n\n customBases.forEach(item => {\n if (typeof item === 'string') {\n if (!PRESET_API_BASES.includes(item)) {\n const option = buildImageApiBaseOption(item, null, true);\n dropdown.appendChild(option);\n }\n } else if (item && item.url) {\n const option = buildImageApiBaseOption(item.url, item.label || null, true);\n dropdown.appendChild(option);\n }\n });\n\n if (typeof callback === 'function') callback();\n });\n}\n\nexport function updateImageApiBaseSelection(url) {\n const dropdown = document.getElementById('imageApiBaseDropdown');\n if (!dropdown) return;\n dropdown.querySelectorAll('.model-option').forEach(opt => {\n if (opt.dataset.value === url) {\n opt.classList.add('selected');\n } else {\n opt.classList.remove('selected');\n }\n });\n}\n\n// 加载配置\nexport function loadConfig() {\n chrome.storage.local.get([\n 'apiBase', 'apiKey', 'modelName', 'customModels', 'systemPrompt',\n 'enableImageInput', 'imageModelName', 'imageModels',\n 'imageApiBase', 'imageApiKey', 'enableFileInput',\n 'reactMaxIterations', 'reactApiTimeout', 'reactLoopTimeout', 'reactToolTimeout',\n 'preselectMinToolCount', 'toolConfirmationEnabled',\n 'enableExecutionLog',\n 'reflectionConfig',\n 'streamEnabled',\n 'streamExpandTools',\n 'completionSoundEnabled', 'completionConfettiEnabled',\n 'completionNotificationEnabled', 'scheduledNotificationEnabled', 'interactionNotificationEnabled',\n 'showMessageTimestamp', 'sidePanelScope', 'autoGroupTabs'\n ], function(result) {\n if (result.apiBase) {\n document.getElementById('apiBase').value = result.apiBase;\n }\n if (result.apiKey) {\n document.getElementById('apiKey').value = decodeSecret(result.apiKey);\n }\n if (result.modelName) {\n setCurrentModel(result.modelName);\n document.getElementById('modelInput').value = result.modelName;\n }\n if (result.systemPrompt) {\n document.getElementById('systemPrompt').value = result.systemPrompt;\n }\n\n // 加载图片识别配置\n const enableImageInputEl = document.getElementById('enableImageInput');\n const imageModelGroup = document.getElementById('imageModelGroup');\n const imageApiGroup = document.getElementById('imageApiGroup');\n const imageApiKeyGroup = document.getElementById('imageApiKeyGroup');\n if (enableImageInputEl && imageModelGroup) {\n enableImageInputEl.checked = result.enableImageInput || false;\n imageModelGroup.style.display = enableImageInputEl.checked ? '' : 'none';\n if (imageApiGroup) {\n imageApiGroup.style.display = enableImageInputEl.checked ? '' : 'none';\n }\n if (imageApiKeyGroup) {\n imageApiKeyGroup.style.display = enableImageInputEl.checked ? '' : 'none';\n }\n }\n if (result.imageModelName) {\n setCurrentImageModel(result.imageModelName);\n const imageModelInput = document.getElementById('imageModelInput');\n if (imageModelInput) imageModelInput.value = result.imageModelName;\n }\n if (result.imageApiBase) {\n document.getElementById('imageApiBase').value = result.imageApiBase;\n }\n if (result.imageApiKey) {\n document.getElementById('imageApiKey').value = decodeSecret(result.imageApiKey);\n }\n\n // 加载 ReAct 配置(时间单位转换)\n document.getElementById('reactMaxIterations').value = \n result.reactMaxIterations || DEFAULT_REACT_CONFIG.maxIterations;\n document.getElementById('reactApiTimeout').value = \n Math.round((result.reactApiTimeout || DEFAULT_REACT_CONFIG.apiTimeout) / 60000);\n document.getElementById('reactLoopTimeout').value = \n Math.round((result.reactLoopTimeout || DEFAULT_REACT_CONFIG.loopTimeout) / 60000);\n document.getElementById('reactToolTimeout').value = \n Math.round((result.reactToolTimeout || DEFAULT_REACT_CONFIG.toolTimeout) / 60000);\n const preselectEl = document.getElementById('enableToolPreselect');\n if (preselectEl) {\n preselectEl.checked = \n result.enableToolPreselect !== undefined ? result.enableToolPreselect : DEFAULT_REACT_CONFIG.enableToolPreselect;\n // 触发 change 事件,联动显示/隐藏预筛选最小工具数\n preselectEl.dispatchEvent(new Event('change'));\n }\n \n // 加载工具预筛选最小触发数量\n const preselectMinEl = document.getElementById('preselectMinToolCount');\n if (preselectMinEl) {\n preselectMinEl.value = \n result.preselectMinToolCount !== undefined ? result.preselectMinToolCount : DEFAULT_REACT_CONFIG.preselectMinToolCount;\n }\n \n // 加载敏感操作确认开关\n document.getElementById('toolConfirmationEnabled').checked =\n result.toolConfirmationEnabled !== undefined ? result.toolConfirmationEnabled : DEFAULT_REACT_CONFIG.toolConfirmationEnabled;\n document.getElementById('toolConfirmationEnabled').dispatchEvent(new Event('change'));\n\n // 加载对话配置\n\n \n // 加载执行日志配置\n document.getElementById('enableExecutionLog').checked = \n result.enableExecutionLog !== undefined ? result.enableExecutionLog : DEFAULT_CHAT_CONFIG.enableExecutionLog;\n document.getElementById('enableExecutionLog').dispatchEvent(new Event('change'));\n \n // 加载反思配置\n const reflection = result.reflectionConfig || DEFAULT_REFLECTION_CONFIG;\n document.getElementById('reflectionEnabled').checked = reflection.enabled !== false;\n document.getElementById('postReflectionEnabled').checked = reflection.postReflection?.enabled !== false;\n document.getElementById('subtaskReflectionEnabled').checked = reflection.subtaskReflection?.enabled === true;\n \n document.getElementById('streamEnabled').checked =\n result.streamEnabled !== undefined ? result.streamEnabled : true;\n\n // 流式工具卡片默认展开(联动 streamEnabled,默认 false=折叠)\n const streamExpandToolsEl = document.getElementById('streamExpandTools');\n const streamExpandToolsLabel = document.getElementById('streamExpandToolsLabel');\n const streamExpandToolsWrap = document.getElementById('streamExpandToolsWrap');\n if (streamExpandToolsEl) {\n streamExpandToolsEl.checked = result.streamExpandTools === true;\n }\n if (streamExpandToolsLabel) {\n streamExpandToolsLabel.textContent = streamExpandToolsEl?.checked ? t('common.enabled') : t('common.disabled');\n }\n if (streamExpandToolsWrap) {\n streamExpandToolsWrap.style.display = (result.streamEnabled !== undefined ? result.streamEnabled : true) ? 'block' : 'none';\n }\n \n // 更新反思配置区域可见性\n function updateReflectionVisibility() {\n const reflectionConfig = document.getElementById('reflectionConfig');\n const reflectionEnabled = document.getElementById('reflectionEnabled');\n \n if (reflectionConfig) {\n if (!reflectionEnabled || !reflectionEnabled.checked) {\n reflectionConfig.classList.add('disabled');\n } else {\n reflectionConfig.classList.remove('disabled');\n }\n }\n \n function updateModule(moduleId, toggleId) {\n const module = document.getElementById(moduleId);\n const toggle = document.getElementById(toggleId);\n if (module && toggle) {\n if (!toggle.checked) {\n module.classList.add('disabled');\n } else {\n module.classList.remove('disabled');\n }\n }\n }\n \n updateModule('postReflectionSection', 'postReflectionEnabled');\n updateModule('subtaskReflectionSection', 'subtaskReflectionEnabled');\n }\n\n // 加载完成反馈开关(默认开启,undefined 视为 true)\n const completionSoundEl = document.getElementById('completionSoundEnabled');\n const completionConfettiEl = document.getElementById('completionConfettiEnabled');\n if (completionSoundEl) completionSoundEl.checked = result.completionSoundEnabled !== false;\n if (completionConfettiEl) completionConfettiEl.checked = result.completionConfettiEnabled !== false;\n\n // 加载桌面通知开关(默认开启,undefined 视为 true)\n const completionNotificationEl = document.getElementById('completionNotificationEnabled');\n const scheduledNotificationEl = document.getElementById('scheduledNotificationEnabled');\n const interactionNotificationEl = document.getElementById('interactionNotificationEnabled');\n if (completionNotificationEl) completionNotificationEl.checked = result.completionNotificationEnabled !== false;\n if (scheduledNotificationEl) scheduledNotificationEl.checked = result.scheduledNotificationEnabled !== false;\n if (interactionNotificationEl) interactionNotificationEl.checked = result.interactionNotificationEnabled !== false;\n\n // 加载消息时间戳开关(默认开启,undefined 视为 true)\n const showMessageTimestampEl = document.getElementById('showMessageTimestamp');\n if (showMessageTimestampEl) showMessageTimestampEl.checked = result.showMessageTimestamp !== false;\n\n // 加载侧边栏作用域模式(默认 global)\n const scopeValue = result.sidePanelScope === 'tab-specific' ? 'tab-specific' : 'global';\n const scopeRadios = document.querySelectorAll('input[name=\"sidePanelScope\"]');\n scopeRadios.forEach(radio => { radio.checked = radio.value === scopeValue; });\n\n // 侧边栏作用域切换立即生效:直接写 storage,background 监听 onChanged 动态应用\n scopeRadios.forEach(radio => {\n if (radio.dataset.scopeListenerBound) return;\n radio.dataset.scopeListenerBound = '1';\n radio.addEventListener('change', () => {\n if (radio.checked) {\n chrome.storage.local.set({ sidePanelScope: radio.value });\n syncAutoGroupTabsDisabled();\n }\n });\n });\n\n // 加载自动分组开关(默认开启,undefined 视为 true)\n const autoGroupTabsEl = document.getElementById('autoGroupTabs');\n if (autoGroupTabsEl) autoGroupTabsEl.checked = result.autoGroupTabs !== false;\n // 自动分组只在标签页绑定模式下生效:全局模式下面板对所有 tab 可用,分组失去意义,\n // 因此把开关置灰不可改(保留用户原有勾选值,切回绑定模式后继续生效)\n function syncAutoGroupTabsDisabled() {\n if (!autoGroupTabsEl) return;\n const scope = document.querySelector('input[name=\"sidePanelScope\"]:checked')?.value || 'global';\n autoGroupTabsEl.disabled = scope !== 'tab-specific';\n }\n syncAutoGroupTabsDisabled();\n // 分组开关切换立即生效:background 在归组时实时读取该设置\n if (autoGroupTabsEl && !autoGroupTabsEl.dataset.groupListenerBound) {\n autoGroupTabsEl.dataset.groupListenerBound = '1';\n autoGroupTabsEl.addEventListener('change', () => {\n chrome.storage.local.set({ autoGroupTabs: autoGroupTabsEl.checked });\n });\n }\n \n // 先加载自定义模型到下拉列表,再更新选中状态\n loadCustomModels(() => {\n if (result.modelName) {\n updateModelSelection(result.modelName);\n }\n updateReflectionVisibility();\n });\n\n // 加载图片识别模型列表\n loadImageModels(() => {\n if (result.imageModelName) {\n updateImageModelSelection(result.imageModelName);\n }\n });\n\n // 加载 API Base URL 列表\n loadApiBases(() => {\n if (result.apiBase) {\n updateApiBaseSelection(result.apiBase);\n }\n });\n\n // 加载图片识别 API Base URL 列表\n loadImageApiBases(() => {\n if (result.imageApiBase) {\n updateImageApiBaseSelection(result.imageApiBase);\n }\n });\n });\n}\n\n// 保存配置\nexport function saveConfig() {\n let apiBase = document.getElementById('apiBase').value.trim();\n const apiKey = document.getElementById('apiKey').value.trim();\n const systemPrompt = document.getElementById('systemPrompt').value.trim();\n \n // 获取 ReAct 配置(时间单位转换)\n const reactMaxIterations = parseInt(document.getElementById('reactMaxIterations').value) || DEFAULT_REACT_CONFIG.maxIterations;\n const reactApiTimeout = (parseInt(document.getElementById('reactApiTimeout').value) || 5) * 60000;\n const reactLoopTimeout = (parseInt(document.getElementById('reactLoopTimeout').value) || 5) * 60000;\n const reactToolTimeout = (parseInt(document.getElementById('reactToolTimeout').value) || 5) * 60000;\n // clarifyTimeout / apiRetryCount / apiRetryBaseDelay / enableToolPreselect / preselectMinToolCount 不再配置,使用默认值\n const toolConfirmationEnabled = document.getElementById('toolConfirmationEnabled').checked;\n const enableExecutionLog = document.getElementById('enableExecutionLog').checked;\n\n // 获取流式输出配置\n const streamEnabled = document.getElementById('streamEnabled')?.checked !== false;\n const streamExpandTools = document.getElementById('streamExpandTools')?.checked === true;\n\n // 获取完成反馈配置(默认开启)\n const completionSoundEnabled = document.getElementById('completionSoundEnabled')?.checked !== false;\n const completionConfettiEnabled = document.getElementById('completionConfettiEnabled')?.checked !== false;\n // 获取桌面通知配置(默认开启)\n const completionNotificationEnabled = document.getElementById('completionNotificationEnabled')?.checked !== false;\n const scheduledNotificationEnabled = document.getElementById('scheduledNotificationEnabled')?.checked !== false;\n const interactionNotificationEnabled = document.getElementById('interactionNotificationEnabled')?.checked !== false;\n const showMessageTimestamp = document.getElementById('showMessageTimestamp')?.checked !== false;\n \n // 获取图片识别配置\n const enableImageInput = document.getElementById('enableImageInput')?.checked || false;\n const imageModelName = currentImageModel || '';\n const imageApiBase = document.getElementById('imageApiBase')?.value.trim() || '';\n const imageApiKey = document.getElementById('imageApiKey')?.value.trim() || '';\n \n // 获取反思配置\n const reflectionConfig = {\n enabled: document.getElementById('reflectionEnabled').checked,\n postReflection: {\n ...DEFAULT_REFLECTION_CONFIG.postReflection,\n enabled: document.getElementById('postReflectionEnabled').checked\n },\n subtaskReflection: {\n ...DEFAULT_REFLECTION_CONFIG.subtaskReflection,\n enabled: document.getElementById('subtaskReflectionEnabled').checked\n }\n };\n \n // 验证 ReAct 配置范围\n if (reactMaxIterations < 10 || reactMaxIterations > 1000) {\n showToast(`❌ ${t('settings.maxIterationsRangeError')}`, 'error');\n return;\n }\n if (reactApiTimeout < 60000 || reactApiTimeout > 600000) {\n showToast(`❌ ${t('settings.apiTimeoutRangeError')}`, 'error');\n return;\n }\n if (reactLoopTimeout < 60000 || reactLoopTimeout > 7200000) {\n showToast(`❌ ${t('settings.loopTimeoutRangeError')}`, 'error');\n return;\n }\n if (reactToolTimeout < 60000 || reactToolTimeout > 1800000) {\n showToast(`❌ ${t('settings.toolTimeoutRangeError')}`, 'error');\n return;\n }\n \n // API Base URL 有默认值\n if (!apiBase) {\n apiBase = 'https://api.deepseek.com';\n }\n \n // 验证必填字段\n if (!apiKey) {\n showToast(`❌ ${t('settings.apiKeyEmpty')}`, 'error');\n return;\n }\n \n // 保存配置(密钥混淆落盘)\n chrome.storage.local.set({ \n apiBase: apiBase,\n apiKey: encodeSecret(apiKey),\n modelName: currentModel || 'deepseek-v4-pro',\n systemPrompt: systemPrompt,\n // ReAct 配置\n reactMaxIterations: reactMaxIterations,\n reactApiTimeout: reactApiTimeout,\n reactLoopTimeout: reactLoopTimeout,\n reactToolTimeout: reactToolTimeout,\n // clarifyTimeout / apiRetryCount / apiRetryBaseDelay / enableToolPreselect / preselectMinToolCount 不再配置,使用默认值\n toolConfirmationEnabled: toolConfirmationEnabled,\n enableExecutionLog: enableExecutionLog,\n // 图片识别配置\n enableImageInput: enableImageInput,\n imageModelName: imageModelName,\n imageApiBase: imageApiBase,\n imageApiKey: encodeSecret(imageApiKey),\n // 反思配置\n reflectionConfig: reflectionConfig,\n // 流式输出配置\n streamEnabled: streamEnabled,\n streamExpandTools: streamExpandTools,\n // 完成反馈配置\n completionSoundEnabled: completionSoundEnabled,\n completionConfettiEnabled: completionConfettiEnabled,\n // 桌面通知配置(默认开启)\n completionNotificationEnabled: completionNotificationEnabled,\n scheduledNotificationEnabled: scheduledNotificationEnabled,\n interactionNotificationEnabled: interactionNotificationEnabled,\n showMessageTimestamp: showMessageTimestamp,\n // 侧边栏作用域模式\n sidePanelScope: document.querySelector('input[name=\"sidePanelScope\"]:checked')?.value || 'global',\n // 自动分组开关\n autoGroupTabs: document.getElementById('autoGroupTabs')?.checked !== false\n }, async function() {\n if (chrome.runtime.lastError) {\n showToast(`❌ ${t('settings.saveFailed', { message: chrome.runtime.lastError.message })}`, 'error');\n } else {\n // 同步当前厂商配置(地址/Key/选中模型/模型列表)\n updateActiveProfile({\n apiBase: apiBase,\n apiKey: apiKey,\n modelName: currentModel || 'deepseek-v4-pro',\n models: collectModelsFromDropdown()\n });\n showToast(`✅ ${t('settings.saveSuccess')}`, 'success');\n const status = document.getElementById('status');\n status.style.display = 'none';\n\n const saveBtn = document.getElementById('saveBtn');\n if (saveBtn) {\n saveBtn.classList.add('saved');\n setTimeout(() => saveBtn.classList.remove('saved'), 1500);\n }\n\n // 读取 Agent 配置(已配对代理列表)\n const agentResult = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n let agentConfig = null;\n const agents = agentResult.pairedAgents || [];\n const activeAgent = agents.find(a => a.id === agentResult.activeAgentId);\n if (activeAgent) {\n agentConfig = { url: activeAgent.url, connected: true, name: activeAgent.name };\n // 尝试获取工作目录\n try {\n const resp = await fetch(`${activeAgent.url}/api/status/detail`, {\n headers: { 'Authorization': `Bearer ${activeAgent.token}` }\n });\n if (resp.ok) {\n const data = await resp.json();\n agentConfig.workdir = data.workdir;\n }\n } catch {}\n }\n }\n });\n}\n\n// 显示状态信息(保留原有逻辑,兼容)\nexport function showStatus(message, type) {\n const status = document.getElementById('status');\n status.textContent = message;\n status.className = 'status ' + type;\n status.style.display = 'block';\n \n if (type === 'success') {\n setTimeout(function() {\n status.style.display = 'none';\n }, 3000);\n }\n}\n\n// 显示 Toast 提示\nexport function showToast(message, type = 'info', duration = 3000) {\n const container = document.getElementById('toastContainer');\n if (!container) return;\n \n const toast = document.createElement('div');\n toast.className = `toast ${type}`;\n toast.textContent = message;\n \n container.appendChild(toast);\n \n // 触发动画\n requestAnimationFrame(() => {\n toast.classList.add('toast-show');\n });\n \n // 自动移除\n setTimeout(() => {\n toast.classList.remove('toast-show');\n setTimeout(() => {\n if (toast.parentNode) {\n toast.parentNode.removeChild(toast);\n }\n }, 300);\n }, duration);\n}\n\n\n","// options/model-fetcher.js - 从厂商 API 拉取模型列表\n// 单一职责:请求 + 容错解析。写入下拉与 toast 由 index.js 负责。\n\n/**\n * 容错解析厂商返回的模型列表,兼容多种格式。\n * 依次尝试 data[] / models[],元素可为字符串或含 id/name/model 的对象。\n * @param {any} json\n * @returns {string[]} 去重、过滤空后的模型名数组\n */\nexport function parseModelsPayload(json) {\n if (!json || typeof json !== 'object') return [];\n const arr = Array.isArray(json.data) ? json.data\n : Array.isArray(json.models) ? json.models\n : null;\n if (!arr) return [];\n\n const seen = new Set();\n const result = [];\n for (const item of arr) {\n let name = '';\n if (typeof item === 'string') {\n name = item;\n } else if (item && typeof item === 'object') {\n const id = item.id || item.name || item.model;\n if (typeof id === 'string') name = id;\n }\n name = (name || '').trim();\n if (!name || seen.has(name)) continue;\n seen.add(name);\n result.push(name);\n }\n return result;\n}\n\n/** 拉取模型失败错误,保留 HTTP 状态码与失败原因 */\nexport class FetchModelsError extends Error {\n constructor(message, { status, reason } = {}) {\n super(message);\n this.name = 'FetchModelsError';\n this.status = status; // HTTP 状态码(若有)\n this.reason = reason; // 'timeout'|'network'|'http'|'parse'|'invalid-config'\n }\n}\n\nfunction buildModelsUrl(apiBase) {\n return `${String(apiBase).trim().replace(/\\/+$/, '')}/models`;\n}\n\nfunction isAnthropic(apiBase) {\n return /anthropic\\.com/i.test(String(apiBase || ''));\n}\n\n/**\n * 请求厂商 /models 接口并解析模型名列表。\n * @param {{apiBase:string, apiKey:string, timeoutMs?:number}} opts\n * @returns {Promise<string[]>}\n * @throws {FetchModelsError}\n */\nexport async function fetchModelList({ apiBase, apiKey, timeoutMs = 15000 } = {}) {\n const base = (apiBase || '').trim();\n const key = (apiKey || '').trim();\n if (!base || !key) {\n throw new FetchModelsError('missing apiBase or apiKey', { reason: 'invalid-config' });\n }\n\n const headers = { 'Authorization': `Bearer ${key}` };\n if (isAnthropic(base)) {\n headers['x-api-key'] = key;\n headers['anthropic-version'] = '2023-06-01';\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let resp;\n try {\n resp = await fetch(buildModelsUrl(base), { method: 'GET', headers, signal: controller.signal });\n } catch (err) {\n clearTimeout(timer);\n if (err && err.name === 'AbortError') {\n throw new FetchModelsError('request timeout', { reason: 'timeout' });\n }\n throw new FetchModelsError((err && err.message) || 'network error', { reason: 'network' });\n }\n clearTimeout(timer);\n\n if (!resp.ok) {\n throw new FetchModelsError(`HTTP ${resp.status}`, { status: resp.status, reason: 'http' });\n }\n\n let json;\n try {\n json = await resp.json();\n } catch (_e) {\n throw new FetchModelsError('invalid JSON', { reason: 'parse' });\n }\n\n const models = parseModelsPayload(json);\n if (!models.length) {\n throw new FetchModelsError('no models parsed', { reason: 'parse' });\n }\n return models;\n}\n","// options/toolbar-config.js - 工具栏配置管理\n\nimport { DEFAULT_TOOLBAR_TOOLS, DEFAULT_TOOLBAR_ICON_ONLY, DEFAULT_ENABLE_SELECTION_TOOLBAR, getBuiltinToolName, getBuiltinToolPrompt } from './constants.js';\nimport logger from '../shared/logger.js';\nimport { t } from '../shared/i18n.js';\n\nlet editingToolIndex = -1; // -1 表示新增,>=0 表示编辑\n\n// 加载工具栏工具列表\nexport function loadToolbarTools() {\n return new Promise((resolve) => {\n chrome.storage.local.get(['toolbarTools', 'toolbarIconOnly', 'enableSelectionToolbar'], (result) => {\n const rawTools = (result.toolbarTools && result.toolbarTools.length > 0)\n ? result.toolbarTools\n : [...DEFAULT_TOOLBAR_TOOLS];\n const iconOnly = result.toolbarIconOnly !== undefined ? result.toolbarIconOnly : DEFAULT_TOOLBAR_ICON_ONLY;\n const enableSelectionToolbar = result.enableSelectionToolbar !== undefined ? result.enableSelectionToolbar : DEFAULT_ENABLE_SELECTION_TOOLBAR;\n \n // 内置工具始终使用当前语言的名称和最新的 systemPrompt(防止旧数据缺失,且名称随语言切换)\n const defaultMap = new Map(DEFAULT_TOOLBAR_TOOLS.map(t => [t.id, t]));\n const tools = rawTools.map(t => {\n if (t.builtin && defaultMap.has(t.id)) {\n const i18nName = getBuiltinToolName(t.id);\n const i18nPrompt = getBuiltinToolPrompt(t.id);\n return { ...t, systemPrompt: i18nPrompt || defaultMap.get(t.id).systemPrompt, name: i18nName || t.name };\n }\n return t;\n });\n \n const iconOnlyCheckbox = document.getElementById('toolbarIconOnly');\n if (iconOnlyCheckbox) iconOnlyCheckbox.checked = iconOnly;\n const enableToolbarCheckbox = document.getElementById('enableSelectionToolbar');\n if (enableToolbarCheckbox) enableToolbarCheckbox.checked = enableSelectionToolbar;\n resolve(tools);\n });\n });\n}\n\n// 渲染工具栏工具列表\nexport function renderToolbarToolsList(tools) {\n const listEl = document.getElementById('toolbarToolsList');\n if (!listEl) return;\n \n const sorted = [...tools].filter(item => item.id !== 'ai-search' && item.id !== 'copy').sort((a, b) => a.order - b.order);\n \n listEl.innerHTML = sorted.map((tool, index) => {\n const isBuiltin = tool.builtin;\n // 内置工具使用当前语言的名称,自定义工具使用存储的名称\n const displayName = isBuiltin ? (getBuiltinToolName(tool.id) || tool.name) : tool.name;\n const badge = isBuiltin\n ? `<span class=\"tool-badge builtin\">${t('toolbar.builtinBadge')}</span>`\n : `<span class=\"tool-badge custom\">${t('toolbar.customBadge')}</span>`;\n const promptPreview = tool.systemPrompt\n ? `<div class=\"tool-prompt-preview\" title=\"${escapeHtml(tool.systemPrompt)}\">${escapeHtml(tool.systemPrompt)}</div>`\n : '';\n\n const isFirst = index === 0;\n const isLast = index === sorted.length - 1;\n\n let actionsHtml = '';\n if (!isBuiltin) {\n actionsHtml += `<button class=\"tool-action-btn\" data-action=\"edit\" data-index=\"${index}\">${t('common.edit')}</button>`;\n actionsHtml += `<button class=\"tool-action-btn danger\" data-action=\"delete\" data-index=\"${index}\">${t('common.delete')}</button>`;\n }\n\n return `\n <div class=\"tool-item\" data-tool-id=\"${tool.id}\" draggable=\"true\" data-sorted-index=\"${index}\">\n <div class=\"tool-drag-handle\" title=\"${t('toolbar.dragSort')}\">⋮⋮</div>\n <div class=\"tool-order-btns\">\n <button class=\"tool-order-btn\" data-action=\"moveUp\" data-index=\"${index}\" ${isFirst ? 'disabled' : ''} title=\"${t('toolbar.moveUp')}\">▲</button>\n <button class=\"tool-order-btn\" data-action=\"moveDown\" data-index=\"${index}\" ${isLast ? 'disabled' : ''} title=\"${t('toolbar.moveDown')}\">▼</button>\n </div>\n <div class=\"tool-info\">\n <div class=\"tool-name\">${escapeHtml(displayName)}${badge}</div>\n ${promptPreview}\n </div>\n <div class=\"tool-actions\">\n ${actionsHtml}\n </div>\n </div>\n `;\n }).join('');\n \n initToolDragAndDrop();\n}\n\n// 初始化拖拽排序\nfunction initToolDragAndDrop() {\n const list = document.getElementById('toolbarToolsList');\n if (!list) return;\n const items = list.querySelectorAll('.tool-item');\n \n items.forEach(item => {\n item.addEventListener('dragstart', (e) => {\n item.classList.add('dragging');\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', item.dataset.sortedIndex);\n });\n \n item.addEventListener('dragend', () => {\n item.classList.remove('dragging');\n items.forEach(i => i.classList.remove('drag-over'));\n });\n \n item.addEventListener('dragover', (e) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n item.classList.add('drag-over');\n });\n \n item.addEventListener('dragleave', () => {\n item.classList.remove('drag-over');\n });\n \n item.addEventListener('drop', (e) => {\n e.stopPropagation();\n e.preventDefault();\n \n const draggedIndex = parseInt(e.dataTransfer.getData('text/plain'));\n const targetIndex = parseInt(item.dataset.sortedIndex);\n \n if (isNaN(draggedIndex) || isNaN(targetIndex) || draggedIndex === targetIndex) return;\n \n // 交换 order\n chrome.storage.local.get(['toolbarTools'], (result) => {\n const tools = (result.toolbarTools && result.toolbarTools.length > 0)\n ? result.toolbarTools\n : [...DEFAULT_TOOLBAR_TOOLS];\n const sorted = [...tools].filter(item => item.id !== 'ai-search' && item.id !== 'copy').sort((a, b) => a.order - b.order);\n \n if (draggedIndex >= sorted.length || targetIndex >= sorted.length) return;\n \n const temp = sorted[draggedIndex].order;\n sorted[draggedIndex].order = sorted[targetIndex].order;\n sorted[targetIndex].order = temp;\n \n // 合并回完整列表\n const fixedTools = tools.filter(item => item.id === 'ai-search' || item.id === 'copy');\n const fullTools = [...sorted, ...fixedTools];\n \n chrome.storage.local.set({ toolbarTools: fullTools }, () => {\n renderToolbarToolsList(fullTools);\n });\n });\n \n item.classList.remove('drag-over');\n });\n });\n}\n\n// 保存工具栏配置\nexport function saveToolbarConfig() {\n const iconOnly = document.getElementById('toolbarIconOnly')?.checked || false;\n const enableSelectionToolbar = document.getElementById('enableSelectionToolbar')?.checked !== false;\n \n chrome.storage.local.get(['toolbarTools'], (result) => {\n const tools = result.toolbarTools || [...DEFAULT_TOOLBAR_TOOLS];\n \n chrome.storage.local.set({\n toolbarTools: tools,\n toolbarIconOnly: iconOnly,\n enableSelectionToolbar: enableSelectionToolbar\n }, () => {\n logger.debug('[Options] toolbarconfiguration saved');\n });\n });\n}\n\n// 移动工具顺序\nexport function moveTool(tools, index, direction) {\n // AI搜索和复制固定位置,不参与排序,先排除\n const fixedTools = tools.filter(item => item.id === 'ai-search' || item.id === 'copy');\n const nonFixedTools = [...tools].filter(item => item.id !== 'ai-search' && item.id !== 'copy').sort((a, b) => a.order - b.order);\n const targetIndex = index + direction;\n \n if (targetIndex < 0 || targetIndex >= nonFixedTools.length) return tools;\n \n const temp = nonFixedTools[index].order;\n nonFixedTools[index].order = nonFixedTools[targetIndex].order;\n nonFixedTools[targetIndex].order = temp;\n \n return [...nonFixedTools, ...fixedTools];\n}\n\n// 删除自定义工具\nexport function deleteTool(tools, index) {\n const fixedTools = tools.filter(item => item.id === 'ai-search' || item.id === 'copy');\n const sorted = [...tools].filter(item => item.id !== 'ai-search' && item.id !== 'copy').sort((a, b) => a.order - b.order);\n const tool = sorted[index];\n \n if (tool.builtin) {\n logger.warn('[Options] cannot deletebuilt-in tool');\n return tools;\n }\n \n sorted.splice(index, 1);\n return [...sorted, ...fixedTools];\n}\n// 打开编辑弹窗\nexport function openToolEditModal(tools, index = -1) {\n editingToolIndex = index;\n const modal = document.getElementById('toolEditModal');\n const title = document.getElementById('toolEditModalTitle');\n const nameInput = document.getElementById('toolEditName');\n const promptInput = document.getElementById('toolEditPrompt');\n \n if (index >= 0) {\n const sorted = [...tools].filter(item => item.id !== 'ai-search' && item.id !== 'copy').sort((a, b) => a.order - b.order);\n const tool = sorted[index];\n title.textContent = t('toolbar.editCustomToolTitle');\n nameInput.value = tool.name;\n promptInput.value = tool.systemPrompt || '';\n } else {\n title.textContent = t('toolbar.toolEditModalTitle');\n nameInput.value = '';\n promptInput.value = '';\n }\n \n modal.style.display = 'flex';\n}\n\n// 关闭编辑弹窗\nexport function closeToolEditModal() {\n document.getElementById('toolEditModal').style.display = 'none';\n editingToolIndex = -1;\n}\n\n// 保存工具编辑\nexport function saveToolEdit(tools) {\n const name = document.getElementById('toolEditName').value.trim();\n const prompt = document.getElementById('toolEditPrompt').value.trim();\n \n if (!name) return { error: t('toolbar.toolNameEmpty') };\n if (!prompt) return { error: t('toolbar.toolPromptEmpty') };\n\n const newTools = [...tools].sort((a, b) => a.order - b.order);\n\n if (editingToolIndex >= 0) {\n // 编辑时索引基于过滤后的列表(不含AI搜索和复制),先过滤再查找\n const filtered = newTools.filter(item => item.id !== 'ai-search' && item.id !== 'copy');\n const tool = filtered[editingToolIndex];\n if (!tool) return { error: t('toolbar.toolNotFound') };\n if (tool.builtin) return { error: t('toolbar.cannotEditBuiltin') };\n tool.name = name;\n tool.systemPrompt = prompt;\n } else {\n const maxOrder = newTools.length > 0\n ? Math.max(...newTools.map(t => t.order))\n : 0;\n newTools.push({\n id: 'custom_' + Date.now(),\n name: name,\n systemPrompt: prompt,\n builtin: false,\n order: maxOrder + 1\n });\n }\n \n closeToolEditModal();\n // 保留固定工具(AI搜索、复制)在最后\n const fixedTools = tools.filter(item => item.id === 'ai-search' || item.id === 'copy');\n fixedTools.forEach(ft => {\n if (!newTools.find(t => t.id === ft.id)) {\n newTools.push(ft);\n }\n });\n return { tools: newTools };\n}\n\nfunction escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n}\n\n// ==================== 域名屏蔽列表管理 ====================\n\n// 加载并渲染屏蔽域名列表\nexport function loadBlockedDomainsUI() {\n chrome.storage.local.get(['blockedDomains'], (result) => {\n const list = result.blockedDomains || [];\n renderBlockedDomainsList(list);\n });\n}\n\n// 渲染屏蔽域名列表\nfunction renderBlockedDomainsList(list) {\n const listEl = document.getElementById('domainBlocklistList');\n if (!listEl) return;\n \n if (list.length === 0) {\n listEl.innerHTML = `<div class=\"domain-blocklist-empty\">${t('toolbar.noBlockedDomains')}</div>`;\n } else {\n listEl.innerHTML = list.map(domain => `\n <div class=\"domain-blocklist-item\">\n <span class=\"domain-blocklist-item-name\" title=\"${escapeHtml(domain)}\">${escapeHtml(domain)}</span>\n <button class=\"domain-blocklist-item-remove\" data-domain=\"${escapeHtml(domain)}\" title=\"${t('toolbar.unblock')}\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>\n </button>\n </div>\n `).join('');\n }\n}\n\n// 添加域名到屏蔽列表\nexport function addBlockedDomain(domain, callback) {\n domain = domain.trim().toLowerCase();\n if (!domain) {\n if (callback) callback({ error: t('toolbar.domainEmpty') });\n return;\n }\n if (!domain.includes('.')) {\n if (callback) callback({ error: t('toolbar.domainInvalid') });\n return;\n }\n\n chrome.storage.local.get(['blockedDomains'], (result) => {\n const list = result.blockedDomains || [];\n if (list.includes(domain)) {\n if (callback) callback({ error: t('toolbar.domainExists') });\n return;\n }\n list.push(domain);\n chrome.storage.local.set({ blockedDomains: list }, () => {\n renderBlockedDomainsList(list);\n if (callback) callback({});\n });\n });\n}\n\n// 移除域名\nexport function removeBlockedDomain(domain) {\n chrome.storage.local.get(['blockedDomains'], (result) => {\n const list = (result.blockedDomains || []).filter(d => d !== domain);\n chrome.storage.local.set({ blockedDomains: list }, () => {\n renderBlockedDomainsList(list);\n });\n });\n}","// config-io.js - 配置导入/导出模块\n\nimport { loadConfig } from './config-manager.js';\nimport { loadToolbarTools, loadBlockedDomainsUI } from './toolbar-config.js';\nimport logger from '../shared/logger.js';\nimport { t, getLanguage } from '../shared/i18n.js';\nimport { ensureProfilesMigrated, updateActiveProfile, normalizeModels, stripProfileSecrets, mergeProfiles, getState, DEFAULT_API_BASE, DEFAULT_MODEL_NAME } from '../shared/model-profiles.js';\nimport { encodeSecret, decodeSecret } from '../shared/secret.js';\n\n// 允许导出的配置项 key 白名单\nconst EXPORT_KEYS = [\n 'apiBase', 'modelName', 'customModels', 'systemPrompt',\n 'enableImageInput', 'imageModelName', 'imageModels',\n 'imageApiBase',\n 'reactMaxIterations', 'reactApiTimeout', 'reactLoopTimeout',\n 'reactToolTimeout', 'enableToolPreselect', 'toolConfirmationEnabled',\n 'reflectionConfig',\n 'enableExecutionLog',\n 'toolbarTools', 'toolbarIconOnly',\n 'enableSelectionToolbar', 'blockedDomains',\n 'streamEnabled',\n 'streamExpandTools',\n // 完成反馈(声音 + 彩带开关)\n 'completionSoundEnabled', 'completionConfettiEnabled',\n // 桌面通知开关(任务完成/失败、定时任务、交互提醒)\n 'completionNotificationEnabled', 'scheduledNotificationEnabled', 'interactionNotificationEnabled',\n // 消息时间戳显示开关\n 'showMessageTimestamp',\n // 侧边栏作用域模式\n 'sidePanelScope',\n // 自动分组开关\n 'autoGroupTabs',\n // 新增:助手、工具、LLM参数、UI开关等配置\n 'customAgents', 'activeAgentId', 'activeAssistantId',\n 'temperature', 'topP', 'selectedTempIndex',\n 'customPrompts',\n 'pairedAgents', 'agentStreamEnabled',\n 'enableTools', 'isolateChat', 'enableSelectionQuery',\n 'deletedPresetModels',\n // 厂商配置(多配置记忆)与激活项\n 'modelProfiles', 'activeProfileId',\n 'mcpEnabled', 'skillsEnabled', 'ragEnabled',\n];\n\n// 敏感的密钥 key(agentToken 已废弃,token 嵌入在 pairedAgents 中)\nconst SECRET_KEYS = ['apiKey', 'imageApiKey', 'agentToken'];\n\n/**\n * 导入时混淆配置中的密钥字段(确保落存储的是 enc: 格式)\n */\nfunction _encodeConfigSecrets(config) {\n for (const key of ['apiKey', 'imageApiKey']) {\n if (config[key]) config[key] = encodeSecret(decodeSecret(config[key]));\n }\n if (Array.isArray(config.modelProfiles)) {\n config.modelProfiles = config.modelProfiles.map(p =>\n p.apiKey ? { ...p, apiKey: encodeSecret(decodeSecret(p.apiKey)) } : p\n );\n }\n if (Array.isArray(config.pairedAgents)) {\n config.pairedAgents = config.pairedAgents.map(a =>\n a.token ? { ...a, token: encodeSecret(decodeSecret(a.token)) } : a\n );\n }\n}\n\n/**\n * 剥离已下线的反思配置字段(工具级反思 toolReflection 已移除)。\n * 旧用户的 storage 或旧导出文件可能仍携带 reflectionConfig.toolReflection,\n * 导出/导入时统一清理,避免死配置被长期携带与回灌。\n * @param {Object} config - 含 reflectionConfig 的配置对象(就地安全:命中时替换为新引用)\n */\nfunction stripRetiredReflectionKeys(config) {\n const rc = config && config.reflectionConfig;\n if (rc && typeof rc === 'object' && 'toolReflection' in rc) {\n const clean = { ...rc };\n delete clean.toolReflection;\n config.reflectionConfig = clean;\n }\n}\n\nasync function collectConfig(includeSecrets) {\n const allKeys = includeSecrets ? [...EXPORT_KEYS, ...SECRET_KEYS] : EXPORT_KEYS;\n \n return new Promise((resolve) => {\n chrome.storage.local.get(null, (result) => {\n const config = {};\n for (const key of allKeys) {\n if (result[key] !== undefined) {\n config[key] = result[key];\n }\n }\n // 收集所有智能体独立的工具配置 key(常规工具勾选 + MCP 服务关闭列表)\n for (const key of Object.keys(result)) {\n if (key.startsWith('agentEnabledTools_') || key.startsWith('agentMcpClosedServers_')) {\n config[key] = result[key];\n }\n }\n // 不包含密钥时,清除 pairedAgents 中的 token\n if (!includeSecrets && config.pairedAgents) {\n config.pairedAgents = config.pairedAgents.map(a => ({ ...a, token: '' }));\n }\n // 不包含密钥时,逐厂商配置剥离 apiKey\n if (!includeSecrets && Array.isArray(config.modelProfiles)) {\n config.modelProfiles = stripProfileSecrets(config.modelProfiles);\n }\n // 剥离已下线的工具级反思配置,避免导出文件携带死字段\n stripRetiredReflectionKeys(config);\n resolve(config);\n });\n });\n}\n\n/**\n * 导出配置到 JSON 文件\n */\nexport async function exportConfig() {\n const includeSecrets = document.getElementById('exportIncludeSecrets')?.checked || false;\n\n try {\n const config = await collectConfig(includeSecrets);\n\n const exportData = {\n version: 1,\n app: 'ai-helper',\n exportedAt: new Date().toISOString(),\n includeSecrets,\n config,\n };\n\n const now = new Date();\n const ts = now.getFullYear() +\n String(now.getMonth() + 1).padStart(2, '0') +\n String(now.getDate()).padStart(2, '0') + '-' +\n String(now.getHours()).padStart(2, '0') +\n String(now.getMinutes()).padStart(2, '0') +\n String(now.getSeconds()).padStart(2, '0');\n\n const fileName = `ai-helper-config-${ts}.json`;\n const jsonData = JSON.stringify(exportData, null, 2);\n const blob = new Blob([jsonData], { type: 'application/json;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = fileName;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n\n hideExportDialog();\n showToast(t('configDialog.exportSuccess'), 'success');\n } catch (err) {\n logger.error('[Options] exportconfiguration failed:', err);\n showToast(t('configDialog.exportFailed', { error: err.message }), 'error');\n }\n}\n\n/**\n * 显示导出选项弹窗\n */\nexport function showExportDialog() {\n const modal = document.getElementById('exportConfigModal');\n if (modal) modal.style.display = 'flex';\n}\n\n/**\n * 隐藏导出选项弹窗\n */\nfunction hideExportDialog() {\n const modal = document.getElementById('exportConfigModal');\n if (modal) modal.style.display = 'none';\n}\n\n/**\n * 显示导入预览弹窗\n * @param {Object} importData - 解析后的导入数据\n */\nfunction showImportPreview(importData) {\n const config = importData.config || importData;\n const count = Object.keys(config).length;\n const exportedAt = importData.exportedAt\n ? new Date(importData.exportedAt).toLocaleString(getLanguage() === 'zh' ? 'zh-CN' : 'en-US')\n : t('configDialog.unknownTime');\n\n const summaryEl = document.getElementById('importSummary');\n if (summaryEl) {\n summaryEl.textContent = t('configDialog.importSummary', { time: exportedAt, count });\n }\n\n const modal = document.getElementById('importConfigModal');\n if (modal) {\n modal.dataset.importData = JSON.stringify(importData);\n modal.style.display = 'flex';\n }\n}\n\n/**\n * 隐藏导入预览弹窗\n */\nfunction hideImportDialog() {\n const modal = document.getElementById('importConfigModal');\n if (modal) modal.style.display = 'none';\n}\n\n/**\n * 触发导入 - 打开文件选择器\n */\nexport function triggerImport() {\n const fileInput = document.getElementById('importConfigFile');\n if (fileInput) {\n fileInput.value = '';\n fileInput.click();\n }\n}\n\n/**\n * 校验导入数据\n * @param {Object} data\n * @returns {{ valid: boolean, error?: string }}\n */\nfunction validateImportData(data) {\n if (!data || typeof data !== 'object') {\n return { valid: false, error: t('configDialog.invalidFileFormat') };\n }\n\n let config;\n if (data.version && data.config && typeof data.config === 'object') {\n config = data.config;\n } else if (data.apiBase || data.modelName !== undefined) {\n // 兼容直接是配置对象的老格式\n config = data;\n } else {\n return { valid: false, error: t('configDialog.unrecognizedFormat') };\n }\n\n // 校验每个 key 都在白名单内(或为智能体工具配置动态 key)\n for (const key of Object.keys(config)) {\n if (!EXPORT_KEYS.includes(key) && !SECRET_KEYS.includes(key) && !key.startsWith('agentEnabledTools_') && !key.startsWith('agentMcpClosedServers_')) {\n return { valid: false, error: t('configDialog.unknownConfigKey', { key }) };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * 处理导入文件\n * @param {File} file\n */\nexport async function handleImportFile(file) {\n try {\n const text = await file.text();\n const data = JSON.parse(text);\n const validation = validateImportData(data);\n\n if (!validation.valid) {\n showToast(t('configDialog.importFailed', { error: validation.error }), 'error');\n return;\n }\n\n showImportPreview(data);\n } catch (err) {\n logger.error('[Options] import fileparse failed:', err);\n showToast(t('configDialog.importParseFailed'), 'error');\n }\n}\n\n/**\n * 确认导入配置\n */\nexport async function confirmImport() {\n const modal = document.getElementById('importConfigModal');\n if (!modal) return;\n\n const importDataStr = modal.dataset.importData;\n if (!importDataStr) return;\n\n try {\n const importData = JSON.parse(importDataStr);\n const config = importData.config || importData;\n // 清理旧导出文件中可能仍存在的已下线反思配置(工具级反思),避免回灌进 storage\n stripRetiredReflectionKeys(config);\n const strategy = document.getElementById('importStrategyReplace')?.checked ? 'replace' : 'merge';\n\n // 合并导入 + 新格式文件:厂商配置按 ID 对齐合并\n // (新 ID 追加;同 ID 以 updatedAt 较新的为准,老的忽略),\n // 之后统一重投影扁平键,保证“扁平键 = 激活配置”一致\n if (strategy === 'merge' && Array.isArray(config.modelProfiles)) {\n const local = await getState();\n config.modelProfiles = mergeProfiles(local.profiles, config.modelProfiles);\n const fileActive = config.activeProfileId;\n config.activeProfileId = config.modelProfiles.some(p => p.id === fileActive)\n ? fileActive\n : local.activeProfileId;\n const activeProfile = config.modelProfiles.find(p => p.id === config.activeProfileId) || config.modelProfiles[0];\n if (activeProfile) {\n config.apiBase = activeProfile.apiBase || DEFAULT_API_BASE;\n config.apiKey = activeProfile.apiKey || '';\n config.modelName = activeProfile.modelName || DEFAULT_MODEL_NAME;\n config.customModels = normalizeModels(activeProfile.models);\n }\n }\n\n if (strategy === 'replace') {\n // 完全替换:密钥混淆后写入\n _encodeConfigSecrets(config);\n await new Promise((resolve) => {\n chrome.storage.local.set(config, resolve);\n });\n } else {\n // 合并导入:密钥混淆后逐项写入\n _encodeConfigSecrets(config);\n await new Promise((resolve) => {\n chrome.storage.local.set(config, resolve);\n });\n }\n\n // 厂商配置一致性处理:\n // - 旧格式导入(无 modelProfiles):用扁平键回写激活配置,避免数据分叉\n // - 其余情况:确保 activeProfileId 有效(导入文件可能缺失)\n await ensureProfilesMigrated();\n if (!Array.isArray(config.modelProfiles)) {\n const flat = await chrome.storage.local.get(['apiBase', 'apiKey', 'modelName', 'customModels']);\n await updateActiveProfile({\n apiBase: flat.apiBase || DEFAULT_API_BASE,\n apiKey: decodeSecret(flat.apiKey) || '',\n modelName: flat.modelName || DEFAULT_MODEL_NAME,\n models: normalizeModels(flat.customModels),\n });\n }\n\n hideImportDialog();\n showToast(t('configDialog.importedCount', { count: Object.keys(config).length }), 'success');\n\n // 刷新页面显示新值\n setTimeout(() => {\n loadConfig(() => {\n // 刷新工具栏\n loadToolbarTools().then(() => {\n // 如果有 renderToolbarToolsList 函数,调用它\n if (typeof updateToolbarUI === 'function') {\n updateToolbarUI();\n }\n });\n loadBlockedDomainsUI();\n });\n }, 300);\n } catch (err) {\n logger.error('[Options] importconfiguration failed:', err);\n showToast(t('configDialog.importFailed', { error: err.message }), 'error');\n }\n}\n\n/**\n * 初始化导入/导出弹窗事件\n */\nexport function initConfigIOEvents() {\n // 导出弹窗\n const exportCloseBtn = document.getElementById('exportConfigCloseBtn');\n const exportCancelBtn = document.getElementById('exportConfigCancelBtn');\n const exportOkBtn = document.getElementById('exportConfigOkBtn');\n const exportModal = document.getElementById('exportConfigModal');\n\n if (exportCloseBtn) exportCloseBtn.addEventListener('click', hideExportDialog);\n if (exportCancelBtn) exportCancelBtn.addEventListener('click', hideExportDialog);\n if (exportOkBtn) exportOkBtn.addEventListener('click', exportConfig);\n if (exportModal) {\n exportModal.addEventListener('click', (e) => {\n if (e.target === exportModal) hideExportDialog();\n });\n }\n\n // 导入弹窗\n const importCloseBtn = document.getElementById('importConfigCloseBtn');\n const importCancelBtn = document.getElementById('importConfigCancelBtn');\n const importOkBtn = document.getElementById('importConfigOkBtn');\n const importModal = document.getElementById('importConfigModal');\n\n if (importCloseBtn) importCloseBtn.addEventListener('click', hideImportDialog);\n if (importCancelBtn) importCancelBtn.addEventListener('click', hideImportDialog);\n if (importOkBtn) importOkBtn.addEventListener('click', confirmImport);\n if (importModal) {\n importModal.addEventListener('click', (e) => {\n if (e.target === importModal) hideImportDialog();\n });\n }\n}\n\n// 简单的 toast 提示(不依赖外部模块)\nfunction showToast(message, type = 'info') {\n const container = document.getElementById('toastContainer');\n if (!container) return;\n\n const toast = document.createElement('div');\n toast.className = `toast ${type}`;\n toast.textContent = message;\n container.appendChild(toast);\n\n requestAnimationFrame(() => toast.classList.add('toast-show'));\n\n setTimeout(() => {\n toast.classList.remove('toast-show');\n setTimeout(() => {\n if (toast.parentNode) toast.parentNode.removeChild(toast);\n }, 300);\n }, 3000);\n}\n","// options/profile-manager.js - 厂商配置切换与记忆管理\n//\n// 基础 tab 顶部的\"厂商配置\"下拉切换器:新建 / 另存为 / 重命名 / 删除,\n// 切换即生效(applyProfile 会把配置字段同步回扁平键)。\n// 数据与同步逻辑统一在 shared/model-profiles.js。\n\nimport {\n getState,\n applyProfile,\n createProfile,\n renameProfile,\n deleteProfile,\n getHostFromApiBase,\n deriveProfileName,\n DEFAULT_API_BASE,\n DEFAULT_MODEL_NAME,\n} from '../shared/model-profiles.js';\nimport {\n currentModel,\n setCurrentModel,\n renderModelDropdownFromList,\n collectModelsFromDropdown,\n updateApiBaseSelection,\n updateSelectedCtxBadge,\n showToast,\n} from './config-manager.js';\nimport { t, subscribe } from '../shared/i18n.js';\n\nlet modalMode = null; // 'name' | 'confirm'\nlet modalResolve = null;\n\n// ============================================================\n// 下拉切换器渲染与事件\n// ============================================================\n\n/**\n * 初始化厂商配置切换器(在 ensureProfilesMigrated 之后调用)\n */\nexport async function initProfileManager() {\n bindProfileEvents();\n watchProfileStorage();\n // 语言切换时刷新动态文案(从属分组的联动提示行)\n subscribe(() => {\n refreshProfileDropdown();\n });\n await refreshProfileDropdown();\n}\n\n/**\n * 重新渲染厂商配置下拉(激活项高亮 + 输入框显示当前名称)\n */\nasync function refreshProfileDropdown() {\n const state = await getState();\n const dropdown = document.getElementById('profileDropdown');\n const input = document.getElementById('profileInput');\n if (!dropdown || !input) return;\n\n const active = state.profiles.find(p => p.id === state.activeProfileId) || state.profiles[0];\n dropdown.innerHTML = '';\n\n for (const profile of state.profiles) {\n const option = document.createElement('div');\n option.className = 'profile-option';\n option.dataset.value = profile.id;\n if (active && profile.id === active.id) option.classList.add('selected');\n\n const leftSpan = document.createElement('span');\n leftSpan.className = 'profile-option-left';\n leftSpan.textContent = profile.name;\n option.appendChild(leftSpan);\n\n const host = getHostFromApiBase(profile.apiBase);\n if (host) {\n const rightSpan = document.createElement('span');\n rightSpan.className = 'profile-option-right';\n rightSpan.textContent = host;\n option.appendChild(rightSpan);\n }\n dropdown.appendChild(option);\n }\n\n input.value = active ? active.name : '';\n applyProfileScopeHint(active);\n}\n\n/**\n * 更新从属分组的联动提示行(实时显示当前配置名)\n */\nfunction applyProfileScopeHint(active) {\n const el = document.getElementById('profileScopeHint');\n if (!el) return;\n el.textContent = active ? t('settings.profileScopeHint', { name: active.name }) : '';\n}\n\nfunction bindProfileEvents() {\n const input = document.getElementById('profileInput');\n const dropdown = document.getElementById('profileDropdown');\n if (!input || !dropdown) return;\n\n input.addEventListener('click', (e) => {\n e.stopPropagation();\n dropdown.classList.toggle('show');\n });\n\n dropdown.addEventListener('click', (e) => {\n const option = e.target.closest('.profile-option');\n if (!option) return;\n e.stopPropagation();\n dropdown.classList.remove('show');\n handleProfileSwitch(option.dataset.value);\n });\n\n document.addEventListener('click', (e) => {\n if (!dropdown.contains(e.target) && e.target !== input) {\n dropdown.classList.remove('show');\n }\n });\n\n document.getElementById('profileNewBtn')?.addEventListener('click', handleProfileNew);\n document.getElementById('profileDupBtn')?.addEventListener('click', handleProfileDuplicate);\n document.getElementById('profileRenameBtn')?.addEventListener('click', handleProfileRename);\n document.getElementById('profileDeleteBtn')?.addEventListener('click', handleProfileDelete);\n\n bindProfileModal();\n}\n\n/**\n * 监听配置列表变化(覆盖侧边栏切换、导入配置等场景)\n */\nfunction watchProfileStorage() {\n chrome.storage.onChanged.addListener(async (changes, areaName) => {\n if (areaName !== 'local') return;\n if (changes.modelProfiles) {\n refreshProfileDropdown();\n }\n if (changes.activeProfileId) {\n const state = await getState();\n const active = state.profiles.find(p => p.id === state.activeProfileId);\n if (active) refreshBasicFieldsFromProfile(active);\n refreshProfileDropdown();\n }\n });\n}\n\n// ============================================================\n// 切换 / 新建 / 另存为 / 重命名 / 删除\n// ============================================================\n\n/**\n * 切换激活配置(立即生效)\n */\nasync function handleProfileSwitch(id) {\n const state = await getState();\n if (id === state.activeProfileId) return;\n\n const profile = await applyProfile(id);\n if (!profile) return;\n\n refreshBasicFieldsFromProfile(profile);\n await refreshProfileDropdown();\n showToast(`✅ ${t('settings.profileSwitched', { name: profile.name })}`, 'success');\n}\n\n/**\n * 新建空配置并激活(名称弹窗默认可由当前表单地址推导)\n */\nasync function handleProfileNew() {\n const suggested = deriveProfileName(profileFormApiBase()) || '';\n const name = await openProfileModal({\n mode: 'name',\n title: t('settings.profileNameTitle'),\n defaultValue: suggested,\n });\n if (!name) return;\n\n const profile = await createProfile({\n name,\n apiBase: DEFAULT_API_BASE,\n apiKey: '',\n modelName: DEFAULT_MODEL_NAME,\n models: [],\n }, { activate: true });\n\n refreshBasicFieldsFromProfile(profile);\n await refreshProfileDropdown();\n showToast(`✅ ${t('settings.profileCreated', { name: profile.name })}`, 'success');\n}\n\n/**\n * 以当前表单内容另存为新配置并激活\n */\nasync function handleProfileDuplicate() {\n const state = await getState();\n const active = state.profiles.find(p => p.id === state.activeProfileId) || state.profiles[0];\n if (!active) return;\n\n const name = await openProfileModal({\n mode: 'name',\n title: t('settings.saveAsProfile'),\n defaultValue: `${active.name} ${t('settings.profileCopySuffix')}`,\n });\n if (!name) return;\n\n const modelInput = document.getElementById('modelInput');\n const profile = await createProfile({\n name,\n apiBase: profileFormApiBase(),\n apiKey: document.getElementById('apiKey')?.value.trim() || '',\n modelName: modelInput?.value || currentModel || DEFAULT_MODEL_NAME,\n models: collectModelsFromDropdown(),\n }, { activate: true });\n\n refreshBasicFieldsFromProfile(profile);\n await refreshProfileDropdown();\n showToast(`✅ ${t('settings.profileCreated', { name: profile.name })}`, 'success');\n}\n\n/**\n * 重命名当前配置\n */\nasync function handleProfileRename() {\n const state = await getState();\n const active = state.profiles.find(p => p.id === state.activeProfileId) || state.profiles[0];\n if (!active) return;\n\n const name = await openProfileModal({\n mode: 'name',\n title: t('settings.renameProfile'),\n defaultValue: active.name,\n });\n if (!name || name === active.name) return;\n\n await renameProfile(active.id, name);\n await refreshProfileDropdown();\n showToast(`✅ ${t('settings.profileRenamed', { name })}`, 'success');\n}\n\n/**\n * 删除当前配置(至少保留一个;删除激活项后自动激活剩余第一个)\n */\nasync function handleProfileDelete() {\n const state = await getState();\n const active = state.profiles.find(p => p.id === state.activeProfileId) || state.profiles[0];\n if (!active) return;\n\n if (state.profiles.length <= 1) {\n showToast(`⚠️ ${t('settings.atLeastOneProfile')}`, 'error');\n return;\n }\n\n const confirmed = await openProfileModal({\n mode: 'confirm',\n title: t('settings.deleteProfile'),\n message: t('settings.deleteProfileConfirm', { name: active.name }),\n });\n if (!confirmed) return;\n\n const result = await deleteProfile(active.id);\n if (!result.deleted) {\n showToast(`⚠️ ${t('settings.atLeastOneProfile')}`, 'error');\n return;\n }\n\n if (result.nextActive) {\n refreshBasicFieldsFromProfile(result.nextActive);\n }\n await refreshProfileDropdown();\n showToast(`🗑️ ${t('settings.profileDeleted', { name: active.name })}`, 'success');\n}\n\n// ============================================================\n// 表单字段刷新\n// ============================================================\n\n/**\n * 读取当前表单中的 API Base(用于默认命名)\n */\nfunction profileFormApiBase() {\n return document.getElementById('apiBase')?.value.trim() || DEFAULT_API_BASE;\n}\n\n/**\n * 用配置数据刷新基础 tab 的表单字段(地址/Key/模型下拉)\n */\nfunction refreshBasicFieldsFromProfile(profile) {\n const apiBase = profile.apiBase || DEFAULT_API_BASE;\n const modelName = profile.modelName || DEFAULT_MODEL_NAME;\n\n const apiBaseInput = document.getElementById('apiBase');\n if (apiBaseInput) apiBaseInput.value = apiBase;\n updateApiBaseSelection(apiBase);\n\n const apiKeyInput = document.getElementById('apiKey');\n if (apiKeyInput) apiKeyInput.value = profile.apiKey || '';\n\n setCurrentModel(modelName);\n const modelInput = document.getElementById('modelInput');\n if (modelInput) modelInput.value = modelName;\n\n renderModelDropdownFromList(profile.models || [], modelName);\n updateSelectedCtxBadge('modelInput', 'modelSelectedCtxBadge', 'modelDropdown');\n}\n\n// ============================================================\n// 弹窗(名称输入 / 删除确认复用)\n// ============================================================\n\nfunction bindProfileModal() {\n const modal = document.getElementById('profileModal');\n if (!modal || modal.dataset.bound) return;\n modal.dataset.bound = '1';\n\n document.getElementById('profileModalOkBtn')?.addEventListener('click', submitProfileModal);\n document.getElementById('profileModalCancelBtn')?.addEventListener('click', () => closeProfileModal(null));\n document.getElementById('profileModalCloseBtn')?.addEventListener('click', () => closeProfileModal(null));\n modal.addEventListener('click', (e) => {\n if (e.target === modal) closeProfileModal(null);\n });\n document.getElementById('profileNameInput')?.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') submitProfileModal();\n if (e.key === 'Escape') closeProfileModal(null);\n });\n}\n\n/**\n * 打开弹窗并等待用户操作\n * @param {{mode:'name'|'confirm', title:string, message?:string, defaultValue?:string}} opts\n * @returns {Promise<string|boolean|null>} 名称模式下返回名称;确认模式下返回 true/false;取消返回 null\n */\nfunction openProfileModal({ mode, title, message = '', defaultValue = '' }) {\n const modal = document.getElementById('profileModal');\n if (!modal) return Promise.resolve(null);\n\n modalMode = mode;\n document.getElementById('profileModalTitle').textContent = title;\n\n const messageEl = document.getElementById('profileModalMessage');\n messageEl.textContent = message;\n messageEl.style.display = message ? '' : 'none';\n\n const input = document.getElementById('profileNameInput');\n input.value = defaultValue;\n input.style.display = mode === 'name' ? '' : 'none';\n\n modal.style.display = 'flex';\n if (mode === 'name') {\n setTimeout(() => input.focus(), 50);\n }\n\n return new Promise((resolve) => {\n modalResolve = resolve;\n });\n}\n\nfunction submitProfileModal() {\n if (!modalResolve) return;\n if (modalMode === 'name') {\n const name = document.getElementById('profileNameInput').value.trim();\n if (!name) {\n showToast(`❌ ${t('settings.profileNameRequired')}`, 'error');\n return;\n }\n closeProfileModal(name);\n } else {\n closeProfileModal(true);\n }\n}\n\nfunction closeProfileModal(result) {\n const modal = document.getElementById('profileModal');\n if (modal) modal.style.display = 'none';\n modalMode = null;\n const resolve = modalResolve;\n modalResolve = null;\n if (resolve) resolve(result);\n}\n","// options/toolbox-shared.js - 工具箱共享状态与辅助函数\n// 从 toolbox-config.js 拆分,提供 MCP/Skills/主入口共享的状态对象与工具函数\n\nimport { showToast } from './config-manager.js';\nimport logger from '../shared/logger.js';\nimport { t } from '../shared/i18n.js';\n\n// ==================== 共享状态 ====================\n// 所有模块通过此对象读写共享状态,确保 live binding 一致性\nexport const state = {\n agentBaseUrl: null,\n agentToken: null,\n agentConnected: false,\n agentName: null,\n agentId: null,\n cachedMcpServers: [],\n cachedSkills: [],\n editingMcpId: null,\n // 刷新回调:由主入口注册,避免循环依赖\n refreshCallback: null,\n};\n\n/**\n * 触发刷新整个工具箱面板\n * 由 toolbox-config.js 的 refreshToolbox 注册\n */\nexport async function triggerRefresh() {\n if (typeof state.refreshCallback === 'function') {\n await state.refreshCallback();\n }\n}\n\n// ==================== 通用 UI 辅助 ====================\n\n/**\n * 自定义确认弹窗(返回 Promise<boolean>)\n */\nexport function showCustomConfirm(message, title = t('customConfirm.defaultTitle')) {\n return new Promise((resolve) => {\n const overlay = document.createElement('div');\n overlay.className = 'modal-overlay';\n overlay.innerHTML = `\n <div class=\"modal-content\" style=\"max-width: 400px;\">\n <div class=\"modal-header\">\n <h3>${escapeHtml(title)}</h3>\n </div>\n <div class=\"modal-body\">\n <p>${escapeHtml(message)}</p>\n </div>\n <div class=\"modal-actions\">\n <button class=\"btn btn-cancel\" id=\"confirmCancelBtn\">${t('common.cancel')}</button>\n <button class=\"btn btn-primary\" id=\"confirmOkBtn\" style=\"width: auto;\">${t('common.ok')}</button>\n </div>\n </div>\n `;\n document.body.appendChild(overlay);\n\n const close = (result) => {\n overlay.remove();\n resolve(result);\n };\n\n overlay.querySelector('#confirmCancelBtn').addEventListener('click', () => close(false));\n overlay.querySelector('#confirmOkBtn').addEventListener('click', () => close(true));\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) close(false);\n });\n });\n}\n\n/**\n * 显示 Agent Skill 只读查看弹窗(用于内置技能)\n */\nexport function showAgentSkillViewer(skillName, data) {\n const existingModal = document.getElementById('agentSkillViewerModal');\n if (existingModal) existingModal.remove();\n\n const modalOverlay = document.createElement('div');\n modalOverlay.className = 'modal-overlay';\n modalOverlay.id = 'agentSkillViewerModal';\n\n const modalContainer = document.createElement('div');\n modalContainer.className = 'modal-content agent-skill-editor-container';\n modalContainer.style.width = '700px';\n modalContainer.style.maxHeight = '85vh';\n\n modalContainer.innerHTML = `\n <div class=\"modal-header\">\n <h3>${t('toolbox.viewSkillTitle', { name: skillName })}</h3>\n <button class=\"modal-close-btn\">✕</button>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label>${t('toolbox.skillNameLabel')}</label>\n <input type=\"text\" value=\"${escapeHtml(data.frontmatter?.name || data.name || '')}\" readonly style=\"background:#f5f5f5;color:#666;\">\n </div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillDescLabel')}</label>\n <input type=\"text\" value=\"${escapeHtml(data.frontmatter?.description || '')}\" readonly style=\"background:#f5f5f5;color:#666;\">\n </div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillVersionLabel')}</label>\n <input type=\"text\" value=\"${escapeHtml(data.frontmatter?.version || '1.0')}\" readonly style=\"background:#f5f5f5;color:#666;\">\n </div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillMarkdownLabel')}</label>\n <textarea readonly style=\"min-height: 350px; font-family: monospace; background:#f5f5f5; color:#666; resize: vertical;\">${escapeHtml(data.markdown || '')}</textarea>\n </div>\n ${data.resources && data.resources.length > 0 ? `\n <div class=\"form-group\">\n <label>${t('toolbox.existingResources')} (${data.resources.length})</label>\n <div class=\"skill-resources-preview\">\n ${data.resources.map(r => `<span class=\"skill-resource-tag\">📄 ${escapeHtml(r.name || r)}</span>`).join('')}\n </div>\n </div>` : ''}\n </div>\n <div class=\"modal-actions\">\n <button class=\"btn btn-cancel\">${t('common.close')}</button>\n </div>\n `;\n\n modalOverlay.appendChild(modalContainer);\n document.body.appendChild(modalOverlay);\n\n const closeModal = () => modalOverlay.remove();\n modalContainer.querySelector('.modal-close-btn').addEventListener('click', closeModal);\n modalContainer.querySelector('.btn-cancel').addEventListener('click', closeModal);\n modalOverlay.addEventListener('click', (e) => {\n if (e.target === modalOverlay) closeModal();\n });\n}\n\n// ==================== Agent 连接与 API ====================\n\n/**\n * 获取 Agent 连接信息(从 pairedAgents 列表获取活跃代理)\n */\nexport async function getAgentConnection() {\n const result = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n const agents = result.pairedAgents || [];\n const active = agents.find(a => a.id === result.activeAgentId);\n\n state.agentBaseUrl = active?.url || null;\n state.agentToken = active?.token || null;\n state.agentConnected = !!active;\n state.agentName = active?.name || null;\n state.agentId = active?.id || null;\n return {\n url: state.agentBaseUrl,\n token: state.agentToken,\n connected: state.agentConnected,\n agentName: state.agentName,\n agentId: state.agentId\n };\n}\n\n/**\n * 通用 Agent API 请求\n */\nexport async function agentApi(method, path, body = null) {\n const opts = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${state.agentToken}`\n }\n };\n if (body) {\n opts.body = JSON.stringify(body);\n }\n const resp = await fetch(`${state.agentBaseUrl}${path}`, opts);\n const data = await resp.json();\n // 保留原始 HTTP 状态码:Agent 的 404/400 都带 JSON 体,只看 body 分不出\n // 「接口不存在(代理版本旧)」与「业务参数错」。字段名用 httpStatus 而非 status,\n // 避开响应体里可能已有的 status(与 background/local-agent-client.js 同一惯例)。\n if (data && typeof data === 'object' && !Array.isArray(data)) {\n data.httpStatus = resp.status;\n }\n return data;\n}\n\n// ==================== 通用工具函数 ====================\n\n/**\n * HTML 转义\n */\nexport function escapeHtml(str) {\n if (!str) return '';\n return String(str)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n// 重新导出 showToast 供其他模块使用\nexport { showToast };\n","// options/toolbox-mcp.js - MCP 服务器管理\n// 从 toolbox-config.js 拆分,包含 MCP 服务器的增删改查与连接管理\n\nimport { state, agentApi, escapeHtml, getAgentConnection } from './toolbox-shared.js';\nimport { t } from '../shared/i18n.js';\n\n// ==================== MCP 服务器管理 ====================\n\n// 筛选状态(模块级,refreshToolbox 重渲染后保持)\nlet mcpFilterText = '';\nlet mcpFilterStatus = 'all'; // 'all' | 'enabled' | 'disabled'\n\n/**\n * 获取 MCP 服务器列表\n */\nexport async function loadMcpServers() {\n await getAgentConnection();\n if (!state.agentConnected) return { servers: [] };\n try {\n return await agentApi('GET', '/api/mcp/servers');\n } catch {\n return { servers: [] };\n }\n}\n\n/**\n * 渲染 MCP 服务器列表\n */\nexport function renderMcpServers(servers) {\n const container = document.getElementById('mcpServerList');\n if (!container) return;\n\n state.cachedMcpServers = servers || [];\n\n if (!state.agentConnected) {\n container.innerHTML = `\n <div class=\"toolbox-empty\">\n <div class=\"toolbox-empty-icon\">🔌</div>\n <div class=\"toolbox-empty-title\">${t('toolbox.emptyAgentNotConnectedTitle')}</div>\n <div class=\"toolbox-empty-desc\">${t('toolbox.emptyAgentNotConnectedDescMcp')}</div>\n </div>`;\n return;\n }\n\n if (!servers || servers.length === 0) {\n container.innerHTML = `\n <div class=\"toolbox-empty\">\n <div class=\"toolbox-empty-icon\">📦</div>\n <div class=\"toolbox-empty-title\">${t('toolbox.emptyNoMcpTitle')}</div>\n <div class=\"toolbox-empty-desc\">${t('toolbox.emptyNoMcpDesc')}</div>\n </div>`;\n return;\n }\n\n // 应用搜索 + 状态筛选\n const filtered = servers.filter(s => {\n // 状态筛选\n if (mcpFilterStatus === 'enabled' && s.enabled === false) return false;\n if (mcpFilterStatus === 'disabled' && s.enabled !== false) return false;\n // 文本搜索:匹配 name / id / command / url\n if (mcpFilterText) {\n const text = mcpFilterText.toLowerCase();\n const haystack = [s.name, s.id, s.command, s.url, (s.args || []).join(' ')]\n .filter(Boolean).join(' ').toLowerCase();\n if (!haystack.includes(text)) return false;\n }\n return true;\n });\n\n if (filtered.length === 0) {\n container.innerHTML = `\n <div class=\"toolbox-empty\">\n <div class=\"toolbox-empty-icon\">🔍</div>\n <div class=\"toolbox-empty-title\">${t('toolbox.noMatchResult')}</div>\n </div>`;\n return;\n }\n\n container.innerHTML = filtered.map(s => {\n // 编辑中的服务器显示编辑表单\n if (s.id === state.editingMcpId) {\n const transport = s.transport || 'stdio';\n return `\n <div class=\"mcp-server-card editing\">\n <div class=\"mcp-add-form\">\n <div class=\"mcp-add-form-row\">\n <input type=\"text\" id=\"mcpEditId\" value=\"${escapeHtml(s.id)}\" placeholder=\"${t('toolbox.mcpIdPlaceholder')}\" class=\"toolbox-input\">\n <input type=\"text\" id=\"mcpEditName\" value=\"${escapeHtml(s.name || '')}\" placeholder=\"${t('toolbox.mcpNamePlaceholder')}\" class=\"toolbox-input\">\n </div>\n ${renderTransportSelector('mcpEdit', transport)}\n ${renderTransportFields('mcpEdit', transport, s.url || '', s.command || '', (s.args || []).join(' '))}\n <div class=\"mcp-env-section\" id=\"mcpEditEnvSection\" style=\"display:${transport === 'stdio' ? '' : 'none'};\">\n <div class=\"mcp-env-header\">\n <span class=\"mcp-env-title\">${t('toolbox.mcpEnvVars')}</span>\n <span class=\"mcp-env-hint\">${t('toolbox.mcpEnvVarsHint')}</span>\n </div>\n <div class=\"mcp-env-rows\" id=\"mcpEditEnvRows\">\n ${renderEnvVarRows(s.env || {})}\n </div>\n <button type=\"button\" class=\"toolbox-btn mcp-env-add-row\" data-action=\"add-env-row\" data-target=\"mcpEditEnvRows\" style=\"font-size: 12px; padding: 2px 10px;\">${t('toolbox.mcpAddVar')}</button>\n </div>\n ${renderHeadersSection('mcpEdit', s.headers || {}, transport)}\n <div class=\"mcp-add-form-actions\">\n <button class=\"toolbox-btn toolbox-btn-cancel\" data-mcp-id=\"${escapeHtml(s.id)}\" data-action=\"cancel-edit\">${t('common.cancel')}</button>\n <button class=\"toolbox-btn toolbox-btn-primary\" data-mcp-id=\"${escapeHtml(s.id)}\" data-action=\"save-edit\">${t('common.save')}</button>\n </div>\n </div>\n </div>`;\n }\n\n const statusClass = s.connected ? 'connected' : 'disconnected';\n const statusText = s.connected ? t('toolbox.mcpConnected') : t('toolbox.mcpDisconnected');\n const statusDot = s.connected ? '🟢' : '🔴';\n const toolCount = s.toolCount || 0;\n const enabledClass = s.enabled !== false ? 'enabled' : 'disabled';\n\n return `\n <div class=\"mcp-server-card ${enabledClass}\">\n <div class=\"mcp-server-header\">\n <div class=\"mcp-server-info\">\n <span class=\"mcp-server-status ${statusClass}\" title=\"${statusText}\">${statusDot}</span>\n <span class=\"mcp-server-name\">${escapeHtml(s.name || s.id)}</span>\n <span class=\"mcp-server-badge\">${escapeHtml(s.transport || 'stdio')}</span>\n </div>\n <div class=\"mcp-server-header-right\">\n <label class=\"toolbox-toggle\" title=\"${s.enabled !== false ? t('toolbox.mcpToggleEnabled') : t('toolbox.mcpToggleDisabled')}\">\n <input type=\"checkbox\" ${s.enabled !== false ? 'checked' : ''} data-mcp-id=\"${escapeHtml(s.id)}\" data-action=\"toggle\">\n <span class=\"toolbox-toggle-slider\"></span>\n </label>\n </div>\n </div>\n <div class=\"mcp-card-body${s.enabled === false ? ' disabled' : ''}\">\n <div class=\"mcp-server-command\">\n <code>${escapeHtml(renderMcpServerAddress(s))}</code>\n </div>\n ${renderMcpMetaInfo(s)}\n ${s.tools && s.tools.length > 0 ? `\n <div class=\"mcp-tools-section\">\n <button class=\"mcp-tools-toggle\" data-mcp-id=\"${escapeHtml(s.id)}\" data-action=\"toggle-tools\">\n ▶ ${t('toolbox.viewTools', { count: s.tools.length })}\n </button>\n <div class=\"mcp-tools-list\" id=\"mcp-tools-${escapeHtml(s.id)}\" style=\"display:none;\">\n ${s.tools.map(tool => `\n <div class=\"mcp-tool-item\">\n <div class=\"mcp-tool-name\">${escapeHtml(tool.name)}</div>\n <div class=\"mcp-tool-desc\">${escapeHtml(tool.description || '')}</div>\n </div>\n `).join('')}\n </div>\n </div>` : ''}\n <div class=\"mcp-server-actions\">\n ${s.connected\n ? `<button class=\"toolbox-btn toolbox-btn-warn\" data-mcp-id=\"${escapeHtml(s.id)}\" data-action=\"disconnect\">${t('toolbox.mcpDisconnectBtn')}</button>`\n : `<button class=\"toolbox-btn toolbox-btn-primary\" data-mcp-id=\"${escapeHtml(s.id)}\" data-action=\"connect\">${t('toolbox.mcpConnectBtn')}</button>`\n }\n <button class=\"toolbox-btn toolbox-btn-edit\" data-mcp-id=\"${escapeHtml(s.id)}\" data-action=\"edit\">${t('common.edit')}</button>\n <button class=\"toolbox-btn toolbox-btn-danger\" data-mcp-id=\"${escapeHtml(s.id)}\" data-action=\"delete\">${t('common.delete')}</button>\n </div>\n </div>\n </div>`;\n }).join('');\n}\n\n/**\n * 渲染环境变量输入行\n * @param {Object} envObj - { KEY: 'value', ... }\n * @returns {string} HTML\n */\nexport function renderEnvVarRows(envObj = {}) {\n const entries = Object.entries(envObj);\n // 至少显示一个空行\n if (entries.length === 0) {\n entries.push(['', '']);\n }\n return entries.map(([key, value]) => `\n <div class=\"mcp-env-row\">\n <input type=\"text\" class=\"mcp-env-key toolbox-input\" placeholder=\"${t('toolbox.mcpVarNamePlaceholder')}\" value=\"${escapeHtml(key)}\" style=\"flex: 1;\">\n <div class=\"mcp-env-value-wrap token-input-wrapper\" style=\"flex: 2;\">\n <input type=\"password\" class=\"mcp-env-value token-input\" placeholder=\"${t('toolbox.mcpVarValuePlaceholder')}\" value=\"${escapeHtml(value)}\">\n <button type=\"button\" class=\"mcp-env-eye toggle-token-btn\" title=\"${t('toolbox.mcpToggleEye')}\" data-action=\"toggle-env-eye\">\n <svg class=\"icon-eye-open\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"></path>\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\n </svg>\n <svg class=\"icon-eye-closed\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"display:none;\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\n </svg>\n </button>\n </div>\n <button type=\"button\" class=\"mcp-env-remove toolbox-btn toolbox-btn-danger\" title=\"${t('toolbox.mcpRemoveRow')}\" data-action=\"remove-env-row\" style=\"flex-shrink: 0;\">×</button>\n </div>\n `).join('');\n}\n\n/**\n * 从容器中解析环境变量\n * @param {HTMLElement} container - 包含 .mcp-env-row 的容器\n * @returns {Object} env 对象\n */\nexport function parseEnvVars(container) {\n const env = {};\n container.querySelectorAll('.mcp-env-row').forEach(row => {\n const key = row.querySelector('.mcp-env-key')?.value.trim();\n const value = row.querySelector('.mcp-env-value')?.value;\n if (key && value !== undefined) {\n env[key] = value;\n }\n });\n return Object.keys(env).length > 0 ? env : {};\n}\n\n/**\n * 渲染请求头输入行\n * @param {Object} headersObj - { 'Header-Name': 'value', ... }\n * @returns {string} HTML\n */\nexport function renderHeaderRows(headersObj = {}) {\n const entries = Object.entries(headersObj);\n if (entries.length === 0) {\n entries.push(['', '']);\n }\n return entries.map(([key, value]) => `\n <div class=\"mcp-env-row\">\n <input type=\"text\" class=\"mcp-env-key toolbox-input\" placeholder=\"${t('toolbox.mcpHeaderNamePlaceholder')}\" value=\"${escapeHtml(key)}\" style=\"flex: 1;\">\n <div class=\"mcp-env-value-wrap token-input-wrapper\" style=\"flex: 2;\">\n <input type=\"password\" class=\"mcp-env-value token-input\" placeholder=\"${t('toolbox.mcpVarValuePlaceholder')}\" value=\"${escapeHtml(value)}\">\n <button type=\"button\" class=\"mcp-env-eye toggle-token-btn\" title=\"${t('toolbox.mcpToggleEye')}\" data-action=\"toggle-env-eye\">\n <svg class=\"icon-eye-open\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"></path>\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\n </svg>\n <svg class=\"icon-eye-closed\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"display:none;\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\n </svg>\n </button>\n </div>\n <button type=\"button\" class=\"mcp-env-remove toolbox-btn toolbox-btn-danger\" title=\"${t('toolbox.mcpRemoveRow')}\" data-action=\"remove-env-row\" style=\"flex-shrink: 0;\">×</button>\n </div>\n `).join('');\n}\n\n/**\n * 从容器中解析请求头\n * @param {HTMLElement} container - 包含 .mcp-env-row 的容器\n * @returns {Object} headers 对象\n */\nexport function parseHeaders(container) {\n const headers = {};\n container.querySelectorAll('.mcp-env-row').forEach(row => {\n const key = row.querySelector('.mcp-env-key')?.value.trim();\n const value = row.querySelector('.mcp-env-value')?.value;\n if (key && value !== undefined) {\n headers[key] = value;\n }\n });\n return Object.keys(headers).length > 0 ? headers : {};\n}\n\n/**\n * 渲染请求头配置区域 HTML\n * @param {string} idPrefix - 元素 ID 前缀(\"mcpAdd\" 或 \"mcpEdit\")\n * @param {Object} headers - 当前 headers\n * @returns {string} HTML\n */\nexport function renderHeadersSection(idPrefix, headers = {}, transport = 'stdio') {\n const isHttp = transport === 'sse' || transport === 'streamableHttp' || transport === 'websocket';\n return `\n <div class=\"mcp-env-section\" id=\"${idPrefix}HeadersSection\" style=\"display:${isHttp ? '' : 'none'};\">\n <div class=\"mcp-env-header\">\n <span class=\"mcp-env-title\">${t('toolbox.mcpHeaders')}</span>\n <span class=\"mcp-env-hint\">${t('toolbox.mcpHeadersHint')}</span>\n </div>\n <div class=\"mcp-env-rows\" id=\"${idPrefix}HeaderRows\">\n ${renderHeaderRows(headers)}\n </div>\n <button type=\"button\" class=\"toolbox-btn mcp-env-add-row\" data-action=\"add-env-row\" data-target=\"${idPrefix}HeaderRows\" style=\"font-size: 12px; padding: 2px 10px;\">${t('toolbox.mcpAddHeader')}</button>\n </div>`;\n}\n\n/**\n * 处理环境变量 UI 操作(事件委托)\n * @param {Event} e - 点击事件\n * @returns {boolean} 是否已处理(true 表示已消费该事件)\n */\nexport function handleEnvAction(e) {\n const actionBtn = e.target.closest('[data-action]');\n if (!actionBtn) return false;\n\n const action = actionBtn.dataset.action;\n\n if (action === 'toggle-env-eye') {\n const wrap = actionBtn.closest('.mcp-env-value-wrap');\n const input = wrap?.querySelector('.mcp-env-value');\n const iconOpen = wrap?.querySelector('.icon-eye-open');\n const iconClosed = wrap?.querySelector('.icon-eye-closed');\n if (input) {\n if (input.type === 'password') {\n input.type = 'text';\n if (iconOpen) iconOpen.style.display = 'none';\n if (iconClosed) iconClosed.style.display = 'block';\n } else {\n input.type = 'password';\n if (iconOpen) iconOpen.style.display = 'block';\n if (iconClosed) iconClosed.style.display = 'none';\n }\n }\n return true;\n }\n\n if (action === 'remove-env-row') {\n const row = actionBtn.closest('.mcp-env-row');\n row?.remove();\n return true;\n }\n\n if (action === 'add-env-row') {\n const targetId = actionBtn.dataset.target;\n const target = targetId ? document.getElementById(targetId) : null;\n if (target) {\n const newRow = document.createElement('div');\n newRow.className = 'mcp-env-row';\n newRow.innerHTML = `\n <input type=\"text\" class=\"mcp-env-key toolbox-input\" placeholder=\"${t('toolbox.mcpVarNamePlaceholder')}\" style=\"flex: 1;\">\n <div class=\"mcp-env-value-wrap token-input-wrapper\" style=\"flex: 2;\">\n <input type=\"password\" class=\"mcp-env-value token-input\" placeholder=\"${t('toolbox.mcpVarValuePlaceholder')}\">\n <button type=\"button\" class=\"mcp-env-eye toggle-token-btn\" title=\"${t('toolbox.mcpToggleEye')}\" data-action=\"toggle-env-eye\">\n <svg class=\"icon-eye-open\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"></path>\n <circle cx=\"12\" cy=\"12\" r=\"3\"></circle>\n </svg>\n <svg class=\"icon-eye-closed\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"display:none;\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\"></path>\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line>\n </svg>\n </button>\n </div>\n <button type=\"button\" class=\"mcp-env-remove toolbox-btn toolbox-btn-danger\" title=\"${t('toolbox.mcpRemoveRow')}\" data-action=\"remove-env-row\" style=\"flex-shrink: 0;\">×</button>\n `;\n target.appendChild(newRow);\n // 聚焦新行的变量名输入框\n const keyInput = newRow.querySelector('.mcp-env-key');\n if (keyInput) setTimeout(() => keyInput.focus(), 0);\n }\n return true;\n }\n\n return false;\n}\n\n/**\n * 显示添加 MCP 服务器表单\n */\n/**\n * 渲染传输方式选择器(自定义按钮组,替代原生 select)\n * @param {string} idPrefix - 元素 ID 前缀\n * @param {string} transport - 当前传输类型\n * @returns {string} HTML\n */\nexport function renderTransportSelector(idPrefix, transport = 'stdio') {\n const options = [\n { value: 'stdio', label: 'stdio', title: t('toolbox.transportStdioTitle') },\n { value: 'sse', label: 'SSE', title: t('toolbox.transportSseTitle') },\n { value: 'streamableHttp', label: 'Streamable HTTP', title: t('toolbox.transportStreamableHttpTitle') },\n { value: 'websocket', label: 'WebSocket', title: t('toolbox.transportWebsocketTitle') }\n ];\n\n return `\n <div class=\"mcp-transport-group\" id=\"${idPrefix}TransportGroup\" data-prefix=\"${idPrefix}\">\n ${options.map(o => `\n <button type=\"button\"\n class=\"mcp-transport-btn${o.value === transport ? ' active' : ''}\"\n data-value=\"${o.value}\"\n data-action=\"transport-change\"\n data-prefix=\"${idPrefix}\"\n title=\"${o.title}\">${o.label}</button>\n `).join('')}\n </div>`;\n}\n\n/**\n * 渲染传输方式配置字段(命令/URL 行,根据传输类型切换显示)\n * @param {string} idPrefix - 元素 ID 前缀\n * @param {string} transport - 当前传输类型\n * @param {string} url - 当前 URL\n * @param {string} command - 当前命令\n * @param {string} args - 当前参数\n * @returns {string} HTML\n */\nexport function renderTransportFields(idPrefix, transport = 'stdio', url = '', command = '', args = '') {\n const isHttp = transport === 'sse' || transport === 'streamableHttp' || transport === 'websocket';\n return `\n <div class=\"mcp-add-form-row\" id=\"${idPrefix}StdioRow\" style=\"display:${isHttp ? 'none' : 'flex'};\">\n <input type=\"text\" id=\"${idPrefix}Command\" value=\"${escapeHtml(command)}\" placeholder=\"${t('toolbox.mcpCommandPlaceholder')}\" class=\"toolbox-input\" style=\"flex: 2;\">\n <input type=\"text\" id=\"${idPrefix}Args\" value=\"${escapeHtml(args)}\" placeholder=\"${t('toolbox.mcpArgsPlaceholder')}\" class=\"toolbox-input\" style=\"flex: 3;\">\n </div>\n <div class=\"mcp-add-form-row\" id=\"${idPrefix}UrlRow\" style=\"display:${isHttp ? 'flex' : 'none'};\">\n <input type=\"text\" id=\"${idPrefix}Url\" value=\"${escapeHtml(url)}\" placeholder=\"http://localhost:3000/mcp\" class=\"toolbox-input\" style=\"flex: 1;\">\n </div>`;\n}\n\n/**\n * 渲染 MCP 服务器连接地址文本\n * @param {Object} server\n * @returns {string}\n */\nexport function renderMcpServerAddress(server) {\n const transport = server.transport || 'stdio';\n if (transport === 'stdio' || !transport) {\n return `${server.command || ''} ${(server.args || []).join(' ')}`;\n }\n return server.url || '';\n}\n\n/**\n * 渲染 MCP 服务器附加信息(headers 数量等)\n * @param {Object} server\n * @returns {string}\n */\nexport function renderMcpMetaInfo(server) {\n const headerCount = Object.keys(server.headers || {}).length;\n if (headerCount > 0) {\n return `<div class=\"mcp-server-meta\">${t('toolbox.mcpCustomHeadersCount', { count: headerCount })}</div>`;\n }\n return '';\n}\n\n/**\n * 获取当前选中的传输方式\n * @param {string} idPrefix - 元素 ID 前缀\n * @returns {string}\n */\nexport function getTransportValue(idPrefix) {\n const activeBtn = document.querySelector(`#${idPrefix}TransportGroup .mcp-transport-btn.active`);\n return activeBtn?.dataset.value || 'stdio';\n}\n\n/**\n * 处理传输方式切换 - 显示/隐藏对应的配置行\n * @param {HTMLSelectElement} selectEl\n */\nexport function handleTransportChange(btn) {\n const prefix = btn.dataset.prefix;\n const value = btn.dataset.value;\n\n // 更新按钮激活状态\n const group = document.getElementById(`${prefix}TransportGroup`);\n if (group) {\n group.querySelectorAll('.mcp-transport-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n }\n\n const isStdio = value === 'stdio';\n const isHttp = value === 'sse' || value === 'streamableHttp' || value === 'websocket';\n const isHttpWithHeaders = value === 'sse' || value === 'streamableHttp';\n\n const stdioRow = document.getElementById(`${prefix}StdioRow`);\n const urlRow = document.getElementById(`${prefix}UrlRow`);\n const envSection = document.getElementById(`${prefix}EnvSection`);\n const headersSection = document.getElementById(`${prefix}HeadersSection`);\n\n if (stdioRow) stdioRow.style.display = isStdio ? 'flex' : 'none';\n if (urlRow) urlRow.style.display = isHttp ? 'flex' : 'none';\n if (envSection) envSection.style.display = isStdio ? '' : 'none';\n if (headersSection) headersSection.style.display = isHttpWithHeaders ? '' : 'none';\n}\n\nexport function showAddMcpForm() {\n const container = document.getElementById('mcpAddForm');\n if (!container) return;\n\n container.innerHTML = `\n <div class=\"mcp-add-form\">\n <div class=\"mcp-add-form-row\">\n <input type=\"text\" id=\"mcpAddId\" placeholder=\"${t('toolbox.mcpIdPlaceholderUnique')}\" class=\"toolbox-input\">\n <input type=\"text\" id=\"mcpAddName\" placeholder=\"${t('toolbox.mcpNamePlaceholder')}\" class=\"toolbox-input\">\n </div>\n ${renderTransportSelector('mcpAdd')}\n ${renderTransportFields('mcpAdd')}\n <div class=\"mcp-env-section\" id=\"mcpAddEnvSection\">\n <div class=\"mcp-env-header\">\n <span class=\"mcp-env-title\">${t('toolbox.mcpEnvVars')}</span>\n <span class=\"mcp-env-hint\">${t('toolbox.mcpEnvVarsHint')}</span>\n </div>\n <div class=\"mcp-env-rows\" id=\"mcpAddEnvRows\">\n ${renderEnvVarRows()}\n </div>\n <button type=\"button\" class=\"toolbox-btn mcp-env-add-row\" data-action=\"add-env-row\" data-target=\"mcpAddEnvRows\" style=\"font-size: 12px; padding: 2px 10px;\">${t('toolbox.mcpAddVar')}</button>\n </div>\n ${renderHeadersSection('mcpAdd', {}, 'stdio')}\n <div class=\"mcp-add-form-actions\">\n <button class=\"toolbox-btn toolbox-btn-cancel\" id=\"mcpAddCancel\">${t('common.cancel')}</button>\n <button class=\"toolbox-btn toolbox-btn-primary\" id=\"mcpAddConfirm\">${t('common.add')}</button>\n </div>\n </div>`;\n}\n\n/**\n * 隐藏添加表单\n */\nexport function hideAddMcpForm() {\n const container = document.getElementById('mcpAddForm');\n if (container) container.innerHTML = '';\n}\n\n/**\n * 添加 MCP 服务器\n */\nexport async function addMcpServer(serverData) {\n try {\n const result = await agentApi('POST', '/api/mcp/servers', serverData);\n if (result.success) {\n return true;\n }\n throw new Error(result.error || t('toolbox.mcpAddFailedShort'));\n } catch (err) {\n throw err;\n }\n}\n\n/**\n * 删除 MCP 服务器\n */\nexport async function removeMcpServer(serverId) {\n try {\n const result = await agentApi('DELETE', '/api/mcp/servers', { id: serverId });\n if (result.success) return true;\n throw new Error(result.error || t('toolbox.mcpDeleteFailedShort'));\n } catch (err) {\n throw err;\n }\n}\n\n/**\n * 连接 MCP 服务器\n */\nexport async function connectMcpServer(serverId) {\n const result = await agentApi('POST', '/api/mcp/servers/connect', { id: serverId });\n if (result.success) return result;\n throw new Error(result.error || t('toolbox.mcpConnectFailedShort'));\n}\n\n/**\n * 断开 MCP 服务器\n */\nexport async function disconnectMcpServer(serverId) {\n const result = await agentApi('POST', '/api/mcp/servers/disconnect', { id: serverId });\n if (result.success) return true;\n throw new Error(result.error || t('toolbox.mcpDisconnectFailedShort'));\n}\n\n/**\n * 切换 MCP 服务器启用状态\n */\nexport async function toggleMcpServer(serverId, enabled) {\n const result = await agentApi('PUT', '/api/mcp/servers/toggle', { id: serverId, enabled });\n if (result.success) return true;\n throw new Error(result.error || t('toolbox.mcpOperationFailedShort'));\n}\n\n/**\n * 初始化 MCP 搜索 + 筛选事件\n */\nexport function initMcpFilter() {\n const searchInput = document.getElementById('mcpSearchInput');\n const filterTabs = document.querySelectorAll('.toolbox-filter-tab[data-target=\"mcp\"]');\n\n if (searchInput) {\n let debounceTimer = null;\n searchInput.addEventListener('input', () => {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n mcpFilterText = searchInput.value.trim();\n renderMcpServers(state.cachedMcpServers);\n }, 200);\n });\n }\n\n const clearBtn = document.getElementById('mcpSearchClear');\n if (clearBtn) {\n clearBtn.addEventListener('click', () => {\n searchInput.value = '';\n mcpFilterText = '';\n renderMcpServers(state.cachedMcpServers);\n searchInput.focus();\n });\n }\n\n filterTabs.forEach(tab => {\n tab.addEventListener('click', () => {\n filterTabs.forEach(t => t.classList.remove('active'));\n tab.classList.add('active');\n mcpFilterStatus = tab.dataset.filter;\n renderMcpServers(state.cachedMcpServers);\n });\n });\n}\n\n// MCP 服务器管理函数已通过 export 暴露给 toolbox-config.js\n","// options/toolbox-skills.js - Skill 管理\n// 从 toolbox-config.js 拆分,包含 Skill 的增删改查、导入与运行\n\nimport { state, agentApi, escapeHtml, getAgentConnection, showCustomConfirm, showAgentSkillViewer, showToast, triggerRefresh } from './toolbox-shared.js';\nimport logger from '../shared/logger.js';\nimport { t } from '../shared/i18n.js';\n\n// ==================== Skill 管理 ====================\n\n// 筛选状态(模块级,refreshToolbox 重渲染后保持)\nlet skillFilterText = '';\nlet skillFilterStatus = 'all'; // 'all' | 'enabled' | 'disabled'\n\n/**\n * 获取 Skill 列表\n */\nexport async function loadSkills() {\n await getAgentConnection();\n if (!state.agentConnected) return { skills: [] };\n try {\n return await agentApi('GET', '/api/skill/list');\n } catch {\n return { skills: [] };\n }\n}\n\n/**\n * 渲染 Skill 列表(区分 Workflow 和 Agent 类型)\n */\nexport function renderSkills(skills) {\n const container = document.getElementById('skillList');\n if (!container) return;\n\n state.cachedSkills = skills || [];\n\n if (!state.agentConnected) {\n container.innerHTML = `\n <div class=\"toolbox-empty\">\n <div class=\"toolbox-empty-icon\">🔌</div>\n <div class=\"toolbox-empty-title\">${t('toolbox.emptyAgentNotConnectedTitle')}</div>\n <div class=\"toolbox-empty-desc\">${t('toolbox.emptyAgentNotConnectedDescSkill')}</div>\n </div>`;\n return;\n }\n\n if (!skills || skills.length === 0) {\n container.innerHTML = `\n <div class=\"toolbox-empty\">\n <div class=\"toolbox-empty-icon\">🧩</div>\n <div class=\"toolbox-empty-title\">${t('toolbox.emptyNoSkillTitle')}</div>\n <div class=\"toolbox-empty-desc\">${t('toolbox.emptyNoSkillDesc')}</div>\n </div>`;\n return;\n }\n\n // 应用搜索 + 状态筛选\n const filtered = skills.filter(s => {\n // 状态筛选\n if (skillFilterStatus === 'enabled' && s.enabled === false) return false;\n if (skillFilterStatus === 'disabled' && s.enabled !== false) return false;\n // 文本搜索:匹配 name / description\n if (skillFilterText) {\n const text = skillFilterText.toLowerCase();\n const haystack = [s.name, s.description].filter(Boolean).join(' ').toLowerCase();\n if (!haystack.includes(text)) return false;\n }\n return true;\n });\n\n if (filtered.length === 0) {\n container.innerHTML = `\n <div class=\"toolbox-empty\">\n <div class=\"toolbox-empty-icon\">🔍</div>\n <div class=\"toolbox-empty-title\">${t('toolbox.noMatchResult')}</div>\n </div>`;\n return;\n }\n\n const workflowSkills = filtered.filter(s => s.type !== 'agent');\n const agentSkills = filtered.filter(s => s.type === 'agent');\n\n let html = '';\n\n function truncateText(text, maxLength = 100) {\n if (!text || text.length <= maxLength) return { content: text, truncated: false };\n return {\n content: text.substring(0, maxLength) + '...',\n truncated: true,\n full: text\n };\n}\n\n// Workflow Skills\n if (workflowSkills.length > 0) {\n html += `<div class=\"skill-section-title\">${t('toolbox.workflowSkillsTitle')}</div>`;\n html += workflowSkills.map(s => {\n const desc = truncateText(s.description || '', 120);\n const hasParams = s.parameters && s.parameters.properties && Object.keys(s.parameters.properties).length > 0;\n return `\n <div class=\"skill-card skill-card-workflow${s.enabled === false ? ' skill-disabled' : ''}\">\n <div class=\"skill-card-header\">\n <div class=\"skill-card-info\">\n <span class=\"skill-card-icon\">⚙️</span>\n <span class=\"skill-card-name\">${escapeHtml(s.name)}</span>\n <span class=\"skill-card-version\">v${escapeHtml(s.version || '1.0')}</span>\n <span class=\"skill-card-badge badge-workflow\">Workflow</span>\n ${s.enabled === false ? `<span class=\"skill-card-badge badge-disabled\">${t('toolbox.disable')}</span>` : ''}\n <span class=\"skill-card-step-count\">${t('toolbox.stepCount', { count: s.stepCount || 0 })}</span>\n </div>\n </div>\n <div class=\"skill-card-body${s.enabled === false ? ' disabled' : ''}\">\n <div class=\"skill-card-desc\">${escapeHtml(desc.content)}</div>\n ${desc.truncated && s.enabled !== false ? `<button class=\"skill-expand-btn\" data-skill-name=\"${escapeHtml(s.name)}\" data-target=\"desc\" data-full=\"${encodeURIComponent(s.description || '')}\">${t('toolbox.expand')}</button>` : ''}\n ${hasParams ? `\n <div class=\"skill-card-params\">\n ${renderSkillParams(s.parameters)}\n </div>` : ''}\n <div class=\"skill-card-actions\">\n ${s.enabled !== false ? `<button class=\"toolbox-btn toolbox-btn-primary\" data-skill-name=\"${escapeHtml(s.name)}\" data-action=\"run-skill\">${t('toolbox.run')}</button>` : ''}\n <button class=\"toolbox-btn toolbox-btn-secondary\" data-skill-name=\"${escapeHtml(s.name)}\" data-action=\"toggle-skill\">${s.enabled === false ? t('toolbox.enable') : t('toolbox.disable')}</button>\n <button class=\"toolbox-btn toolbox-btn-danger\" data-skill-name=\"${escapeHtml(s.name)}\" data-action=\"delete-skill\">${t('common.delete')}</button>\n </div>\n </div>\n </div>\n `}).join('');\n }\n\n // Agent Skills\n if (agentSkills.length > 0) {\n html += `<div class=\"skill-section-title\">${t('toolbox.agentSkillsTitle')}</div>`;\n html += agentSkills.map(s => {\n const isBuiltin = s.builtin === true;\n const canEdit = s.editable !== false;\n const canDelete = s.deletable !== false;\n const desc = truncateText(s.description || '', 120);\n const hasResources = s.resources && s.resources.length > 0;\n const showResourcesInFull = !hasResources || s.resources.length <= 5;\n return `\n <div class=\"skill-card skill-card-agent${s.enabled === false ? ' skill-disabled' : ''}\">\n <div class=\"skill-card-header\">\n <div class=\"skill-card-info\">\n <span class=\"skill-card-icon\">🤖</span>\n <span class=\"skill-card-name\">${escapeHtml(s.name)}</span>\n <span class=\"skill-card-version\">v${escapeHtml(s.version || '1.0')}</span>\n <span class=\"skill-card-badge badge-agent\">Agent</span>\n ${isBuiltin ? `<span class=\"skill-card-badge badge-builtin\">${t('toolbox.builtin')}</span>` : ''}\n ${s.enabled === false ? `<span class=\"skill-card-badge badge-disabled\">${t('toolbox.disable')}</span>` : ''}\n <span class=\"skill-card-step-count\">${t('toolbox.resourceCount', { count: s.resourceCount || 0 })}</span>\n </div>\n </div>\n <div class=\"skill-card-body${s.enabled === false ? ' disabled' : ''}\">\n <div class=\"skill-card-desc\">${escapeHtml(desc.content)}</div>\n ${desc.truncated && s.enabled !== false ? `<button class=\"skill-expand-btn\" data-skill-name=\"${escapeHtml(s.name)}\" data-target=\"desc\" data-full=\"${encodeURIComponent(s.description || '')}\">${t('toolbox.expand')}</button>` : ''}\n ${hasResources ? `\n <div class=\"skill-card-params\">\n ${showResourcesInFull\n ? s.resources.map(r => `<span class=\"skill-param-tag\" title=\"${t('toolbox.sizeBytes', { size: r.size })}\">📄 ${escapeHtml(r.name)}</span>`).join('')\n : s.resources.slice(0, 5).map(r => `<span class=\"skill-param-tag\" title=\"${t('toolbox.sizeBytes', { size: r.size })}\">📄 ${escapeHtml(r.name)}</span>`).join('') + `<button class=\"skill-expand-btn skill-expand-resources\" data-skill-name=\"${escapeHtml(s.name)}\" data-target=\"resources\" data-full=\"${encodeURIComponent(JSON.stringify(s.resources))}\">${t('toolbox.moreCount', { count: s.resources.length - 5 })}</button>`\n }\n </div>` : ''}\n <div class=\"skill-card-actions\">\n ${canEdit ? `<button class=\"toolbox-btn toolbox-btn-secondary\" data-skill-name=\"${escapeHtml(s.name)}\" data-action=\"edit-agent-skill\">${t('toolbox.editSkillMd')}</button>` : `<button class=\"toolbox-btn toolbox-btn-secondary\" data-skill-name=\"${escapeHtml(s.name)}\" data-action=\"view-agent-skill\">${t('toolbox.viewDetails')}</button>`}\n <button class=\"toolbox-btn toolbox-btn-secondary\" data-skill-name=\"${escapeHtml(s.name)}\" data-action=\"toggle-skill\">${s.enabled === false ? t('toolbox.enable') : t('toolbox.disable')}</button>\n ${canDelete ? `<button class=\"toolbox-btn toolbox-btn-danger\" data-skill-name=\"${escapeHtml(s.name)}\" data-action=\"delete-skill\">${t('common.delete')}</button>` : ''}\n </div>\n </div>\n </div>\n `}).join('');\n }\n\n container.innerHTML = html;\n}\n\n/**\n * 渲染 Skill 参数\n */\nexport function renderSkillParams(parameters) {\n if (!parameters || !parameters.properties) return '';\n const props = parameters.properties;\n const required = parameters.required || [];\n\n return Object.entries(props).map(([key, prop]) => {\n const isRequired = required.includes(key);\n return `<span class=\"skill-param-tag ${isRequired ? 'required' : ''}\" title=\"${escapeHtml(prop.description || '')}\">${escapeHtml(key)}${isRequired ? '*' : ''}</span>`;\n }).join('');\n}\n\n/**\n * 导入 Workflow Skill\n */\nexport async function importSkill(skillDef) {\n const result = await agentApi('POST', '/api/skill/import', skillDef);\n if (result.success) return true;\n throw new Error(result.error || t('toolbox.skillImportFailedShort'));\n}\n\n/**\n * 删除 Skill\n */\nexport async function deleteSkill(name) {\n const result = await agentApi('DELETE', '/api/skill/delete', { name });\n if (result.success) return true;\n throw new Error(result.error || t('toolbox.skillDeleteFailedShort'));\n}\n\n/**\n * 切换 Skill 启用/停用状态\n */\nexport async function toggleSkill(name) {\n const result = await agentApi('POST', '/api/skill/toggle', { name });\n if (result.success) return result.enabled !== false;\n throw new Error(result.error || t('toolbox.skillOperationFailedShort'));\n}\n\n/**\n * 运行 Workflow Skill\n */\nexport async function runSkill(name, params = {}) {\n const result = await agentApi('POST', '/api/skill/run', { name, params });\n return result;\n}\n\n/**\n * 解析 Skill 的参数定义(兼容 JSON Schema 格式)\n */\nexport function parseSkillParams(skill) {\n const params = skill.parameters || {};\n // 兼容 JSON Schema 格式: { type: \"object\", properties: {...}, required: [...] }\n const props = params.properties || {};\n const requiredList = Array.isArray(params.required) ? params.required : [];\n const entries = Object.entries(props);\n return {\n hasRequired: entries.length > 0,\n required: entries.filter(([key]) => requiredList.includes(key)),\n optional: entries.filter(([key]) => !requiredList.includes(key)),\n all: entries\n };\n}\n\n/**\n * 显示 Skill 参数输入弹窗\n * @returns {Promise<Object|null>} - 用户填写的参数,取消则返回 null\n */\nexport function showSkillParamsDialog(skillName, paramsDef) {\n return new Promise((resolve) => {\n const overlay = document.createElement('div');\n overlay.className = 'modal-overlay';\n\n // 生成参数输入字段\n const requiredFields = paramsDef.required.map(([key, def]) => `\n <div class=\"form-group\">\n <label for=\"skill-param-${key}\">${escapeHtml(def.description || key)} <span class=\"param-required\">${t('toolbox.required')}</span></label>\n <input type=\"text\" id=\"skill-param-${key}\" class=\"form-input\"\n placeholder=\"${escapeHtml(def.type || 'string')}${def.default !== undefined ? t('toolbox.defaultParamHint', { default: def.default }) : ''}\" />\n </div>\n `).join('');\n\n const optionalFields = paramsDef.optional.map(([key, def]) => `\n <div class=\"form-group\">\n <label for=\"skill-param-${key}\">${escapeHtml(def.description || key)} <span class=\"param-optional\">${t('toolbox.optional')}</span></label>\n <input type=\"text\" id=\"skill-param-${key}\" class=\"form-input\"\n placeholder=\"${escapeHtml(def.type || 'string')}${def.default !== undefined ? t('toolbox.defaultParamHint', { default: def.default }) : ''}\" />\n </div>\n `).join('');\n\n overlay.innerHTML = `\n <div class=\"modal-content\" style=\"max-width:500px;\">\n <div class=\"modal-header\">\n <h2>${t('toolbox.runSkillTitle', { name: skillName })}</h2>\n <button class=\"modal-close-btn\" id=\"skillParamsCancel\">×</button>\n </div>\n <div class=\"modal-body\">\n <p style=\"margin:0 0 16px;color:#666;font-size:13px;\">${t('toolbox.fillParamsHint')}</p>\n ${requiredFields}\n ${paramsDef.required.length > 0 && paramsDef.optional.length > 0 ? '<div style=\"border-top:1px dashed #e0e0e0;margin:12px 0;\"></div>' : ''}\n ${optionalFields}\n </div>\n <div class=\"modal-actions\">\n <button class=\"btn btn-cancel\" id=\"skillParamsCancel\">${t('common.cancel')}</button>\n <button class=\"btn btn-primary\" id=\"skillParamsSubmit\">${t('toolbox.run')}</button>\n </div>\n </div>\n `;\n\n document.body.appendChild(overlay);\n\n const close = () => { overlay.remove(); resolve(null); };\n\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) close();\n });\n overlay.querySelectorAll('#skillParamsCancel').forEach(btn => {\n btn.addEventListener('click', close);\n });\n\n overlay.querySelector('#skillParamsSubmit').addEventListener('click', () => {\n const values = {};\n let allRequiredFilled = true;\n for (const [key, def] of paramsDef.required) {\n const input = overlay.querySelector(`#skill-param-${key}`);\n const val = input?.value?.trim();\n if (!val) {\n allRequiredFilled = false;\n if (input) input.style.borderColor = '#e53e3e';\n } else {\n values[key] = val;\n if (input) input.style.borderColor = '';\n }\n }\n for (const [key] of paramsDef.optional) {\n const input = overlay.querySelector(`#skill-param-${key}`);\n if (input) {\n values[key] = input.value;\n }\n }\n if (!allRequiredFilled) return;\n overlay.remove();\n resolve(values);\n });\n\n // 回车提交\n overlay.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n overlay.querySelector('#skillParamsSubmit').click();\n }\n });\n\n // 自动聚焦第一个输入框\n const firstInput = overlay.querySelector('input');\n if (firstInput) setTimeout(() => firstInput.focus(), 100);\n });\n}\nexport function showSkillRunResult(name, skillInfo, result) {\n const overlay = document.createElement('div');\n overlay.className = 'modal-overlay';\n overlay.id = 'skillResultModal';\n\n const steps = skillInfo?.success ? (skillInfo.skill?.steps || []) : [];\n const stepMap = {};\n steps.forEach(s => { stepMap[s.id] = s; });\n\n const results = result.results || {};\n const stepIds = Object.keys(results);\n\n let stepsHtml = '';\n let successCount = 0;\n let failCount = 0;\n let skipCount = 0;\n\n if (stepIds.length === 0) {\n stepsHtml = `<div style=\"padding:16px;text-align:center;color:#999;\">${result.error || t('toolbox.noExecutionResult')}</div>`;\n } else {\n stepsHtml = stepIds.map((stepId, idx) => {\n const stepRes = results[stepId];\n const stepDef = stepMap[stepId] || {};\n const stepName = stepDef.name || stepDef.description || stepId;\n const tool = stepDef.tool || '';\n const isSuccess = stepRes?.success;\n const isSkipped = stepRes?.skipped;\n\n if (isSkipped) skipCount++;\n else if (isSuccess) successCount++;\n else failCount++;\n\n const statusIcon = isSkipped ? '⊘' : (isSuccess ? '✓' : '✗');\n const statusClass = isSkipped ? 'step-skipped' : (isSuccess ? 'step-success' : 'step-error');\n const outputText = isSkipped\n ? (stepRes.message || t('toolbox.conditionNotMetSkipped'))\n : (isSuccess\n ? (stepRes.content || stepRes.stdout || stepRes.message || t('toolbox.executionSuccess'))\n : (stepRes.error || t('toolbox.executionFailed')));\n\n // 截断过长输出\n const truncated = outputText.length > 500\n ? outputText.substring(0, 500) + `\\n${t('toolbox.truncated', { count: outputText.length })}`\n : outputText;\n\n return `\n <div class=\"skill-run-step ${statusClass}\">\n <div class=\"step-header\">\n <span class=\"step-status-icon\">${statusIcon}</span>\n <span class=\"step-title\">${escapeHtml(stepName)}</span>\n ${tool ? `<span class=\"step-tool-tag\">${escapeHtml(tool)}</span>` : ''}\n </div>\n <pre class=\"step-output\">${escapeHtml(truncated)}</pre>\n </div>\n `;\n }).join('');\n }\n\n const icon = result.success ? '✓' : '✗';\n const statusText = result.success ? t('toolbox.executionComplete') : t('toolbox.executionFailed');\n const summaryText = result.success\n ? t('toolbox.executionSummary', { success: successCount, fail: failCount, skip: skipCount })\n : (result.error || t('agentConfig.unknownError'));\n\n overlay.innerHTML = `\n <div class=\"modal-content\" style=\"max-width:700px;max-height:85vh;\">\n <div class=\"modal-header\">\n <h2>${icon} ${t('toolbox.skillRunResultTitle', { name, status: statusText })}</h2>\n <button class=\"modal-close-btn\">×</button>\n </div>\n <div class=\"modal-body\" style=\"max-height:calc(85vh - 120px);overflow-y:auto;\">\n <div class=\"skill-run-summary\">${escapeHtml(summaryText)}</div>\n ${stepsHtml}\n </div>\n <div class=\"modal-actions\">\n <button class=\"btn btn-primary modal-close-btn\">${t('common.close')}</button>\n </div>\n </div>\n `;\n\n document.body.appendChild(overlay);\n\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay || e.target.classList.contains('modal-close-btn')) {\n overlay.remove();\n }\n });\n}\n\n/**\n * 重新加载 Skill\n */\nexport async function reloadSkills() {\n const result = await agentApi('POST', '/api/skill/reload');\n return result;\n}\n\n/**\n * 导入 Agent Skill(从 JSON/Markdown/Zip/URL)\n */\nexport async function importAgentSkill(skillData) {\n // 判断导入方式\n if (skillData.markdown || skillData.prompt) {\n // Markdown 内容直接保存\n return await agentApi('POST', '/api/skill/save-markdown', skillData);\n } else if (skillData.zipData) {\n // Base64 Zip 导入\n return await agentApi('POST', '/api/skill/import-zip', skillData);\n } else if (skillData.url) {\n // URL 导入\n return await agentApi('POST', '/api/skill/import-url', skillData);\n }\n throw new Error(t('toolbox.invalidSkillDataFormat'));\n}\n\n/**\n * 获取 Agent Skill 的 SKILL.md 内容\n */\nexport async function getAgentSkillMarkdown(name) {\n return await agentApi('GET', `/api/skill/markdown?name=${encodeURIComponent(name)}`);\n}\n\n/**\n * 显示 Agent Skill 编辑器弹窗\n */\nexport function showAgentSkillEditor(skillName, existingData = null) {\n const existingModal = document.getElementById('agentSkillEditorModal');\n if (existingModal) existingModal.remove();\n\n const isEdit = !!existingData;\n\n const modalOverlay = document.createElement('div');\n modalOverlay.className = 'modal-overlay';\n modalOverlay.id = 'agentSkillEditorModal';\n\n const modalContainer = document.createElement('div');\n modalContainer.className = 'modal-content agent-skill-editor-container';\n modalContainer.style.width = '700px';\n modalContainer.style.maxHeight = '85vh';\n\n modalContainer.innerHTML = `\n <div class=\"modal-header\">\n <h3>${isEdit ? t('toolbox.editAgentSkill') : t('toolbox.newAgentSkill')}</h3>\n <button class=\"modal-close-btn\">✕</button>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label>${t('toolbox.skillNameLabel')}</label>\n <input type=\"text\" id=\"agentSkillName\" placeholder=\"e.g. code-review\" value=\"${escapeHtml(isEdit ? existingData.name : '')}\" ${isEdit ? 'readonly' : ''}>\n </div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillDescLabel')}</label>\n <input type=\"text\" id=\"agentSkillDesc\" placeholder=\"${t('toolbox.skillDescPlaceholder')}\" value=\"${escapeHtml(isEdit ? (existingData.frontmatter?.description || '') : '')}\">\n </div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillVersionLabel')}</label>\n <input type=\"text\" id=\"agentSkillVersion\" placeholder=\"1.0\" value=\"${escapeHtml(isEdit ? (existingData.frontmatter?.version || '1.0') : '1.0')}\">\n </div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillMarkdownLabel')}</label>\n <textarea id=\"agentSkillMarkdown\" style=\"min-height: 300px; font-family: monospace;\" placeholder=\"${t('toolbox.skillMarkdownPlaceholder')}\">${escapeHtml(isEdit ? (existingData.markdown || '') : '')}</textarea>\n </div>\n ${isEdit && existingData.resources && existingData.resources.length > 0 ? `\n <div class=\"form-group\">\n <label>${t('toolbox.existingResources')}</label>\n <div class=\"skill-resource-list\">\n ${existingData.resources.map(r => `<span class=\"skill-resource-tag\">📄 ${escapeHtml(r.name)} (${r.size} ${t('common.bytes')})</span>`).join('')}\n </div>\n </div>` : ''}\n </div>\n <div class=\"modal-actions\">\n <button class=\"btn btn-cancel\" id=\"cancelAgentSkillBtn\">${t('common.cancel')}</button>\n <button class=\"btn btn-primary\" id=\"saveAgentSkillBtn\" style=\"width: auto;\">${t('common.save')}</button>\n </div>\n `;\n\n modalOverlay.appendChild(modalContainer);\n document.body.appendChild(modalOverlay);\n\n // 关闭\n const closeModal = () => modalOverlay.remove();\n modalOverlay.querySelector('.modal-close-btn').addEventListener('click', closeModal);\n modalOverlay.querySelector('#cancelAgentSkillBtn').addEventListener('click', closeModal);\n\n // 保存\n modalOverlay.querySelector('#saveAgentSkillBtn').addEventListener('click', async () => {\n const name = modalOverlay.querySelector('#agentSkillName').value.trim();\n const description = modalOverlay.querySelector('#agentSkillDesc').value.trim();\n const version = modalOverlay.querySelector('#agentSkillVersion').value.trim() || '1.0';\n const markdown = modalOverlay.querySelector('#agentSkillMarkdown').value.trim();\n const enabled = isEdit ? (existingData.frontmatter?.enabled !== false) : true;\n\n if (!name) return showToast(t('toolbox.skillNameRequired'), 'error');\n if (!markdown) return showToast(t('toolbox.skillMarkdownRequired'), 'error');\n\n try {\n const result = await agentApi('POST', '/api/skill/save-markdown', {\n name, description, version, markdown, enabled\n });\n if (result.success) {\n showToast(t('toolbox.agentSkillSaved', { name }), 'success');\n closeModal();\n triggerRefresh();\n } else {\n showToast(result.error || t('toolbox.saveFailedShort'), 'error');\n }\n } catch (err) {\n showToast(t('toolbox.saveFailed', { error: err.message }), 'error');\n }\n });\n}\n\n/**\n * 显示 Zip/URL 导入弹窗\n */\nexport function showImportDialog() {\n const existingModal = document.getElementById('skillImportModal');\n if (existingModal) existingModal.remove();\n\n const modalOverlay = document.createElement('div');\n modalOverlay.className = 'modal-overlay';\n modalOverlay.id = 'skillImportModal';\n\n const modalContainer = document.createElement('div');\n modalContainer.className = 'modal-content';\n modalContainer.style.width = '640px';\n\n modalContainer.innerHTML = `\n <div class=\"modal-header\">\n <h3>${t('toolbox.importSkillTitle')}</h3>\n <button class=\"modal-close-btn\">×</button>\n </div>\n <div style=\"padding: 0 24px 8px 24px;\">\n <div class=\"import-tabs\">\n <button class=\"import-tab active\" data-tab=\"json\">${t('toolbox.jsonImport')}</button>\n <button class=\"import-tab\" data-tab=\"markdown\">${t('toolbox.newAuthoring')}</button>\n <button class=\"import-tab\" data-tab=\"zip\">${t('toolbox.zipPackage')}</button>\n <button class=\"import-tab\" data-tab=\"url\">${t('toolbox.urlImport')}</button>\n </div>\n\n <!-- JSON Import -->\n <div class=\"import-panel active\" data-panel=\"json\">\n <div class=\"import-panel-desc\">${t('toolbox.jsonImportDesc')}</div>\n <div class=\"upload-drop-zone\" id=\"jsonDropZone\">\n <span class=\"upload-icon\">📄</span>\n <span class=\"upload-text\">${t('toolbox.clickOrDragJson')}</span>\n <span class=\"upload-hint\">${t('toolbox.jsonFileHint')}</span>\n <input type=\"file\" id=\"skillJsonFile\" accept=\".json\">\n </div>\n </div>\n\n <!-- Agent Skill Markdown -->\n <div class=\"import-panel\" data-panel=\"markdown\">\n <div class=\"import-panel-desc\">${t('toolbox.markdownImportDesc')}</div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillNameLabel')}</label>\n <input type=\"text\" id=\"quickAgentName\" placeholder=\"${t('toolbox.skillNameExamplePlaceholder')}\">\n </div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillDescLabel')}</label>\n <input type=\"text\" id=\"quickAgentDesc\" placeholder=\"${t('toolbox.skillDescScenePlaceholder')}\">\n </div>\n <div class=\"form-group\">\n <label>${t('toolbox.skillMarkdownLabel')}</label>\n <textarea id=\"quickAgentMarkdown\" style=\"min-height: 180px; font-family: 'SF Mono', 'Monaco', 'Menlo', monospace; font-size: 13px; line-height: 1.6;\" placeholder=\"${t('toolbox.skillMarkdownPlaceholderShort')}\"></textarea>\n </div>\n </div>\n\n <!-- Zip Import -->\n <div class=\"import-panel\" data-panel=\"zip\">\n <div class=\"import-panel-desc\">${t('toolbox.zipImportDesc')}</div>\n <div class=\"upload-drop-zone\" id=\"zipDropZone\">\n <span class=\"upload-icon\">📦</span>\n <span class=\"upload-text\">${t('toolbox.clickOrDragZip')}</span>\n <span class=\"upload-hint\">${t('toolbox.zipFileHint')}</span>\n <input type=\"file\" id=\"skillZipFile\" accept=\".zip\">\n </div>\n </div>\n\n <!-- URL Import -->\n <div class=\"import-panel\" data-panel=\"url\">\n <div class=\"import-panel-desc\">${t('toolbox.urlImportDesc')}</div>\n <div class=\"form-group\">\n <label>${t('toolbox.downloadUrl')}</label>\n <div class=\"url-input-wrapper\">\n <span class=\"url-input-icon\">🔗</span>\n <input type=\"url\" id=\"skillUrl\" placeholder=\"https://example.com/skills/my-skill.zip\">\n </div>\n </div>\n </div>\n </div>\n <div class=\"modal-actions\">\n <button class=\"btn btn-cancel\" id=\"cancelImportBtn\">${t('common.cancel')}</button>\n <button class=\"btn btn-primary\" id=\"confirmImportBtn\" style=\"width: auto;\">${t('toolbox.importBtn')}</button>\n </div>\n `;\n\n modalOverlay.appendChild(modalContainer);\n document.body.appendChild(modalOverlay);\n\n const closeModal = () => modalOverlay.remove();\n modalOverlay.querySelector('.modal-close-btn').addEventListener('click', closeModal);\n modalOverlay.querySelector('#cancelImportBtn').addEventListener('click', closeModal);\n\n // Tab 切换\n modalOverlay.querySelectorAll('.import-tab').forEach(tab => {\n tab.addEventListener('click', () => {\n modalOverlay.querySelectorAll('.import-tab').forEach(tabEl => tabEl.classList.remove('active'));\n tab.classList.add('active');\n modalOverlay.querySelectorAll('.import-panel').forEach(p => p.classList.remove('active'));\n modalOverlay.querySelector(`[data-panel=\"${tab.dataset.tab}\"]`).classList.add('active');\n });\n });\n\n // Drop Zone 点击触发 file input\n const setupDropZone = (zoneId, fileInputId) => {\n const zone = modalOverlay.querySelector(zoneId);\n const input = modalOverlay.querySelector(fileInputId);\n if (!zone || !input) return;\n\n zone.addEventListener('click', () => input.click());\n zone.addEventListener('dragover', (e) => { e.preventDefault(); zone.style.borderColor = '#667eea'; zone.style.background = '#f5f7ff'; });\n zone.addEventListener('dragleave', () => { zone.style.borderColor = '#d0d5dd'; zone.style.background = '#fafbfc'; });\n zone.addEventListener('drop', (e) => {\n e.preventDefault();\n zone.style.borderColor = '#d0d5dd';\n zone.style.background = '#fafbfc';\n const files = e.dataTransfer.files;\n if (files.length > 0) {\n input.files = files;\n showDropFileName(zone, files[0].name);\n }\n });\n\n input.addEventListener('change', () => {\n if (input.files && input.files[0]) {\n showDropFileName(zone, input.files[0].name);\n }\n });\n };\n\n const showDropFileName = (zone, name) => {\n zone.classList.add('has-file');\n const existing = zone.querySelector('.file-name');\n if (existing) existing.remove();\n const nameEl = document.createElement('span');\n nameEl.className = 'file-name';\n nameEl.textContent = `✓ ${name}`;\n zone.appendChild(nameEl);\n };\n\n setupDropZone('#jsonDropZone', '#skillJsonFile');\n setupDropZone('#zipDropZone', '#skillZipFile');\n\n // 确认导入\n modalOverlay.querySelector('#confirmImportBtn').addEventListener('click', async () => {\n const activeTab = modalOverlay.querySelector('.import-tab.active')?.dataset.tab;\n\n try {\n if (activeTab === 'json') {\n const fileInput = modalOverlay.querySelector('#skillJsonFile');\n const file = fileInput.files[0];\n if (!file) return showToast(t('toolbox.selectJsonFile'), 'warning');\n\n const text = await file.text();\n const skillDef = JSON.parse(text);\n await importSkill(skillDef);\n showToast(t('toolbox.workflowSkillImported', { name: skillDef.name }), 'success');\n } else if (activeTab === 'markdown') {\n const name = modalOverlay.querySelector('#quickAgentName').value.trim();\n const description = modalOverlay.querySelector('#quickAgentDesc').value.trim();\n const markdown = modalOverlay.querySelector('#quickAgentMarkdown').value.trim();\n if (!name) return showToast(t('toolbox.skillNameRequired'), 'warning');\n if (!markdown) return showToast(t('toolbox.skillMarkdownRequired'), 'warning');\n\n const result = await agentApi('POST', '/api/skill/save-markdown', {\n name, description, version: '1.0', markdown\n });\n if (result.success) {\n showToast(t('toolbox.agentSkillCreated', { name }), 'success');\n } else {\n return showToast(result.error || t('toolbox.createFailedShort'), 'error');\n }\n } else if (activeTab === 'zip') {\n const fileInput = modalOverlay.querySelector('#skillZipFile');\n const file = fileInput.files[0];\n if (!file) return showToast(t('toolbox.selectZipFile'), 'warning');\n\n // 文件大小限制:50MB\n const MAX_ZIP_SIZE = 50 * 1024 * 1024;\n if (file.size > MAX_ZIP_SIZE) {\n return showToast(t('toolbox.fileTooLarge'), 'warning');\n }\n\n // 使用 FileReader 安全地将文件转为 base64\n const base64 = await new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result.split(',')[1]);\n reader.onerror = () => reject(new Error(t('toolbox.fileReadFailed')));\n reader.readAsDataURL(file);\n });\n const result = await agentApi('POST', '/api/skill/import-zip', { zipData: base64 });\n if (result.success) {\n showToast(t('toolbox.agentSkillImported', { name: result.skill?.name || 'unknown' }), 'success');\n } else {\n return showToast(result.error || t('toolbox.skillImportFailedShort'), 'error');\n }\n } else if (activeTab === 'url') {\n const url = modalOverlay.querySelector('#skillUrl').value.trim();\n if (!url) return showToast(t('toolbox.urlRequired'), 'warning');\n\n const result = await agentApi('POST', '/api/skill/import-url', { url });\n if (result.success) {\n showToast(t('toolbox.agentSkillImported', { name: result.skill?.name || 'unknown' }), 'success');\n } else {\n return showToast(result.error || t('toolbox.skillImportFailedShort'), 'error');\n }\n }\n\n closeModal();\n triggerRefresh();\n } catch (err) {\n showToast(t('toolbox.importFailed', { error: err.message }), 'error');\n }\n });\n}\n\n\n/**\n * 初始化 Skill 搜索 + 筛选事件\n */\nexport function initSkillFilter() {\n const searchInput = document.getElementById('skillSearchInput');\n const filterTabs = document.querySelectorAll('.toolbox-filter-tab[data-target=\"skill\"]');\n\n if (searchInput) {\n let debounceTimer = null;\n searchInput.addEventListener('input', () => {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n skillFilterText = searchInput.value.trim();\n renderSkills(state.cachedSkills || []);\n }, 200);\n });\n }\n\n const clearBtn = document.getElementById('skillSearchClear');\n if (clearBtn) {\n clearBtn.addEventListener('click', () => {\n searchInput.value = '';\n skillFilterText = '';\n renderSkills(state.cachedSkills || []);\n searchInput.focus();\n });\n }\n\n filterTabs.forEach(tab => {\n tab.addEventListener('click', () => {\n filterTabs.forEach(t => t.classList.remove('active'));\n tab.classList.add('active');\n skillFilterStatus = tab.dataset.filter;\n renderSkills(state.cachedSkills || []);\n });\n });\n}\n\n// Skill 管理函数已通过 export 暴露给 toolbox-config.js\n","// options/toolbox-config.js - 工具箱配置主入口(MCP 服务器 & Skill 管理)\n// 依赖 Agent 连接,未连接时禁用所有操作\n// MCP 管理、Skill 管理、共享辅助函数已分别拆分到:\n// toolbox-mcp.js / toolbox-skills.js / toolbox-shared.js\n\nimport { state, showCustomConfirm, showAgentSkillViewer, agentApi, getAgentConnection, escapeHtml, showToast } from './toolbox-shared.js';\nimport {\n loadMcpServers, renderMcpServers, addMcpServer, removeMcpServer,\n connectMcpServer, disconnectMcpServer, toggleMcpServer,\n showAddMcpForm, hideAddMcpForm, handleEnvAction, handleTransportChange,\n getTransportValue, parseEnvVars, parseHeaders,\n initMcpFilter\n} from './toolbox-mcp.js';\nimport {\n loadSkills, renderSkills, deleteSkill, toggleSkill, runSkill,\n parseSkillParams, showSkillParamsDialog, showSkillRunResult,\n reloadSkills, showAgentSkillEditor, getAgentSkillMarkdown, showImportDialog,\n initSkillFilter\n} from './toolbox-skills.js';\nimport logger from '../shared/logger.js';\nimport { t } from '../shared/i18n.js';\n\n// 注册刷新回调(refreshToolbox 是函数声明,会被 hoist,可安全引用)\n// 供 toolbox-skills.js 调用,避免循环依赖\nstate.refreshCallback = refreshToolbox;\n\n// ==================== 主入口 ====================\n\n/**\n * 通知 Extension Background 重新加载 MCP 工具\n */\nfunction notifyMcpChange() {\n try {\n chrome.runtime.sendMessage({ type: 'RELOAD_MCP_TOOLS' }, (resp) => {\n if (resp?.success) {\n logger.debug(`[Toolbox] Background re loaded ${resp.count} MCP tool`);\n }\n });\n } catch (_) { /* 忽略错误,background 可能未运行 */ }\n}\n\n/**\n * 更新 MCP 服务数量统计(显示在 section 标题旁)\n */\nfunction updateMcpToolCount(servers) {\n const mcpSection = document.getElementById('mcpSection');\n if (!mcpSection) return;\n let countEl = mcpSection.querySelector('.toolbox-tool-count');\n if (!countEl) {\n countEl = document.createElement('span');\n countEl.className = 'toolbox-tool-count';\n // 插入到标题 h2 之后(toggle 按钮保持在最右边不动)\n const titleEl = mcpSection.querySelector('.toolbox-section-title');\n if (titleEl) {\n titleEl.insertAdjacentElement('afterend', countEl);\n }\n }\n const enabled = servers.filter(s => s.enabled !== false).length;\n const total = servers.length;\n if (total === 0) {\n countEl.style.display = 'none';\n } else {\n countEl.textContent = t('toolbox.enabledCountTotal', { enabled, total });\n countEl.style.display = '';\n }\n}\n\n/**\n * 更新 Skill 启用/总数统计(显示在 section 标题旁)\n */\nfunction updateSkillCount(skills) {\n const skillSection = document.getElementById('skillSection');\n if (!skillSection) return;\n let countEl = skillSection.querySelector('.toolbox-tool-count');\n if (!countEl) {\n countEl = document.createElement('span');\n countEl.className = 'toolbox-tool-count';\n // 插入到标题 h2 之后(toggle 按钮保持在最右边不动)\n const titleEl = skillSection.querySelector('.toolbox-section-title');\n if (titleEl) {\n titleEl.insertAdjacentElement('afterend', countEl);\n }\n }\n const enabled = skills.filter(s => s.enabled !== false).length;\n const total = skills.length;\n if (total === 0) {\n countEl.style.display = 'none';\n } else {\n countEl.textContent = t('toolbox.enabledCountTotal', { enabled, total });\n countEl.style.display = '';\n }\n}\n\n/**\n * 刷新整个工具箱面板\n */\nexport async function refreshToolbox() {\n const [mcpResult, skillResult] = await Promise.all([\n loadMcpServers(),\n loadSkills()\n ]);\n\n const mcpServers = mcpResult.servers || [];\n const skills = skillResult.skills || [];\n\n renderMcpServers(mcpServers);\n renderSkills(skills);\n\n // 更新 MCP 工具数量统计\n updateMcpToolCount(mcpServers);\n // 更新 Skill 启用/停用数量统计\n updateSkillCount(skills);\n\n // 更新顶部连接状态提示\n const statusEl = document.getElementById('toolboxAgentStatus');\n if (statusEl) {\n if (state.agentConnected) {\n const agentName = state.agentName || t('toolbox.unknownAgent');\n statusEl.innerHTML = `<span class=\"toolbox-status-dot connected\"></span> ${t('toolbox.currentAgentSupports', { name: escapeHtml(agentName) })}`;\n statusEl.className = 'toolbox-agent-status connected';\n } else {\n statusEl.innerHTML = `<span class=\"toolbox-status-dot disconnected\"></span> ${t('toolbox.agentNotConnectedHint')}`;\n statusEl.className = 'toolbox-agent-status disconnected';\n }\n }\n\n // 禁用/启用操作按钮\n const disabled = !state.agentConnected;\n const addBtn = document.getElementById('addMcpServerBtn');\n const importBtn = document.getElementById('importSkillBtn');\n const reloadBtn = document.getElementById('reloadSkillsBtn');\n if (addBtn) addBtn.disabled = disabled;\n if (importBtn) importBtn.disabled = disabled;\n if (reloadBtn) reloadBtn.disabled = disabled;\n}\n\n/**\n * 开始编辑 MCP 服务器(卡片内替换为编辑表单)\n */\nfunction startEditMcpServer(serverId) {\n const server = state.cachedMcpServers.find(s => s.id === serverId);\n if (!server) return;\n\n state.editingMcpId = serverId;\n const container = document.getElementById('mcpServerList');\n if (!container) return;\n\n // 重新渲染,编辑中的卡片替换为表单\n renderMcpServers(state.cachedMcpServers);\n}\n\n/**\n * 取消编辑\n */\nfunction cancelEditMcp() {\n state.editingMcpId = null;\n renderMcpServers(state.cachedMcpServers);\n}\n\n/**\n * 保存编辑\n */\nasync function saveMcpEdit() {\n const idInput = document.getElementById('mcpEditId');\n const nameInput = document.getElementById('mcpEditName');\n const cmdInput = document.getElementById('mcpEditCommand');\n const argsInput = document.getElementById('mcpEditArgs');\n const urlInput = document.getElementById('mcpEditUrl');\n\n const id = idInput?.value.trim();\n const name = nameInput?.value.trim();\n const transport = getTransportValue('mcpEdit');\n const command = cmdInput?.value.trim();\n const argsRaw = argsInput?.value.trim();\n const url = urlInput?.value.trim();\n\n const isHttp = transport === 'sse' || transport === 'streamableHttp' || transport === 'websocket';\n\n if (!id) {\n showToolboxToast(t('toolbox.mcpIdRequired'), 'warning');\n return;\n }\n\n if (isHttp) {\n if (!url) {\n showToolboxToast(t('toolbox.mcpUrlRequired'), 'warning');\n return;\n }\n } else {\n if (!command) {\n showToolboxToast(t('toolbox.mcpCommandRequired'), 'warning');\n return;\n }\n }\n\n const serverData = {\n id,\n name: name || id,\n transport,\n command,\n args: argsRaw ? argsRaw.split(/\\s+/) : [],\n url,\n env: parseEnvVars(document.getElementById('mcpEditEnvRows')),\n headers: parseHeaders(document.getElementById('mcpEditHeaderRows')),\n enabled: state.cachedMcpServers.find(s => s.id === state.editingMcpId)?.enabled !== false\n };\n\n try {\n await addMcpServer(serverData);\n\n // 如果编辑前处于连接状态,断开旧连接并使用新配置重连\n const wasConnected = state.cachedMcpServers.find(s => s.id === state.editingMcpId)?.connected;\n if (wasConnected) {\n showToolboxToast(t('toolbox.mcpReconnecting'), 'info');\n // 用旧 ID 断开,用新 ID 重连(防止 ID 被修改的情况)\n await disconnectMcpServer(state.editingMcpId);\n // 短暂等待确保旧进程完全退出\n await new Promise(r => setTimeout(r, 500));\n const connectResult = await connectMcpServer(id);\n if (connectResult.success) {\n showToolboxToast(t('toolbox.mcpUpdateReconnected', { count: connectResult.toolCount || 0 }), 'success');\n } else {\n const errMsg = connectResult.error || t('toolbox.mcpUnknownError');\n showToolboxToast(t('toolbox.mcpUpdatedReconnectFailed', { error: errMsg }), 'warning');\n }\n } else {\n showToolboxToast(t('toolbox.mcpUpdateSuccess'), 'success');\n }\n\n state.editingMcpId = null;\n notifyMcpChange();\n await refreshToolbox();\n } catch (err) {\n showToolboxToast(t('toolbox.mcpUpdateFailed', { error: err.message }), 'error');\n }\n}\n\n/**\n * 更新全局开关的 UI 状态\n */\nfunction updateGlobalToggleUI(type, enabled) {\n const idMap = {\n mcp: ['mcpToggleLabel', 'mcpSection'],\n skill: ['skillToggleLabel', 'skillSection']\n };\n const [labelId, sectionId] = idMap[type] || idMap.mcp;\n const label = document.getElementById(labelId);\n const section = document.getElementById(sectionId);\n if (label) {\n label.textContent = enabled ? t('common.enabled') : t('common.disabled');\n label.style.color = enabled ? '#666' : '#999';\n }\n if (section) {\n if (enabled) {\n section.classList.remove('disabled-section');\n } else {\n section.classList.add('disabled-section');\n }\n }\n}\n\n/**\n * 初始化工具箱 Tab\n */\nexport function initToolbox() {\n // 全局开关:MCP 服务\n const mcpToggle = document.getElementById('mcpGlobalToggle');\n const skillToggle = document.getElementById('skillGlobalToggle');\n const mcpToggleLabel = document.getElementById('mcpToggleLabel');\n const skillToggleLabel = document.getElementById('skillToggleLabel');\n const mcpSection = document.getElementById('mcpSection');\n const skillSection = document.getElementById('skillSection');\n\n // 加载全局开关状态\n chrome.storage.local.get(['mcpEnabled', 'skillsEnabled'], (result) => {\n const mcpEnabled = result.mcpEnabled === true;\n const skillsEnabled = result.skillsEnabled !== false;\n if (mcpToggle) mcpToggle.checked = mcpEnabled;\n if (skillToggle) skillToggle.checked = skillsEnabled;\n updateGlobalToggleUI('mcp', mcpEnabled);\n updateGlobalToggleUI('skill', skillsEnabled);\n });\n\n // MCP 全局开关\n if (mcpToggle) {\n mcpToggle.addEventListener('change', () => {\n const enabled = mcpToggle.checked;\n chrome.storage.local.set({ mcpEnabled: enabled });\n updateGlobalToggleUI('mcp', enabled);\n showToolboxToast(enabled ? t('toolbox.mcpServiceEnabled') : t('toolbox.mcpServiceDisabled'), 'info');\n });\n }\n\n // Skill 全局开关\n if (skillToggle) {\n skillToggle.addEventListener('change', () => {\n const enabled = skillToggle.checked;\n chrome.storage.local.set({ skillsEnabled: enabled });\n updateGlobalToggleUI('skill', enabled);\n showToolboxToast(enabled ? t('toolbox.skillServiceEnabled') : t('toolbox.skillServiceDisabled'), 'info');\n });\n }\n\n // MCP 添加按钮\n const addMcpBtn = document.getElementById('addMcpServerBtn');\n if (addMcpBtn) {\n addMcpBtn.addEventListener('click', () => {\n const formContainer = document.getElementById('mcpAddForm');\n if (formContainer && formContainer.innerHTML.trim()) {\n hideAddMcpForm();\n } else {\n showAddMcpForm();\n }\n });\n }\n\n // MCP 添加表单事件(事件委托)\n const mcpAddForm = document.getElementById('mcpAddForm');\n if (mcpAddForm) {\n mcpAddForm.addEventListener('click', async (e) => {\n // 传输方式切换(按钮点击)\n if (e.target.dataset.action === 'transport-change') {\n handleTransportChange(e.target);\n return;\n }\n\n // 环境变量操作\n if (handleEnvAction(e)) return;\n\n if (e.target.id === 'mcpAddCancel') {\n hideAddMcpForm();\n }\n if (e.target.id === 'mcpAddConfirm') {\n const id = document.getElementById('mcpAddId')?.value.trim();\n const name = document.getElementById('mcpAddName')?.value.trim();\n const transport = getTransportValue('mcpAdd');\n const command = document.getElementById('mcpAddCommand')?.value.trim();\n const argsRaw = document.getElementById('mcpAddArgs')?.value.trim();\n const url = document.getElementById('mcpAddUrl')?.value.trim();\n\n const isHttp = transport === 'sse' || transport === 'streamableHttp' || transport === 'websocket';\n\n if (!id) {\n showToolboxToast(t('toolbox.mcpIdRequired'), 'warning');\n return;\n }\n\n if (isHttp) {\n if (!url) {\n showToolboxToast(t('toolbox.mcpUrlRequired'), 'warning');\n return;\n }\n } else {\n if (!command) {\n showToolboxToast(t('toolbox.mcpCommandRequired'), 'warning');\n return;\n }\n }\n\n const envRows = document.getElementById('mcpAddEnvRows');\n const headerRows = document.getElementById('mcpAddHeaderRows');\n const serverData = {\n id,\n name: name || id,\n transport,\n command,\n args: argsRaw ? argsRaw.split(/\\s+/) : [],\n url,\n env: envRows ? parseEnvVars(envRows) : {},\n headers: headerRows ? parseHeaders(headerRows) : {},\n enabled: true\n };\n\n try {\n await addMcpServer(serverData);\n showToolboxToast(t('toolbox.mcpAddSuccess'), 'success');\n hideAddMcpForm();\n notifyMcpChange();\n await refreshToolbox();\n } catch (err) {\n showToolboxToast(t('toolbox.mcpAddFailed', { error: err.message }), 'error');\n }\n }\n });\n }\n\n // MCP 服务器列表事件委托\n const mcpList = document.getElementById('mcpServerList');\n if (mcpList) {\n mcpList.addEventListener('click', async (e) => {\n // 传输方式切换(按钮点击)\n if (e.target.dataset.action === 'transport-change') {\n handleTransportChange(e.target);\n return;\n }\n\n // 环境变量操作(不需要 serverId)\n if (handleEnvAction(e)) return;\n\n const btn = e.target.closest('button, input');\n if (!btn) return;\n\n const action = btn.dataset.action;\n const serverId = btn.dataset.mcpId;\n\n if (!action || !serverId) return;\n\n try {\n if (action === 'connect') {\n showToolboxToast(t('toolbox.mcpConnecting'), 'info');\n await connectMcpServer(serverId);\n showToolboxToast(t('toolbox.mcpConnectSuccess'), 'success');\n notifyMcpChange();\n await refreshToolbox();\n } else if (action === 'disconnect') {\n await disconnectMcpServer(serverId);\n showToolboxToast(t('toolbox.mcpDisconnectSuccess'), 'success');\n notifyMcpChange();\n await refreshToolbox();\n } else if (action === 'toggle') {\n const enabled = btn.checked;\n await toggleMcpServer(serverId, enabled);\n showToolboxToast(enabled ? t('toolbox.mcpEnabledToast') : t('toolbox.mcpDisabledToast'), 'success');\n notifyMcpChange();\n await refreshToolbox();\n } else if (action === 'delete') {\n const confirmed = await showCustomConfirm(t('toolbox.mcpConfirmDelete'), t('toolbox.deleteConfirmTitle'));\n if (!confirmed) return;\n await removeMcpServer(serverId);\n showToolboxToast(t('toolbox.deleteSuccess'), 'success');\n notifyMcpChange();\n await refreshToolbox();\n } else if (action === 'edit') {\n startEditMcpServer(serverId);\n } else if (action === 'cancel-edit') {\n cancelEditMcp();\n } else if (action === 'save-edit') {\n await saveMcpEdit();\n } else if (action === 'toggle-tools') {\n const toolsList = document.getElementById(`mcp-tools-${serverId}`);\n const toggleBtn = btn;\n if (toolsList) {\n const isHidden = toolsList.style.display === 'none';\n toolsList.style.display = isHidden ? 'block' : 'none';\n toggleBtn.innerHTML = isHidden\n ? `▼ ${t('toolbox.collapseTools', { count: toolsList.children.length })}`\n : `▶ ${t('toolbox.viewTools', { count: toolsList.children.length })}`;\n }\n }\n } catch (err) {\n showToolboxToast(t('toolbox.operationFailed', { error: err.message }), 'error');\n }\n });\n }\n\n // Skill 列表事件委托\n const skillList = document.getElementById('skillList');\n if (skillList) {\n skillList.addEventListener('click', async (e) => {\n const btn = e.target.closest('button');\n if (!btn) return;\n\n const action = btn.dataset.action;\n const skillName = btn.dataset.skillName;\n\n if (btn.classList.contains('skill-expand-btn')) {\n const target = btn.dataset.target;\n if (target === 'desc') {\n const descEl = btn.previousElementSibling;\n if (descEl && descEl.classList.contains('skill-card-desc')) {\n const fullText = decodeURIComponent(btn.dataset.full || '');\n descEl.textContent = fullText;\n btn.remove();\n }\n } else if (target === 'resources') {\n try {\n const resources = JSON.parse(decodeURIComponent(btn.dataset.full || '[]'));\n const paramsContainer = btn.parentElement;\n if (paramsContainer && paramsContainer.classList.contains('skill-card-params')) {\n const allTags = resources.map(r => `<span class=\"skill-param-tag\" title=\"${t('toolbox.sizeBytes', { size: r.size })}\">📄 ${escapeHtml(r.name)}</span>`).join('');\n paramsContainer.innerHTML = allTags;\n }\n } catch (e) {\n logger.error('Failed to expand resources:', e);\n }\n }\n return;\n }\n\n if (!action || !skillName) return;\n\n try {\n if (action === 'delete-skill') {\n const confirmed = await showCustomConfirm(t('toolbox.skillConfirmDelete', { name: skillName }), t('toolbox.deleteConfirmTitle'));\n if (!confirmed) return;\n await deleteSkill(skillName);\n showToolboxToast(t('toolbox.deleteSuccess'), 'success');\n await refreshToolbox();\n } else if (action === 'toggle-skill') {\n const enabled = await toggleSkill(skillName);\n showToolboxToast(enabled ? t('toolbox.skillEnabledToast', { name: skillName }) : t('toolbox.skillDisabledToast', { name: skillName }), 'success');\n await refreshToolbox();\n } else if (action === 'run-skill') {\n const skillInfo = await agentApi('GET', `/api/skill/detail?name=${encodeURIComponent(skillName)}`);\n if (!skillInfo?.success || !skillInfo.skill) {\n showToolboxToast(t('toolbox.skillInfoFailed', { error: skillInfo?.error || t('agentConfig.unknownError') }), 'error');\n return;\n }\n // 解析参数定义\n const paramsDef = parseSkillParams(skillInfo.skill);\n if (paramsDef.hasRequired) {\n // 有参数需要填写,弹窗收集\n const userParams = await showSkillParamsDialog(skillName, paramsDef);\n if (userParams === null) return; // 用户取消\n showToolboxToast(t('toolbox.skillRunning', { name: skillName }), 'info');\n const result = await runSkill(skillName, userParams);\n showSkillRunResult(skillName, skillInfo, result);\n } else {\n // 无必填参数,直接执行\n showToolboxToast(t('toolbox.skillRunning', { name: skillName }), 'info');\n const result = await runSkill(skillName);\n showSkillRunResult(skillName, skillInfo, result);\n }\n } else if (action === 'edit-agent-skill') {\n try {\n const data = await getAgentSkillMarkdown(skillName);\n if (data.success) {\n showAgentSkillEditor(skillName, data);\n } else {\n showToolboxToast(data.error || t('toolbox.skillContentFailed'), 'error');\n }\n } catch (err) {\n showToolboxToast(t('toolbox.skillGetFailed', { error: err.message }), 'error');\n }\n } else if (action === 'view-agent-skill') {\n try {\n const data = await getAgentSkillMarkdown(skillName);\n if (data.success) {\n showAgentSkillViewer(skillName, data);\n } else {\n showToolboxToast(data.error || t('toolbox.skillContentFailed'), 'error');\n }\n } catch (err) {\n showToolboxToast(t('toolbox.skillGetFailed', { error: err.message }), 'error');\n }\n }\n } catch (err) {\n showToolboxToast(t('toolbox.operationFailed', { error: err.message }), 'error');\n }\n });\n }\n\n // 导入 Skill(使用统一的导入弹窗)\n const importBtn = document.getElementById('importSkillBtn');\n if (importBtn) {\n importBtn.addEventListener('click', () => showImportDialog());\n }\n\n // 重新加载 Skill\n const reloadBtn = document.getElementById('reloadSkillsBtn');\n if (reloadBtn) {\n reloadBtn.addEventListener('click', async () => {\n try {\n const result = await reloadSkills();\n showToolboxToast(t('toolbox.skillsReloaded', { count: result.count || 0 }), 'success');\n await refreshToolbox();\n } catch (err) {\n showToolboxToast(t('toolbox.skillsReloadFailed', { error: err.message }), 'error');\n }\n });\n }\n\n // 初始化搜索 + 筛选事件\n initMcpFilter();\n initSkillFilter();\n\n // 初始加载数据\n refreshToolbox();\n}\n\n/**\n * 简易 Toast 提示\n */\nfunction showToolboxToast(message, type = 'info') {\n const container = document.getElementById('toastContainer');\n if (!container) return;\n\n const toast = document.createElement('div');\n toast.className = `toast ${type}`;\n toast.textContent = message;\n container.appendChild(toast);\n\n requestAnimationFrame(() => toast.classList.add('toast-show'));\n\n setTimeout(() => {\n toast.classList.remove('toast-show');\n setTimeout(() => toast.remove(), 300);\n }, 2500);\n}\n\n// escapeHtml 已拆分到 toolbox-shared.js(通过顶部 import 引入)\n\n\n","// options/toolbox-rag.js - 知识库检索(RAG)能力探测与依赖安装引导\n// 职责:读取代理端 RAG 能力状态(/api/status 的 ragAvailable)→ 渲染状态区\n// 依赖未安装时展示安装引导(包清单/手动命令/一键安装)→ 安装进度轮询 → 重新检测\n// 依赖可用性检测由代理端(agent/src/rag/detect.js)负责,前端只消费 ragAvailable 字段\n\nimport { state, agentApi, getAgentConnection, escapeHtml, showToast } from './toolbox-shared.js';\nimport { t } from '../shared/i18n.js';\nimport logger from '../shared/logger.js';\n\n// 安装状态轮询间隔(毫秒)\nconst INSTALL_POLL_INTERVAL_MS = 2000;\n\n// 兜底包清单(与 agent/package.json 的 ragDependencies 一致;正常情况从安装状态接口获取)\nconst DEFAULT_RAG_PACKAGES = ['vectra', '@huggingface/transformers', 'pdf-parse', 'mammoth', 'officeparser', 'cheerio'];\n\n// 安装进度轮询定时器\nlet installPollTimer = null;\n\n// 依赖就绪回调(知识库面板注册,安装成功 / 重新检测通过后自动刷新门控与列表)\nlet availabilityChangeHandler = null;\n\n/**\n * 注册「RAG 可用性变化」回调\n */\nexport function setRagAvailabilityChangeHandler(fn) {\n availabilityChangeHandler = fn;\n}\n\n// ==================== 能力探测 ====================\n\n/**\n * 探测代理端 RAG 能力(读取 /api/status 的 ragAvailable / nodeVersion)\n * @returns {Promise<{connected: boolean, ragAvailable: boolean, nodeVersion: string|null, unreachable?: boolean}>}\n */\nexport async function loadRagStatus() {\n await getAgentConnection();\n if (!state.agentConnected) {\n return { connected: false, ragAvailable: false, nodeVersion: null };\n }\n try {\n const res = await agentApi('GET', '/api/status');\n return {\n connected: true,\n ragAvailable: res.ragAvailable === true,\n nodeVersion: res.nodeVersion || null,\n };\n } catch (err) {\n logger.warn('[RAG] Failed to fetch agent status:', err.message);\n return { connected: true, ragAvailable: false, nodeVersion: null, unreachable: true };\n }\n}\n\n/**\n * 读取安装任务状态(依赖未安装时才调用)\n * @returns {Promise<object|null>}\n */\nasync function loadRagInstallStatus() {\n try {\n return await agentApi('GET', '/api/rag/install/status');\n } catch (err) {\n logger.warn('[RAG] Failed to fetch install status:', err.message);\n return null;\n }\n}\n\n/**\n * RAG 功能入口显示条件 = 总开关开启 AND 代理端支持(供后续阶段的门控使用)\n * @param {{ragAvailable?: boolean}} [agentCapabilities]\n * @returns {Promise<boolean>}\n */\nexport async function shouldShowRagFeatures(agentCapabilities) {\n const { ragEnabled } = await chrome.storage.local.get('ragEnabled');\n return ragEnabled === true && agentCapabilities?.ragAvailable === true;\n}\n\n/**\n * 通知 Background 重新注册 RAG 工具(依赖就绪时保证 LLM 能调用知识库工具)\n */\nfunction notifyRagToolsChange() {\n try {\n chrome.runtime.sendMessage({ type: 'RELOAD_RAG_TOOLS' }, (resp) => {\n if (resp?.success) {\n logger.debug(`[Toolbox] Background re loaded ${resp.count} RAG tool`);\n }\n });\n } catch (_) { /* 忽略错误,background 可能未运行 */ }\n}\n\n// ==================== 状态渲染 ====================\n\n/**\n * 判断 Node 版本是否满足 RAG 前置要求(Node 22+)\n * 解析失败时不拦截(避免误报),由代理端检测结果兜底\n */\nfunction isNode22Plus(nodeVersion) {\n if (!nodeVersion) return true;\n const major = parseInt(String(nodeVersion).replace(/^v/i, ''), 10);\n return !Number.isFinite(major) || major >= 22;\n}\n\n/**\n * 通用状态块(左对齐展示)\n */\nfunction renderStatusBlock(icon, title, desc, extraHtml = '') {\n return `\n <div class=\"toolbox-empty\" style=\"text-align:left; padding:20px 16px;\">\n <div class=\"toolbox-empty-title\" style=\"display:flex; align-items:center; gap:8px; font-size:14px;\">\n <span style=\"font-size:20px;\">${icon}</span><span>${title}</span>\n </div>\n <div class=\"toolbox-empty-desc\" style=\"margin-top:6px; line-height:1.6;\">${desc}</div>\n ${extraHtml}\n </div>`;\n}\n\n/**\n * 安装引导面板(依赖未安装时)\n */\nfunction renderInstallGuide(install) {\n const packages = (install && Array.isArray(install.packages) && install.packages.length > 0)\n ? install.packages : DEFAULT_RAG_PACKAGES;\n const cmd = `npm install ${packages.join(' ')}`;\n\n // 上一次安装失败的详情(错误 + 日志尾部)\n const failed = install && install.done && !install.success;\n const logTail = failed && Array.isArray(install.logTail) && install.logTail.length > 0\n ? escapeHtml(install.logTail.join('\\n')) : '';\n const failHtml = failed ? `\n <div style=\"margin-top:12px; padding:10px 12px; background:#fdf1f0; border:1px solid #f2c9c4; border-radius:6px;\">\n <div style=\"color:#c0392b; font-weight:600; font-size:12px; margin-bottom:6px;\">⚠️ ${escapeHtml(install.error || t('toolbox.ragInstallFailedToast'))}</div>\n ${logTail ? `<div style=\"font-size:11px; color:#888; margin-bottom:4px;\">${t('toolbox.ragInstallLogLabel')}</div>\n <pre style=\"margin:0; max-height:130px; overflow:auto; font-size:11px; color:#666; white-space:pre-wrap; word-break:break-all;\">${logTail}</pre>` : ''}\n <div style=\"margin-top:8px; font-size:11px; color:#b06a2b; line-height:1.6;\">💡 ${t('toolbox.ragInstallFailedAdvice')}</div>\n </div>` : '';\n\n const pkgHtml = packages\n .map(p => `<code style=\"background:#f2f3f5; padding:1px 6px; border-radius:4px; margin:0 6px 4px 0; font-size:12px; display:inline-block;\">${escapeHtml(p)}</code>`)\n .join('');\n\n const extraHtml = `\n <div style=\"margin-top:12px; font-size:13px;\">\n <div style=\"color:#555; margin-bottom:6px;\"><strong>${t('toolbox.ragInstallPackagesLabel')}</strong></div>\n <div style=\"margin-bottom:8px;\">${pkgHtml}</div>\n <div style=\"color:#999; font-size:12px; line-height:1.6; margin-bottom:4px;\">${t('toolbox.ragInstallRequirements')}</div>\n <div style=\"color:#8a8f99; font-size:12px; line-height:1.6; margin-bottom:10px;\">💡 ${t('toolbox.ragInstallNoRestartNeeded')}</div>\n <div style=\"color:#555; margin-bottom:6px;\"><strong>${t('toolbox.ragInstallCommandLabel')}</strong></div>\n <div style=\"display:flex; align-items:center; gap:8px;\">\n <code id=\"ragInstallCmd\" style=\"flex:1; min-width:0; background:#2d2d2d; color:#e8e8e8; padding:8px 10px; border-radius:6px; font-size:12px; overflow-x:auto; white-space:nowrap; display:block;\">${escapeHtml(cmd)}</code>\n <button class=\"toolbox-add-btn\" id=\"ragCopyCmdBtn\" style=\"width:auto; flex:none; margin:0; padding:6px 12px; font-size:12px;\">${t('toolbox.ragCopyCmd')}</button>\n </div>\n ${failHtml}\n </div>\n <div style=\"margin-top:16px; display:flex; gap:10px;\">\n <button class=\"toolbox-add-btn\" id=\"ragInstallBtn\" style=\"width:auto; flex:none; margin:0; padding:8px 18px; border-style:solid; color:#667eea; border-color:#667eea;\">${t('toolbox.ragInstallBtn')}</button>\n <button class=\"toolbox-add-btn\" id=\"ragRedetectBtn\" style=\"width:auto; flex:none; margin:0; padding:8px 18px;\">${t('toolbox.ragRedetectBtn')}</button>\n </div>`;\n\n return renderStatusBlock('📦', t('toolbox.ragStatusNotInstalledTitle'), t('toolbox.ragStatusNotInstalledDesc'), extraHtml);\n}\n\n/**\n * 安装进行中面板\n */\nfunction renderInstalling(install) {\n // 自动恢复(trigger=auto):代理更新后依赖被 npm 清理,启动时自动重装\n const isAutoRestore = Boolean(install && install.trigger === 'auto');\n const phaseText = install && install.phase === 'verifying'\n ? t('toolbox.ragInstallVerifying')\n : (isAutoRestore ? t('toolbox.ragAutoRestoreRunning') : t('toolbox.ragInstallRunning'));\n const logTail = install && Array.isArray(install.logTail) && install.logTail.length > 0\n ? escapeHtml(install.logTail.join('\\n')) : '';\n const extraHtml = logTail\n ? `<pre style=\"margin-top:12px; max-height:160px; overflow:auto; background:#2d2d2d; color:#c8c8c8; padding:10px; border-radius:6px; font-size:11px; white-space:pre-wrap; word-break:break-all;\">${logTail}</pre>`\n : '';\n return renderStatusBlock('⏳', phaseText, t('toolbox.ragInstallTip'), extraHtml);\n}\n\n/**\n * 渲染 RAG 状态区\n * @param {{connected: boolean, ragAvailable: boolean, nodeVersion: string|null, unreachable?: boolean, install?: object|null}} status\n */\nexport function renderRagStatus(status) {\n const container = document.getElementById('ragSectionContent');\n if (!container) return;\n\n const { connected, ragAvailable, nodeVersion, unreachable, install } = status || {};\n\n // 1) Agent 未连接\n if (!connected) {\n container.innerHTML = renderStatusBlock('🔌', t('toolbox.ragStatusAgentOffTitle'), t('toolbox.ragStatusAgentOffDesc'));\n return;\n }\n\n // 2) 无法获取代理状态\n if (unreachable) {\n container.innerHTML = renderStatusBlock('⚠️', t('toolbox.ragStatusUnreachableTitle'), t('toolbox.ragStatusUnreachableDesc'));\n return;\n }\n\n // 3) 安装任务进行中\n if (install && install.running) {\n container.innerHTML = renderInstalling(install);\n return;\n }\n\n // 4) 依赖已就绪\n if (ragAvailable) {\n container.innerHTML = renderStatusBlock('✅', t('toolbox.ragStatusReadyTitle'), t('toolbox.ragStatusReadyDesc'));\n return;\n }\n\n // 5) Node 版本不满足(代理端前置检查会返回 ragAvailable=false)\n if (!isNode22Plus(nodeVersion)) {\n container.innerHTML = renderStatusBlock(\n '⚠️',\n t('toolbox.ragStatusNodeLowTitle'),\n t('toolbox.ragStatusNodeLowDesc', { version: escapeHtml(nodeVersion || '?') })\n );\n return;\n }\n\n // 6) 依赖未安装 → 安装引导\n container.innerHTML = renderInstallGuide(install);\n}\n\n// ==================== 安装与轮询 ====================\n\n/**\n * 停止安装进度轮询\n */\nfunction stopInstallPolling() {\n if (installPollTimer) {\n clearTimeout(installPollTimer);\n installPollTimer = null;\n }\n}\n\n/**\n * 启动安装进度轮询:每 2 秒查询一次安装状态,结束后刷新完整能力状态\n */\nfunction startInstallPolling() {\n stopInstallPolling();\n const poll = async () => {\n const install = await loadRagInstallStatus();\n\n // 仍在安装:更新进度面板并继续轮询\n if (install && install.running) {\n renderRagStatus({ connected: true, ragAvailable: false, nodeVersion: null, install });\n installPollTimer = setTimeout(poll, INSTALL_POLL_INTERVAL_MS);\n return;\n }\n\n // 安装结束:刷新完整状态(ragAvailable 由代理端安装后重新检测的结果决定)\n stopInstallPolling();\n const status = await loadRagStatus();\n if (status.connected && !status.ragAvailable) {\n status.install = install;\n }\n renderRagStatus(status);\n\n if (install && install.done) {\n showToast(\n install.success ? t('toolbox.ragInstallSuccess') : t('toolbox.ragInstallFailedToast'),\n install.success ? 'success' : 'error'\n );\n if (install.success) {\n notifyRagToolsChange();\n await availabilityChangeHandler?.();\n }\n }\n };\n installPollTimer = setTimeout(poll, INSTALL_POLL_INTERVAL_MS);\n}\n\n/**\n * 触发一键安装(代理端固定白名单,无参数)\n */\nexport async function startRagInstall() {\n const btn = document.getElementById('ragInstallBtn');\n if (btn) {\n btn.disabled = true;\n btn.textContent = t('toolbox.ragInstallBtnBusy');\n }\n try {\n const res = await agentApi('POST', '/api/rag/install');\n if (!res || !res.started) {\n showToast(res && res.error ? res.error : t('toolbox.ragInstallFailedToast'), 'error');\n // 可能是并发安装(409):直接进入轮询展示进度\n const install = await loadRagInstallStatus();\n if (install && install.running) {\n startInstallPolling();\n return;\n }\n if (btn) {\n btn.disabled = false;\n btn.textContent = t('toolbox.ragInstallBtn');\n }\n return;\n }\n showToast(t('toolbox.ragInstallStarted'), 'info');\n startInstallPolling();\n } catch (err) {\n showToast(`${t('toolbox.ragInstallFailedToast')}: ${err.message}`, 'error');\n if (btn) {\n btn.disabled = false;\n btn.textContent = t('toolbox.ragInstallBtn');\n }\n }\n}\n\n/**\n * 重新检测 RAG 依赖可用性(手动安装依赖后使用)\n */\nexport async function redetectRag() {\n const btn = document.getElementById('ragRedetectBtn');\n if (btn) btn.disabled = true;\n try {\n const res = await agentApi('POST', '/api/rag/detect');\n if (res && res.success) {\n if (res.available) {\n showToast(t('toolbox.ragRedetectReady'), 'success');\n notifyRagToolsChange();\n await availabilityChangeHandler?.();\n } else if (res.error) {\n // 展示代理端子进程探测到的真实原因(此前只有「仍缺失」,无法定位失败原因)\n showToast(t('toolbox.ragRedetectStillMissingDetail', { error: res.error }), 'error');\n } else {\n showToast(t('toolbox.ragRedetectStillMissing'), 'info');\n }\n } else {\n showToast(t('toolbox.ragRedetectFailed', { error: (res && res.error) || 'unknown' }), 'error');\n }\n } catch (err) {\n showToast(t('toolbox.ragRedetectFailed', { error: err.message }), 'error');\n }\n await refreshRagSection();\n}\n\n/**\n * 复制手动安装命令到剪贴板\n */\nasync function copyRagCommand() {\n const cmdEl = document.getElementById('ragInstallCmd');\n const cmd = cmdEl ? cmdEl.textContent : `npm install ${DEFAULT_RAG_PACKAGES.join(' ')}`;\n try {\n await navigator.clipboard.writeText(cmd);\n showToast(t('toolbox.ragCopied'), 'success');\n } catch (err) {\n logger.warn('[RAG] Clipboard write failed:', err.message);\n showToast(t('toolbox.ragCopyFailed'), 'error');\n }\n}\n\n// ==================== 刷新与事件 ====================\n\n/**\n * 刷新 RAG 状态区(知识库面板门控调用;容器 #ragSectionContent 位于门控 HTML 内)\n */\nexport async function refreshRagSection() {\n const status = await loadRagStatus();\n if (!status.connected || status.ragAvailable || status.unreachable) {\n if (status.ragAvailable) notifyRagToolsChange();\n renderRagStatus(status);\n stopInstallPolling();\n return;\n }\n // 依赖未安装:附带安装任务状态(失败详情/进行中进度)\n status.install = await loadRagInstallStatus();\n renderRagStatus(status);\n if (status.install && status.install.running) {\n startInstallPolling();\n } else {\n stopInstallPolling();\n }\n}\n\n/**\n * 初始化 RAG 状态区事件(事件委托 + 防重绑定;容器随门控重建后需重新调用)\n */\nexport function initRagEvents() {\n const container = document.getElementById('ragSectionContent');\n if (!container || container.dataset.ragEventsBound === '1') return;\n container.dataset.ragEventsBound = '1';\n container.addEventListener('click', async (e) => {\n const btn = e.target.closest('button');\n if (!btn) return;\n if (btn.id === 'ragInstallBtn') {\n await startRagInstall();\n } else if (btn.id === 'ragRedetectBtn') {\n await redetectRag();\n } else if (btn.id === 'ragCopyCmdBtn') {\n await copyRagCommand();\n }\n });\n}\n","// options/knowledge-panel.js - 知识库管理面板(options.html「知识库」标签页)\n// 职责:\n// - 门控引导:Agent 未连接 / RAG 总开关未启用 / 代理端能力未就绪\n// - 知识库列表(卡片):新建 / 删除 / 导入文档 / 文档管理 / 检索测试\n// 数据来源:Agent /api/rag/* 接口(agentApi);门控状态复用 toolbox-rag.js 的 loadRagStatus()\n\nimport { agentApi, escapeHtml, showToast, showCustomConfirm } from './toolbox-shared.js';\nimport { loadRagStatus, refreshRagSection, initRagEvents, setRagAvailabilityChangeHandler } from './toolbox-rag.js';\nimport { t } from '../shared/i18n.js';\nimport logger from '../shared/logger.js';\n\n// 单文件大小上限(agent 端 body 上限 200MB,base64 膨胀 33%,前端限 50MB 已足够覆盖文档场景)\nconst MAX_FILE_SIZE = 50 * 1024 * 1024;\n// 默认分块参数(与 agent config.js 的 DEFAULT_CHUNK_CONFIG 一致)\nconst DEFAULT_CHUNK_SIZE = 400;\nconst DEFAULT_CHUNK_OVERLAP = 80;\n// 检索默认参数(与 agent searcher 默认一致)\nconst DEFAULT_TOP_K = 5;\nconst DEFAULT_THRESHOLD = 0.3;\n\n// 支持的文件格式(与 agent document/loader.js 的路由一致)\nconst ACCEPT_EXTS = '.txt,.md,.markdown,.pdf,.docx,.doc,.xlsx,.xls,.csv,.pptx,.html,.htm,.json,.rtf,.odt,.odp,.ods,.epub';\n\n// 最近一次加载的知识库列表(供卡片操作查找)\nlet cachedCollections = [];\n// 列表搜索过滤关键字(模块级,重渲染后保持;与 MCP/Skill 列表模式一致)\nlet kbFilterText = '';\n// 状态筛选(模块级,重渲染后保持):'all' | 'enabled' | 'disabled'\nlet kbFilterStatus = 'all';\nlet initialized = false;\n\n// ==================== 入口 ====================\n\n/**\n * 初始化知识库面板(事件绑定 + 初始加载)\n */\nexport function initKnowledgePanel() {\n if (initialized) return;\n initialized = true;\n\n const listEl = document.getElementById('knowledgeList');\n if (listEl) {\n listEl.addEventListener('click', onListClick);\n }\n\n // 搜索过滤(防抖 200ms,与 MCP / Skill 列表一致)\n const searchInput = document.getElementById('kbSearchInput');\n if (searchInput) {\n let debounceTimer = null;\n searchInput.addEventListener('input', () => {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n kbFilterText = searchInput.value.trim();\n renderList(cachedCollections);\n }, 200);\n });\n const clearBtn = document.getElementById('kbSearchClear');\n if (clearBtn) {\n clearBtn.addEventListener('click', () => {\n searchInput.value = '';\n kbFilterText = '';\n renderList(cachedCollections);\n searchInput.focus();\n });\n }\n }\n\n // 状态筛选标签(全部 / 已启用 / 已停用,与 MCP / Skill 列表一致)\n const filterTabs = document.querySelectorAll('.toolbox-filter-tab[data-target=\"kb\"]');\n filterTabs.forEach(tab => {\n tab.addEventListener('click', () => {\n filterTabs.forEach(t => t.classList.remove('active'));\n tab.classList.add('active');\n kbFilterStatus = tab.dataset.filter;\n renderList(cachedCollections);\n });\n });\n\n const createBtn = document.getElementById('createKbBtn');\n if (createBtn) {\n createBtn.addEventListener('click', () => showCreateDialog());\n }\n\n // RAG 总开关(面板头部):初始状态回填 + 切换写入 storage\n const ragToggle = document.getElementById('ragGlobalToggle');\n if (ragToggle) {\n chrome.storage.local.get('ragEnabled', (result) => {\n const enabled = result.ragEnabled === true;\n ragToggle.checked = enabled;\n updateRagToggleLabel(enabled);\n });\n ragToggle.addEventListener('change', () => onRagToggleChange(ragToggle.checked));\n }\n\n // 依赖安装成功 / 重新检测通过后,自动刷新面板(门控 → 列表)\n setRagAvailabilityChangeHandler(() => refreshKnowledgePanel());\n\n // 门控引导区(「前往 XX 标签页」/「立即启用」按钮)\n const gateEl = document.getElementById('knowledgeGate');\n if (gateEl) {\n gateEl.addEventListener('click', (e) => {\n const gotoBtn = e.target.closest('[data-goto-tab]');\n if (gotoBtn) {\n const tabBtn = document.querySelector(`.tab-nav-btn[data-tab=\"${gotoBtn.dataset.gotoTab}\"]`);\n if (tabBtn) tabBtn.click();\n return;\n }\n if (e.target.closest('[data-action=\"enable-rag\"]')) {\n onRagToggleChange(true);\n }\n });\n }\n\n refreshKnowledgePanel();\n}\n\n/**\n * 刷新知识库面板:门控检查 → 列表加载(切换 tab / 操作后调用)\n */\nexport async function refreshKnowledgePanel() {\n const gateEl = document.getElementById('knowledgeGate');\n const mainEl = document.getElementById('knowledgeMain');\n if (!gateEl || !mainEl) return;\n\n const status = await loadRagStatus();\n const { ragEnabled } = await chrome.storage.local.get('ragEnabled');\n const createBtn = document.getElementById('createKbBtn');\n\n // 1) 代理未连接\n if (!status.connected) {\n renderGate('agentOff');\n if (createBtn) createBtn.disabled = true;\n return;\n }\n // 2) 总开关未启用\n if (ragEnabled !== true) {\n renderGate('ragOff');\n if (createBtn) createBtn.disabled = true;\n return;\n }\n // 3) 代理端能力未就绪(依赖未装 / Node 版本不足 / 状态不可达)→ 就地展示安装引导\n if (!status.ragAvailable) {\n renderGate('ragUnavailable');\n if (createBtn) createBtn.disabled = true;\n initRagEvents();\n await refreshRagSection();\n return;\n }\n\n // 就绪:加载知识库列表\n if (createBtn) createBtn.disabled = false;\n gateEl.style.display = 'none';\n mainEl.style.display = '';\n await loadAndRenderList();\n}\n\n// ==================== RAG 总开关 ====================\n\n/**\n * 更新总开关标签文案(与工具箱开关样式一致)\n */\nfunction updateRagToggleLabel(enabled) {\n const label = document.getElementById('ragToggleLabel');\n if (label) {\n label.textContent = enabled ? t('common.enabled') : t('common.disabled');\n label.style.color = enabled ? '#666' : '#999';\n }\n}\n\n/**\n * 总开关切换:写入 storage → 更新开关 UI → 刷新面板门控\n * (工具注册/注销由 background 监听 storage.onChanged 自动处理)\n */\nasync function onRagToggleChange(enabled) {\n await chrome.storage.local.set({ ragEnabled: enabled });\n const ragToggle = document.getElementById('ragGlobalToggle');\n if (ragToggle) ragToggle.checked = enabled;\n updateRagToggleLabel(enabled);\n showToast(enabled ? t('toolbox.ragServiceEnabled') : t('toolbox.ragServiceDisabled'), 'info');\n await refreshKnowledgePanel();\n}\n\n// ==================== 门控引导 ====================\n\nfunction renderGate(kind) {\n const gateEl = document.getElementById('knowledgeGate');\n const mainEl = document.getElementById('knowledgeMain');\n if (!gateEl || !mainEl) return;\n\n mainEl.style.display = 'none';\n gateEl.style.display = '';\n\n // 依赖未就绪:就地渲染安装引导容器(内容由 toolbox-rag.js 的 refreshRagSection 填充)\n if (kind === 'ragUnavailable') {\n gateEl.innerHTML = `<div id=\"ragSectionContent\">${renderLoading()}</div>`;\n return;\n }\n\n const cfg = {\n agentOff: {\n icon: '🔌',\n title: t('knowledge.gateAgentOffTitle'),\n desc: t('knowledge.gateAgentOffDesc'),\n btn: t('knowledge.goToAgentTab'),\n tab: 'agent'\n },\n ragOff: {\n icon: '📚',\n title: t('knowledge.gateRagOffTitle'),\n desc: t('knowledge.gateRagOffDesc'),\n btn: t('knowledge.enableNow'),\n action: 'enable-rag'\n }\n }[kind];\n\n const btnHtml = cfg.tab\n ? `<button class=\"toolbox-add-btn\" data-goto-tab=\"${cfg.tab}\" style=\"width:auto; margin-top:14px; padding:8px 18px; border-style:solid; color:#667eea; border-color:#667eea;\">${escapeHtml(cfg.btn)}</button>`\n : `<button class=\"toolbox-add-btn\" data-action=\"${cfg.action}\" style=\"width:auto; margin-top:14px; padding:8px 18px; border-style:solid; color:#667eea; border-color:#667eea;\">${escapeHtml(cfg.btn)}</button>`;\n\n gateEl.innerHTML = `\n <div class=\"toolbox-empty\" style=\"text-align:left; padding:24px 20px;\">\n <div style=\"display:flex; align-items:center; gap:10px; font-size:15px; font-weight:600; color:#555;\">\n <span style=\"font-size:22px;\">${cfg.icon}</span><span>${escapeHtml(cfg.title)}</span>\n </div>\n <div style=\"margin-top:8px; line-height:1.7; color:#888; font-size:13px;\">${escapeHtml(cfg.desc)}</div>\n ${btnHtml}\n </div>`;\n}\n\n// ==================== 列表与卡片 ====================\n\nasync function loadAndRenderList() {\n const listEl = document.getElementById('knowledgeList');\n if (!listEl) return;\n listEl.innerHTML = renderLoading();\n\n try {\n const res = await agentApi('GET', '/api/rag/collections');\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n cachedCollections = res.collections || [];\n renderList(cachedCollections);\n } catch (err) {\n logger.warn('[Knowledge] Failed to load collections:', err.message);\n updateCount([]);\n listEl.innerHTML = `\n <div class=\"toolbox-empty\" style=\"grid-column:1/-1;\">\n <div class=\"toolbox-empty-icon\">⚠️</div>\n <div class=\"toolbox-empty-title\">${escapeHtml(t('knowledge.loadFailed', { error: err.message }))}</div>\n <button class=\"toolbox-add-btn\" id=\"kbRetryBtn\" style=\"width:auto; margin-top:12px; padding:8px 18px;\">${escapeHtml(t('knowledge.retry'))}</button>\n </div>`;\n }\n}\n\nfunction renderLoading() {\n return `\n <div class=\"toolbox-empty\" style=\"grid-column:1/-1;\">\n <div class=\"toolbox-empty-icon\">⏳</div>\n <div class=\"toolbox-empty-title\">${escapeHtml(t('toolbox.loading'))}</div>\n </div>`;\n}\n\nfunction renderList(collections) {\n const listEl = document.getElementById('knowledgeList');\n if (!listEl) return;\n\n // 无知识库(未创建)\n if (collections.length === 0) {\n updateCount(collections);\n listEl.innerHTML = `\n <div class=\"toolbox-empty\" style=\"grid-column:1/-1; padding:50px 20px;\">\n <div class=\"toolbox-empty-icon\">📚</div>\n <div class=\"toolbox-empty-title\">${escapeHtml(t('knowledge.emptyTitle'))}</div>\n <div class=\"toolbox-empty-desc\">${escapeHtml(t('knowledge.emptyDesc'))}</div>\n </div>`;\n return;\n }\n\n // 应用状态筛选(启用状态)+ 搜索过滤(匹配名称 / 描述)\n const filtered = collections.filter(c => {\n if (kbFilterStatus === 'enabled' && c.enabled === false) return false;\n if (kbFilterStatus === 'disabled' && c.enabled !== false) return false;\n if (!kbFilterText) return true;\n const haystack = [c.name, c.description].filter(Boolean).join(' ').toLowerCase();\n return haystack.includes(kbFilterText.toLowerCase());\n });\n\n // 计数始终基于全量(与 MCP / 技能一致),不随筛选 / 搜索变化\n updateCount(collections);\n\n // 过滤后无匹配\n if (filtered.length === 0) {\n listEl.innerHTML = `\n <div class=\"toolbox-empty\" style=\"grid-column:1/-1; padding:50px 20px;\">\n <div class=\"toolbox-empty-icon\">🔍</div>\n <div class=\"toolbox-empty-title\">${escapeHtml(t('toolbox.noMatchResult'))}</div>\n </div>`;\n return;\n }\n\n listEl.innerHTML = filtered.map(renderCard).join('');\n}\n\nfunction renderCard(c) {\n // 停用库:卡片弱化 + 徽标 + 启停按钮(与技能卡片停用态一致)\n const disabled = c.enabled === false;\n const modeLabel = c.embeddingConfig?.mode === 'openai-compat'\n ? t('knowledge.modeRemote') : t('knowledge.modeLocal');\n const model = c.embeddingConfig?.modelName || '';\n const modelLine = model ? `${modeLabel} · ${model}` : modeLabel;\n return `\n <div class=\"kb-card${disabled ? ' kb-card-disabled' : ''}\" data-kb-id=\"${escapeHtml(c.id)}\">\n <div class=\"kb-card-title\">\n <span>📚</span>\n <span class=\"kb-card-name\" title=\"${escapeHtml(c.name)}\">${escapeHtml(c.name)}</span>\n ${disabled ? `<span class=\"kb-card-badge badge-disabled\">${escapeHtml(t('knowledge.badgeDisabled'))}</span>` : ''}\n </div>\n ${c.description ? `<div class=\"kb-card-desc\" title=\"${escapeHtml(c.description)}\">${escapeHtml(c.description)}</div>` : ''}\n <div class=\"kb-card-meta\">${escapeHtml(t('knowledge.statsSummary', { docs: c.documentCount || 0, chunks: c.chunkCount || 0 }))}</div>\n <div class=\"kb-card-model\">${escapeHtml(modelLine)}</div>\n <div class=\"kb-card-actions\">\n <button class=\"kb-action-btn\" type=\"button\" data-action=\"edit\">${escapeHtml(t('knowledge.actionEdit'))}</button>\n <button class=\"kb-action-btn\" type=\"button\" data-action=\"ingest\">${escapeHtml(t('knowledge.actionIngest'))}</button>\n <button class=\"kb-action-btn\" type=\"button\" data-action=\"search\">${escapeHtml(t('knowledge.actionSearch'))}</button>\n <button class=\"kb-action-btn\" type=\"button\" data-action=\"docs\">${escapeHtml(t('knowledge.actionDocs'))}</button>\n <button class=\"kb-action-btn\" type=\"button\" data-action=\"toggle\">${escapeHtml(disabled ? t('knowledge.actionEnable') : t('knowledge.actionDisable'))}</button>\n <button class=\"kb-action-btn kb-action-danger\" type=\"button\" data-action=\"delete\">${escapeHtml(t('knowledge.actionDelete'))}</button>\n </div>\n </div>`;\n}\n\nfunction updateCount(collections) {\n const el = document.getElementById('knowledgeCount');\n if (!el) return;\n const total = collections.length;\n if (total === 0) {\n el.style.display = 'none';\n return;\n }\n // 与 MCP / 技能一致:展示「已启用 X / 共 Y」\n const enabled = collections.filter(c => c.enabled !== false).length;\n el.textContent = t('toolbox.enabledCountTotal', { enabled, total });\n el.style.display = '';\n}\n\n// ==================== 卡片操作 ====================\n\nasync function onListClick(e) {\n // 重试按钮\n if (e.target.closest('#kbRetryBtn')) {\n await loadAndRenderList();\n return;\n }\n\n const btn = e.target.closest('.kb-action-btn');\n if (!btn) return;\n\n const card = btn.closest('[data-kb-id]');\n const collection = cachedCollections.find(x => x.id === card?.dataset.kbId);\n if (!collection) return;\n\n const action = btn.dataset.action;\n if (action === 'ingest') {\n showIngestDialog(collection);\n } else if (action === 'edit') {\n showEditDialog(collection);\n } else if (action === 'search') {\n showSearchDialog(collection);\n } else if (action === 'docs') {\n showDocsDialog(collection);\n } else if (action === 'toggle') {\n await handleToggleCollection(collection);\n } else if (action === 'delete') {\n await handleDeleteCollection(collection);\n }\n}\n\nasync function handleDeleteCollection(c) {\n const confirmed = await showCustomConfirm(\n t('knowledge.deleteConfirm', {\n name: c.name,\n docs: c.documentCount || 0,\n chunks: c.chunkCount || 0\n }),\n t('knowledge.deleteConfirmTitle')\n );\n if (!confirmed) return;\n\n try {\n const res = await agentApi('DELETE', `/api/rag/collections/${encodeURIComponent(c.id)}`);\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n showToast(t('knowledge.deleteSuccess'), 'success');\n await loadAndRenderList();\n } catch (err) {\n showToast(t('knowledge.opFailed', { error: err.message }), 'error');\n }\n}\n\n/**\n * 启停知识库:停用后大模型自主检索/导入不再使用该库;用户仍可在侧边栏 @ 手动引用\n */\nasync function handleToggleCollection(c) {\n try {\n const res = await agentApi('POST', `/api/rag/collections/${encodeURIComponent(c.id)}/toggle`);\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n showToast(\n res.enabled === false\n ? t('knowledge.disableSuccess', { name: c.name })\n : t('knowledge.enableSuccess', { name: c.name }),\n 'success'\n );\n await loadAndRenderList();\n } catch (err) {\n showToast(t('knowledge.opFailed', { error: err.message }), 'error');\n }\n}\n\n// ==================== 向量配置表单 ====================\n\n/**\n * 向量配置表单(创建/编辑弹窗共用)\n * @param {object} cfg 当前 embeddingConfig(编辑场景;创建时传空对象)\n * @returns {string} HTML\n */\nfunction renderVectorForm(cfg = {}) {\n const remote = cfg.mode === 'openai-compat';\n return `\n <div class=\"form-group\">\n <label>${escapeHtml(t('knowledge.vectorSectionTitle'))}</label>\n <div class=\"kb-radio-row\">\n <label class=\"kb-radio-inline\">\n <input type=\"radio\" name=\"kbVecMode\" value=\"local\"${remote ? '' : ' checked'}>\n ${escapeHtml(t('knowledge.vectorModeLocal'))}\n </label>\n <label class=\"kb-radio-inline\">\n <input type=\"radio\" name=\"kbVecMode\" value=\"openai-compat\"${remote ? ' checked' : ''}>\n ${escapeHtml(t('knowledge.vectorModeRemote'))}\n </label>\n </div>\n <div class=\"kb-vec-hint\" id=\"kbVecHint\">${escapeHtml(remote ? t('knowledge.vectorModeRemoteHint') : t('knowledge.vectorModeLocalHint'))}</div>\n <div id=\"kbVecRemoteFields\"${remote ? '' : ' style=\"display:none\"'}>\n <div class=\"form-group\" style=\"margin-top:10px;\">\n <label>${escapeHtml(t('knowledge.vectorEndpointLabel'))}</label>\n <input type=\"text\" id=\"kbVecEndpoint\" placeholder=\"${escapeHtml(t('knowledge.vectorEndpointPlaceholder'))}\" value=\"${escapeHtml(cfg.endpoint || '')}\">\n </div>\n <div class=\"form-group\">\n <label>${escapeHtml(t('knowledge.vectorApiKeyLabel'))}</label>\n <input type=\"password\" id=\"kbVecApiKey\" value=\"\" placeholder=\"${escapeHtml(cfg.apiKey || cfg.hasApiKey ? t('knowledge.vectorApiKeyKeep') : t('knowledge.vectorApiKeyPlaceholder'))}\">\n </div>\n <div class=\"form-group\">\n <label>${escapeHtml(t('knowledge.vectorModelLabel'))}</label>\n <input type=\"text\" id=\"kbVecModel\" placeholder=\"${escapeHtml(t('knowledge.vectorModelPlaceholder'))}\" value=\"${escapeHtml(remote ? (cfg.modelName || '') : '')}\">\n </div>\n <div class=\"form-group\" style=\"margin-bottom:0;\">\n <label>${escapeHtml(t('knowledge.vectorDimsLabel'))}</label>\n <div class=\"kb-vec-test-row\">\n <input type=\"number\" id=\"kbVecDims\" min=\"1\" max=\"8192\" placeholder=\"${escapeHtml(t('knowledge.vectorDimsPlaceholder'))}\" value=\"${remote && cfg.dimensions ? escapeHtml(String(cfg.dimensions)) : ''}\">\n <button class=\"kb-action-btn\" type=\"button\" id=\"kbVecTestBtn\">${escapeHtml(t('knowledge.vectorTestBtn'))}</button>\n <span class=\"kb-vec-test-result\" id=\"kbVecTestResult\"></span>\n </div>\n </div>\n </div>\n </div>`;\n}\n\n/**\n * 绑定向量表单交互(模式切换显隐 + 测试连通性自动探测维度)\n * @param {HTMLElement} overlay 弹窗根元素\n * @param {string|null} [collectionId] 编辑场景的知识库 ID(测试连通性时空密钥复用已保存密钥)\n */\nfunction bindVectorFormEvents(overlay, collectionId = null) {\n const remoteFields = overlay.querySelector('#kbVecRemoteFields');\n const hintEl = overlay.querySelector('#kbVecHint');\n const syncMode = () => {\n const remote = overlay.querySelector('input[name=\"kbVecMode\"]:checked')?.value === 'openai-compat';\n if (remoteFields) remoteFields.style.display = remote ? '' : 'none';\n if (hintEl) hintEl.textContent = remote ? t('knowledge.vectorModeRemoteHint') : t('knowledge.vectorModeLocalHint');\n };\n overlay.querySelectorAll('input[name=\"kbVecMode\"]').forEach(r => r.addEventListener('change', syncMode));\n syncMode();\n\n const testBtn = overlay.querySelector('#kbVecTestBtn');\n if (!testBtn) return;\n const resultEl = overlay.querySelector('#kbVecTestResult');\n const dimsInput = overlay.querySelector('#kbVecDims');\n testBtn.addEventListener('click', async () => {\n const endpoint = overlay.querySelector('#kbVecEndpoint').value.trim();\n const modelName = overlay.querySelector('#kbVecModel').value.trim();\n if (!endpoint) {\n showToast(t('knowledge.vectorEndpointRequired'), 'warning');\n return;\n }\n if (!modelName) {\n showToast(t('knowledge.vectorModelRequired'), 'warning');\n return;\n }\n // API Key 留空:编辑场景由代理端经 collectionId 复用已保存密钥(不回传明文)\n const apiKey = overlay.querySelector('#kbVecApiKey').value.trim();\n // 维度填了就透传(支持降维的模型按指定维度探测);留空则用平台默认维度\n const typedDims = parseInt(dimsInput?.value.trim() || '', 10);\n\n testBtn.disabled = true;\n const prevText = testBtn.textContent;\n testBtn.textContent = t('knowledge.vectorTesting');\n if (resultEl) {\n resultEl.textContent = '';\n resultEl.style.color = '';\n }\n try {\n const res = await agentApi('POST', '/api/rag/test-embedding', {\n mode: 'openai-compat', endpoint, apiKey, modelName,\n ...(collectionId ? { collectionId } : {}),\n ...(Number.isFinite(typedDims) && typedDims > 0 ? { dimensions: typedDims } : {}),\n });\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n if (resultEl) {\n resultEl.textContent = `✓ ${t('knowledge.vectorTestOk', { model: res.model, dims: res.dimensions })}`;\n resultEl.style.color = '#38a169';\n }\n // 自动填充/修正维度\n if (dimsInput && res.dimensions) {\n const prevDims = dimsInput.value.trim();\n dimsInput.value = String(res.dimensions);\n if (prevDims !== String(res.dimensions)) {\n showToast(t('knowledge.vectorDimsFilled', { dims: res.dimensions }), 'info');\n }\n }\n } catch (err) {\n if (resultEl) {\n resultEl.textContent = `✕ ${t('knowledge.vectorTestFailed', { error: err.message })}`;\n resultEl.style.color = '#c0392b';\n }\n } finally {\n testBtn.disabled = false;\n testBtn.textContent = prevText || t('knowledge.vectorTestBtn');\n }\n });\n}\n\n/**\n * 读取向量表单为提交体(local 仅模式;remote 含完整字段)\n * @param {HTMLElement} overlay 弹窗根元素\n * @returns {object} embeddingConfig\n */\nfunction readVectorForm(overlay) {\n const remote = overlay.querySelector('input[name=\"kbVecMode\"]:checked')?.value === 'openai-compat';\n if (!remote) return { mode: 'local' };\n const dims = parseInt(overlay.querySelector('#kbVecDims').value.trim(), 10);\n return {\n mode: 'openai-compat',\n endpoint: overlay.querySelector('#kbVecEndpoint').value.trim(),\n // 留空沿用已保存密钥(代理端在更新时保留原值,见 updateCollection 的空值继承)\n apiKey: overlay.querySelector('#kbVecApiKey').value.trim(),\n modelName: overlay.querySelector('#kbVecModel').value.trim(),\n ...(Number.isFinite(dims) && dims > 0 ? { dimensions: dims } : {}),\n };\n}\n\n/**\n * 判断向量配置变更是否影响向量空间(需重建索引;与 agent 端 needsIndexRebuild 对齐)\n * 未提供的字段视为沿用已保存值(与后端 resolveEmbeddingConfig 语义一致)\n */\nfunction isEmbeddingSpaceChanged(prev = {}, next = {}) {\n const prevMode = prev.mode || 'local';\n const nextMode = next.mode || 'local';\n if (prevMode !== nextMode) return true;\n if (nextMode === 'local') return false; // 本地模式无可选参数\n return ['endpoint', 'modelName', 'dimensions'].some(k => {\n if (next[k] === undefined) return false;\n return String(prev[k] ?? '') !== String(next[k] ?? '');\n });\n}\n\n/**\n * 轮询索引重建进度直至完成/失败(编辑弹窗关闭后后台进行;上限 30 分钟)\n * @returns {Promise<{phase: string, error?: string}>}\n */\nfunction waitForRebuildDone(collectionId) {\n const POLL_INTERVAL = 1000;\n const TIMEOUT = 30 * 60 * 1000;\n return new Promise((resolve, reject) => {\n const startedAt = Date.now();\n const timer = setInterval(async () => {\n if (Date.now() - startedAt > TIMEOUT) {\n clearInterval(timer);\n reject(new Error('rebuild polling timeout'));\n return;\n }\n try {\n const s = await agentApi('GET', `/api/rag/collections/${encodeURIComponent(collectionId)}/ingest/status`);\n const p = s?.progress;\n if (p && p.done) {\n clearInterval(timer);\n resolve(p);\n }\n } catch {\n // 单次轮询失败忽略,等待下次重试\n }\n }, POLL_INTERVAL);\n });\n}\n\n// ==================== 编辑知识库 ====================\n\nfunction showEditDialog(c) {\n const modal = createModal({\n title: t('knowledge.editTitle'),\n bodyHtml: `\n <div class=\"form-group\">\n <label>${escapeHtml(t('knowledge.nameLabel'))}</label>\n <input type=\"text\" id=\"kbEditName\" maxlength=\"100\" placeholder=\"${escapeHtml(t('knowledge.namePlaceholder'))}\" value=\"${escapeHtml(c.name || '')}\">\n </div>\n <div class=\"form-group\">\n <label>${escapeHtml(t('knowledge.descLabel'))}</label>\n <input type=\"text\" id=\"kbEditDesc\" maxlength=\"500\" placeholder=\"${escapeHtml(t('knowledge.descPlaceholder'))}\" value=\"${escapeHtml(c.description || '')}\">\n </div>\n ${renderVectorForm(c.embeddingConfig || {})}`,\n confirmText: t('knowledge.editConfirm'),\n closeOnOverlay: false,\n onConfirm: async ({ close, setBusy }) => {\n const { overlay } = modal;\n const name = overlay.querySelector('#kbEditName').value.trim();\n const description = overlay.querySelector('#kbEditDesc').value.trim();\n if (!name) {\n showToast(t('knowledge.nameRequired'), 'warning');\n return;\n }\n\n const embeddingConfig = readVectorForm(overlay);\n if (embeddingConfig.mode === 'openai-compat') {\n if (!embeddingConfig.endpoint) {\n showToast(t('knowledge.vectorEndpointRequired'), 'warning');\n return;\n }\n if (!embeddingConfig.modelName) {\n showToast(t('knowledge.vectorModelRequired'), 'warning');\n return;\n }\n }\n\n // 向量空间变更 → 二次确认(提交后将触发索引重建)\n if (isEmbeddingSpaceChanged(c.embeddingConfig || {}, embeddingConfig)) {\n const ok = await showCustomConfirm(t('knowledge.rebuildConfirm'), t('knowledge.rebuildConfirmTitle'));\n if (!ok) return;\n }\n\n setBusy(true, t('knowledge.saving'));\n try {\n const res = await agentApi('PUT', `/api/rag/collections/${encodeURIComponent(c.id)}`, { name, description, embeddingConfig });\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n close();\n if (res.collection?.rebuilding) {\n // 后台重建中:先提示,轮询完成后自动刷新列表\n showToast(t('knowledge.rebuildStarted'), 'info');\n await loadAndRenderList();\n waitForRebuildDone(c.id).then(p => {\n if (p.phase === 'error') {\n showToast(t('knowledge.rebuildFailed', { error: p.error || 'unknown error' }), 'error');\n } else {\n showToast(t('knowledge.rebuildDone'), 'success');\n }\n loadAndRenderList();\n }).catch(err => {\n showToast(t('knowledge.rebuildFailed', { error: err.message }), 'error');\n loadAndRenderList();\n });\n } else {\n showToast(t('knowledge.editSuccess'), 'success');\n await loadAndRenderList();\n }\n } catch (err) {\n setBusy(false, t('knowledge.editConfirm'));\n showToast(t('knowledge.opFailed', { error: err.message }), 'error');\n }\n }\n });\n bindVectorFormEvents(modal.overlay, c.id);\n // 预填名称:聚焦并全选,便于直接覆盖输入\n const nameInput = modal.overlay.querySelector('#kbEditName');\n nameInput?.focus();\n nameInput?.select();\n}\n\n// ==================== 新建知识库 ====================\n\nfunction showCreateDialog() {\n const modal = createModal({\n title: t('knowledge.createTitle'),\n bodyHtml: `\n <div class=\"form-group\">\n <label>${escapeHtml(t('knowledge.nameLabel'))}</label>\n <input type=\"text\" id=\"kbCreateName\" maxlength=\"100\" placeholder=\"${escapeHtml(t('knowledge.namePlaceholder'))}\">\n </div>\n <div class=\"form-group\">\n <label>${escapeHtml(t('knowledge.descLabel'))}</label>\n <input type=\"text\" id=\"kbCreateDesc\" maxlength=\"500\" placeholder=\"${escapeHtml(t('knowledge.descPlaceholder'))}\">\n </div>\n ${renderVectorForm({})}`,\n confirmText: t('knowledge.createConfirm'),\n closeOnOverlay: false,\n onConfirm: async ({ close, setBusy }) => {\n const { overlay } = modal;\n const name = overlay.querySelector('#kbCreateName').value.trim();\n const description = overlay.querySelector('#kbCreateDesc').value.trim();\n if (!name) {\n showToast(t('knowledge.nameRequired'), 'warning');\n return;\n }\n\n const embeddingConfig = readVectorForm(overlay);\n if (embeddingConfig.mode === 'openai-compat') {\n if (!embeddingConfig.endpoint) {\n showToast(t('knowledge.vectorEndpointRequired'), 'warning');\n return;\n }\n if (!embeddingConfig.modelName) {\n showToast(t('knowledge.vectorModelRequired'), 'warning');\n return;\n }\n }\n\n setBusy(true, t('knowledge.creating'));\n try {\n const res = await agentApi('POST', '/api/rag/collections', { name, description, embeddingConfig });\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n close();\n showToast(t('knowledge.createSuccess', { name: res.collection?.name || name }), 'success');\n await loadAndRenderList();\n } catch (err) {\n setBusy(false, t('knowledge.createConfirm'));\n showToast(t('knowledge.opFailed', { error: err.message }), 'error');\n }\n }\n });\n bindVectorFormEvents(modal.overlay);\n modal.overlay.querySelector('#kbCreateName')?.focus();\n}\n\n// ==================== 导入文档 ====================\n\n/**\n * 导入进度快照 → 状态文案(阶段 + 向量化进度;快照缺失时回退笼统文案)\n */\nfunction formatIngestProgress(p) {\n switch (p?.phase) {\n case 'parsing':\n return `⏳ ${t('knowledge.progressParsing')}`;\n case 'chunking':\n return `⏳ ${t('knowledge.progressChunking')}`;\n case 'embedding':\n return `⏳ ${t('knowledge.progressEmbedding', { current: p.current || 0, total: p.total || 0 })}`;\n case 'storing':\n return `⏳ ${t('knowledge.progressStoring')}`;\n case 'rebuilding':\n return `⏳ ${t('knowledge.progressRebuilding', { current: p.current || 0, total: p.total || 0 })}`;\n default:\n return `⏳ ${t('knowledge.ingesting')}`;\n }\n}\n\nfunction showIngestDialog(c) {\n const bodyHtml = `\n <div class=\"import-tabs\" id=\"kbIngestTabs\">\n <button type=\"button\" class=\"import-tab active\" data-tab=\"file\">${escapeHtml(t('knowledge.tabFile'))}</button>\n <button type=\"button\" class=\"import-tab\" data-tab=\"text\">${escapeHtml(t('knowledge.tabText'))}</button>\n <button type=\"button\" class=\"import-tab\" data-tab=\"url\">${escapeHtml(t('knowledge.tabUrl'))}</button>\n </div>\n <div class=\"import-panel active\" data-panel=\"file\">\n <div class=\"upload-drop-zone\" id=\"kbDropzone\">\n <div class=\"upload-icon\">📄</div>\n <div style=\"margin-top:8px; color:#666; font-size:13px;\">${escapeHtml(t('knowledge.dropHint'))}</div>\n <div style=\"margin-top:6px; font-size:12px; color:#999;\">${escapeHtml(t('knowledge.dropSupported'))}</div>\n <div id=\"kbSelectedFile\" style=\"display:none; margin-top:8px; color:#38a169; font-weight:500; font-size:13px;\"></div>\n </div>\n <input type=\"file\" id=\"kbFileInput\" style=\"display:none\" accept=\"${ACCEPT_EXTS}\">\n </div>\n <div class=\"import-panel\" data-panel=\"text\">\n <div class=\"form-group\">\n <label>${escapeHtml(t('knowledge.textNameLabel'))}</label>\n <input type=\"text\" id=\"kbIngestName\" maxlength=\"100\" placeholder=\"${escapeHtml(t('knowledge.textNamePlaceholder'))}\">\n </div>\n <div class=\"form-group\" style=\"margin-bottom:0;\">\n <label>${escapeHtml(t('knowledge.textContentLabel'))}</label>\n <textarea id=\"kbIngestText\" rows=\"8\" placeholder=\"${escapeHtml(t('knowledge.textContentPlaceholder'))}\"></textarea>\n </div>\n </div>\n <div class=\"import-panel\" data-panel=\"url\">\n <div class=\"form-group\" style=\"margin-bottom:0;\">\n <label>${escapeHtml(t('knowledge.urlLabel'))}</label>\n <input type=\"url\" id=\"kbIngestUrl\" placeholder=\"${escapeHtml(t('knowledge.urlPlaceholder'))}\">\n </div>\n </div>\n <div class=\"kb-chunk-row\">\n <span class=\"kb-chunk-title\">${escapeHtml(t('knowledge.chunkSettingsTitle'))}</span>\n <label class=\"kb-chunk-inline\">${escapeHtml(t('knowledge.chunkSizeLabel'))}\n <input type=\"number\" id=\"kbChunkSize\" min=\"100\" max=\"2000\" step=\"50\" value=\"${DEFAULT_CHUNK_SIZE}\">\n </label>\n <label class=\"kb-chunk-inline\">${escapeHtml(t('knowledge.chunkOverlapLabel'))}\n <input type=\"number\" id=\"kbChunkOverlap\" min=\"0\" max=\"500\" step=\"10\" value=\"${DEFAULT_CHUNK_OVERLAP}\">\n </label>\n </div>\n <div id=\"kbIngestStatus\" class=\"kb-status\" style=\"display:none;\">⏳ ${escapeHtml(t('knowledge.ingesting'))}</div>`;\n\n let selectedFile = null;\n\n const modal = createModal({\n title: t('knowledge.ingestTitle', { name: c.name }),\n bodyHtml,\n confirmText: t('knowledge.ingestConfirm'),\n onConfirm: async ({ close, setBusy }) => {\n const { overlay } = modal;\n const activeTab = overlay.querySelector('.import-tab.active')?.dataset.tab || 'file';\n\n // 按来源构造请求体\n let payload = null;\n if (activeTab === 'file') {\n if (!selectedFile) {\n showToast(t('knowledge.fileRequired'), 'warning');\n return;\n }\n const contentBase64 = await readFileBase64(selectedFile);\n payload = { type: 'file', fileName: selectedFile.name, contentBase64 };\n } else if (activeTab === 'text') {\n const content = overlay.querySelector('#kbIngestText').value;\n const name = overlay.querySelector('#kbIngestName').value.trim();\n if (!content.trim()) {\n showToast(t('knowledge.contentRequired'), 'warning');\n return;\n }\n payload = { type: 'text', content, name: name || undefined };\n } else {\n const url = overlay.querySelector('#kbIngestUrl').value.trim();\n if (!url) {\n showToast(t('knowledge.urlRequired'), 'warning');\n return;\n }\n payload = { type: 'url', url };\n }\n\n // 分块设置(数值合法性校验,非法值回退默认)\n const chunkSize = parseInt(overlay.querySelector('#kbChunkSize').value, 10);\n const overlap = parseInt(overlay.querySelector('#kbChunkOverlap').value, 10);\n payload.chunkConfig = {\n ...(Number.isFinite(chunkSize) && chunkSize >= 100 && chunkSize <= 2000 ? { chunkSize } : {}),\n ...(Number.isFinite(overlap) && overlap >= 0 && overlap <= 500 ? { overlap } : {}),\n };\n\n // 进行中状态(ingest 为同步接口:解析 + 向量化可能耗时较久,首次还含模型加载)\n const statusEl = overlay.querySelector('#kbIngestStatus');\n if (statusEl) {\n statusEl.style.display = '';\n statusEl.textContent = `⏳ ${t('knowledge.ingesting')}`;\n }\n setBusy(true);\n\n // 进度轮询:POST 阻塞期间经 /ingest/status 展示分阶段进度(解析 → 分块 → 向量化 x/y → 存储)\n const pollTimer = setInterval(async () => {\n try {\n const s = await agentApi('GET', `/api/rag/collections/${encodeURIComponent(c.id)}/ingest/status`);\n if (statusEl && s && s.success && s.progress) {\n statusEl.textContent = formatIngestProgress(s.progress);\n }\n } catch {}\n }, 800);\n\n try {\n const res = await agentApi('POST', `/api/rag/collections/${encodeURIComponent(c.id)}/ingest`, payload);\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n close();\n showToast(t('knowledge.ingestSuccess', { name: res.name, chunks: res.chunkCount }), 'success');\n await loadAndRenderList();\n } catch (err) {\n if (statusEl) statusEl.style.display = 'none';\n setBusy(false);\n showToast(t('knowledge.ingestFailed', { error: err.message }), 'error');\n } finally {\n clearInterval(pollTimer);\n }\n }\n });\n\n // Tab 切换 / 文件选择 / 拖拽(弹窗局部交互)\n const { overlay } = modal;\n overlay.querySelector('#kbIngestTabs').addEventListener('click', (e) => {\n const tab = e.target.closest('.import-tab');\n if (!tab) return;\n overlay.querySelectorAll('.import-tab').forEach(x => x.classList.toggle('active', x === tab));\n overlay.querySelectorAll('.import-panel').forEach(p => {\n p.classList.toggle('active', p.dataset.panel === tab.dataset.tab);\n });\n });\n\n const dropzone = overlay.querySelector('#kbDropzone');\n const fileInput = overlay.querySelector('#kbFileInput');\n const selectedFileEl = overlay.querySelector('#kbSelectedFile');\n const pickFile = (file) => {\n if (!file) return;\n if (file.size > MAX_FILE_SIZE) {\n showToast(t('knowledge.fileTooLarge'), 'warning');\n return;\n }\n selectedFile = file;\n dropzone.classList.add('has-file');\n selectedFileEl.style.display = '';\n selectedFileEl.textContent = t('knowledge.selectedFile', { name: file.name });\n };\n dropzone.addEventListener('click', () => fileInput.click());\n fileInput.addEventListener('change', () => pickFile(fileInput.files?.[0]));\n dropzone.addEventListener('dragover', (e) => e.preventDefault());\n dropzone.addEventListener('drop', (e) => {\n e.preventDefault();\n pickFile(e.dataTransfer?.files?.[0]);\n });\n}\n\n/**\n * 读取文件为 base64(去掉 data URL 前缀)\n */\nfunction readFileBase64(file) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = String(reader.result || '');\n resolve(result.slice(result.indexOf(',') + 1));\n };\n reader.onerror = () => reject(reader.error || new Error('File read failed'));\n reader.readAsDataURL(file);\n });\n}\n\n// ==================== 文档管理 ====================\n\nfunction showDocsDialog(c) {\n const modal = createModal({\n title: t('knowledge.docsTitle', { name: c.name }),\n bodyHtml: `<div id=\"kbDocsBody\">${renderLoading()}</div>`,\n confirmText: null,\n cancelText: t('common.close'),\n wide: true,\n });\n\n const body = modal.overlay.querySelector('#kbDocsBody');\n\n // 文档删除(事件委托,重渲染后仍有效)\n body.addEventListener('click', async (e) => {\n const btn = e.target.closest('[data-doc-delete]');\n if (!btn) return;\n const row = btn.closest('[data-doc-id]');\n const docId = row?.dataset.docId;\n const docName = row?.querySelector('.kb-doc-name')?.textContent || docId;\n if (!docId) return;\n\n const confirmed = await showCustomConfirm(\n t('knowledge.docDeleteConfirm', { name: docName }),\n t('knowledge.docDeleteConfirmTitle')\n );\n if (!confirmed) return;\n\n try {\n const res = await agentApi(\n 'DELETE',\n `/api/rag/collections/${encodeURIComponent(c.id)}/documents/${encodeURIComponent(docId)}`\n );\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n showToast(t('knowledge.docDeleteSuccess'), 'success');\n await refreshDocsList(body, c);\n loadAndRenderList(); // 后台刷新卡片计数\n } catch (err) {\n showToast(t('knowledge.opFailed', { error: err.message }), 'error');\n }\n });\n\n refreshDocsList(body, c);\n}\n\nasync function refreshDocsList(body, c) {\n body.innerHTML = renderLoading();\n try {\n const res = await agentApi('GET', `/api/rag/collections/${encodeURIComponent(c.id)}/documents`);\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n const docs = res.documents || [];\n\n if (docs.length === 0) {\n body.innerHTML = `<div class=\"kb-status\" style=\"color:#999;\">${escapeHtml(t('knowledge.docsEmpty'))}</div>`;\n return;\n }\n\n body.innerHTML = `<div class=\"kb-doc-list\">${docs.map(d => `\n <div class=\"kb-doc-row\" data-doc-id=\"${escapeHtml(d.id)}\">\n <div class=\"kb-doc-info\">\n <div class=\"kb-doc-name\" title=\"${escapeHtml(d.name)}\">${escapeHtml(d.name)}</div>\n <div class=\"kb-doc-meta\">${escapeHtml(t('knowledge.docChunkCount', { count: d.chunkCount }))} · ${escapeHtml(formatTime(d.createdAt))}</div>\n </div>\n <button class=\"kb-doc-del\" type=\"button\" data-doc-delete title=\"${escapeHtml(t('knowledge.actionDelete'))}\">✕</button>\n </div>`).join('')}</div>`;\n } catch (err) {\n body.innerHTML = `<div class=\"kb-status\" style=\"color:#c0392b;\">${escapeHtml(t('knowledge.loadFailed', { error: err.message }))}</div>`;\n }\n}\n\n// ==================== 检索测试 ====================\n\nfunction showSearchDialog(c) {\n const bodyHtml = `\n <div class=\"kb-search-scope\">${escapeHtml(t('knowledge.searchScope', { name: c.name }))}</div>\n <div class=\"kb-search-bar\">\n <input type=\"text\" id=\"kbSearchQuery\" placeholder=\"${escapeHtml(t('knowledge.searchQueryPlaceholder'))}\">\n <button class=\"btn btn-primary\" type=\"button\" id=\"kbSearchBtn\" style=\"width:auto; padding:8px 20px; flex:none;\">${escapeHtml(t('knowledge.searchBtn'))}</button>\n </div>\n <div class=\"kb-search-opts\">\n <label class=\"kb-chunk-inline\">${escapeHtml(t('knowledge.searchTopKLabel'))}\n <input type=\"number\" id=\"kbSearchTopK\" min=\"1\" max=\"20\" value=\"${DEFAULT_TOP_K}\">\n </label>\n <label class=\"kb-chunk-inline\">${escapeHtml(t('knowledge.searchThresholdLabel'))}\n <input type=\"number\" id=\"kbSearchThreshold\" min=\"0\" max=\"1\" step=\"0.05\" value=\"${DEFAULT_THRESHOLD}\">\n </label>\n </div>\n <div id=\"kbSearchResults\"></div>`;\n\n const modal = createModal({\n title: t('knowledge.searchTitle'),\n bodyHtml,\n confirmText: null,\n cancelText: t('common.close'),\n wide: true,\n });\n\n const { overlay } = modal;\n const queryInput = overlay.querySelector('#kbSearchQuery');\n const searchBtn = overlay.querySelector('#kbSearchBtn');\n const resultsEl = overlay.querySelector('#kbSearchResults');\n\n const runSearch = async () => {\n const query = queryInput.value.trim();\n if (!query) {\n showToast(t('knowledge.contentRequired'), 'warning');\n return;\n }\n\n const topK = parseInt(overlay.querySelector('#kbSearchTopK').value, 10);\n const threshold = parseFloat(overlay.querySelector('#kbSearchThreshold').value);\n\n searchBtn.disabled = true;\n searchBtn.textContent = t('knowledge.searching');\n resultsEl.innerHTML = `<div class=\"kb-status\">⏳ ${escapeHtml(t('knowledge.searching'))}</div>`;\n\n try {\n const res = await agentApi('POST', `/api/rag/collections/${encodeURIComponent(c.id)}/search`, {\n query,\n topK: Number.isFinite(topK) && topK > 0 ? topK : DEFAULT_TOP_K,\n threshold: Number.isFinite(threshold) ? threshold : DEFAULT_THRESHOLD,\n });\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown error');\n renderSearchResults(resultsEl, res);\n } catch (err) {\n resultsEl.innerHTML = `<div class=\"kb-status\" style=\"color:#c0392b;\">${escapeHtml(t('knowledge.searchFailed', { error: err.message }))}</div>`;\n } finally {\n searchBtn.disabled = false;\n searchBtn.textContent = t('knowledge.searchBtn');\n }\n };\n\n searchBtn.addEventListener('click', runSearch);\n queryInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') runSearch();\n });\n queryInput.focus();\n}\n\nfunction renderSearchResults(container, result) {\n const results = result.results || [];\n\n if (results.length === 0) {\n container.innerHTML = `<div class=\"kb-status\" style=\"color:#999;\">${escapeHtml(t('knowledge.searchNoHits'))}</div>`;\n return;\n }\n\n const fallbackHtml = result.fallback\n ? `<div class=\"kb-fallback-hint\">⚠️ ${escapeHtml(t('knowledge.searchFallbackHint'))}</div>`\n : '';\n\n container.innerHTML = `\n ${fallbackHtml}\n <div class=\"kb-hit-count\">${escapeHtml(t('knowledge.searchHitCount', { count: results.length }))}</div>\n <div class=\"kb-hit-list\">${results.map(r => `\n <div class=\"kb-hit\">\n <div class=\"kb-hit-head\">\n <span class=\"kb-hit-score\">${escapeHtml((r.score ?? 0).toFixed(3))}</span>\n <span class=\"kb-hit-doc\" title=\"${escapeHtml(r.metadata?.documentName || '')}\">${escapeHtml(r.metadata?.documentName || '')}</span>\n </div>\n <div class=\"kb-hit-text\">${escapeHtml(r.content || '')}</div>\n </div>`).join('')}</div>`;\n\n // 长文本折叠 + 点击展开/收起\n container.querySelectorAll('.kb-hit-text').forEach(el => {\n if (el.textContent.length > 180) {\n el.classList.add('kb-hit-collapsed');\n el.title = t('knowledge.expandFull');\n el.addEventListener('click', () => {\n const collapsed = el.classList.toggle('kb-hit-collapsed');\n el.title = collapsed ? t('knowledge.expandFull') : t('knowledge.collapse');\n });\n }\n });\n}\n\n// ==================== 通用弹窗工厂 ====================\n\n/**\n * 创建弹窗(返回 { overlay, close })\n * @param {{title: string, bodyHtml: string, confirmText?: string|null, cancelText?: string, onConfirm?: Function, wide?: boolean, closeOnOverlay?: boolean}} opts\n * confirmText 为 null 时隐藏确认按钮(纯查看型弹窗)\n * closeOnOverlay 为 false 时点击遮罩空白区不关闭(默认 true;编辑/新建知识库传 false 防误触丢失表单)\n */\nfunction createModal({ title, bodyHtml, confirmText, cancelText, onConfirm, wide, closeOnOverlay = true }) {\n const overlay = document.createElement('div');\n overlay.className = 'modal-overlay';\n overlay.innerHTML = `\n <div class=\"modal-content kb-modal${wide ? ' kb-modal-wide' : ''}\">\n <div class=\"modal-header\">\n <h3>${escapeHtml(title)}</h3>\n <button class=\"modal-close-btn\" type=\"button\">✕</button>\n </div>\n <div class=\"kb-modal-body\">${bodyHtml}</div>\n <div class=\"modal-actions\">\n <button class=\"btn btn-cancel\" type=\"button\" data-modal-cancel>${escapeHtml(cancelText || t('common.cancel'))}</button>\n ${confirmText ? `<button class=\"btn btn-primary\" type=\"button\" data-modal-confirm>${escapeHtml(confirmText)}</button>` : ''}\n </div>\n </div>`;\n document.body.appendChild(overlay);\n\n const close = () => overlay.remove();\n overlay.querySelector('.modal-close-btn').addEventListener('click', close);\n overlay.querySelector('[data-modal-cancel]').addEventListener('click', close);\n overlay.querySelector('[data-modal-confirm]')?.addEventListener('click', (e) => e.stopPropagation());\n if (closeOnOverlay) {\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) close();\n });\n }\n\n const confirmBtn = overlay.querySelector('[data-modal-confirm]');\n if (confirmBtn && typeof onConfirm === 'function') {\n let busy = false;\n confirmBtn.addEventListener('click', async () => {\n if (busy) return;\n try {\n await onConfirm({\n overlay,\n close,\n busy: () => busy,\n setBusy: (isBusy, text) => {\n busy = isBusy;\n confirmBtn.disabled = isBusy;\n if (typeof text === 'string') confirmBtn.textContent = text;\n },\n });\n } catch (err) {\n // onConfirm 内部自行处理错误提示;此处兜底避免未捕获拒绝\n logger.warn('[Knowledge] Modal confirm error:', err.message);\n }\n });\n }\n\n return { overlay, close };\n}\n\n// ==================== 工具函数 ====================\n\nfunction formatTime(iso) {\n if (!iso) return '';\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return String(iso);\n const pad = (n) => String(n).padStart(2, '0');\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;\n}\n","// src/options/memory-entries.js - 选项页「记忆」Tab 的记忆条目查看与管理区\n// 真源是 agent 的 global-memory.json;逻辑与数据同在 agent 进程,本模块只做「读列表 + 转发增删改」。\n// 数据源用 manage(review) 而不是 recall:review 是只读枚举,返回全量 scored + 低价值 candidates + stats;\n// recall 会写入 accessCount/lastAccessAt 并做会话级去重,拿来做管理列表会污染召回统计,绝不能替换。\n// 编辑/删除走 store(agent 端已封装向量懒补 / dropVectors,UI 无需操心向量一致性);低价值淘汰走 manage(compact)。\n// 与向量配置面板(memory-panel.js)同走 agentApi 直连,不新增 SW 消息、不动扩展端转发层。\n\nimport { agentApi, escapeHtml, showToast, showCustomConfirm, getAgentConnection } from './toolbox-shared.js';\nimport { t, subscribe } from '../shared/i18n.js';\nimport logger from '../shared/logger.js';\n\nlet initialized = false;\nlet allEntries = []; // review 返回的 scored 全量(结构化字段,列表与筛选都基于它)\nlet candidates = []; // 低价值淘汰候选(value < 10)\nlet entriesBusy = false; // 写操作在途锁定:防连点发出第二次 store/manage\n// 状态行只存语义(键+参数),渲染时现取 t():语言热切换时纯 JS 渲染的动态文案\n// 不会自己重画(探针实测:静态标签与 toast 已是英文、列表徽标仍中文),订阅变更后整区重渲染\nlet lastStatusState = null; // { key, params } | { rawText }\n\nconst $ = (id) => document.getElementById(id);\n\nfunction currentFilter() {\n return {\n keyword: ($('memoryEntriesSearch')?.value || '').trim().toLowerCase(),\n type: $('memoryEntriesTypeFilter')?.value || 'all',\n };\n}\n\n/** 排序规则直接写在筛选行的下拉选项文案里;默认创建时间倒序 */\nfunction currentSort() {\n return $('memoryEntriesSort')?.value || 'createdDesc';\n}\n\nconst byCreatedDesc = (a, b) => String(b.createdAt || '').localeCompare(String(a.createdAt || ''));\nconst num = (v) => (Number.isFinite(parseFloat(v)) ? parseFloat(v) : 0);\n\n// 同分一律回落到创建时间倒序:否则相等元素的相对次序依赖引擎实现,用户看到的是「随机抖动」\nfunction sortEntries(items) {\n const sorted = [...items];\n switch (currentSort()) {\n case 'importanceDesc':\n return sorted.sort((a, b) => num(b.importance) - num(a.importance) || byCreatedDesc(a, b));\n case 'valueDesc':\n return sorted.sort((a, b) => num(b.value) - num(a.value) || byCreatedDesc(a, b));\n case 'valueAsc':\n return sorted.sort((a, b) => num(a.value) - num(b.value) || byCreatedDesc(a, b));\n default:\n return sorted.sort(byCreatedDesc);\n }\n}\n\nfunction matchesFilter(m, f) {\n if (f.type !== 'all' && m.type !== f.type) return false;\n if (!f.keyword) return true;\n const hay = [m.content, m.title, m.category, ...(m.tags || [])]\n .filter(Boolean).join(' ').toLowerCase();\n return hay.includes(f.keyword);\n}\n\nfunction typeLabel(type) {\n return type === 'summary' ? t('memoryEntries.filterSummary') : t('memoryEntries.filterFact');\n}\n\n/** 逗号(中英文)/顿号分隔 → 去空数组;空串得到 [],与 agent 端「tags 传了就整覆」语义对齐 */\nfunction parseTags(raw) {\n return String(raw || '').split(/[,,、]/).map((s) => s.trim()).filter(Boolean);\n}\n\nfunction fmtDate(iso) {\n return iso ? String(iso).slice(0, 10) : '';\n}\n\n// ==================== 渲染 ====================\n\nfunction renderStatus(state) {\n lastStatusState = state;\n const el = $('memoryEntriesStatus');\n if (!el) return;\n if (state) {\n el.style.display = '';\n el.innerHTML = escapeHtml(state.rawText != null ? state.rawText : t(state.key, state.params));\n } else {\n el.style.display = 'none';\n el.innerHTML = '';\n }\n}\n\nfunction entryItemHtml(m) {\n const id = escapeHtml(m.id);\n const tags = (m.tags || [])\n .map((x) => `<span class=\"memory-entry-tag-chip\">${escapeHtml(x)}</span>`)\n .join('');\n const lastAccess = m.lastAccessAt\n ? fmtDate(m.lastAccessAt)\n : t('memoryEntries.neverAccessed');\n return `\n <div class=\"memory-entry-item${candidates.some((c) => c.id === m.id) ? ' low-value' : ''}\" data-id=\"${id}\">\n <div class=\"memory-entry-top\">\n <span class=\"memory-entry-badge ${m.type === 'summary' ? 'summary' : 'fact'}\">${escapeHtml(typeLabel(m.type))}</span>\n <div class=\"memory-entry-content\">${escapeHtml(m.content || '')}</div>\n <div class=\"memory-entry-actions\">\n <button type=\"button\" data-act=\"edit\">${escapeHtml(t('memoryEntries.editBtn'))}</button>\n <button type=\"button\" data-act=\"delete\" class=\"memory-entry-del\">${escapeHtml(t('memoryEntries.deleteBtn'))}</button>\n </div>\n </div>\n <div class=\"memory-entry-meta\">\n <span>${escapeHtml(t('memoryEntries.importanceShort'))} ${Number(m.importance) || 0}</span>\n <span>${escapeHtml(t('memoryEntries.valueShort'))} ${escapeHtml(m.value)}</span>\n <span>${escapeHtml(t('memoryEntries.createdLabel'))} ${escapeHtml(fmtDate(m.createdAt) || '—')}</span>\n <span>${escapeHtml(t('memoryEntries.lastAccessLabel'))} ${escapeHtml(lastAccess)} · ${escapeHtml(t('memoryEntries.accessCount', { count: m.accessCount || 0 }))}</span>\n ${tags ? `<span>${escapeHtml(t('memoryEntries.tagsLabel'))}: ${tags}</span>` : ''}\n </div>\n </div>`;\n}\n\nfunction renderList() {\n const listEl = $('memoryEntriesList');\n if (!listEl) return;\n const f = currentFilter();\n const shown = sortEntries(allEntries.filter((m) => matchesFilter(m, f)));\n\n const countEl = $('memoryEntriesCount');\n if (countEl) countEl.textContent = t('memoryEntries.count', { shown: shown.length, total: allEntries.length });\n\n if (!shown.length) {\n listEl.innerHTML = `<div class=\"memory-entries-empty\">${escapeHtml(\n allEntries.length ? t('memoryEntries.noMatch') : t('memoryEntries.empty'))}</div>`;\n return;\n }\n listEl.innerHTML = shown.map(entryItemHtml).join('');\n}\n\nfunction renderCandidates() {\n const box = $('memoryEntriesCandidates');\n if (!box) return;\n if (!candidates.length) {\n box.style.display = 'none';\n box.innerHTML = '';\n return;\n }\n box.style.display = '';\n box.innerHTML = `\n <span class=\"memory-entries-candidates-text\">${escapeHtml(t('memoryEntries.candidatesHint', { count: candidates.length }))}</span>\n <button type=\"button\" class=\"memory-entries-compact-btn\">${escapeHtml(t('memoryEntries.compactBtn'))}</button>`;\n}\n\n// ==================== 数据加载 ====================\n\nasync function loadEntries() {\n const listEl = $('memoryEntriesList');\n if (!listEl) return;\n renderStatus({ key: 'memoryEntries.loading' });\n listEl.innerHTML = '';\n try {\n const res = await agentApi('POST', '/api/memory/manage', { action: 'review' });\n // 404 = 旧代理没有 manage 端点:必须说「版本不支持」而不是转圈或泛化失败(同状态卡事故教训)\n if (res && res.httpStatus === 404) {\n renderStatus({ key: 'memoryEntries.unsupported' });\n return;\n }\n if (!res || res.success !== true) {\n renderStatus({ key: 'memoryEntries.loadFail', params: { error: (res && res.error) || 'unknown' } });\n return;\n }\n allEntries = Array.isArray(res.scored) ? res.scored : [];\n candidates = Array.isArray(res.candidates) ? res.candidates : [];\n renderStatus('');\n renderCandidates();\n renderList();\n } catch (err) {\n logger.warn('[MemoryEntries] load failed:', err.message);\n renderStatus({ key: 'memoryEntries.loadFail', params: { error: err.message } });\n }\n}\n\n/** 记忆 Tab 刷新入口:代理未连接时不发请求、如实展示离线态(连接态由 memory-panel 门控整块收起) */\nexport async function refreshMemoryEntries() {\n if (!$('memoryEntriesList')) return;\n const conn = await getAgentConnection();\n if (!conn || !conn.connected) {\n allEntries = [];\n candidates = [];\n renderCandidates();\n $('memoryEntriesList').innerHTML = '';\n renderStatus({ key: 'memoryEntries.offline' });\n return;\n }\n await loadEntries();\n}\n\n// ==================== 写操作 ====================\n\nfunction setWriteBusy(on) {\n entriesBusy = on;\n ['memoryEntriesRefreshBtn'].forEach((id) => { const el = $(id); if (el) el.disabled = on; });\n const compact = document.querySelector('.memory-entries-compact-btn');\n if (compact) compact.disabled = on;\n document.querySelectorAll('.memory-entry-actions button').forEach((b) => { b.disabled = on; });\n}\n\n/** store 提交统一出口:成功提示 + 重拉列表;失败按调用方给的文案键报错,列表保持原样不猜测 */\nasync function submitStore(body, okKey, failKey) {\n if (entriesBusy) return false;\n setWriteBusy(true);\n try {\n const res = await agentApi('POST', '/api/memory/store', body);\n if (!res || res.success !== true) throw new Error((res && res.error) || 'unknown');\n showToast(t(okKey), 'info');\n await loadEntries();\n return true;\n } catch (err) {\n logger.warn('[MemoryEntries] store failed:', err.message);\n showToast(t(failKey, { error: err.message }), 'error');\n return false;\n } finally {\n setWriteBusy(false);\n }\n}\n\nasync function onDelete(m) {\n const ok = await showCustomConfirm(t('memoryEntries.deleteConfirm'));\n if (!ok) return;\n // delete 只需 memoryId(agent 在 facts/summaries 双数组内自行定位,不依赖 type)\n await submitStore({ action: 'delete', memoryId: m.id }, 'memoryEntries.deleted', 'memoryEntries.deleteFail');\n}\n\nasync function onCompact() {\n const ok = await showCustomConfirm(t('memoryEntries.compactConfirm', { count: candidates.length, threshold: 10 }));\n if (!ok) return;\n if (entriesBusy) return;\n setWriteBusy(true);\n try {\n const res = await agentApi('POST', '/api/memory/manage', { action: 'compact' });\n if (!res || res.success !== true) throw new Error((res && res.error) || 'unknown');\n showToast(t('memoryEntries.compactDone', {\n facts: res.removedFacts ?? 0, summaries: res.removedSummaries ?? 0,\n }), 'info');\n await loadEntries();\n } catch (err) {\n logger.warn('[MemoryEntries] compact failed:', err.message);\n showToast(t('memoryEntries.compactFail', { error: err.message }), 'error');\n } finally {\n setWriteBusy(false);\n }\n}\n\n// ==================== 编辑弹窗 ====================\n\nfunction openEditModal(m) {\n const existing = document.getElementById('memoryEntryEditModal');\n if (existing) existing.remove();\n\n const overlay = document.createElement('div');\n overlay.className = 'modal-overlay';\n overlay.id = 'memoryEntryEditModal';\n overlay.innerHTML = `\n <div class=\"modal-content\" style=\"max-width: 560px;\">\n <div class=\"modal-header\">\n <h3>${escapeHtml(t('memoryEntries.editTitle'))}</h3>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label>${escapeHtml(t('memoryEntries.typeLabel'))}</label>\n <input type=\"text\" value=\"${escapeHtml(typeLabel(m.type))}${m.category ? ` · ${escapeHtml(m.category)}` : ''}\" readonly style=\"background:#f5f5f5;color:#666;\">\n </div>\n <div class=\"form-group\">\n <label>${escapeHtml(t('memoryEntries.contentLabel'))}</label>\n <textarea id=\"memoryEntryEditContent\" rows=\"5\">${escapeHtml(m.content || '')}</textarea>\n </div>\n <div class=\"form-group\">\n <label>${escapeHtml(t('memoryEntries.importanceLabel'))}</label>\n <input type=\"number\" id=\"memoryEntryEditImportance\" min=\"1\" max=\"10\" value=\"${Number(m.importance) || 5}\">\n </div>\n <div class=\"form-group\">\n <label>${escapeHtml(t('memoryEntries.tagsLabel'))}</label>\n <input type=\"text\" id=\"memoryEntryEditTags\" value=\"${escapeHtml((m.tags || []).join(', '))}\" placeholder=\"${escapeHtml(t('memoryEntries.tagsPlaceholder'))}\">\n </div>\n </div>\n <div class=\"modal-actions\">\n <button class=\"btn btn-cancel\" id=\"memoryEntryEditCancel\">${escapeHtml(t('memoryEntries.cancelBtn'))}</button>\n <button class=\"btn btn-primary\" id=\"memoryEntryEditSave\" style=\"width:auto;\">${escapeHtml(t('memoryEntries.saveBtn'))}</button>\n </div>\n </div>`;\n document.body.appendChild(overlay);\n\n const close = () => overlay.remove();\n overlay.querySelector('#memoryEntryEditCancel').addEventListener('click', close);\n overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });\n\n overlay.querySelector('#memoryEntryEditSave').addEventListener('click', async () => {\n const content = overlay.querySelector('#memoryEntryEditContent').value.trim();\n if (!content) {\n showToast(t('memoryEntries.contentEmpty'), 'error');\n return;\n }\n const body = {\n action: 'update',\n memoryId: m.id,\n type: m.type,\n content,\n tags: parseTags(overlay.querySelector('#memoryEntryEditTags').value),\n };\n const imp = Number(overlay.querySelector('#memoryEntryEditImportance').value);\n if (Number.isFinite(imp)) body.importance = imp;\n const saved = await submitStore(body, 'memoryEntries.saved', 'memoryEntries.saveFail');\n if (saved) close();\n });\n}\n\n// ==================== 入口 ====================\n\n/** 语言热切换时的整区重渲染出口:候选条 + 列表 + 状态行都是 JS 拼的,不重画会停在旧语言 */\nexport function renderMemoryEntries() {\n renderCandidates();\n renderList();\n if (lastStatusState) renderStatus(lastStatusState);\n}\n\nexport function initMemoryEntries() {\n if (initialized) return;\n initialized = true;\n\n $('memoryEntriesRefreshBtn')?.addEventListener('click', () => refreshMemoryEntries());\n $('memoryEntriesSearch')?.addEventListener('input', () => renderList());\n $('memoryEntriesTypeFilter')?.addEventListener('change', () => renderList());\n $('memoryEntriesSort')?.addEventListener('change', () => renderList());\n\n // 列表行按钮走事件委托:列表每次重建 DOM,直接绑会丢;条目数据按 data-id 回查全量\n $('memoryEntriesList')?.addEventListener('click', (e) => {\n const item = e.target.closest('.memory-entry-item');\n if (!item) return;\n const m = allEntries.find((x) => String(x.id) === item.dataset.id);\n if (!m) return;\n const actBtn = e.target.closest('[data-act]');\n if (actBtn) {\n if (actBtn.dataset.act === 'edit') openEditModal(m);\n else if (actBtn.dataset.act === 'delete') onDelete(m);\n return;\n }\n // 点内容区展开/收起长文案(默认两行截断)\n if (e.target.closest('.memory-entry-content')) item.classList.toggle('expanded');\n });\n\n $('memoryEntriesCandidates')?.addEventListener('click', (e) => {\n if (e.target.closest('.memory-entries-compact-btn')) onCompact();\n });\n\n // 语言热切换:本区文案全是 JS 拼的,applyI18n 只刷静态标签,不订阅就会停在旧语言\n // (探针实测缺口:静态标题与 toast 已是英文,列表徽标/候选条仍中文)\n subscribe(() => renderMemoryEntries());\n}\n","// src/options/memory-panel.js - 选项页「记忆」面板:语义召回向量的配置、状态与重建\n// 数据源:Agent /api/memory/* 端点。配置真值只在 agent 的 config.json,本页读写都经 HTTP,\n// 明文 apiKey 永不回显(agent 已脱敏),所以表单的 key 框只有「填了才提交」一种语义。\n// 与知识库向量卡同构但不共用代码:记忆配置是 agent 全局单份,不是每库一份,字段与生效路径都不同。\n\nimport { agentApi, escapeHtml, showToast, showCustomConfirm, getAgentConnection } from './toolbox-shared.js';\nimport { loadRagStatus } from './toolbox-rag.js';\nimport { initMemoryEntries, refreshMemoryEntries } from './memory-entries.js';\nimport { t } from '../shared/i18n.js';\nimport logger from '../shared/logger.js';\n\nlet initialized = false;\nlet lastStatus = { connected: true, ragAvailable: true }; // mode 切换要重算门控,而连接态只有 refresh 时才知道\nlet gateBlocked = false; // 门控(代理未连接等)导致的锁定\nlet toggleBusy = false; // 开关写入在途的锁定\nlet configLoadFailed = false; // 配置 GET 失败时的保存守卫:WYSIWYG 下空表单保存=把代理真配置覆盖成空\n\nconst $ = (id) => document.getElementById(id);\n\nfunction currentMode() {\n return document.querySelector('input[name=\"memoryEmbeddingMode\"]:checked')?.value || 'openai-compat';\n}\nconst isLocal = () => currentMode() === 'local';\n\n/** 本地档没有远端端点概念:模型固定内置、地址/key/维度/前缀/探活对它无意义,隐藏且不提交 */\nfunction applyModeVisibility() {\n const hide = isLocal() ? 'none' : '';\n ['memoryRemoteGroup', 'memoryModelGroup', 'memoryApiKeyGroup', 'memoryDimsGroup', 'memoryPrefixGroup', 'memoryTestRow']\n .forEach((id) => { const el = $(id); if (el) el.style.display = hide; });\n const notice = $('memoryLocalNotice');\n if (notice) notice.style.display = isLocal() ? '' : 'none';\n}\n\n/**\n * 组装提交体。远端档「所见即所存」:地址/模型/前缀/维度始终提交(含空值/0),\n * apiKey 是唯一例外——明文永不回显,留空只能解释为「不修改」,清除走专用按钮。\n * 为什么改掉原来的「只收非空」:清空 queryPrefix 后字段缺席 = agent 端「不传即不改」\n * =清空保存静默无效;清空维度=0 会改指纹、状态卡冒报陈旧——这是配置真变了,\n * 报得对,不该由提交体替用户捂掉。\n * 本地档是反面:「缺席」= 保存层沿用旧值 = 远端残留渗进本地档,\n * 所以会参与本地行为的字段必须显式清空(逐字段理由见 isLocal 分支)。\n */\nfunction collectPayload() {\n const payload = {\n enabled: $('memoryEnabled')?.checked === true,\n mode: currentMode()\n };\n if (isLocal()) {\n // modelName 残留会劫持模型(local.js `modelName || 内置默认`,非空残留被拿去 transformers 下载);\n // queryPrefix 残留会顶替 BGE 查询前缀(provider `resolved.queryPrefix ?? 本地默认` 只认 nullish,\n // 非空残留原样生效,漏前缀对中文召回是实质质量损失);\n // dimensions 不参与本地向量,但会拼进 memoryModelKey 指纹——残留值随远端配置历史漂移,\n // 同一份本地配置会莫名冒报 stale(本地档重建实测 23 分钟,白跑不能接受)。\n // baseUrl/apiKey 不在此列:本地档根本不建远端后端,对它们完全惰性,省略留给切回远端用。\n payload.modelName = '';\n payload.queryPrefix = '';\n payload.dimensions = 0;\n return payload;\n }\n payload.modelName = ($('memoryModelInput')?.value || '').trim();\n payload.baseUrl = ($('memoryBaseUrlInput')?.value || '').trim();\n payload.queryPrefix = ($('memoryPrefixInput')?.value || '').trim();\n const dims = Number($('memoryDimsInput')?.value);\n payload.dimensions = Number.isFinite(dims) && dims > 0 ? Math.floor(dims) : 0;\n const apiKey = ($('memoryApiKeyInput')?.value || '').trim();\n if (apiKey) payload.apiKey = apiKey;\n return payload;\n}\n\n/** 开关文案的唯一出口:只认真值 checkbox,别处不要直接写这个 span */\nfunction syncEnabledLabel() {\n const el = $('memoryEnabledLabel');\n if (el) el.textContent = $('memoryEnabled')?.checked === true ? t('memoryPanel.enabled') : t('memoryPanel.disabled');\n}\n\n/**\n * 开关的两把锁合并下发:门控不通过(根本发不出请求)或写入在途(防连点发两轮)都要禁用。\n * 原来 renderGate 只锁三个按钮,开关照样能点 —— 点了又落不了盘,正是用户报「控制不了」的观感来源。\n */\nfunction applyToggleLock() {\n const box = $('memoryEnabled');\n if (box) box.disabled = gateBlocked || toggleBusy;\n}\n\nfunction fillForm(cfg) {\n $('memoryEnabled').checked = cfg.enabled === true;\n syncEnabledLabel();\n const radio = document.querySelector(`input[name=\"memoryEmbeddingMode\"][value=\"${cfg.mode === 'local' ? 'local' : 'openai-compat'}\"]`);\n if (radio) radio.checked = true;\n $('memoryBaseUrlInput').value = cfg.baseUrl || '';\n $('memoryModelInput').value = cfg.modelName || '';\n $('memoryApiKeyInput').value = ''; // 明文 key 不回显,占位文案已说明「留空表示不修改」\n $('memoryApiKeyClearBtn').style.display = cfg.hasApiKey ? '' : 'none'; // 没有已存 key 就没有可清除的东西\n $('memoryDimsInput').value = cfg.dimensions > 0 ? String(cfg.dimensions) : '';\n $('memoryPrefixInput').value = cfg.queryPrefix || '';\n applyModeVisibility();\n}\n\nfunction renderStatus(s) {\n const box = $('memoryStatus');\n if (!box) return;\n if (!s) {\n box.innerHTML = `<span class=\"memory-status-muted\">${escapeHtml(t('memoryPanel.statusLoading'))}</span>`;\n return;\n }\n box.innerHTML = `\n <div class=\"memory-status-row\">\n <span>${escapeHtml(t('memoryPanel.statusIndexed', { indexed: s.indexed, total: s.total }))}</span>\n <span class=\"memory-status-badge ${s.stale ? 'stale' : 'fresh'}\">${escapeHtml(s.stale ? t('memoryPanel.statusStale') : t('memoryPanel.statusFresh'))}</span>\n </div>\n <div class=\"memory-status-keys\">\n <span>${escapeHtml(t('memoryPanel.statusStored'))}: <code>${escapeHtml(s.storedModelKey || '—')}</code></span>\n <span>${escapeHtml(t('memoryPanel.statusCurrent'))}: <code>${escapeHtml(s.currentModelKey)}</code></span>\n <span>${escapeHtml(t('memoryPanel.statusTier'))}: <code>${escapeHtml(s.tier)}</code></span>\n </div>\n ${s.stale ? `<div class=\"memory-status-hint\">${escapeHtml(t('memoryPanel.staleHint'))}</div>` : ''}`;\n}\n\n// 状态卡失败态。本面板上线后的第一个真实事故:agent 进程载的还是旧代码时,\n// vector-status 返回 404,而失败当时被当成「还没读到」交给 renderStatus(null),\n// 于是页面永远停在「读取中…」——失败必须说失败,还要把能做的事告诉用户。\n// 404 单独给「重启代理」提示:新端点不存在的最常见原因就是进程未重启。\nfunction renderStatusFail(err) {\n const box = $('memoryStatus');\n if (!box) return;\n const label = err && err.httpStatus === 404\n ? t('memoryPanel.statusOldAgent')\n : t('memoryPanel.statusFail');\n const detail = (err && (err.error || err.message)) || 'unknown';\n box.innerHTML = `\n <div class=\"memory-status-row\">\n <span class=\"memory-status-badge fail\">${escapeHtml(label)}</span>\n </div>\n <div class=\"memory-status-keys\"><span>${escapeHtml(t('memoryPanel.statusDetail'))}: <code>${escapeHtml(detail)}</code></span></div>`;\n}\n\n// 代理未连接时不谎称「读取中」:这个态不是一次失败的请求,而是根本没法发请求\nfunction renderStatusOffline() {\n const box = $('memoryStatus');\n if (!box) return;\n box.innerHTML = `<span class=\"memory-status-muted\">${escapeHtml(t('memoryPanel.statusOffline'))}</span>`;\n}\n\n// 写操作进行中锁住三个按钮:重建是长任务(本地档 41 条实测 23 分钟),\n// 而 agentApi 是裸 fetch 无超时,中途再点一次会并发两个全量重建。\nfunction setBusy(on) {\n $('memoryReindexBtn').disabled = on;\n $('memoryTestBtn').disabled = on || isLocal();\n $('memoryApiKeyClearBtn').disabled = on; // 清除也是写操作,防连点\n}\n\n// ==================== 动作 ====================\n\nasync function onTest() {\n if (isLocal()) return;\n setBusy(true);\n const result = $('memoryTestResult');\n result.textContent = t('memoryPanel.testRunning');\n try {\n const res = await agentApi('POST', '/api/memory/test-embedding', collectPayload());\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown');\n result.textContent = t('memoryPanel.testOk', { model: collectPayload().modelName, dims: res.dimensions });\n const dimsInput = $('memoryDimsInput');\n dimsInput.value = String(res.dimensions); // 厂商实际维度优先于用户猜测\n showToast(t('memoryPanel.testOk', { model: collectPayload().modelName, dims: res.dimensions }), 'info');\n } catch (err) {\n result.textContent = t('memoryPanel.testFail', { error: err.message });\n showToast(t('memoryPanel.testFail', { error: err.message }), 'error');\n } finally {\n setBusy(false);\n }\n}\n\n// onSave 只负责写代理并返回结果,不再自己弹 toast:\n// 底部那颗是全局「保存配置」,记忆写入成功时由它那条统一代表,避免连弹两条。\n// reason 供上层决定文案:notLoaded=配置没读到(禁止把代理真配置覆盖成空);error=接口失败。\nasync function onSave() {\n if (configLoadFailed) return { ok: false, reason: 'notLoaded' };\n setBusy(true);\n try {\n const res = await agentApi('POST', '/api/memory/embedding-config', collectPayload());\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown');\n $('memoryApiKeyInput').value = '';\n await loadAll();\n return { ok: true };\n } catch (err) {\n return { ok: false, reason: 'error', error: err.message };\n } finally {\n setBusy(false);\n }\n}\n\n// 底部全局「保存配置」在记忆 Tab 上的代理保存入口(原独立的「保存到代理」按钮已并入)。\n// toast 收敛(方案B):代理已连接且写入成功 → 不弹自己那条,交给底部「保存成功」统一代表;\n// 只有离线跳过 / 配置未加载 / 接口失败才补一条,让异常不被子按钮的成功提示淹没。\nexport async function saveMemoryFromGlobalSave() {\n const conn = await getAgentConnection();\n if (!conn || !conn.connected) {\n showToast(t('memoryPanel.saveSkippedOffline'), 'info');\n return;\n }\n const result = await onSave();\n if (result.ok) return;\n if (result.reason === 'notLoaded') {\n showToast(t('memoryPanel.configNotLoaded'), 'error');\n } else {\n showToast(t('memoryPanel.saveFail', { error: result.error }), 'error');\n }\n}\n\nasync function onReindex() {\n const ok = await showCustomConfirm(t('memoryPanel.reindexConfirm'));\n if (!ok) return;\n setBusy(true);\n const out = $('memoryResult');\n out.style.display = '';\n out.textContent = t('memoryPanel.reindexRunning');\n try {\n const res = await agentApi('POST', '/api/memory/reindex', {});\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown');\n if (res.skipped === true) {\n out.textContent = t('memoryPanel.reindexSkipped');\n } else {\n // 三个计数照实展示:批失败与超窗都不会让重建\"看起来失败\",藏起来等于骗人\n let text = t('memoryPanel.reindexDone', { indexed: res.indexed, total: res.total });\n if (res.failedBatches > 0) text += t('memoryPanel.reindexBatchFail', { count: res.failedBatches });\n if (res.overWindow && res.overWindow.count > 0) {\n text += t('memoryPanel.reindexOverWindow', { count: res.overWindow.count, window: res.overWindow.window });\n }\n out.textContent = text;\n showToast(t('memoryPanel.reindexDone', { indexed: res.indexed, total: res.total }), 'info');\n }\n await loadAll(); // 重建后指纹必然对齐,状态卡要跟着收敛\n } catch (err) {\n out.textContent = t('memoryPanel.reindexFail', { error: err.message });\n showToast(t('memoryPanel.reindexFail', { error: err.message }), 'error');\n } finally {\n setBusy(false);\n }\n}\n\n/**\n * 清除已存 API Key:明文永不回显 + 留空=不修改 ⇒ 这是用户唯一的清除通道。\n * 立即落盘(只发 clearApiKey 一个字段,agent 端「不传的字段不改」),成功后本地隐藏\n * 按钮——绝不 loadAll:会把用户未保存的草稿盖掉(与总开关同一惯例)。\n */\nasync function onClearApiKey() {\n const ok = await showCustomConfirm(t('memoryPanel.apiKeyClearConfirm'));\n if (!ok) return;\n setBusy(true);\n try {\n const res = await agentApi('POST', '/api/memory/embedding-config', { clearApiKey: true });\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown');\n $('memoryApiKeyClearBtn').style.display = 'none';\n showToast(t('memoryPanel.apiKeyCleared'), 'info');\n } catch (err) {\n showToast(t('memoryPanel.apiKeyClearFail', { error: err.message }), 'error');\n } finally {\n setBusy(false);\n }\n}\n\n// ==================== 加载 ====================\n\n/** 只重拉状态卡:开关切换后档位会从 remote/local 变 none,状态必须收敛,但不能顺带回填表单 */\nasync function loadStatus() {\n try {\n const statusRes = await agentApi('GET', '/api/memory/vector-status');\n if (statusRes && statusRes.success === true) {\n renderStatus(statusRes);\n } else {\n renderStatusFail(statusRes);\n }\n } catch (err) {\n logger.warn('[MemoryPanel] status load failed:', err.message);\n renderStatusFail(err);\n }\n}\n\nasync function loadAll() {\n // 两条请求并发(进面板不等两个往返串行),但各自独立降级:\n // 旧代理可能只缺其中一个端点,捆在同一个 try 里会让能拿到的那份也被拖成失败\n const cfgTask = (async () => {\n configLoadFailed = true; // 先上锁:GET 成功为止,保存一律拒绝(WYSIWYG:空表单=覆盖成空)\n try {\n const cfgRes = await agentApi('GET', '/api/memory/embedding-config');\n if (cfgRes && cfgRes.success === true && cfgRes.config) {\n fillForm(cfgRes.config);\n configLoadFailed = false;\n }\n } catch (err) {\n logger.warn('[MemoryPanel] config load failed:', err.message);\n showToast(t('memoryPanel.statusFail') + ' — ' + err.message, 'error');\n }\n })();\n\n const statusTask = loadStatus();\n\n await Promise.all([cfgTask, statusTask]);\n}\n\nfunction renderGate(status) {\n const gate = $('memoryGate');\n const main = $('memoryMain');\n const hint = document.querySelector('.memory-scope-hint');\n const blocked = !status.connected || (currentMode() === 'local' && !status.ragAvailable);\n gateBlocked = blocked;\n applyToggleLock();\n ['memoryReindexBtn', 'memoryTestBtn'].forEach((id) => { const el = $(id); if (el) el.disabled = blocked; });\n\n if (!blocked) {\n // 就绪:收起门控,正常展示配置与说明行\n gate.style.display = 'none';\n gate.innerHTML = '';\n main.style.display = '';\n if (hint) hint.style.display = '';\n return;\n }\n\n // 未就绪:整块隐藏配置(对齐知识库,不再半透明叠放),同时隐藏说明行;\n // 门控卡片用知识库同款 .toolbox-empty(图标 + 标题 + 描述 + 跳转按钮),保证视觉统一。\n main.style.display = 'none';\n if (hint) hint.style.display = 'none';\n gate.style.display = '';\n const agentOff = !status.connected;\n const icon = agentOff ? '🔌' : '📚';\n const title = agentOff ? t('memoryPanel.gateAgentOffTitle') : t('memoryPanel.gateRagTitle');\n const desc = agentOff ? t('memoryPanel.gateAgentOffBody') : t('memoryPanel.gateRagMissing');\n const btnLabel = agentOff ? t('memoryPanel.goToAgentTab') : t('memoryPanel.goToToolboxTab');\n const gotoTab = agentOff ? 'agent' : 'toolbox';\n gate.innerHTML = `\n <div class=\"toolbox-empty\" style=\"text-align:left; padding:24px 20px;\">\n <div style=\"display:flex; align-items:center; gap:10px; font-size:15px; font-weight:600; color:#555;\">\n <span style=\"font-size:22px;\">${icon}</span><span>${escapeHtml(title)}</span>\n </div>\n <div style=\"margin-top:8px; line-height:1.7; color:#888; font-size:13px;\">${escapeHtml(desc)}</div>\n <button class=\"toolbox-add-btn\" data-goto-tab=\"${gotoTab}\" style=\"width:auto; margin-top:14px; padding:8px 18px; border-style:solid; color:#667eea; border-color:#667eea;\">${escapeHtml(btnLabel)}</button>\n </div>`;\n}\n\n/**\n * 总开关切换即持久化(与仓库其它顶层开关同惯例)。\n * 只发 { enabled }:agent 端语义是「不传的字段不改」,实测这种最小体不会抹掉地址/模型/维度,\n * 也不会改动 modelKey 指纹。成功后只重拉状态卡,绝不 loadAll —— 那会把用户尚未保存的草稿盖掉。\n * 失败必须回滚勾选框:界面显示「已停用」而代理里还开着,比原来那个「刷新后自己弹回去」更坏。\n */\nasync function onToggleEnabled() {\n const box = $('memoryEnabled');\n const target = box.checked === true;\n toggleBusy = true;\n applyToggleLock();\n try {\n const res = await agentApi('POST', '/api/memory/embedding-config', { enabled: target });\n if (!res || res.success !== true) throw new Error(res?.error || 'unknown');\n showToast(target ? t('memoryPanel.toggleOnSaved') : t('memoryPanel.toggleOffSaved'), 'info');\n await loadStatus();\n } catch (err) {\n logger.warn('[MemoryPanel] toggle persist failed:', err.message);\n box.checked = !target;\n showToast(t('memoryPanel.toggleFail', { error: err.message }), 'error');\n } finally {\n toggleBusy = false;\n applyToggleLock();\n syncEnabledLabel();\n }\n}\n\n// ==================== 入口 ====================\n\nexport function initMemoryPanel() {\n if (initialized) return;\n initialized = true;\n\n initMemoryEntries();\n $('memoryTestBtn')?.addEventListener('click', onTest);\n $('memoryReindexBtn')?.addEventListener('click', onReindex);\n $('memoryApiKeyClearBtn')?.addEventListener('click', onClearApiKey);\n // 门控卡片的「前往…标签页」按钮:事件委托(卡片随门控重建,不能直接绑),\n // 点对应 tab 导航按钮即可,复用 index.js 的 switchTab 链路(与知识库同款)\n $('memoryGate')?.addEventListener('click', (e) => {\n const gotoBtn = e.target.closest('[data-goto-tab]');\n if (!gotoBtn) return;\n document.querySelector(`.tab-nav-btn[data-tab=\"${gotoBtn.dataset.gotoTab}\"]`)?.click();\n });\n document.querySelectorAll('input[name=\"memoryEmbeddingMode\"]').forEach((r) => {\n r.addEventListener('change', () => { applyModeVisibility(); renderGate(lastStatus); });\n });\n $('memoryEnabled')?.addEventListener('change', () => {\n syncEnabledLabel(); // 先跟着用户动作走,失败时 onToggleEnabled 会回滚\n onToggleEnabled();\n });\n}\n\nexport async function refreshMemoryPanel() {\n const status = await loadRagStatus();\n lastStatus = status;\n renderGate(status);\n // 条目区自带连接态判断(离线时如实展示),两条路径都要带刷:\n // 它是本 Tab 的第二个数据入口,不能只刷配置不刷列表\n if (!status.connected) { renderStatusOffline(); await refreshMemoryEntries(); return; }\n await loadAll();\n await refreshMemoryEntries();\n}\n","// options/save-bar.js - 保存按钮栏的吸附行为\n//\n// 配置项较多时保存按钮会被挤出可视区,用户必须滚到页面底部才能保存。\n// 期望:放得下时按钮正常处于文档流;被视口底边截住时才吸附在视口底部保持可见。\n//\n// 吸附本身由 CSS 承担(.save-bar 的 position: sticky; bottom: 0),其语义恰好\n// 就是上面描述的行为;这里只负责在「吸附生效中」加上 .floating 类,用于展示\n// 悬浮层次(阴影),让按钮栏与滚动经过的内容有清晰分界。\n//\n// 判定:吸附中时按钮栏底边恒贴视口底边;回到文档流位置后底边位于视口底边之上。\n\nconst FLOATING_EPSILON = 1; // 亚像素容差\n\n/**\n * 初始化保存按钮栏的吸附状态检测(按需更新 .floating 类)\n */\nexport function initSaveBarSticky() {\n const saveBar = document.getElementById('saveBar');\n if (!saveBar) return;\n\n const updateFloating = () => {\n const rect = saveBar.getBoundingClientRect();\n const floating = rect.bottom >= window.innerHeight - FLOATING_EPSILON;\n saveBar.classList.toggle('floating', floating);\n };\n\n window.addEventListener('scroll', updateFloating, { passive: true });\n window.addEventListener('resize', updateFloating);\n // 切换 Tab、展开折叠块等会改变页面高度且不一定触发 scroll/resize,观察 body 尺寸兜底\n if (typeof ResizeObserver !== 'undefined') {\n new ResizeObserver(updateFloating).observe(document.body);\n }\n updateFloating();\n}\n","// options/install-guide.js - 代理端安装指引(版本匹配 + 一键复制)\n//\n// 版本来源:src/config/version.json(publish.sh 单一版本流写入,= agent npm 包版本 = git tag)。\n// 命令用环境变量传版本——curl|bash / irm|iex 管道无法传命令行参数,故走 AGENT_VERSION 环境变量。\n// 目的:引导用户安装与当前插件版本一致的代理端,避免版本不匹配导致连接失败。\n\nimport versionMeta from '../config/version.json';\nimport logger from '../shared/logger.js';\nimport { subscribe, t } from '../shared/i18n.js';\nimport { showToast } from './config-manager.js';\n\nconst RAW_BASE = 'https://raw.githubusercontent.com/xiweicheng/ai-helper/main';\n\n// 当前插件对应的代理版本;version.json 缺失时回退 latest\nexport function getAgentVersion() {\n return versionMeta && versionMeta.version ? versionMeta.version : 'latest';\n}\n\n// macOS / Linux:curl 管道执行,版本经环境变量传给 bash\nexport function buildUnixInstallCmd(version) {\n return `curl -fsSL ${RAW_BASE}/install.sh | AGENT_VERSION=${version} bash`;\n}\n\n// Windows:管道 iex 执行。版本走 $env:AGENT_VERSION(脚本内 Get-TargetVersion 会读)——\n// 不依赖 param 在 iex 下的绑定;前置 TLS12 兼容旧版 PowerShell 5.1 默认安全协议。\nexport function buildWinInstallCmd(version) {\n return `[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $env:AGENT_VERSION='${version}'; irm ${RAW_BASE}/install.ps1 | iex`;\n}\n\n// 粗略判定当前是否 Windows(用于「一键复制」自动选命令)\n// 只认 platform 的 Win* 前缀与 UA 的 Windows 标记;避免 macOS 的 \"darwin\" 被子串 \"win\" 误命中\nexport function isWindowsPlatform() {\n const platform = navigator.platform || '';\n const ua = navigator.userAgent || '';\n return /^win/i.test(platform) || /\\bwindows\\b/i.test(ua);\n}\n\nasync function copyInstallCmd(text, notifier) {\n if (!text) return;\n try {\n await navigator.clipboard.writeText(text);\n notifier(t('agentConfig.copyCmdSuccess'), 'success');\n } catch (err) {\n logger.warn('[Options] clipboard write failed:', err.message);\n notifier(t('agentConfig.copyCmdFailed'), 'error');\n }\n}\n\n// 渲染带版本号的标题与两条命令(标题含动态版本,不走 data-i18n 静态覆盖)\nexport function renderInstallGuide() {\n const version = getAgentVersion();\n const titleEl = document.getElementById('agentInstallTitle');\n if (titleEl) titleEl.textContent = t('agentConfig.installGuideTitle', { version });\n const unixEl = document.getElementById('installCmdUnix');\n const winEl = document.getElementById('installCmdWin');\n if (unixEl) unixEl.textContent = buildUnixInstallCmd(version);\n if (winEl) winEl.textContent = buildWinInstallCmd(version);\n}\n\n// 初始化安装指引区块。notify 可注入以便测试(默认用 config-manager 的 showToast)。\nexport function initInstallGuide(notify = showToast) {\n const guideEl = document.getElementById('agentInstallGuide');\n if (!guideEl) return;\n renderInstallGuide();\n // 语言切换后重渲染(标题含版本且无 data-i18n,applyI18n 不会覆盖它)\n subscribe(renderInstallGuide);\n\n const unixEl = document.getElementById('installCmdUnix');\n const winEl = document.getElementById('installCmdWin');\n document.getElementById('copyInstallUnix')?.addEventListener('click', () => copyInstallCmd(unixEl?.textContent, notify));\n document.getElementById('copyInstallWin')?.addEventListener('click', () => copyInstallCmd(winEl?.textContent, notify));\n // 一键复制:按当前操作系统选对应命令\n document.getElementById('copyInstallAuto')?.addEventListener('click', () => {\n copyInstallCmd(isWindowsPlatform() ? winEl?.textContent : unixEl?.textContent, notify);\n });\n}\n","// options/index.js - 选项页面入口\n\nimport { currentModel, setCurrentModel, PRESET_MODELS, loadConfig, saveConfig, addCustomModelToDropdown, removeCustomModel, saveCustomModels, loadCustomModels, updateModelSelection, updateSelectedCtxBadge, applyModelContextWindow, commitModelContextWindow, filterModelDropdown, showStatus, showToast } from './config-manager.js';\nimport { openCtxPopover, closeCtxPopover, containsCtxPopoverEl } from './ctx-popover.js';\nimport { encodeSecret, decodeSecret } from '../shared/secret.js';\nimport { currentImageModel, setCurrentImageModel, addCustomImageModelToDropdown, removeImageModel, loadImageModels, updateImageModelSelection, commitImageModelContextWindow } from './config-manager.js';\nimport { addCustomApiBase, removeApiBase, saveApiBases, loadApiBases, updateApiBaseSelection } from './config-manager.js';\nimport { addCustomImageApiBase, removeImageApiBase, saveImageApiBases, loadImageApiBases, updateImageApiBaseSelection } from './config-manager.js';\nimport { getDefaultSystemPrompt } from './constants.js';\nimport { fetchModelList } from './model-fetcher.js';\nimport {\n loadToolbarTools,\n renderToolbarToolsList,\n saveToolbarConfig,\n moveTool,\n deleteTool,\n openToolEditModal,\n closeToolEditModal,\n saveToolEdit,\n loadBlockedDomainsUI,\n addBlockedDomain,\n removeBlockedDomain\n} from './toolbar-config.js';\nimport { showExportDialog, triggerImport, handleImportFile, initConfigIOEvents } from './config-io.js';\nimport { initProfileManager } from './profile-manager.js';\nimport { ensureProfilesMigrated } from '../shared/model-profiles.js';\nimport { initToolbox, refreshToolbox } from './toolbox-config.js';\nimport { initKnowledgePanel, refreshKnowledgePanel } from './knowledge-panel.js';\nimport { initMemoryPanel, refreshMemoryPanel, saveMemoryFromGlobalSave } from './memory-panel.js';\nimport { initSaveBarSticky } from './save-bar.js';\nimport { initInstallGuide } from './install-guide.js';\nimport { showCustomConfirm } from './toolbox-shared.js';\nimport logger from '../shared/logger.js';\nimport { initI18n, applyI18n, subscribe, setLanguage, getLanguage, SUPPORTED_LANGUAGES, t } from '../shared/i18n.js';\n\nlet currentTools = [];\n\n// Tab 激活时的数据刷新注册表。\n// 激活一共有四条路径(按钮点击、hash 直达、optionsInitialTab、SWITCH_TAB 消息),它们最终都汇到\n// switchTab;刷新若只挂在按钮 click 上,F5 和从侧边栏跳进来这两条就会停在空数据。\nconst tabRefreshers = {};\n\n// Tab 的「代理端保存」注册表。底部那颗全局「保存配置」在存完扩展自身设置后,\n// 若当前 Tab 在此注册了代理保存入口,就顺带把该 Tab 的配置推到代理端;\n// 连接态判断与离线跳过由各个 saver 自己负责(记忆 Tab 未连接时静默跳过)。\nconst tabAgentSavers = {};\n\nfunction getActiveTabName() {\n return document.querySelector('.tab-panel.active')?.dataset.tab || null;\n}\n\n// 全局保存:先走原有的扩展设置保存,再触发当前 Tab 已注册的代理端保存。\n// 两者解耦——即使 saveConfig 因扩展必填项校验失败提前返回,代理端那份仍按连接态独立处理。\nasync function onGlobalSave() {\n saveConfig();\n const saver = tabAgentSavers[getActiveTabName()];\n if (saver) {\n try {\n await saver();\n } catch (err) {\n logger.warn('[Options] tab agent save failed:', err.message);\n }\n }\n}\n\n// Tab 切换函数\nfunction switchTab(tabName) {\n document.querySelectorAll('.tab-nav-btn').forEach(b => b.classList.remove('active'));\n document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));\n \n const btn = document.querySelector(`.tab-nav-btn[data-tab=\"${tabName}\"]`);\n const panel = document.querySelector(`.tab-panel[data-tab=\"${tabName}\"]`);\n if (btn) btn.classList.add('active');\n if (panel) panel.classList.add('active');\n \n // 更新 hash(不触发 hashchange)\n if (window.location.hash !== '#' + tabName) {\n history.replaceState(null, '', '#' + tabName);\n }\n\n // 已注册刷新入口的面板进入时重拉一次:三个面板的数据源都在 agent / storage,\n // 只挂在按钮 click 上会让 hash 直达与 F5 两条路径停在空数据\n tabRefreshers[tabName]?.();\n}\n\n// 根据 hash 激活对应 tab\nfunction activateByHash() {\n const hash = window.location.hash.replace('#', '');\n const validTabs = ['basic', 'toolbar', 'react', 'reflection', 'chat', 'agent', 'toolbox', 'knowledge', 'memory'];\n if (validTabs.includes(hash)) {\n switchTab(hash);\n }\n}\n\n// 页面加载时获取已保存的配置\ndocument.addEventListener('DOMContentLoaded', async function() {\n // 初始化国际化(读取语言偏好 + 跨环境同步监听)\n await initI18n();\n applyI18n();\n subscribe(() => {\n applyI18n();\n // 语言切换时重新加载并渲染工具栏列表(内置工具名称随语言变化)\n loadToolbarTools().then((tools) => {\n currentTools = tools;\n renderToolbarToolsList(currentTools);\n });\n });\n\n // 初始化语言选择器(自定义下拉框,与其他下拉框风格一致)\n const langInput = document.getElementById('languageInput');\n const langDropdown = document.getElementById('languageDropdown');\n if (langInput && langDropdown) {\n const renderLanguageOptions = (selectedCode) => {\n langDropdown.innerHTML = '';\n SUPPORTED_LANGUAGES.forEach(({ code, label }) => {\n const option = document.createElement('div');\n option.className = 'model-option' + (code === selectedCode ? ' selected' : '');\n option.dataset.value = code;\n option.textContent = label;\n langDropdown.appendChild(option);\n });\n const current = SUPPORTED_LANGUAGES.find(x => x.code === selectedCode);\n langInput.value = current ? current.label : '';\n };\n renderLanguageOptions(getLanguage());\n\n langInput.addEventListener('click', (e) => {\n e.stopPropagation();\n langDropdown.classList.toggle('show');\n });\n\n langDropdown.addEventListener('click', (e) => {\n const option = e.target.closest('.model-option');\n if (!option) return;\n e.stopPropagation();\n const code = option.dataset.value;\n langDropdown.querySelectorAll('.model-option').forEach(o => o.classList.remove('selected'));\n option.classList.add('selected');\n langInput.value = option.textContent;\n langDropdown.classList.remove('show');\n setLanguage(code);\n applyI18n();\n showToast(t('settings.languageChanged'), 'success');\n });\n\n document.addEventListener('click', (e) => {\n if (!langDropdown.contains(e.target) && e.target !== langInput) {\n langDropdown.classList.remove('show');\n }\n });\n }\n\n // Tab 切换事件\n document.querySelectorAll('.tab-nav-btn').forEach(btn => {\n btn.addEventListener('click', function() {\n switchTab(this.dataset.tab);\n });\n });\n\n // 初始化可折叠说明块(所有 .info-box 和 .warning-box 默认折叠)\n document.querySelectorAll('.info-box, .warning-box').forEach(box => {\n const h3 = box.querySelector('h3');\n if (!h3 || box.classList.contains('collapsible')) return;\n\n // 添加 collapsible 结构和箭头\n box.classList.add('collapsible', 'collapsed');\n h3.classList.add('collapsible-header');\n const arrow = document.createElement('span');\n arrow.className = 'collapsible-arrow';\n arrow.textContent = '▶';\n h3.appendChild(arrow);\n\n // 将 h3 之后的内容包裹到 collapsible-body\n const body = document.createElement('div');\n body.className = 'collapsible-body';\n body.style.display = 'none';\n while (h3.nextSibling) {\n body.appendChild(h3.nextSibling);\n }\n box.appendChild(body);\n });\n \n // 统一为所有 collapsible 元素绑定点击事件\n document.querySelectorAll('.collapsible .collapsible-header').forEach(h3 => {\n const box = h3.closest('.collapsible');\n const body = box?.querySelector('.collapsible-body');\n const arrow = h3.querySelector('.collapsible-arrow');\n if (!box || !body) return;\n h3.addEventListener('click', () => {\n const isOpen = box.classList.contains('open');\n if (isOpen) {\n box.classList.remove('open');\n box.classList.add('collapsed');\n body.style.display = 'none';\n if (arrow) arrow.textContent = '▶';\n } else {\n box.classList.add('open');\n box.classList.remove('collapsed');\n body.style.display = '';\n if (arrow) arrow.textContent = '▼';\n }\n });\n });\n \n // 代理端安装指引(版本匹配 + 一键复制)\n initInstallGuide();\n\n // 记忆面板的数据源在 agent 侧,注册刷新入口必须在 activateByHash 之前:\n // F5 时 hash 会直接激活记忆 Tab,等到下面 initMemoryPanel() 才注册就漏了这一条路径\n tabRefreshers.memory = refreshMemoryPanel;\n tabAgentSavers.memory = saveMemoryFromGlobalSave;\n\n // 工具箱与知识库同理,必须挤在同一个窗口里注册。两者的 refresh 都是自包含的\n // (只读 storage / DOM,不依赖各自 init 里的临时状态),因此早于 initToolbox()\n // 被调用也不会出错;异步失败在此吞掉,避免 switchTab 里浮出未处理的 rejection\n tabRefreshers.toolbox = () => { refreshToolbox().catch(() => {}); };\n tabRefreshers.knowledge = () => { refreshKnowledgePanel().catch(() => {}); };\n\n // 根据 hash 激活对应 tab\n activateByHash();\n window.addEventListener('hashchange', activateByHash);\n\n // 检查是否从侧边栏跳转过来,自动切换到代理 Tab\n chrome.storage.local.get(['optionsInitialTab'], (result) => {\n if (result.optionsInitialTab) {\n switchTab(result.optionsInitialTab);\n chrome.storage.local.remove('optionsInitialTab');\n }\n });\n\n // 监听侧边栏发来的切换 Tab 消息\n chrome.runtime.onMessage.addListener((msg) => {\n if (msg.type === 'SWITCH_TAB' && msg.tab) {\n switchTab(msg.tab);\n }\n });\n \n // 厂商配置:先确保迁移就绪(同步扁平键),再加载配置并初始化切换器\n await ensureProfilesMigrated();\n loadConfig();\n initProfileManager();\n \n // 加载工具栏配置\n currentTools = await loadToolbarTools();\n renderToolbarToolsList(currentTools);\n \n // 初始化工具栏总开关状态\n const enableSelToolbarCheckbox = document.getElementById('enableSelectionToolbar');\n const toolbarConfigEl = document.getElementById('toolbarConfig');\n const toolbarToggleLabelEl = document.getElementById('toolbarToggleLabel');\n if (toolbarConfigEl && enableSelToolbarCheckbox) {\n if (enableSelToolbarCheckbox.checked) {\n toolbarConfigEl.classList.remove('disabled');\n } else {\n toolbarConfigEl.classList.add('disabled');\n }\n if (toolbarToggleLabelEl) {\n toolbarToggleLabelEl.textContent = enableSelToolbarCheckbox.checked ? t('common.enabled') : t('common.disabled');\n }\n }\n \n // 保存按钮事件:存扩展设置 + 顺带触发当前 Tab 的代理端保存\n document.getElementById('saveBtn').addEventListener('click', onGlobalSave);\n\n // 保存按钮栏:内容超出一屏时吸附视口底部(保证保存按钮始终可见)\n initSaveBarSticky();\n \n // 模型选择器事件\n const modelInput = document.getElementById('modelInput');\n const modelDropdown = document.getElementById('modelDropdown');\n \n modelInput.addEventListener('click', function(e) {\n e.stopPropagation();\n if (!modelDropdown.classList.contains('show')) {\n // 打开时重置过滤状态、高亮当前选中项,并全选文本方便直接打字搜索\n filterModelDropdown(modelDropdown, '');\n updateModelSelection(currentModel);\n modelInput.select();\n }\n modelDropdown.classList.toggle('show');\n });\n\n // 输入时实时过滤下拉选项(输入框即搜索框)\n modelInput.addEventListener('input', function() {\n closeCtxPopover();\n modelDropdown.classList.add('show');\n filterModelDropdown(modelDropdown, this.value);\n });\n\n // 失焦时恢复显示当前选中模型,避免残留搜索词\n modelInput.addEventListener('blur', function() {\n modelInput.value = currentModel;\n updateModelSelection(currentModel);\n });\n \n modelDropdown.addEventListener('click', function(e) {\n if (e.target.classList.contains('delete-model-btn')) {\n e.stopPropagation();\n const option = e.target.closest('.model-option');\n const value = option.dataset.value;\n removeCustomModel(value);\n return;\n }\n // 上下文窗口滑杆入口:弹出浮层(不触发模型选中)\n const ctxBtn = e.target.closest('.ctx-set-btn');\n if (ctxBtn) {\n e.stopPropagation();\n const option = e.target.closest('.model-option');\n if (option) {\n openCtxPopover(ctxBtn, option, {\n onPreview: (tokens) => {\n applyModelContextWindow(option, tokens);\n updateSelectedCtxBadge('modelInput', 'modelSelectedCtxBadge', 'modelDropdown');\n },\n onCommit: (tokens) => commitModelContextWindow(option, tokens),\n onReset: () => commitModelContextWindow(option, 0),\n });\n }\n return;\n }\n const option = e.target.closest('.model-option');\n if (option) {\n e.stopPropagation();\n closeCtxPopover();\n const value = option.dataset.value;\n setCurrentModel(value);\n modelInput.value = value;\n updateModelSelection(value);\n modelDropdown.classList.remove('show');\n }\n });\n \n document.addEventListener('click', function(e) {\n if (!modelDropdown.contains(e.target) && e.target !== modelInput && !containsCtxPopoverEl(e.target)) {\n modelDropdown.classList.remove('show');\n }\n });\n\n // ==================== 添加自定义模型表单事件 ====================\n\n const addModelToggleBtn = document.getElementById('addModelToggleBtn');\n const addModelForm = document.getElementById('addModelForm');\n const addModelName = document.getElementById('addModelName');\n const addModelContextWindow = document.getElementById('addModelContextWindow');\n const confirmAddModelBtn = document.getElementById('confirmAddModelBtn');\n const cancelAddModelBtn = document.getElementById('cancelAddModelBtn');\n\n if (addModelToggleBtn && addModelForm) {\n addModelToggleBtn.addEventListener('click', function() {\n const isVisible = addModelForm.style.display !== 'none';\n addModelForm.style.display = isVisible ? 'none' : '';\n addModelToggleBtn.textContent = isVisible ? t('settings.addModel') : t('settings.collapse');\n if (!isVisible) {\n addModelName.value = '';\n addModelContextWindow.value = '';\n addModelName.focus();\n }\n });\n\n cancelAddModelBtn.addEventListener('click', function() {\n addModelForm.style.display = 'none';\n addModelToggleBtn.textContent = t('settings.addModel');\n addModelName.value = '';\n addModelContextWindow.value = '';\n });\n\n confirmAddModelBtn.addEventListener('click', function() {\n const name = addModelName.value.trim();\n const ctxWindow = parseInt(addModelContextWindow.value) || 0;\n if (!name) {\n showToast('❌ ' + t('settings.modelNameRequired'), 'error');\n return;\n }\n addCustomModelToDropdown(name, ctxWindow);\n modelInput.value = name;\n setCurrentModel(name);\n updateModelSelection(name);\n chrome.storage.local.set({ modelName: name });\n addModelForm.style.display = 'none';\n addModelToggleBtn.textContent = t('settings.addModel');\n addModelName.value = '';\n addModelContextWindow.value = '';\n showToast('✅ ' + t('settings.modelAdded'), 'success');\n });\n\n // 回车键确认添加\n addModelName.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') {\n confirmAddModelBtn.click();\n }\n });\n addModelContextWindow.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') {\n confirmAddModelBtn.click();\n }\n });\n }\n\n // ==================== 图片识别模型选择器事件 ====================\n\n const enableImageInputEl = document.getElementById('enableImageInput');\n const imageModelGroup = document.getElementById('imageModelGroup');\n const imageApiGroup = document.getElementById('imageApiGroup');\n const imageApiKeyGroup = document.getElementById('imageApiKeyGroup');\n if (enableImageInputEl && imageModelGroup) {\n enableImageInputEl.addEventListener('change', function() {\n imageModelGroup.style.display = this.checked ? '' : 'none';\n if (imageApiGroup) {\n imageApiGroup.style.display = this.checked ? '' : 'none';\n }\n if (imageApiKeyGroup) {\n imageApiKeyGroup.style.display = this.checked ? '' : 'none';\n }\n // 立即持久化开关状态\n chrome.storage.local.set({ enableImageInput: this.checked });\n });\n }\n\n const imageModelInput = document.getElementById('imageModelInput');\n const imageModelDropdown = document.getElementById('imageModelDropdown');\n\n if (imageModelInput && imageModelDropdown) {\n imageModelInput.addEventListener('click', function(e) {\n e.stopPropagation();\n if (!imageModelDropdown.classList.contains('show')) {\n // 打开时重置过滤状态、高亮当前选中项,并全选文本方便直接打字搜索\n filterModelDropdown(imageModelDropdown, '');\n updateImageModelSelection(currentImageModel);\n imageModelInput.select();\n }\n imageModelDropdown.classList.toggle('show');\n });\n\n // 输入时实时过滤下拉选项(输入框即搜索框)\n imageModelInput.addEventListener('input', function() {\n closeCtxPopover();\n imageModelDropdown.classList.add('show');\n filterModelDropdown(imageModelDropdown, this.value);\n });\n\n // 失焦时恢复显示当前选中模型,避免残留搜索词\n imageModelInput.addEventListener('blur', function() {\n imageModelInput.value = currentImageModel;\n updateImageModelSelection(currentImageModel);\n });\n\n imageModelDropdown.addEventListener('click', function(e) {\n if (e.target.classList.contains('delete-model-btn')) {\n e.stopPropagation();\n const option = e.target.closest('.model-option');\n const value = option.dataset.value;\n removeImageModel(value);\n return;\n }\n // 上下文窗口滑杆入口:弹出浮层(不触发模型选中)\n const ctxBtn = e.target.closest('.ctx-set-btn');\n if (ctxBtn) {\n e.stopPropagation();\n const option = e.target.closest('.model-option');\n if (option) {\n openCtxPopover(ctxBtn, option, {\n onPreview: (tokens) => {\n applyModelContextWindow(option, tokens);\n updateSelectedCtxBadge('imageModelInput', 'imageModelSelectedCtxBadge', 'imageModelDropdown');\n },\n onCommit: (tokens) => commitImageModelContextWindow(option, tokens),\n onReset: () => commitImageModelContextWindow(option, 0),\n });\n }\n return;\n }\n const option = e.target.closest('.model-option');\n if (option) {\n e.stopPropagation();\n closeCtxPopover();\n const value = option.dataset.value;\n setCurrentImageModel(value);\n imageModelInput.value = value;\n updateImageModelSelection(value);\n imageModelDropdown.classList.remove('show');\n }\n });\n\n document.addEventListener('click', function(e) {\n if (!imageModelDropdown.contains(e.target) && e.target !== imageModelInput && !containsCtxPopoverEl(e.target)) {\n imageModelDropdown.classList.remove('show');\n }\n });\n }\n\n // ==================== 图片模型添加表单事件 ====================\n\n const addImageModelToggleBtn = document.getElementById('addImageModelToggleBtn');\n const addImageModelForm = document.getElementById('addImageModelForm');\n const addImageModelName = document.getElementById('addImageModelName');\n const addImageModelContextWindow = document.getElementById('addImageModelContextWindow');\n const confirmAddImageModelBtn = document.getElementById('confirmAddImageModelBtn');\n const cancelAddImageModelBtn = document.getElementById('cancelAddImageModelBtn');\n\n if (addImageModelToggleBtn && addImageModelForm) {\n addImageModelToggleBtn.addEventListener('click', function() {\n const isVisible = addImageModelForm.style.display !== 'none';\n addImageModelForm.style.display = isVisible ? 'none' : '';\n addImageModelToggleBtn.textContent = isVisible ? t('settings.addModel') : t('settings.collapse');\n if (!isVisible) {\n addImageModelName.value = '';\n addImageModelContextWindow.value = '';\n addImageModelName.focus();\n }\n });\n\n cancelAddImageModelBtn.addEventListener('click', function() {\n addImageModelForm.style.display = 'none';\n addImageModelToggleBtn.textContent = t('settings.addModel');\n addImageModelName.value = '';\n addImageModelContextWindow.value = '';\n });\n\n confirmAddImageModelBtn.addEventListener('click', function() {\n const name = addImageModelName.value.trim();\n const ctxWindow = parseInt(addImageModelContextWindow.value) || 0;\n if (!name) {\n showToast('❌ ' + t('settings.modelNameRequired'), 'error');\n return;\n }\n addCustomImageModelToDropdown(name, ctxWindow);\n imageModelInput.value = name;\n setCurrentImageModel(name);\n updateImageModelSelection(name);\n chrome.storage.local.set({ imageModelName: name });\n addImageModelForm.style.display = 'none';\n addImageModelToggleBtn.textContent = t('settings.addModel');\n addImageModelName.value = '';\n addImageModelContextWindow.value = '';\n showToast('✅ ' + t('settings.imageModelAdded'), 'success');\n });\n\n // 回车键确认添加\n addImageModelName.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') {\n confirmAddImageModelBtn.click();\n }\n });\n addImageModelContextWindow.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') {\n confirmAddImageModelBtn.click();\n }\n });\n }\n\n // ==================== 从 API 获取模型列表 ====================\n\n /**\n * 拉取厂商模型并合并追加进指定下拉。\n * @param {object} cfg\n * @param {HTMLInputElement} cfg.apiBaseEl\n * @param {HTMLInputElement} cfg.apiKeyEl\n * @param {string} cfg.dropdownId 目标下拉容器 id\n * @param {(name:string, ctx:number)=>void} cfg.addFn 写入函数\n * @param {HTMLButtonElement|null} cfg.btn 触发按钮(用于 loading 态)\n * @param {boolean} cfg.silent 静默模式(自动拉取用,不弹 toast)\n */\n async function runFetchModels({ apiBaseEl, apiKeyEl, dropdownId, addFn, btn, silent }) {\n const apiBase = (apiBaseEl?.value || '').trim();\n const apiKey = (apiKeyEl?.value || '').trim();\n if (!apiBase || !apiKey) {\n if (!silent) showToast('⚠️ ' + t('settings.fetchModelsNeedConfig'), 'error');\n return;\n }\n const originalText = btn ? btn.textContent : '';\n if (btn) { btn.disabled = true; btn.textContent = t('settings.fetchModelsLoading'); }\n try {\n const models = await fetchModelList({ apiBase, apiKey });\n let added = 0;\n const dropdown = document.getElementById(dropdownId);\n for (const name of models) {\n const exists = dropdown && dropdown.querySelector(`.model-option[data-value=\"${CSS.escape(name)}\"]`);\n if (!exists) { addFn(name, 0); added++; }\n }\n if (!silent) {\n showToast(t('settings.fetchModelsSuccess', { total: models.length, added }), 'success');\n }\n } catch (err) {\n if (!silent) {\n if (err && (err.status === 401 || err.status === 403)) {\n showToast(t('settings.fetchModelsAuthFailed'), 'error');\n } else if (err && err.reason === 'parse') {\n showToast(t('settings.fetchModelsEmpty'), 'info');\n } else {\n showToast(t('settings.fetchModelsFailed'), 'error');\n }\n }\n } finally {\n if (btn) { btn.disabled = false; btn.textContent = originalText || t('settings.fetchModels'); }\n }\n }\n\n // 选定厂商后自动拉取一次(防抖 + 去重,静默失败)\n let lastAutoFetchKey = '';\n let autoFetchTimer = null;\n function maybeAutoFetchModels(apiBase, apiKey) {\n const base = (apiBase || '').trim();\n const key = (apiKey || '').trim();\n if (!base || !key) return;\n const dedupeKey = `${base}|${key}`;\n if (dedupeKey === lastAutoFetchKey) return;\n clearTimeout(autoFetchTimer);\n autoFetchTimer = setTimeout(() => {\n lastAutoFetchKey = dedupeKey;\n const btn = document.getElementById('fetchModelsBtn');\n // 手动请求进行中则跳过,避免与按钮 loading 态争抢\n if (btn && btn.disabled) return;\n runFetchModels({\n apiBaseEl: document.getElementById('apiBase'),\n apiKeyEl: document.getElementById('apiKey'),\n dropdownId: 'modelDropdown',\n addFn: addCustomModelToDropdown,\n btn: null,\n silent: true,\n });\n }, 500);\n }\n\n // 主模型区「从 API 获取」按钮\n const fetchModelsBtn = document.getElementById('fetchModelsBtn');\n if (fetchModelsBtn) {\n fetchModelsBtn.addEventListener('click', () => {\n runFetchModels({\n apiBaseEl: document.getElementById('apiBase'),\n apiKeyEl: document.getElementById('apiKey'),\n dropdownId: 'modelDropdown',\n addFn: addCustomModelToDropdown,\n btn: fetchModelsBtn,\n silent: false,\n });\n });\n }\n\n // 视觉模型区「从 API 获取」按钮\n const fetchImageModelsBtn = document.getElementById('fetchImageModelsBtn');\n if (fetchImageModelsBtn) {\n fetchImageModelsBtn.addEventListener('click', () => {\n runFetchModels({\n apiBaseEl: document.getElementById('imageApiBase'),\n apiKeyEl: document.getElementById('imageApiKey'),\n dropdownId: 'imageModelDropdown',\n addFn: addCustomImageModelToDropdown,\n btn: fetchImageModelsBtn,\n silent: false,\n });\n });\n }\n\n // ==================== API Base URL 选择器事件 ====================\n\n const apiBaseInput = document.getElementById('apiBase');\n const apiBaseDropdown = document.getElementById('apiBaseDropdown');\n\n if (apiBaseInput && apiBaseDropdown) {\n apiBaseInput.addEventListener('click', function(e) {\n e.stopPropagation();\n // 点击时重置过滤状态,显示全部选项\n apiBaseDropdown.querySelectorAll('.model-option').forEach(opt => { opt.style.display = ''; });\n apiBaseDropdown.classList.toggle('show');\n // 高亮当前值匹配的选项\n const currentVal = apiBaseInput.value.trim();\n updateApiBaseSelection(currentVal);\n });\n\n apiBaseDropdown.addEventListener('click', function(e) {\n if (e.target.classList.contains('delete-model-btn')) {\n e.stopPropagation();\n const option = e.target.closest('.model-option');\n const value = option.dataset.value;\n removeApiBase(value);\n return;\n }\n const option = e.target.closest('.model-option');\n if (option) {\n e.stopPropagation();\n const value = option.dataset.value;\n apiBaseInput.value = value;\n updateApiBaseSelection(value);\n apiBaseDropdown.classList.remove('show');\n // 自动保存\n chrome.storage.local.set({ apiBase: value });\n showToast('✅ ' + t('settings.apiBaseSwitched'), 'info');\n maybeAutoFetchModels(value, (document.getElementById('apiKey')?.value || '').trim());\n }\n });\n\n document.addEventListener('click', function(e) {\n if (!apiBaseDropdown.contains(e.target) && e.target !== apiBaseInput) {\n apiBaseDropdown.classList.remove('show');\n }\n });\n\n // 手动输入后失去焦点时自动保存并添加到下拉\n apiBaseInput.addEventListener('blur', function() {\n const val = this.value.trim();\n if (val) {\n chrome.storage.local.set({ apiBase: val });\n addCustomApiBase(val);\n updateApiBaseSelection(val);\n }\n });\n\n // 输入时实时过滤下拉选项\n apiBaseInput.addEventListener('input', function() {\n const searchText = this.value.trim().toLowerCase();\n apiBaseDropdown.classList.add('show');\n const options = apiBaseDropdown.querySelectorAll('.model-option');\n let hasMatch = false;\n options.forEach(opt => {\n const url = (opt.dataset.value || '').toLowerCase();\n const label = (opt.dataset.label || '').toLowerCase();\n if (!searchText || url.includes(searchText) || label.includes(searchText)) {\n opt.style.display = '';\n hasMatch = true;\n } else {\n opt.style.display = 'none';\n }\n });\n // 如果输入内容无匹配且非空,显示所有(方便手动输入新地址)\n if (!hasMatch && searchText) {\n options.forEach(opt => { opt.style.display = ''; });\n }\n });\n }\n\n // ==================== API Base 添加表单事件 ====================\n\n const addApiBaseToggleBtn = document.getElementById('addApiBaseToggleBtn');\n const addApiBaseForm = document.getElementById('addApiBaseForm');\n const addApiBaseUrl = document.getElementById('addApiBaseUrl');\n const addApiBaseLabel = document.getElementById('addApiBaseLabel');\n const confirmAddApiBaseBtn = document.getElementById('confirmAddApiBaseBtn');\n const cancelAddApiBaseBtn = document.getElementById('cancelAddApiBaseBtn');\n\n if (addApiBaseToggleBtn && addApiBaseForm) {\n addApiBaseToggleBtn.addEventListener('click', function() {\n const isVisible = addApiBaseForm.style.display !== 'none';\n addApiBaseForm.style.display = isVisible ? 'none' : '';\n addApiBaseToggleBtn.textContent = isVisible ? t('settings.addAddress') : t('settings.collapse');\n if (!isVisible) {\n addApiBaseUrl.value = '';\n addApiBaseLabel.value = '';\n addApiBaseUrl.focus();\n }\n });\n\n cancelAddApiBaseBtn.addEventListener('click', function() {\n addApiBaseForm.style.display = 'none';\n addApiBaseToggleBtn.textContent = t('settings.addAddress');\n addApiBaseUrl.value = '';\n addApiBaseLabel.value = '';\n });\n\n confirmAddApiBaseBtn.addEventListener('click', function() {\n const url = addApiBaseUrl.value.trim();\n const label = addApiBaseLabel.value.trim();\n if (!url) {\n showToast('❌ ' + t('settings.apiBaseRequired'), 'error');\n return;\n }\n // 简单校验 URL 格式\n try {\n new URL(url);\n } catch {\n showToast('❌ ' + t('settings.urlInvalid'), 'error');\n return;\n }\n addCustomApiBase(url, label);\n apiBaseInput.value = url;\n updateApiBaseSelection(url);\n chrome.storage.local.set({ apiBase: url });\n addApiBaseForm.style.display = 'none';\n addApiBaseToggleBtn.textContent = t('settings.addAddress');\n addApiBaseUrl.value = '';\n addApiBaseLabel.value = '';\n showToast('✅ ' + t('settings.apiBaseAdded'), 'success');\n });\n\n // 回车键确认添加\n addApiBaseUrl.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') confirmAddApiBaseBtn.click();\n });\n addApiBaseLabel.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') confirmAddApiBaseBtn.click();\n });\n }\n\n // ==================== 图片 API Base URL 选择器事件 ====================\n\n const imageApiBaseInput = document.getElementById('imageApiBase');\n const imageApiBaseDropdown = document.getElementById('imageApiBaseDropdown');\n\n if (imageApiBaseInput && imageApiBaseDropdown) {\n imageApiBaseInput.addEventListener('click', function(e) {\n e.stopPropagation();\n // 点击时重置过滤状态,显示全部选项\n imageApiBaseDropdown.querySelectorAll('.model-option').forEach(opt => { opt.style.display = ''; });\n imageApiBaseDropdown.classList.toggle('show');\n const currentVal = imageApiBaseInput.value.trim();\n updateImageApiBaseSelection(currentVal);\n });\n\n imageApiBaseDropdown.addEventListener('click', function(e) {\n if (e.target.classList.contains('delete-model-btn')) {\n e.stopPropagation();\n const option = e.target.closest('.model-option');\n const value = option.dataset.value;\n removeImageApiBase(value);\n return;\n }\n const option = e.target.closest('.model-option');\n if (option) {\n e.stopPropagation();\n const value = option.dataset.value;\n imageApiBaseInput.value = value;\n updateImageApiBaseSelection(value);\n imageApiBaseDropdown.classList.remove('show');\n chrome.storage.local.set({ imageApiBase: value });\n showToast('✅ ' + t('settings.imageApiBaseSwitched'), 'info');\n }\n });\n\n document.addEventListener('click', function(e) {\n if (!imageApiBaseDropdown.contains(e.target) && e.target !== imageApiBaseInput) {\n imageApiBaseDropdown.classList.remove('show');\n }\n });\n\n imageApiBaseInput.addEventListener('blur', function() {\n const val = this.value.trim();\n if (val) {\n chrome.storage.local.set({ imageApiBase: val });\n addCustomImageApiBase(val);\n updateImageApiBaseSelection(val);\n }\n });\n\n // 输入时实时过滤下拉选项\n imageApiBaseInput.addEventListener('input', function() {\n const searchText = this.value.trim().toLowerCase();\n imageApiBaseDropdown.classList.add('show');\n const options = imageApiBaseDropdown.querySelectorAll('.model-option');\n let hasMatch = false;\n options.forEach(opt => {\n const url = (opt.dataset.value || '').toLowerCase();\n const label = (opt.dataset.label || '').toLowerCase();\n if (!searchText || url.includes(searchText) || label.includes(searchText)) {\n opt.style.display = '';\n hasMatch = true;\n } else {\n opt.style.display = 'none';\n }\n });\n if (!hasMatch && searchText) {\n options.forEach(opt => { opt.style.display = ''; });\n }\n });\n }\n\n // ==================== 图片 API Base 添加表单事件 ====================\n\n const addImageApiBaseToggleBtn = document.getElementById('addImageApiBaseToggleBtn');\n const addImageApiBaseForm = document.getElementById('addImageApiBaseForm');\n const addImageApiBaseUrl = document.getElementById('addImageApiBaseUrl');\n const addImageApiBaseLabel = document.getElementById('addImageApiBaseLabel');\n const confirmAddImageApiBaseBtn = document.getElementById('confirmAddImageApiBaseBtn');\n const cancelAddImageApiBaseBtn = document.getElementById('cancelAddImageApiBaseBtn');\n\n if (addImageApiBaseToggleBtn && addImageApiBaseForm) {\n addImageApiBaseToggleBtn.addEventListener('click', function() {\n const isVisible = addImageApiBaseForm.style.display !== 'none';\n addImageApiBaseForm.style.display = isVisible ? 'none' : '';\n addImageApiBaseToggleBtn.textContent = isVisible ? t('settings.addAddress') : t('settings.collapse');\n if (!isVisible) {\n addImageApiBaseUrl.value = '';\n addImageApiBaseLabel.value = '';\n addImageApiBaseUrl.focus();\n }\n });\n\n cancelAddImageApiBaseBtn.addEventListener('click', function() {\n addImageApiBaseForm.style.display = 'none';\n addImageApiBaseToggleBtn.textContent = t('settings.addAddress');\n addImageApiBaseUrl.value = '';\n addImageApiBaseLabel.value = '';\n });\n\n confirmAddImageApiBaseBtn.addEventListener('click', function() {\n const url = addImageApiBaseUrl.value.trim();\n const label = addImageApiBaseLabel.value.trim();\n if (!url) {\n showToast('❌ ' + t('settings.apiBaseRequired'), 'error');\n return;\n }\n try {\n new URL(url);\n } catch {\n showToast('❌ ' + t('settings.urlInvalid'), 'error');\n return;\n }\n addCustomImageApiBase(url, label);\n imageApiBaseInput.value = url;\n updateImageApiBaseSelection(url);\n chrome.storage.local.set({ imageApiBase: url });\n addImageApiBaseForm.style.display = 'none';\n addImageApiBaseToggleBtn.textContent = t('settings.addAddress');\n addImageApiBaseUrl.value = '';\n addImageApiBaseLabel.value = '';\n showToast('✅ ' + t('settings.imageApiBaseAdded'), 'success');\n });\n\n addImageApiBaseUrl.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') confirmAddImageApiBaseBtn.click();\n });\n addImageApiBaseLabel.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') confirmAddImageApiBaseBtn.click();\n });\n }\n\n // 图片识别 API Key 自动保存\n const imageApiKeyEl = document.getElementById('imageApiKey');\n const toggleImageApiKeyBtn = document.getElementById('toggleImageApiKeyBtn');\n if (imageApiKeyEl) {\n imageApiKeyEl.addEventListener('blur', function() {\n chrome.storage.local.set({ imageApiKey: encodeSecret(this.value.trim()) });\n });\n }\n if (imageApiKeyEl && toggleImageApiKeyBtn) {\n const imgIconEyeOpen = toggleImageApiKeyBtn.querySelector('.icon-eye-open');\n const imgIconEyeClosed = toggleImageApiKeyBtn.querySelector('.icon-eye-closed');\n toggleImageApiKeyBtn.addEventListener('click', function() {\n if (imageApiKeyEl.type === 'password') {\n imageApiKeyEl.type = 'text';\n imgIconEyeOpen.style.display = 'none';\n imgIconEyeClosed.style.display = 'block';\n } else {\n imageApiKeyEl.type = 'password';\n imgIconEyeOpen.style.display = 'block';\n imgIconEyeClosed.style.display = 'none';\n }\n });\n }\n \n // 显示/隐藏 Token 切换\n const toggleToken = document.getElementById('toggleToken');\n const apiKeyInput = document.getElementById('apiKey');\n const iconEyeOpen = toggleToken.querySelector('.icon-eye-open');\n const iconEyeClosed = toggleToken.querySelector('.icon-eye-closed');\n \n toggleToken.addEventListener('click', function() {\n if (apiKeyInput.type === 'password') {\n apiKeyInput.type = 'text';\n iconEyeOpen.style.display = 'none';\n iconEyeClosed.style.display = 'block';\n } else {\n apiKeyInput.type = 'password';\n iconEyeOpen.style.display = 'block';\n iconEyeClosed.style.display = 'none';\n }\n });\n \n // 重置系统提示词按钮事件\n const resetSystemPromptBtn = document.getElementById('resetSystemPromptBtn');\n if (resetSystemPromptBtn) {\n resetSystemPromptBtn.addEventListener('click', function() {\n document.getElementById('systemPrompt').value = getDefaultSystemPrompt();\n });\n }\n \n // ==================== 工具栏配置事件 ====================\n \n // 工具栏图标精简模式变更\n const iconOnlyCheckbox = document.getElementById('toolbarIconOnly');\n if (iconOnlyCheckbox) {\n iconOnlyCheckbox.addEventListener('change', function() {\n saveToolbarConfig();\n });\n }\n \n // 选中内容工具栏开关变更\n const enableSelectionToolbarCheckbox = document.getElementById('enableSelectionToolbar');\n const toolbarConfig = document.getElementById('toolbarConfig');\n const toolbarToggleLabel = document.getElementById('toolbarToggleLabel');\n \n function updateToolbarToggleState() {\n const enabled = enableSelectionToolbarCheckbox && enableSelectionToolbarCheckbox.checked;\n if (toolbarConfig) {\n if (enabled) {\n toolbarConfig.classList.remove('disabled');\n } else {\n toolbarConfig.classList.add('disabled');\n }\n }\n if (toolbarToggleLabel) {\n toolbarToggleLabel.textContent = enabled ? t('common.enabled') : t('common.disabled');\n }\n }\n \n if (enableSelectionToolbarCheckbox) {\n enableSelectionToolbarCheckbox.addEventListener('change', function() {\n updateToolbarToggleState();\n saveToolbarConfig();\n });\n }\n \n // 工具栏工具列表事件委托\n document.getElementById('toolbarToolsList').addEventListener('click', function(e) {\n const btn = e.target.closest('[data-action]');\n if (!btn) return;\n \n const action = btn.dataset.action;\n const index = parseInt(btn.dataset.index);\n \n switch (action) {\n case 'moveUp':\n currentTools = moveTool(currentTools, index, -1);\n persistAndRender();\n break;\n case 'moveDown':\n currentTools = moveTool(currentTools, index, 1);\n persistAndRender();\n break;\n case 'edit':\n openToolEditModal(currentTools, index);\n break;\n case 'delete':\n currentTools = deleteTool(currentTools, index);\n persistAndRender();\n break;\n }\n });\n \n // 添加自定义工具按钮\n document.getElementById('addCustomToolBtn').addEventListener('click', function() {\n openToolEditModal(currentTools, -1);\n });\n \n // 域名屏蔽 - 添加\n document.getElementById('domainAddBtn').addEventListener('click', function() {\n const input = document.getElementById('domainAddInput');\n addBlockedDomain(input.value, (result) => {\n if (result.error) {\n showToast('❌ ' + result.error, 'error');\n } else {\n input.value = '';\n }\n });\n });\n \n // 域名屏蔽 - 回车添加\n document.getElementById('domainAddInput').addEventListener('keydown', function(e) {\n if (e.key === 'Enter') {\n addBlockedDomain(this.value, (result) => {\n if (result.error) {\n showToast('❌ ' + result.error, 'error');\n } else {\n this.value = '';\n }\n });\n }\n });\n \n // 域名屏蔽 - 移除(事件委托)\n document.getElementById('domainBlocklistList').addEventListener('click', function(e) {\n const removeBtn = e.target.closest('.domain-blocklist-item-remove');\n if (removeBtn) {\n removeBlockedDomain(removeBtn.dataset.domain);\n }\n });\n \n // 加载域名屏蔽列表\n loadBlockedDomainsUI();\n \n // 监听域名屏蔽列表变化\n chrome.storage.onChanged.addListener((changes, areaName) => {\n if (areaName === 'local' && changes.blockedDomains) {\n loadBlockedDomainsUI();\n }\n });\n \n // 编辑弹窗取消\n document.getElementById('toolEditCancel').addEventListener('click', function() {\n closeToolEditModal();\n });\n \n // 编辑弹窗右上角关闭按钮\n document.getElementById('toolEditClose').addEventListener('click', function() {\n closeToolEditModal();\n });\n \n // 编辑弹窗保存\n document.getElementById('toolEditSave').addEventListener('click', function() {\n const result = saveToolEdit(currentTools);\n if (result.error) {\n showToast('❌ ' + result.error, 'error');\n return;\n }\n currentTools = result.tools;\n persistAndRender();\n });\n \n // 反思总开关控制\n const reflectionEnabled = document.getElementById('reflectionEnabled');\n const reflectionConfig = document.getElementById('reflectionConfig');\n const reflectionToggleLabel = document.getElementById('reflectionToggleLabel');\n \n function updateReflectionSectionVisibility() {\n if (!reflectionConfig) return;\n \n const enabled = reflectionEnabled && reflectionEnabled.checked;\n if (!enabled) {\n reflectionConfig.classList.add('disabled');\n } else {\n reflectionConfig.classList.remove('disabled');\n }\n \n if (reflectionToggleLabel) {\n reflectionToggleLabel.textContent = enabled ? t('common.enabled') : t('common.disabled');\n }\n }\n \n function updateReflectionModuleVisibility(moduleId, toggleId) {\n const module = document.getElementById(moduleId);\n const toggle = document.getElementById(toggleId);\n \n if (!module || !toggle) return;\n \n if (!toggle.checked) {\n module.classList.add('disabled');\n } else {\n module.classList.remove('disabled');\n }\n }\n \n if (reflectionEnabled) {\n reflectionEnabled.addEventListener('change', function() {\n updateReflectionSectionVisibility();\n // 立即持久化反思总开关状态,无需点保存\n const enabled = this.checked;\n chrome.storage.local.get(['reflectionConfig'], (result) => {\n const config = result.reflectionConfig || { postReflection: {}, subtaskReflection: {} };\n config.enabled = enabled;\n chrome.storage.local.set({ reflectionConfig: config });\n });\n });\n // 初始化反思总开关状态\n updateReflectionSectionVisibility();\n }\n \n // 立即持久化反思子开关状态\n function persistReflectionSubToggle(subKey, enabled) {\n chrome.storage.local.get(['reflectionConfig'], (result) => {\n const config = result.reflectionConfig || { postReflection: {}, subtaskReflection: {} };\n if (!config[subKey]) config[subKey] = {};\n config[subKey].enabled = enabled;\n chrome.storage.local.set({ reflectionConfig: config });\n });\n }\n\n // 后置反思开关控制\n const postReflectionEnabled = document.getElementById('postReflectionEnabled');\n if (postReflectionEnabled) {\n postReflectionEnabled.addEventListener('change', function() {\n updateReflectionModuleVisibility('postReflectionSection', 'postReflectionEnabled');\n persistReflectionSubToggle('postReflection', this.checked);\n });\n }\n \n // 子任务反思开关控制\n const subtaskReflectionEnabled = document.getElementById('subtaskReflectionEnabled');\n if (subtaskReflectionEnabled) {\n subtaskReflectionEnabled.addEventListener('change', function() {\n updateReflectionModuleVisibility('subtaskReflectionSection', 'subtaskReflectionEnabled');\n persistReflectionSubToggle('subtaskReflection', this.checked);\n });\n }\n \n // 初始化时更新可见性\n updateReflectionSectionVisibility();\n updateReflectionModuleVisibility('postReflectionSection', 'postReflectionEnabled');\n updateReflectionModuleVisibility('subtaskReflectionSection', 'subtaskReflectionEnabled');\n\n // ==================== 流式输出配置 ====================\n const streamEnabledEl = document.getElementById('streamEnabled');\n const streamEnabledLabel = document.getElementById('streamEnabledLabel');\n // 联动:streamExpandToolsWrap 仅在 streamEnabled 开启时显示\n function updateStreamExpandToolsVisibility() {\n const wrap = document.getElementById('streamExpandToolsWrap');\n if (wrap) {\n wrap.style.display = streamEnabledEl && streamEnabledEl.checked ? 'block' : 'none';\n }\n }\n if (streamEnabledEl && streamEnabledLabel) {\n streamEnabledEl.addEventListener('change', function() {\n if (streamEnabledLabel) {\n streamEnabledLabel.textContent = streamEnabledEl.checked ? t('common.enabled') : t('common.disabled');\n }\n chrome.storage.local.set({ streamEnabled: streamEnabledEl.checked });\n updateStreamExpandToolsVisibility();\n });\n if (streamEnabledLabel) {\n streamEnabledLabel.textContent = streamEnabledEl.checked ? t('common.enabled') : t('common.disabled');\n }\n updateStreamExpandToolsVisibility();\n }\n\n // 流式工具卡片默认展开开关\n const streamExpandToolsEl = document.getElementById('streamExpandTools');\n const streamExpandToolsLabel = document.getElementById('streamExpandToolsLabel');\n if (streamExpandToolsEl && streamExpandToolsLabel) {\n streamExpandToolsEl.addEventListener('change', function() {\n streamExpandToolsLabel.textContent = this.checked ? t('common.enabled') : t('common.disabled');\n chrome.storage.local.set({ streamExpandTools: this.checked });\n });\n streamExpandToolsLabel.textContent = streamExpandToolsEl.checked ? t('common.enabled') : t('common.disabled');\n }\n\n // 敏感操作确认开关状态标签\n const toolConfirmationEnabledEl = document.getElementById('toolConfirmationEnabled');\n const toolConfirmationEnabledLabel = document.getElementById('toolConfirmationEnabledLabel');\n if (toolConfirmationEnabledEl && toolConfirmationEnabledLabel) {\n toolConfirmationEnabledEl.addEventListener('change', function() {\n toolConfirmationEnabledLabel.textContent = this.checked ? t('common.enabled') : t('common.disabled');\n chrome.storage.local.set({ toolConfirmationEnabled: this.checked });\n });\n toolConfirmationEnabledLabel.textContent = toolConfirmationEnabledEl.checked ? t('common.enabled') : t('common.disabled');\n }\n\n // 启用执行日志开关状态标签\n const enableExecutionLogEl = document.getElementById('enableExecutionLog');\n const enableExecutionLogLabel = document.getElementById('enableExecutionLogLabel');\n if (enableExecutionLogEl && enableExecutionLogLabel) {\n enableExecutionLogEl.addEventListener('change', function() {\n enableExecutionLogLabel.textContent = this.checked ? t('common.enabled') : t('common.disabled');\n chrome.storage.local.set({ enableExecutionLog: this.checked });\n });\n enableExecutionLogLabel.textContent = enableExecutionLogEl.checked ? t('common.enabled') : t('common.disabled');\n }\n\n // 完成反馈:提示音开关(切换即写入 storage,无需点保存按钮)\n const completionSoundEl = document.getElementById('completionSoundEnabled');\n if (completionSoundEl) {\n completionSoundEl.addEventListener('change', function() {\n chrome.storage.local.set({ completionSoundEnabled: this.checked });\n });\n }\n\n // 完成反馈:彩带动画开关(切换即写入 storage,无需点保存按钮)\n const completionConfettiEl = document.getElementById('completionConfettiEnabled');\n if (completionConfettiEl) {\n completionConfettiEl.addEventListener('change', function() {\n chrome.storage.local.set({ completionConfettiEnabled: this.checked });\n });\n }\n\n // 桌面通知:任务完成/失败通知开关(切换即写入 storage,无需点保存按钮)\n const completionNotificationEl = document.getElementById('completionNotificationEnabled');\n if (completionNotificationEl) {\n completionNotificationEl.addEventListener('change', function() {\n chrome.storage.local.set({ completionNotificationEnabled: this.checked });\n });\n }\n\n // 桌面通知:定时任务通知开关(切换即写入 storage,无需点保存按钮)\n const scheduledNotificationEl = document.getElementById('scheduledNotificationEnabled');\n if (scheduledNotificationEl) {\n scheduledNotificationEl.addEventListener('change', function() {\n chrome.storage.local.set({ scheduledNotificationEnabled: this.checked });\n });\n }\n\n // 桌面通知:交互提醒开关(切换即写入 storage,无需点保存按钮)\n const interactionNotificationEl = document.getElementById('interactionNotificationEnabled');\n if (interactionNotificationEl) {\n interactionNotificationEl.addEventListener('change', function() {\n chrome.storage.local.set({ interactionNotificationEnabled: this.checked });\n });\n }\n\n // 消息时间戳开关(切换即写入 storage,无需点保存按钮)\n const showMessageTimestampEl = document.getElementById('showMessageTimestamp');\n if (showMessageTimestampEl) {\n showMessageTimestampEl.addEventListener('change', function() {\n chrome.storage.local.set({ showMessageTimestamp: this.checked });\n });\n }\n\n // ==================== Agent 配置 ====================\n initAgentConfig();\n\n // ==================== 配置导入/导出 ====================\n document.getElementById('exportConfigBtn').addEventListener('click', showExportDialog);\n document.getElementById('importConfigBtn').addEventListener('click', triggerImport);\n document.getElementById('importConfigFile').addEventListener('change', function(e) {\n if (e.target.files && e.target.files.length > 0) {\n handleImportFile(e.target.files[0]);\n }\n });\n initConfigIOEvents();\n\n // 初始化工具箱 Tab(延迟加载,页面对 Agent 连接状态有依赖)\n // 刷新入口已在 activateByHash 之前注册进 tabRefreshers,这里不再绑点击监听,\n // 否则与 switchTab 双触发会并发拉两轮数据\n initToolbox();\n\n // 初始化知识库 Tab(门控引导 + 列表加载;刷新入口同上已进注册表)\n initKnowledgePanel();\n\n // 初始化记忆向量面板(配置与状态都来自 agent,需连接后才有内容)\n // 刷新入口已在 activateByHash 之前注册进 tabRefreshers,这里只绑事件\n initMemoryPanel();\n});\n\n// 持久化并重新渲染\nfunction persistAndRender() {\n chrome.storage.local.set({ toolbarTools: currentTools }, () => {\n renderToolbarToolsList(currentTools);\n saveToolbarConfig();\n });\n}\n\n// ==================== Agent 配置函数 ====================\n\n/**\n * 初始化 Agent 配置界面\n */\nfunction initAgentConfig() {\n const agentUrlInput = document.getElementById('agentUrl');\n const pairCodeInput = document.getElementById('agentPairCode');\n const agentNameInput = document.getElementById('agentName');\n const connectBtn = document.getElementById('agentConnectBtn');\n const statusDot = document.getElementById('agentStatusDot');\n const statusText = document.getElementById('agentStatusText');\n const agentStatus = document.getElementById('agentStatus');\n const pairCodeGroup = document.getElementById('pairCodeGroup');\n const agentInfo = document.getElementById('agentInfo');\n const agentWorkdirEl = document.getElementById('agentWorkdir');\n const agentDetailToggle = document.getElementById('agentDetailToggle');\n const agentDetailPanel = document.getElementById('agentDetailPanel');\n const pairedAgentsSection = document.getElementById('pairedAgentsSection');\n const pairedAgentsList = document.getElementById('pairedAgentsList');\n const addAgentTitle = document.getElementById('addAgentTitle');\n\n if (!agentUrlInput || !connectBtn) return;\n\n let lastDetailData = null;\n\n // ========== 多代理列表渲染 ==========\n\n /** 把 #agentDetailArea 移回原位(已配对代理列表之后) */\n function restoreDetailArea() {\n const detailArea = document.getElementById('agentDetailArea');\n if (!detailArea) return;\n if (pairedAgentsSection.nextSibling !== detailArea) {\n pairedAgentsSection.parentNode.insertBefore(detailArea, pairedAgentsSection.nextSibling);\n }\n }\n\n /** 把 #agentDetailArea 移到活跃代理项内部,使其与当前连接的代理在同一区域 */\n function placeDetailAreaInActiveItem() {\n const detailArea = document.getElementById('agentDetailArea');\n if (!detailArea) return;\n const activeItem = pairedAgentsList.querySelector('.paired-agent-item.active');\n if (activeItem && activeItem !== detailArea.parentNode) {\n activeItem.appendChild(detailArea);\n } else if (!activeItem) {\n // 没有活跃代理时,保持在原位\n restoreDetailArea();\n }\n }\n\n /** ping 代理检查在线状态 */\n async function pingAgent(url) {\n try {\n const controller = new AbortController();\n const tid = setTimeout(() => controller.abort(), 2000);\n const resp = await fetch(`${url}/api/status`, { signal: controller.signal, cache: 'no-cache' });\n clearTimeout(tid);\n return resp.ok;\n } catch { return false; }\n }\n\n /** 渲染已配对代理列表 */\n async function renderPairedAgents() {\n // 先把详情区域移回原位,避免被 innerHTML 清空\n restoreDetailArea();\n\n const storage = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n const agents = storage.pairedAgents || [];\n const activeId = storage.activeAgentId;\n _lastActiveAgentOnline = null;\n\n if (agents.length === 0) {\n pairedAgentsSection.style.display = 'none';\n addAgentTitle.textContent = t('agentConfig.addAgentTitle');\n return;\n }\n\n pairedAgentsSection.style.display = '';\n addAgentTitle.textContent = t('agentConfig.addAnotherAgent');\n\n // 先渲染列表,状态统一显示\"检测中\",停用的显示\"已停用\",活跃的显示\"已连接\"\n pairedAgentsList.innerHTML = agents.map((a) => {\n const isActive = a.id === activeId;\n const isDisabled = !!a.disabled;\n let dotClass, statusLabel, statusClass;\n\n if (isDisabled) {\n dotClass = isActive ? 'active-disabled' : 'disabled';\n statusLabel = t('common.disabled');\n statusClass = 'disabled';\n } else if (isActive) {\n dotClass = 'active-checking';\n statusLabel = t('agentConfig.connected');\n statusClass = 'connected';\n } else {\n dotClass = 'checking';\n statusLabel = t('agentConfig.checking');\n statusClass = 'checking';\n }\n\n return `\n <div class=\"paired-agent-item${isActive ? ' active' : ''}${isDisabled ? ' disabled' : ''}\" data-agent-id=\"${a.id}\">\n <span class=\"paired-agent-dot ${dotClass}\" title=\"${statusLabel}\"></span>\n <span class=\"paired-agent-name\" data-agent-id=\"${a.id}\" data-original=\"${escHtml(a.name)}\" title=\"${t('agentConfig.clickToEditName')}\">${escHtml(a.name)}</span>\n <span class=\"paired-agent-url\">${escHtml(a.url)}</span>\n <span class=\"paired-agent-status-text ${statusClass}\">${statusLabel}</span>\n <div class=\"paired-agent-actions\">\n ${isDisabled\n ? `<button class=\"paired-agent-btn enable-btn\" data-action=\"enable\" data-id=\"${a.id}\">${t('agentConfig.enable')}</button>`\n : (isActive\n ? `<button class=\"paired-agent-btn disconnect-btn\" data-action=\"deactivate\" data-id=\"${a.id}\">${t('agentConfig.disconnect')}</button>`\n : `<button class=\"paired-agent-btn switch-btn\" data-action=\"switch\" data-id=\"${a.id}\">${t('agentConfig.connect')}</button>`\n )\n }\n ${isDisabled ? '' : `<button class=\"paired-agent-btn disable-btn\" data-action=\"disable\" data-id=\"${a.id}\">${t('agentConfig.disable')}</button>`}\n <button class=\"paired-agent-btn delete-btn\" data-action=\"delete\" data-id=\"${a.id}\">${t('common.delete')}</button>\n </div>\n </div>`;\n }).join('');\n\n bindPairedAgentEvents(agents);\n\n // 把详情区域移到活跃代理项内部,使其与当前连接的代理在同一区域\n placeDetailAreaInActiveItem();\n\n // 异步 ping 所有非停用代理,逐个回填状态\n agents.forEach((a) => {\n if (a.disabled) return; // 停用的代理跳过 ping\n pingAgent(a.url).then((online) => {\n _updateAgentItemStatus(a.id, online);\n });\n });\n }\n\n let _lastActiveAgentOnline = null;\n\n /** 更新单个代理列表项的状态 */\n function _updateAgentItemStatus(agentId, online) {\n const item = pairedAgentsList.querySelector(`[data-agent-id=\"${agentId}\"]`);\n if (!item) return;\n\n const dotEl = item.querySelector('.paired-agent-dot');\n const statusEl = item.querySelector('.paired-agent-status-text');\n const isActive = item.classList.contains('active');\n\n const dotClass = isActive\n ? (online ? 'active-online' : 'active-offline')\n : (online ? 'online' : 'offline');\n const statusLabel = isActive\n ? (online ? t('agentConfig.connected') : t('agentConfig.notConnected'))\n : (online ? t('agentConfig.online') : t('agentConfig.offline'));\n\n if (dotEl) {\n dotEl.className = `paired-agent-dot ${dotClass}`;\n dotEl.title = `${isActive ? t('agentConfig.currentInUse') : ''}${statusLabel}`;\n }\n if (statusEl) {\n statusEl.className = `paired-agent-status-text ${online ? 'online' : 'offline'}`;\n statusEl.textContent = statusLabel;\n }\n\n if (isActive && _lastActiveAgentOnline !== null && _lastActiveAgentOnline !== online) {\n chrome.runtime.sendMessage({\n type: 'AGENT_STATUS_CHANGE',\n connected: online,\n status: online ? t('agentConfig.online') : t('agentConfig.offline'),\n agentId\n }).catch(() => {});\n }\n\n if (isActive) {\n _lastActiveAgentOnline = online;\n }\n }\n\n /** 绑定代理列表事件(名称编辑 + 切换 + 删除) */\n function bindPairedAgentEvents(agents) {\n // 名称点击编辑\n pairedAgentsList.querySelectorAll('.paired-agent-name').forEach(nameEl => {\n nameEl.addEventListener('click', () => {\n if (nameEl.querySelector('input')) return; // 已在编辑中\n const agentId = nameEl.dataset.agentId;\n const original = nameEl.dataset.original;\n const input = document.createElement('input');\n input.type = 'text';\n input.value = original;\n input.className = 'paired-agent-name-input';\n input.style.cssText = 'flex:1;font-size:14px;font-weight:500;border:1px solid #667eea;border-radius:4px;padding:2px 6px;outline:none;color:#2d3748;';\n nameEl.innerHTML = '';\n nameEl.appendChild(input);\n input.focus();\n input.select();\n\n const finishEdit = async () => {\n const newName = input.value.trim() || original;\n input.remove();\n nameEl.textContent = newName;\n nameEl.dataset.original = newName;\n\n const storage = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n const currentAgents = storage.pairedAgents || [];\n const idx = currentAgents.findIndex(a => a.id === agentId);\n if (idx !== -1) {\n currentAgents[idx].name = newName;\n await chrome.storage.local.set({ pairedAgents: currentAgents });\n }\n };\n\n input.addEventListener('blur', finishEdit);\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') { input.blur(); }\n if (e.key === 'Escape') {\n input.value = original;\n input.blur();\n }\n });\n });\n });\n\n // 事件委托:切换 + 断开 + 停用/启用 + 删除\n pairedAgentsList.querySelectorAll('.paired-agent-btn').forEach(btn => {\n btn.addEventListener('click', async (e) => {\n e.stopPropagation();\n const action = btn.dataset.action;\n const agentId = btn.dataset.id;\n if (action === 'switch') {\n await switchToAgent(agentId);\n } else if (action === 'deactivate') {\n await deactivateAgent(agentId);\n } else if (action === 'disable') {\n await disableAgent(agentId);\n } else if (action === 'enable') {\n await enableAgent(agentId);\n } else if (action === 'delete') {\n await deletePairedAgent(agentId);\n }\n });\n });\n }\n\n /** 切换到指定代理 */\n async function switchToAgent(agentId) {\n const agents = await chrome.storage.local.get('pairedAgents');\n const agent = (agents.pairedAgents || []).find(a => a.id === agentId);\n if (!agent) return;\n\n // 写入 storage 设为活跃\n await chrome.storage.local.set({ activeAgentId: agentId });\n agentUrlInput.value = agent.url;\n\n // 获取代理详情并展示\n await fetchAndShowAgentDetail(agent.url, decodeSecret(agent.token));\n\n // 通知 background + Side Panel\n chrome.runtime.sendMessage({\n type: 'AGENT_CONNECTION_CHANGED',\n connected: true,\n agentId\n }).catch(() => {});\n\n showToast(t('agentConfig.connectedTo', { name: agent.name }), 'success');\n\n // 重新渲染列表\n await renderPairedAgents();\n\n // 刷新工具箱面板(MCP/Skill 数据随代理切换联动)\n refreshToolbox().catch(() => {});\n }\n\n /** 删除已配对代理 */\n async function deletePairedAgent(agentId) {\n const storage = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n const agents = storage.pairedAgents || [];\n const agent = agents.find(a => a.id === agentId);\n if (!agent) return;\n\n // 自定义确认弹窗\n const confirmed = await showCustomConfirm(\n t('agentConfig.confirmDeleteAgent', { name: agent.name, url: agent.url }),\n t('agentConfig.deleteAgentTitle')\n );\n if (!confirmed) return;\n\n const newAgents = agents.filter(a => a.id !== agentId);\n let newActiveId = storage.activeAgentId;\n\n if (storage.activeAgentId === agentId) {\n newActiveId = newAgents.length > 0 ? newAgents[0].id : null;\n }\n\n await chrome.storage.local.set({\n pairedAgents: newAgents,\n activeAgentId: newActiveId\n });\n\n // 如果删的是活跃代理,切换到第一个\n if (newActiveId && newActiveId !== storage.activeAgentId) {\n const newActive = newAgents.find(a => a.id === newActiveId);\n chrome.runtime.sendMessage({\n type: 'AGENT_CONNECTION_CHANGED',\n connected: true,\n agentId: newActiveId\n }).catch(() => {});\n } else if (!newActiveId) {\n chrome.runtime.sendMessage({\n type: 'AGENT_CONNECTION_CHANGED',\n connected: false\n }).catch(() => {});\n }\n\n showToast(t('agentConfig.agentDeleted', { name: agent.name }), 'info');\n await renderPairedAgents();\n await refreshActiveAgentUI();\n // 切换了代理时同步刷新工具箱\n if (newActiveId !== storage.activeAgentId) {\n refreshToolbox().catch(() => {});\n }\n }\n\n /** 停用指定代理 */\n async function disableAgent(agentId) {\n const storage = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n const agents = storage.pairedAgents || [];\n const agent = agents.find(a => a.id === agentId);\n if (!agent) return;\n\n // 如果停用的是当前活跃代理,先切换到其他可用代理(或断开)\n if (storage.activeAgentId === agentId) {\n const next = agents.find(a => a.id !== agentId && !a.disabled);\n if (next) {\n await chrome.storage.local.set({ activeAgentId: next.id });\n chrome.runtime.sendMessage({\n type: 'AGENT_CONNECTION_CHANGED', connected: true, agentId: next.id\n }).catch(() => {});\n } else {\n // 没有其他可用代理,断开连接\n await chrome.storage.local.set({ activeAgentId: null });\n chrome.runtime.sendMessage({\n type: 'AGENT_CONNECTION_CHANGED', connected: false\n }).catch(() => {});\n }\n }\n\n const idx = agents.findIndex(a => a.id === agentId);\n agents[idx].disabled = true;\n await chrome.storage.local.set({ pairedAgents: agents });\n\n showToast(t('agentConfig.agentDisabledToast', { name: agent.name }), 'info');\n await renderPairedAgents();\n await refreshActiveAgentUI();\n // 切换了代理时同步刷新工具箱\n if (storage.activeAgentId === agentId) {\n refreshToolbox().catch(() => {});\n }\n }\n\n /** 启用指定代理 */\n async function enableAgent(agentId) {\n const storage = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n const agents = storage.pairedAgents || [];\n const idx = agents.findIndex(a => a.id === agentId);\n if (idx === -1) return;\n\n agents[idx].disabled = false;\n await chrome.storage.local.set({ pairedAgents: agents });\n\n showToast(t('agentConfig.agentEnabledToast', { name: agents[idx].name }), 'success');\n await renderPairedAgents();\n }\n\n /** 获取活跃代理详情并更新状态 UI */\n async function fetchAndShowAgentDetail(url, token) {\n updateStatusUI('checking', t('agentConfig.fetchingAgentInfo'));\n\n try {\n const statusResp = await fetch(`${url}/api/status`, { cache: 'no-cache' });\n if (!statusResp.ok) {\n updateStatusUI('disconnected', t('agentConfig.agentUnreachable'));\n return;\n }\n const statusData = await statusResp.json();\n\n // 获取详细信息\n try {\n const detailResp = await fetch(`${url}/api/status/detail`, {\n cache: 'no-cache',\n headers: { 'Authorization': `Bearer ${token}` }\n });\n if (detailResp.ok) {\n const detailData = await detailResp.json();\n const merged = mergeDetail(statusData, detailData);\n const labelParts = [`Agent v${merged.version}`];\n if (merged.platformName && merged.arch) labelParts.push(`${merged.platformName} ${merged.arch}`);\n if (merged.nodeVersion) labelParts.push(`Node ${merged.nodeVersion}`);\n updateStatusUI('connected', labelParts.join(' | '), merged);\n } else {\n updateStatusUI('disconnected', t('agentConfig.tokenExpired'));\n }\n } catch {\n updateStatusUI('disconnected', t('agentConfig.tokenExpired'));\n }\n } catch {\n updateStatusUI('disconnected', t('agentConfig.cannotConnectAgent'));\n }\n }\n\n /** 刷新活跃代理的 UI(状态 + 详情) */\n async function refreshActiveAgentUI() {\n const storage = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n const agents = storage.pairedAgents || [];\n const activeId = storage.activeAgentId;\n\n if (!activeId || agents.length === 0) {\n const hasAgents = agents.length > 0;\n updateStatusUI('disconnected', hasAgents ? t('agentConfig.notConnectedSelect') : t('agentConfig.notConnectedAdd'));\n return;\n }\n\n const active = agents.find(a => a.id === activeId);\n if (active) {\n agentUrlInput.value = active.url;\n await fetchAndShowAgentDetail(active.url, decodeSecret(active.token));\n }\n }\n\n // ========== 原有详情渲染辅助函数 ==========\n\n function formatBytes(bytes) {\n if (bytes == null || bytes === 0) return t('agentConfig.unlimited');\n const units = ['B', 'KB', 'MB', 'GB'];\n let i = 0;\n let val = bytes;\n while (val >= 1024 && i < units.length - 1) { val /= 1024; i++; }\n return (Math.round(val * 10) / 10) + ' ' + units[i];\n }\n\n function formatDuration(ms) {\n if (ms == null || ms === 0) return t('agentConfig.unlimited');\n if (ms < 1000) return ms + 'ms';\n if (ms < 60000) return (ms / 1000).toFixed(0) + 's';\n return (ms / 60000).toFixed(1) + ' min';\n }\n\n // 格式化 Agent 运行时长(秒 → 可读格式)\n function formatUptime(seconds) {\n if (seconds == null || seconds < 0) return '-';\n if (seconds < 60) return seconds + 's';\n if (seconds < 3600) return Math.floor(seconds / 60) + 'min ' + (seconds % 60) + 's';\n if (seconds < 86400) {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n return h + 'h ' + m + 'min';\n }\n const d = Math.floor(seconds / 86400);\n const h = Math.floor((seconds % 86400) / 3600);\n return d + 'd ' + h + 'h';\n }\n\n // 格式化 CPU 时间(微秒 → 可读格式)\n function formatCpuTime(us) {\n if (us == null || us < 0) return '-';\n if (us < 1000) return us + 'μs';\n if (us < 1000000) return (us / 1000).toFixed(1) + 'ms';\n return (us / 1000000).toFixed(2) + 's';\n }\n\n // 格式化运行时长(用于进程已运行时间,始终显示具体值,不出现\"无限制\")\n function formatRunDuration(ms) {\n if (ms == null || ms < 0) return '-';\n if (ms < 1000) return ms + 'ms';\n if (ms < 60000) return (ms / 1000).toFixed(1) + 's';\n if (ms < 3600000) return (ms / 60000).toFixed(1) + 'min';\n return (ms / 3600000).toFixed(2) + 'h';\n }\n\n // 格式化运行中进程列表:空 → 空闲;非空 → 数量 + 每条命令摘要(截断 + pid + 时长)\n function formatRunningProcesses(procs) {\n if (!Array.isArray(procs) || procs.length === 0) return t('agentConfig.idle');\n return procs.map(p => {\n const cmd = (p.command || '').replace(/\\s+/g, ' ').trim();\n const short = cmd.length > 48 ? cmd.slice(0, 48) + '…' : (cmd || t('agentConfig.unknownCommand'));\n const pid = p.pid ? `pid=${escHtml(String(p.pid))}` : '';\n const dur = formatRunDuration(p.duration);\n const parts = [`<code>${escHtml(short)}</code>`, pid, escHtml(dur)].filter(Boolean);\n return parts.join(' · ');\n }).join('<br>');\n }\n\n function renderAgentDetail(data) {\n lastDetailData = data;\n if (!agentDetailPanel) return;\n\n const rows = [];\n\n if (data.version) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailVersion')}</span><span class=\"detail-value\">${escHtml(data.version)}</span></div>`);\n }\n if (data.platformName) {\n const archInfo = data.arch ? ` (${data.arch})` : '';\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailSystem')}</span><span class=\"detail-value\">${escHtml(data.platformName)}${archInfo}</span></div>`);\n } else if (data.platform) {\n const archInfo = data.arch ? ` (${data.arch})` : '';\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailSystem')}</span><span class=\"detail-value\">${escHtml(data.platform)}${archInfo}</span></div>`);\n }\n if (data.hostname) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailHostname')}</span><span class=\"detail-value\">${escHtml(data.hostname)}</span></div>`);\n }\n if (data.shell) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">Shell</span><span class=\"detail-value\">${escHtml(data.shell)}</span></div>`);\n }\n if (data.homeDir) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailHomeDir')}</span><span class=\"detail-value\">${escHtml(data.homeDir)}</span></div>`);\n }\n if (data.nodeVersion) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">Node.js</span><span class=\"detail-value\">${escHtml(data.nodeVersion)}</span></div>`);\n }\n if (data.runningProcesses != null) {\n const count = Array.isArray(data.runningProcesses) ? data.runningProcesses.length : 0;\n const label = count > 0 ? t('agentConfig.detailRunningProcessesCount', { count }) : t('agentConfig.detailRunningProcesses');\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${label}</span><span class=\"detail-value\">${formatRunningProcesses(data.runningProcesses)}</span></div>`);\n }\n if (data.resourceUsage) {\n const ru = data.resourceUsage;\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailMemoryUsage')}</span><span class=\"detail-value\">${t('agentConfig.detailMemoryDetail', { used: ru.memoryUsedMB, total: ru.memoryTotalMB, rss: ru.memoryRssMB })}</span></div>`);\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailUptime')}</span><span class=\"detail-value\">${formatUptime(ru.uptimeSeconds)}</span></div>`);\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailCpuUser')}</span><span class=\"detail-value\">${formatCpuTime(ru.cpuUser)}</span></div>`);\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailCpuSystem')}</span><span class=\"detail-value\">${formatCpuTime(ru.cpuSystem)}</span></div>`);\n }\n if (data.commandTimeout != null) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailCommandTimeout')}</span><span class=\"detail-value\">${formatDuration(data.commandTimeout)}</span></div>`);\n }\n if (data.uploadMaxSize != null) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailUploadMaxSize')}</span><span class=\"detail-value\">${formatBytes(data.uploadMaxSize)}</span></div>`);\n }\n if (data.fileMaxSize != null) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailFileMaxSize')}</span><span class=\"detail-value\">${formatBytes(data.fileMaxSize)}</span></div>`);\n }\n if (data.allowedPaths && data.allowedPaths.length > 0) {\n const pathsHtml = data.allowedPaths.map(p => `<code>${escHtml(p)}</code>`).join('<br>');\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailAllowedPaths')}</span><span class=\"detail-value\">${pathsHtml}</span></div>`);\n }\n if (data.pairCodeTTL != null) {\n rows.push(`<div class=\"detail-row\"><span class=\"detail-label\">${t('agentConfig.detailPairCodeTTL')}</span><span class=\"detail-value\">${data.pairCodeTTL}s</span></div>`);\n }\n\n agentDetailPanel.innerHTML = rows.join('');\n agentDetailToggle.style.display = 'block';\n }\n\n function escHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n function updateStatusUI(status, message, detailData) {\n agentStatus.className = 'agent-status ' + status;\n statusText.textContent = message;\n\n // 配对表单始终可见,方便随时添加新代理\n pairCodeGroup.style.display = '';\n\n if (status === 'connected') {\n agentStatus.className = 'agent-status connected';\n\n if (detailData) {\n agentInfo.style.display = '';\n if (detailData.workdir) {\n agentWorkdirEl.innerHTML = t('agentConfig.detailWorkdir', { dir: detailData.workdir });\n }\n renderAgentDetail(detailData);\n }\n } else if (status === 'checking') {\n agentStatus.className = 'agent-status checking';\n } else {\n agentInfo.style.display = 'none';\n agentStatus.className = 'agent-status disconnected';\n if (agentDetailToggle) agentDetailToggle.style.display = 'none';\n if (agentDetailPanel) agentDetailPanel.innerHTML = '';\n lastDetailData = null;\n }\n }\n\n function mergeDetail(statusData, detailData) {\n return {\n version: detailData?.version || statusData?.version,\n platformName: detailData?.platformName || statusData?.platformName,\n platform: detailData?.platform || statusData?.platform,\n arch: detailData?.arch || statusData?.arch,\n hostname: detailData?.hostname || statusData?.hostname,\n shell: detailData?.shell || statusData?.shell,\n homeDir: detailData?.homeDir || statusData?.homeDir,\n nodeVersion: detailData?.nodeVersion || statusData?.nodeVersion,\n searchTools: detailData?.searchTools || statusData?.searchTools,\n runningProcesses: detailData?.runningProcesses ?? statusData?.runningProcesses,\n workdir: detailData?.workdir || statusData?.workdir || '',\n allowedPaths: detailData?.allowedPaths || statusData?.allowedPaths || [],\n commandTimeout: detailData?.commandTimeout || statusData?.commandTimeout,\n fileMaxSize: detailData?.fileMaxSize || statusData?.fileMaxSize,\n uploadMaxSize: detailData?.uploadMaxSize ?? statusData?.uploadMaxSize,\n pairCodeTTL: detailData?.pairCodeTTL || statusData?.pairCodeTTL,\n resourceUsage: detailData?.resourceUsage || null\n };\n }\n\n // ========== 连接 / 断开 ==========\n\n async function connectToAgent() {\n const url = agentUrlInput.value.trim() || 'http://127.0.0.1:18910';\n const code = pairCodeInput.value.trim();\n\n if (!code || code.length !== 6) {\n showToast(t('agentConfig.pairCodeRequired'), 'warning');\n return;\n }\n\n updateStatusUI('checking', t('agentConfig.pairing'));\n connectBtn.disabled = true;\n connectBtn.textContent = t('agentConfig.pairingBtn');\n\n try {\n const response = await fetch(`${url}/api/pair`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code, extensionId: chrome.runtime.id })\n });\n const data = await response.json();\n\n if (data.success && data.token) {\n const customName = agentNameInput?.value.trim();\n let name = customName || null;\n if (!name) {\n try {\n const [statusResp, detailResp] = await Promise.all([\n fetch(`${url}/api/status`),\n fetch(`${url}/api/status/detail`, { headers: { 'Authorization': `Bearer ${data.token}` } })\n ]);\n \n const statusData = statusResp.ok ? await statusResp.json() : {};\n const detailData = detailResp.ok ? await detailResp.json() : {};\n \n const parts = [];\n if (detailData.platformName) parts.push(detailData.platformName);\n else if (statusData.platformName) parts.push(statusData.platformName);\n if (detailData.arch) parts.push(detailData.arch);\n else if (statusData.arch) parts.push(statusData.arch);\n name = parts.length > 0 ? parts.join(' ') : new URL(url).hostname;\n } catch { name = new URL(url).hostname; }\n if (!name) name = t('agentConfig.unnamedAgent');\n }\n\n // 生成 ID 并加入列表\n const id = `pa_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n const newAgent = { id, name, url, token: data.token, pairedAt: new Date().toISOString() };\n\n const storage = await chrome.storage.local.get('pairedAgents');\n const agents = storage.pairedAgents || [];\n agents.push({ ...newAgent, token: encodeSecret(newAgent.token) });\n await chrome.storage.local.set({ pairedAgents: agents, activeAgentId: id });\n\n pairCodeInput.value = '';\n if (agentNameInput) agentNameInput.value = '';\n showToast(t('agentConfig.pairSuccess', { name }), 'success');\n\n // 展示详情\n try {\n const [statusResp2, detailResp2] = await Promise.all([\n fetch(`${url}/api/status`),\n fetch(`${url}/api/status/detail`, { headers: { 'Authorization': `Bearer ${data.token}` } })\n ]);\n if (statusResp2.ok && detailResp2.ok) {\n const merged = mergeDetail(await statusResp2.json(), await detailResp2.json());\n const labelParts = [`Agent v${merged.version}`];\n if (merged.platformName && merged.arch) labelParts.push(`${merged.platformName} ${merged.arch}`);\n if (merged.nodeVersion) labelParts.push(`Node ${merged.nodeVersion}`);\n updateStatusUI('connected', labelParts.join(' | '), merged);\n }\n } catch { /* ignore */ }\n\n // 重新渲染列表\n await renderPairedAgents();\n\n chrome.runtime.sendMessage({\n type: 'AGENT_CONNECTION_CHANGED',\n connected: true,\n agentId: id\n }).catch(() => {});\n } else {\n updateStatusUI('disconnected', t('agentConfig.pairFailed', { error: data.error || t('agentConfig.unknownError') }));\n showToast(data.error || t('agentConfig.pairFailedShort'), 'error');\n }\n } catch (err) {\n updateStatusUI('disconnected', t('agentConfig.connectFailed'));\n showToast(t('agentConfig.cannotConnectAgentMsg', { error: err.message }), 'error');\n } finally {\n connectBtn.disabled = false;\n connectBtn.textContent = t('agentConfig.connect');\n }\n }\n\n /** 断开连接:仅取消激活,不删除配对信息 */\n async function deactivateAgent(agentId) {\n const storage = await chrome.storage.local.get(['pairedAgents', 'activeAgentId']);\n if (storage.activeAgentId !== agentId) return;\n\n await chrome.storage.local.set({ activeAgentId: null });\n\n updateStatusUI('disconnected', t('agentConfig.notConnectedSelect'));\n showToast(t('agentConfig.disconnectedToast'), 'info');\n\n await renderPairedAgents();\n\n chrome.runtime.sendMessage({\n type: 'AGENT_CONNECTION_CHANGED',\n connected: false\n }).catch(() => {});\n }\n\n // 详情面板折叠切换\n if (agentDetailToggle && agentDetailPanel) {\n agentDetailToggle.addEventListener('click', () => {\n const isOpen = getComputedStyle(agentDetailPanel).display !== 'none';\n agentDetailPanel.style.display = isOpen ? 'none' : 'block';\n agentDetailToggle.textContent = isOpen ? t('agentConfig.detailToggle') : t('agentConfig.detailToggleOpen');\n agentDetailToggle.classList.toggle('open', !isOpen);\n });\n }\n\n // 绑定事件\n connectBtn.addEventListener('click', connectToAgent);\n\n pairCodeInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') connectToAgent();\n });\n\n // 启动时:渲染列表 + 展示活跃代理状态\n (async () => {\n await renderPairedAgents();\n await refreshActiveAgentUI();\n })();\n\n // 通知 background:配置页面已打开,触发全量心跳\n chrome.runtime.sendMessage({ type: 'OPTIONS_PAGE_OPEN' }).catch(() => {});\n\n // 监听 background 发送的代理状态变化消息,实时更新配置页面 UI\n chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {\n if (message.type === 'AGENT_STATUS_CHANGE') {\n (async () => {\n await renderPairedAgents();\n await refreshActiveAgentUI();\n // 同步刷新工具箱面板\n refreshToolbox().catch(() => {});\n })();\n }\n });\n\n // 页面关闭时通知 background 停止全量心跳\n window.addEventListener('beforeunload', () => {\n chrome.runtime.sendMessage({ type: 'OPTIONS_PAGE_CLOSED' }).catch(() => {});\n });\n}"],"mappings":"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"}
|