From 10d2310eb4faee02116025a973f7d03d9a1b7de4 Mon Sep 17 00:00:00 2001 From: cc-dan Date: Sat, 14 Mar 2026 04:55:35 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=9A=E7=9F=A5AI=E6=91=98=E8=A6=81?= =?UTF-8?q?=20+=20=E9=80=9A=E7=9F=A5=E9=85=8D=E7=BD=AE=E6=94=B6=E8=BF=9B?= =?UTF-8?q?=E4=BA=8C=E7=BA=A7=E8=8F=9C=E5=8D=95=20(v1.2.9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 ++ public/app.js | 277 +++++++++++++++++++++++++------------------------- 2 files changed, 148 insertions(+), 136 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c81a682..b5bf088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 更新记录 +## v1.2.9 + +### 新功能 + +- **通知 AI 摘要** — 任务完成时调用 Claude API 生成摘要内容推送,支持正常完成/异常/上下文压缩等多种情况分类,摘要 API 凭证可独立配置或复用活跃 Claude 模板/Codex Profile,各渠道按字符限制自动截断,摘要失败时降级为原始信息 +- **通知配置收进二级菜单** — Claude 和 Codex 设置面板中的通知区域改为 nav-card 入口,点击进入独立子页,与主题设置风格统一 + ## v1.2.8 ### 新功能 diff --git a/public/app.js b/public/app.js index 7a56d3b..2ce7369 100644 --- a/public/app.js +++ b/public/app.js @@ -222,6 +222,134 @@ `; } + function buildNotifyEntryHtml(config) { + const provider = config?.provider || 'off'; + const providerLabel = PROVIDER_OPTIONS.find(o => o.value === provider)?.label || '关闭'; + const summaryOn = config?.summary?.enabled ? '摘要已启用' : '摘要关闭'; + const meta = provider === 'off' ? '未启用' : `${providerLabel} · ${summaryOn}`; + return ` +
通知
+ + `; + } + + function openNotifySubpage() { + send({ type: 'get_notify_config' }); + + const overlay = document.createElement('div'); + overlay.className = 'settings-overlay settings-subpage-overlay'; + overlay.style.zIndex = '10001'; + + const panel = document.createElement('div'); + panel.className = 'settings-panel settings-subpage-panel'; + panel.innerHTML = ` +
+ +
+
Notification
+

通知设置

+
+ +
+
+ + +
+
+
+
+ + +
+
+ `; + + overlay.appendChild(panel); + document.body.appendChild(overlay); + + const providerSelect = panel.querySelector('#notify-provider'); + const fieldsDiv = panel.querySelector('#notify-fields'); + const summaryArea = panel.querySelector('#notify-summary-area'); + const statusDiv = panel.querySelector('#notify-status'); + const testBtn = panel.querySelector('#notify-test-btn'); + const saveBtn = panel.querySelector('#notify-save-btn'); + + let currentNotifyConfig = null; + + function renderFields(provider) { + renderNotifyFields(fieldsDiv, currentNotifyConfig, provider); + if (summaryArea) { + summaryArea.innerHTML = buildSummarySettingsHtml(currentNotifyConfig); + bindSummarySettingsEvents(panel); + } + } + + function collectConfig() { + return collectNotifyConfigFromPanel(panel, currentNotifyConfig, providerSelect.value); + } + + function showStatus(msg, type) { + statusDiv.textContent = msg; + statusDiv.className = 'settings-status ' + (type || ''); + } + + function refreshParentSummary(config) { + const provider = config?.provider || 'off'; + const providerLabel = PROVIDER_OPTIONS.find(o => o.value === provider)?.label || '关闭'; + const summaryOn = config?.summary?.enabled ? '摘要已启用' : '摘要关闭'; + const meta = provider === 'off' ? '未启用' : `${providerLabel} · ${summaryOn}`; + document.querySelectorAll('[data-notify-summary]').forEach(el => { el.textContent = meta; }); + } + + const savedOnNotifyConfig = _onNotifyConfig; + _onNotifyConfig = (config) => { + currentNotifyConfig = config; + providerSelect.value = config.provider || 'off'; + renderFields(config.provider || 'off'); + if (savedOnNotifyConfig) savedOnNotifyConfig(config); + }; + + const savedOnNotifyTestResult = _onNotifyTestResult; + _onNotifyTestResult = (msg) => { + showStatus(msg.message, msg.success ? 'success' : 'error'); + if (savedOnNotifyTestResult) savedOnNotifyTestResult(msg); + }; + + providerSelect.addEventListener('change', () => renderFields(providerSelect.value)); + + testBtn.addEventListener('click', () => { + const config = collectConfig(); + send({ type: 'save_notify_config', config }); + showStatus('正在发送测试消息...', ''); + send({ type: 'test_notify' }); + }); + + saveBtn.addEventListener('click', () => { + const config = collectConfig(); + send({ type: 'save_notify_config', config }); + refreshParentSummary(config); + showStatus('已保存', 'success'); + }); + + const closeSubpage = () => { + _onNotifyConfig = savedOnNotifyConfig; + _onNotifyTestResult = savedOnNotifyTestResult; + if (overlay.parentNode) overlay.parentNode.removeChild(overlay); + }; + + panel.querySelector('.settings-back').addEventListener('click', closeSubpage); + panel.querySelector('.settings-close').addEventListener('click', closeSubpage); + overlay.addEventListener('click', (e) => { if (e.target === overlay) closeSubpage(); }); + } + function openThemeSubpage() { const overlay = document.createElement('div'); overlay.className = 'settings-overlay settings-subpage-overlay'; @@ -3025,7 +3153,6 @@ } function showCodexSettingsPanel() { - send({ type: 'get_notify_config' }); send({ type: 'get_codex_config' }); const overlay = document.createElement('div'); @@ -3060,20 +3187,7 @@
-
通知设置
-
- - -
-
-
-
- - -
-
+ ${buildNotifyEntryHtml(null)}
@@ -3089,6 +3203,8 @@ document.body.appendChild(overlay); const themePageBtn = panel.querySelector('[data-open-theme-page]'); if (themePageBtn) themePageBtn.addEventListener('click', openThemeSubpage); + const notifyPageBtn = panel.querySelector('[data-open-notify-page]'); + if (notifyPageBtn) notifyPageBtn.addEventListener('click', openNotifySubpage); const closeBtn = panel.querySelector('.settings-close'); const codexModeSelect = panel.querySelector('#codex-mode'); @@ -3096,17 +3212,10 @@ const codexStatus = panel.querySelector('#codex-status'); const codexSaveBtn = panel.querySelector('#codex-save-btn'); - const providerSelect = panel.querySelector('#notify-provider'); - const fieldsDiv = panel.querySelector('#notify-fields'); - const summaryArea = panel.querySelector('#notify-summary-area'); - const statusDiv = panel.querySelector('#notify-status'); - const testBtn = panel.querySelector('#notify-test-btn'); - const saveBtn = panel.querySelector('#notify-save-btn'); const pwOpenModalBtn = panel.querySelector('#pw-open-modal-btn'); const checkUpdateBtn = panel.querySelector('#check-update-btn'); const updateStatusEl = panel.querySelector('#update-status'); - let currentNotifyConfig = null; let currentCodexConfig = null; let codexEditingProfiles = []; let codexActiveProfile = ''; @@ -3117,23 +3226,6 @@ codexStatus.className = 'settings-status ' + (type || ''); } - function renderFields(provider) { - renderNotifyFields(fieldsDiv, currentNotifyConfig, provider); - if (summaryArea) { - summaryArea.innerHTML = buildSummarySettingsHtml(currentNotifyConfig); - bindSummarySettingsEvents(panel); - } - } - - function collectNotifyConfig() { - return collectNotifyConfigFromPanel(panel, currentNotifyConfig, providerSelect.value); - } - - function showNotifyStatus(msg, type) { - statusDiv.textContent = msg; - statusDiv.className = 'settings-status ' + (type || ''); - } - function renderCodexProfileArea() { const mode = codexModeSelect.value; if (mode === 'local') { @@ -3293,17 +3385,6 @@ renderCodexProfileArea(); }; - _onNotifyConfig = (config) => { - currentNotifyConfig = config; - providerSelect.value = config.provider || 'off'; - renderFields(config.provider || 'off'); - }; - - _onNotifyTestResult = (msg) => { - showNotifyStatus(msg.message, msg.success ? 'success' : 'error'); - }; - - providerSelect.addEventListener('change', () => renderFields(providerSelect.value)); codexModeSelect.addEventListener('change', renderCodexProfileArea); codexSaveBtn.addEventListener('click', () => { @@ -3321,19 +3402,6 @@ showCodexStatus('已保存', 'success'); }); - testBtn.addEventListener('click', () => { - const config = collectNotifyConfig(); - send({ type: 'save_notify_config', config }); - showNotifyStatus('正在发送测试消息...', ''); - send({ type: 'test_notify' }); - }); - - saveBtn.addEventListener('click', () => { - const config = collectNotifyConfig(); - send({ type: 'save_notify_config', config }); - showNotifyStatus('已保存', 'success'); - }); - pwOpenModalBtn.addEventListener('click', openPasswordModal); checkUpdateBtn.addEventListener('click', () => { @@ -3369,8 +3437,7 @@ showCodexSettingsPanel(); return; } - // Request current configs - send({ type: 'get_notify_config' }); + // Request current configs (notify config is loaded on demand inside subpage) send({ type: 'get_model_config' }); const overlay = document.createElement('div'); @@ -3406,20 +3473,7 @@
-
通知设置
-
- - -
-
-
-
- - -
-
+ ${buildNotifyEntryHtml(null)}
@@ -3435,6 +3489,8 @@ document.body.appendChild(overlay); const themePageBtn = panel.querySelector('[data-open-theme-page]'); if (themePageBtn) themePageBtn.addEventListener('click', openThemeSubpage); + const notifyPageBtn2 = panel.querySelector('[data-open-notify-page]'); + if (notifyPageBtn2) notifyPageBtn2.addEventListener('click', openNotifySubpage); // === Model Config UI === const modelModeSelect = panel.querySelector('#model-mode'); @@ -3696,64 +3752,10 @@ renderModelCustomArea(); }; - // === Notify Config UI === - const providerSelect = panel.querySelector('#notify-provider'); - const fieldsDiv = panel.querySelector('#notify-fields'); - const summaryArea = panel.querySelector('#notify-summary-area'); - const statusDiv = panel.querySelector('#notify-status'); + // === Notify Config UI (moved to subpage) === + // notify config is handled by openNotifySubpage() + const closeBtn = panel.querySelector('.settings-close'); - const testBtn = panel.querySelector('#notify-test-btn'); - const saveBtn = panel.querySelector('#notify-save-btn'); - - let currentConfig = null; - - function renderFields(provider) { - renderNotifyFields(fieldsDiv, currentConfig, provider); - if (summaryArea) { - summaryArea.innerHTML = buildSummarySettingsHtml(currentConfig); - bindSummarySettingsEvents(panel); - } - } - - providerSelect.addEventListener('change', () => renderFields(providerSelect.value)); - - function collectConfig() { - return collectNotifyConfigFromPanel(panel, currentConfig, providerSelect.value); - } - - function showStatus(msg, type) { - statusDiv.textContent = msg; - statusDiv.className = 'settings-status ' + type; - } - - _onNotifyConfig = (config) => { - currentConfig = config; - providerSelect.value = config.provider || 'off'; - renderFields(config.provider || 'off'); - }; - - _onNotifyTestResult = (msg) => { - showStatus(msg.message, msg.success ? 'success' : 'error'); - }; - - closeBtn.addEventListener('click', hideSettingsPanel); - overlay.addEventListener('click', (e) => { if (e.target === overlay) hideSettingsPanel(); }); - - testBtn.addEventListener('click', () => { - // Save first then test - const config = collectConfig(); - send({ type: 'save_notify_config', config }); - showStatus('正在发送测试消息...', ''); - send({ type: 'test_notify' }); - }); - - saveBtn.addEventListener('click', () => { - const config = collectConfig(); - send({ type: 'save_notify_config', config }); - showStatus('已保存', 'success'); - }); - - // Password change button -> opens modal const pwOpenModalBtn = panel.querySelector('#pw-open-modal-btn'); pwOpenModalBtn.addEventListener('click', openPasswordModal); @@ -3786,6 +3788,9 @@ const _origOnUpdateInfo = window._ccOnUpdateInfo; window._ccOnUpdateInfo = (info) => { if (_onUpdateInfo) _onUpdateInfo(info); }; + closeBtn.addEventListener('click', hideSettingsPanel); + overlay.addEventListener('click', (e) => { if (e.target === overlay) hideSettingsPanel(); }); + document.addEventListener('keydown', _settingsEscape); }