codexmate 0.0.54 → 0.0.56

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.
Files changed (42) hide show
  1. package/README.md +3 -0
  2. package/README.vi.md +172 -0
  3. package/README.zh.md +2 -0
  4. package/cli/local-bridge.js +221 -22
  5. package/cli.js +666 -6
  6. package/package.json +1 -1
  7. package/web-ui/app.js +40 -0
  8. package/web-ui/logic.claude.mjs +8 -0
  9. package/web-ui/modules/app.computed.session.mjs +210 -0
  10. package/web-ui/modules/app.methods.claude-config.mjs +196 -13
  11. package/web-ui/modules/app.methods.codex-config.mjs +294 -65
  12. package/web-ui/modules/app.methods.index.mjs +4 -0
  13. package/web-ui/modules/app.methods.navigation.mjs +7 -1
  14. package/web-ui/modules/app.methods.provider-cache.mjs +223 -0
  15. package/web-ui/modules/app.methods.providers.mjs +15 -1
  16. package/web-ui/modules/app.methods.runtime.mjs +7 -2
  17. package/web-ui/modules/app.methods.session-actions.mjs +36 -0
  18. package/web-ui/modules/app.methods.session-browser.mjs +3 -0
  19. package/web-ui/modules/app.methods.session-trash.mjs +3 -0
  20. package/web-ui/modules/app.methods.startup-claude.mjs +47 -4
  21. package/web-ui/modules/app.methods.web-ui-preferences.mjs +161 -0
  22. package/web-ui/modules/i18n/locales/en.mjs +106 -3
  23. package/web-ui/modules/i18n/locales/ja.mjs +103 -3
  24. package/web-ui/modules/i18n/locales/vi.mjs +950 -0
  25. package/web-ui/modules/i18n/locales/zh-tw.mjs +103 -3
  26. package/web-ui/modules/i18n/locales/zh.mjs +103 -3
  27. package/web-ui/modules/provider-default-names.mjs +25 -0
  28. package/web-ui/partials/index/layout-header.html +18 -5
  29. package/web-ui/partials/index/modal-health-check.html +69 -5
  30. package/web-ui/partials/index/modals-basic.html +182 -0
  31. package/web-ui/partials/index/panel-config-codex.html +2 -2
  32. package/web-ui/partials/index/panel-sessions.html +99 -19
  33. package/web-ui/partials/index/panel-settings.html +20 -0
  34. package/web-ui/res/web-ui-render.precompiled.js +742 -113
  35. package/web-ui/session-helpers.mjs +4 -1
  36. package/web-ui/styles/layout-shell.css +51 -1
  37. package/web-ui/styles/responsive.css +98 -0
  38. package/web-ui/styles/sessions-preview.css +212 -2
  39. package/web-ui/styles/sessions-toolbar-trash.css +61 -18
  40. package/web-ui/styles/settings-panel.css +398 -0
  41. package/web-ui/styles/skills-list.css +122 -0
  42. package/web-ui/styles/titles-cards.css +52 -0
@@ -436,6 +436,8 @@ const zhTw = Object.freeze({
436
436
  'toast.apply.fail': '應用設定失敗',
437
437
  'toast.check.success': '檢查通過',
438
438
  'toast.check.fail': '檢查失敗',
439
+ 'toast.health.noFailedProviderSelection': '請先選擇至少一個失敗提供商',
440
+ 'toast.health.deleteFailedProvidersDone': '已刪除 {count} 個失敗提供商',
439
441
  'toast.noChanges': '未檢測到改動',
440
442
  'toast.template.loadFail': '載入模板失敗',
441
443
  'toast.template.empty': '模板不能為空',
@@ -659,6 +661,32 @@ const zhTw = Object.freeze({
659
661
  'sessions.preview.rerender': '重新渲染',
660
662
  'sessions.preview.preparing': '正在準備會話內容...',
661
663
  'sessions.preview.clipped': '僅展示最近 {count} 條消息。',
664
+ 'sessions.workspace.kicker': '工作記憶',
665
+ 'sessions.workspace.title': '會話利用摘要',
666
+ 'sessions.workspace.subtitle': '從已載入的 {count} 條消息提取線索、命令、文件、連結與風險。',
667
+ 'sessions.workspace.copy': '複製工作簡報',
668
+ 'sessions.workspace.copy.title': '會話工作簡報',
669
+ 'sessions.workspace.copy.source': '來源',
670
+ 'sessions.workspace.copy.path': '路徑',
671
+ 'sessions.workspace.copy.empty': '暫無可複製的會話摘要',
672
+ 'sessions.workspace.copy.success': '已複製工作簡報',
673
+ 'sessions.workspace.metric.messages': '消息',
674
+ 'sessions.workspace.metric.user': '使用者',
675
+ 'sessions.workspace.metric.assistant': '助手',
676
+ 'sessions.workspace.metric.commands': '命令',
677
+ 'sessions.workspace.metric.artifacts': '材料',
678
+ 'sessions.workspace.metric.risks': '風險/待辦',
679
+ 'sessions.workspace.signals': '任務線索',
680
+ 'sessions.workspace.commands': '可複用命令',
681
+ 'sessions.workspace.files': '相關文件',
682
+ 'sessions.workspace.links': '相關連結',
683
+ 'sessions.workspace.risks': '風險與待辦',
684
+ 'sessions.workspace.nextSteps': '下一步',
685
+ 'sessions.workspace.empty': '暫無明顯線索,可從下方原始記錄繼續查看。',
686
+ 'sessions.workspace.noneCommands': '暫無命令線索',
687
+ 'sessions.workspace.noneFiles': '暫無文件線索',
688
+ 'sessions.workspace.noneLinks': '暫無連結線索',
689
+ 'sessions.workspace.noneRisks': '暫未識別到風險或待辦',
662
690
  'sessions.preview.shownCount': '已顯示 {shown} / {total} 條',
663
691
  'sessions.preview.loadMore': '載入更多(剩餘 {remain})',
664
692
  'sessions.preview.loadingMore': '正在載入更早消息...',
@@ -894,7 +922,18 @@ const zhTw = Object.freeze({
894
922
  'config.health.title': '設定健康檢查',
895
923
  'config.health.run': '運行檢查',
896
924
  'config.health.running': '檢查中...',
897
- 'config.health.hint': '會批量探測所有提供商可用性,並重新整理延遲顯示。',
925
+ 'config.health.hint': '僅探測目前選中的提供商;未選提供商的遠端異常不計入本次錯誤。',
926
+ 'config.health.codexHint': '透過本機橋接探測目前 Codex 路由 provider、endpoint、鑑權與模型。',
927
+ 'config.health.providersSummary': '提供商:共 {total} · 正常 {green} · 設定問題 {yellow} · 失敗 {red}',
928
+ 'config.health.failedProviders.title': '待刪除的失敗提供商',
929
+ 'config.health.failedProviders.selectAria': '選擇失敗提供商 {name} 進行刪除',
930
+ 'config.health.failedProviders.clearSelection': '清空選擇',
931
+ 'config.health.failedProviders.selectAll': '全選',
932
+ 'config.health.failedProviders.selectAllAria': '全選可刪除的失敗提供商',
933
+ 'config.health.failedProviders.notDeletable': '保留/系統提供商,不能在這裡刪除。',
934
+ 'config.health.failedProviders.writeRequired': '刪除前請先打開本 tab 的提供商寫入開關。',
935
+ 'config.health.failedProviders.deleteSelected': '移除所選設定',
936
+ 'config.health.failedProviders.deleting': '刪除中...',
898
937
  'config.health.progress': '已完成 {done}/{total} · 失敗 {failed}',
899
938
  'config.health.ok': '檢查通過',
900
939
  'config.health.fail': '檢查未通過',
@@ -1088,6 +1127,39 @@ const zhTw = Object.freeze({
1088
1127
  'settings.tabs.aria': '設定分類',
1089
1128
  'settings.quickSettings.title': '快捷設定',
1090
1129
  'settings.language.sideLabel': '語言:{language}',
1130
+ 'announcement.providerCache.open': '功能公告',
1131
+ 'announcement.project.eyebrow': '新手入口',
1132
+ 'announcement.project.closeAria': '關閉功能公告',
1133
+ 'announcement.project.primaryAction': '進入工作台',
1134
+ 'announcement.project.title': '你的 AI 工具入口在這裡',
1135
+ 'announcement.project.subtitle': '從這裡連接模型、找回對話、查看用量,並把常用維護動作放到一個工作台裡。',
1136
+ 'announcement.project.features.aria': 'Codex Mate 功能概覽',
1137
+ 'announcement.project.feature.config.title': '連接模型',
1138
+ 'announcement.project.feature.config.meta': '配置常用 AI 工具的模型和服務地址。',
1139
+ 'announcement.project.feature.sessions.title': '找回對話',
1140
+ 'announcement.project.feature.sessions.meta': '按來源搜尋、匯出或清理歷史會話。',
1141
+ 'announcement.project.feature.usage.title': '查看花費',
1142
+ 'announcement.project.feature.usage.meta': '看最近和長期的本地用量趨勢。',
1143
+ 'announcement.project.feature.tasks.title': '追蹤任務',
1144
+ 'announcement.project.feature.tasks.meta': '查看計畫、佇列和執行記錄。',
1145
+ 'announcement.project.feature.skills.title': '複用工作流',
1146
+ 'announcement.project.feature.skills.meta': '管理 Skills 和提示詞範本。',
1147
+ 'announcement.project.feature.data.title': '整理資料',
1148
+ 'announcement.project.feature.data.meta': '處理備份、匯入、回收站和快取。',
1149
+ 'announcement.project.status.aria': '目前工作台狀態',
1150
+ 'announcement.project.status.provider': 'Provider',
1151
+ 'announcement.project.status.model': '目前模型',
1152
+ 'announcement.project.status.cacheFiles': '快取檔案',
1153
+ 'announcement.project.cache.title': '進階維護狀態',
1154
+ 'announcement.project.cache.meta': '僅在需要排查或同步時展開;日常使用可以忽略。',
1155
+ 'announcement.project.cache.files': '快取檔案',
1156
+ 'announcement.project.cache.providers': 'Provider 摘要',
1157
+ 'announcement.project.cache.groups': '快取分組',
1158
+ 'announcement.project.cache.groupList': 'Provider 快取分組摘要',
1159
+ 'announcement.project.cache.groupSummary': '{files} 檔案 · {providers} provider',
1160
+ 'announcement.project.cache.sync': '同步快取',
1161
+ 'announcement.project.cache.refresh': '重新整理摘要',
1162
+ 'announcement.project.cache.details': '查看快取詳情',
1091
1163
  'settings.language.title': '語言',
1092
1164
  'settings.language.meta': '選擇 Web UI 的顯示語言',
1093
1165
  'settings.language.label': '界面語言',
@@ -1095,7 +1167,7 @@ const zhTw = Object.freeze({
1095
1167
  'settings.sharePrefix.title': '分享命令前綴',
1096
1168
  'settings.sharePrefix.meta': '影響 Web UI 裡“複製分享命令”的前綴',
1097
1169
  'settings.sharePrefix.label': '前綴',
1098
- 'settings.sharePrefix.hint': '預設走項目內 npm start,也可切到全局 codexmate。該設定會緩存到瀏覽器本地。',
1170
+ 'settings.sharePrefix.hint': '預設走項目內 npm start,也可切到全局 codexmate。該設定會寫入 ~/.codexmate/preferences.json。',
1099
1171
  'settings.backup.title': '資料備份',
1100
1172
  'settings.backup.meta': '匯出 / 匯入 Claude 與 Codex 設定',
1101
1173
  'settings.claude.title': 'Claude 設定',
@@ -1109,6 +1181,34 @@ const zhTw = Object.freeze({
1109
1181
  'settings.backup.importCodex': '匯入 ~/.codex 備份',
1110
1182
  'settings.importing': '匯入中...',
1111
1183
 
1184
+ 'settings.providerCache.title': 'Provider 快取記錄',
1185
+ 'settings.providerCache.meta': '查看 ~/.codexmate 內的 Claude / Codex / OpenCode 快取',
1186
+ 'settings.providerCache.open': '查看快取記錄',
1187
+ 'settings.providerCache.sync': '同步快取記錄',
1188
+ 'settings.providerCache.syncing': '同步中...',
1189
+ 'settings.providerCache.loading': '載入中...',
1190
+ 'settings.providerCache.hint': '只讀展示快取文件,敏感欄位會自動遮罩。同步會把目前 provider 設定寫入 ~/.codexmate 快取文件。',
1191
+ 'modal.providerCache.title': 'Provider 快取記錄',
1192
+ 'modal.providerCache.root': '快取目錄',
1193
+ 'modal.providerCache.refresh': '重新整理',
1194
+ 'modal.providerCache.refreshing': '重新整理中...',
1195
+ 'modal.providerCache.sync': '同步',
1196
+ 'modal.providerCache.syncing': '同步中...',
1197
+ 'modal.providerCache.syncSucceeded': '已同步 {count} 個 provider 到 {fileCount} 個快取文件',
1198
+ 'modal.providerCache.syncFailed': '同步快取記錄失敗',
1199
+ 'modal.providerCache.noSyncableProviders': '沒有可同步的 provider',
1200
+ 'modal.providerCache.loading': '正在載入快取記錄...',
1201
+ 'modal.providerCache.loadedAt': '載入時間',
1202
+ 'modal.providerCache.groupMeta': '已發現 {count} 個快取文件',
1203
+ 'modal.providerCache.empty': '沒有發現快取文件',
1204
+ 'modal.providerCache.providerCount': '{count} 個 provider',
1205
+ 'modal.providerCache.rawJsonOnly': '未識別到 provider 摘要,顯示原始 JSON',
1206
+ 'modal.providerCache.tooLarge': '文件過大,已跳過 JSON 讀取',
1207
+ 'modal.providerCache.parseFailed': 'JSON 解析失敗',
1208
+ 'modal.providerCache.rawJson': '原始 JSON',
1209
+ 'modal.providerCache.errorDetails': '錯誤詳情',
1210
+ 'modal.providerCache.loadFailed': '載入快取記錄失敗',
1211
+
1112
1212
  'settings.trashConfig.title': '回收站設定',
1113
1213
  'settings.trashConfig.meta': '回收站開關與自動清理天數',
1114
1214
  'settings.deleteBehavior.title': '會話刪除行為',
@@ -1240,7 +1340,7 @@ const zhTw = Object.freeze({
1240
1340
  'claude.health.title': '設定健康檢查',
1241
1341
  'claude.health.run': '運行檢查',
1242
1342
  'claude.health.running': '檢查中...',
1243
- 'claude.health.hint': '會批量探測所有 Claude 設定可用性,並重新整理延遲顯示。',
1343
+ 'claude.health.hint': '僅探測目前選中的 Claude 設定;未選設定的遠端異常不計入本次錯誤。',
1244
1344
  'claude.health.progress': '已完成 {done}/{total} · 失敗 {failed}',
1245
1345
  'claude.md.title': 'CLAUDE.md',
1246
1346
  'claude.md.open': '打開 CLAUDE.md',
@@ -436,6 +436,8 @@ const zh = Object.freeze({
436
436
  'toast.apply.fail': '应用配置失败',
437
437
  'toast.check.success': '检查通过',
438
438
  'toast.check.fail': '检查失败',
439
+ 'toast.health.noFailedProviderSelection': '请先选择至少一个失败提供商',
440
+ 'toast.health.deleteFailedProvidersDone': '已删除 {count} 个失败提供商',
439
441
  'toast.noChanges': '未检测到改动',
440
442
  'toast.template.loadFail': '加载模板失败',
441
443
  'toast.template.empty': '模板不能为空',
@@ -659,6 +661,32 @@ const zh = Object.freeze({
659
661
  'sessions.preview.rerender': '重新渲染',
660
662
  'sessions.preview.preparing': '正在准备会话内容...',
661
663
  'sessions.preview.clipped': '仅展示最近 {count} 条消息。',
664
+ 'sessions.workspace.kicker': '工作记忆',
665
+ 'sessions.workspace.title': '会话利用摘要',
666
+ 'sessions.workspace.subtitle': '从已加载的 {count} 条消息提取线索、命令、文件、链接与风险。',
667
+ 'sessions.workspace.copy': '复制工作简报',
668
+ 'sessions.workspace.copy.title': '会话工作简报',
669
+ 'sessions.workspace.copy.source': '来源',
670
+ 'sessions.workspace.copy.path': '路径',
671
+ 'sessions.workspace.copy.empty': '暂无可复制的会话摘要',
672
+ 'sessions.workspace.copy.success': '已复制工作简报',
673
+ 'sessions.workspace.metric.messages': '消息',
674
+ 'sessions.workspace.metric.user': '用户',
675
+ 'sessions.workspace.metric.assistant': '助手',
676
+ 'sessions.workspace.metric.commands': '命令',
677
+ 'sessions.workspace.metric.artifacts': '材料',
678
+ 'sessions.workspace.metric.risks': '风险/待办',
679
+ 'sessions.workspace.signals': '任务线索',
680
+ 'sessions.workspace.commands': '可复用命令',
681
+ 'sessions.workspace.files': '相关文件',
682
+ 'sessions.workspace.links': '相关链接',
683
+ 'sessions.workspace.risks': '风险与待办',
684
+ 'sessions.workspace.nextSteps': '下一步',
685
+ 'sessions.workspace.empty': '暂无明显线索,可从下方原始记录继续查看。',
686
+ 'sessions.workspace.noneCommands': '暂无命令线索',
687
+ 'sessions.workspace.noneFiles': '暂无文件线索',
688
+ 'sessions.workspace.noneLinks': '暂无链接线索',
689
+ 'sessions.workspace.noneRisks': '暂未识别到风险或待办',
662
690
  'sessions.preview.shownCount': '已显示 {shown} / {total} 条',
663
691
  'sessions.preview.loadMore': '加载更多(剩余 {remain})',
664
692
  'sessions.preview.loadingMore': '正在加载更早消息...',
@@ -894,7 +922,18 @@ const zh = Object.freeze({
894
922
  'config.health.title': '配置健康检查',
895
923
  'config.health.run': '运行检查',
896
924
  'config.health.running': '检查中...',
897
- 'config.health.hint': '会批量探测所有提供商可用性,并刷新延迟显示。',
925
+ 'config.health.hint': '仅探测当前选中的提供商;未选提供商的远程异常不计入本次错误。',
926
+ 'config.health.codexHint': '通过本地桥接探测当前 Codex 路由 provider、endpoint、鉴权和模型。',
927
+ 'config.health.providersSummary': '提供商:共 {total} · 正常 {green} · 配置问题 {yellow} · 失败 {red}',
928
+ 'config.health.failedProviders.title': '待删除的失败提供商',
929
+ 'config.health.failedProviders.selectAria': '选择失败提供商 {name} 进行删除',
930
+ 'config.health.failedProviders.clearSelection': '清空选择',
931
+ 'config.health.failedProviders.selectAll': '全选',
932
+ 'config.health.failedProviders.selectAllAria': '全选可删除的失败提供商',
933
+ 'config.health.failedProviders.notDeletable': '保留/系统提供商,不能在这里删除。',
934
+ 'config.health.failedProviders.writeRequired': '删除前请先打开本 tab 的提供商写入开关。',
935
+ 'config.health.failedProviders.deleteSelected': '移除所选配置',
936
+ 'config.health.failedProviders.deleting': '删除中...',
898
937
  'config.health.progress': '已完成 {done}/{total} · 失败 {failed}',
899
938
  'config.health.ok': '检查通过',
900
939
  'config.health.fail': '检查未通过',
@@ -1088,6 +1127,39 @@ const zh = Object.freeze({
1088
1127
  'settings.tabs.aria': '设置分类',
1089
1128
  'settings.quickSettings.title': '快捷设置',
1090
1129
  'settings.language.sideLabel': '语言:{language}',
1130
+ 'announcement.providerCache.open': '功能公告',
1131
+ 'announcement.project.eyebrow': '新手入口',
1132
+ 'announcement.project.closeAria': '关闭功能公告',
1133
+ 'announcement.project.primaryAction': '进入工作台',
1134
+ 'announcement.project.title': '你的 AI 工具入口在这里',
1135
+ 'announcement.project.subtitle': '从这里连接模型、找回对话、查看用量,并把常用维护动作放到一个工作台里。',
1136
+ 'announcement.project.features.aria': 'Codex Mate 功能概览',
1137
+ 'announcement.project.feature.config.title': '连接模型',
1138
+ 'announcement.project.feature.config.meta': '配置常用 AI 工具的模型和服务地址。',
1139
+ 'announcement.project.feature.sessions.title': '找回对话',
1140
+ 'announcement.project.feature.sessions.meta': '按来源搜索、导出或清理历史会话。',
1141
+ 'announcement.project.feature.usage.title': '查看花费',
1142
+ 'announcement.project.feature.usage.meta': '看最近和长期的本地用量趋势。',
1143
+ 'announcement.project.feature.tasks.title': '跟踪任务',
1144
+ 'announcement.project.feature.tasks.meta': '查看计划、队列和运行记录。',
1145
+ 'announcement.project.feature.skills.title': '复用工作流',
1146
+ 'announcement.project.feature.skills.meta': '管理 Skills 和提示词模板。',
1147
+ 'announcement.project.feature.data.title': '整理数据',
1148
+ 'announcement.project.feature.data.meta': '处理备份、导入、回收站和缓存。',
1149
+ 'announcement.project.status.aria': '当前工作台状态',
1150
+ 'announcement.project.status.provider': 'Provider',
1151
+ 'announcement.project.status.model': '当前模型',
1152
+ 'announcement.project.status.cacheFiles': '缓存文件',
1153
+ 'announcement.project.cache.title': '高级维护状态',
1154
+ 'announcement.project.cache.meta': '仅在需要排查或同步时展开;日常使用可以忽略。',
1155
+ 'announcement.project.cache.files': '缓存文件',
1156
+ 'announcement.project.cache.providers': 'Provider 摘要',
1157
+ 'announcement.project.cache.groups': '缓存分组',
1158
+ 'announcement.project.cache.groupList': 'Provider 缓存分组摘要',
1159
+ 'announcement.project.cache.groupSummary': '{files} 文件 · {providers} provider',
1160
+ 'announcement.project.cache.sync': '同步缓存',
1161
+ 'announcement.project.cache.refresh': '刷新摘要',
1162
+ 'announcement.project.cache.details': '查看缓存详情',
1091
1163
  'settings.language.title': '语言',
1092
1164
  'settings.language.meta': '选择 Web UI 的显示语言',
1093
1165
  'settings.language.label': '界面语言',
@@ -1095,7 +1167,7 @@ const zh = Object.freeze({
1095
1167
  'settings.sharePrefix.title': '分享命令前缀',
1096
1168
  'settings.sharePrefix.meta': '影响 Web UI 里“复制分享命令”的前缀',
1097
1169
  'settings.sharePrefix.label': '前缀',
1098
- 'settings.sharePrefix.hint': '默认走项目内 npm start,也可切到全局 codexmate。该设置会缓存到浏览器本地。',
1170
+ 'settings.sharePrefix.hint': '默认走项目内 npm start,也可切到全局 codexmate。该设置会写入 ~/.codexmate/preferences.json。',
1099
1171
  'settings.backup.title': '数据备份',
1100
1172
  'settings.backup.meta': '导出 / 导入 Claude 与 Codex 配置',
1101
1173
  'settings.claude.title': 'Claude 配置',
@@ -1109,6 +1181,34 @@ const zh = Object.freeze({
1109
1181
  'settings.backup.importCodex': '导入 ~/.codex 备份',
1110
1182
  'settings.importing': '导入中...',
1111
1183
 
1184
+ 'settings.providerCache.title': 'Provider 缓存记录',
1185
+ 'settings.providerCache.meta': '查看 ~/.codexmate 内的 Claude / Codex / OpenCode 缓存',
1186
+ 'settings.providerCache.open': '查看缓存记录',
1187
+ 'settings.providerCache.sync': '同步缓存记录',
1188
+ 'settings.providerCache.syncing': '同步中...',
1189
+ 'settings.providerCache.loading': '加载中...',
1190
+ 'settings.providerCache.hint': '只读展示缓存文件,敏感字段会自动遮罩。同步会把当前 provider 配置写入 ~/.codexmate 缓存文件。',
1191
+ 'modal.providerCache.title': 'Provider 缓存记录',
1192
+ 'modal.providerCache.root': '缓存目录',
1193
+ 'modal.providerCache.refresh': '刷新',
1194
+ 'modal.providerCache.refreshing': '刷新中...',
1195
+ 'modal.providerCache.sync': '同步',
1196
+ 'modal.providerCache.syncing': '同步中...',
1197
+ 'modal.providerCache.syncSucceeded': '已同步 {count} 个 provider 到 {fileCount} 个缓存文件',
1198
+ 'modal.providerCache.syncFailed': '同步缓存记录失败',
1199
+ 'modal.providerCache.noSyncableProviders': '没有可同步的 provider',
1200
+ 'modal.providerCache.loading': '正在加载缓存记录...',
1201
+ 'modal.providerCache.loadedAt': '加载时间',
1202
+ 'modal.providerCache.groupMeta': '已发现 {count} 个缓存文件',
1203
+ 'modal.providerCache.empty': '没有发现缓存文件',
1204
+ 'modal.providerCache.providerCount': '{count} 个 provider',
1205
+ 'modal.providerCache.rawJsonOnly': '未识别到 provider 摘要,显示原始 JSON',
1206
+ 'modal.providerCache.tooLarge': '文件过大,已跳过 JSON 读取',
1207
+ 'modal.providerCache.parseFailed': 'JSON 解析失败',
1208
+ 'modal.providerCache.rawJson': '原始 JSON',
1209
+ 'modal.providerCache.errorDetails': '错误详情',
1210
+ 'modal.providerCache.loadFailed': '加载缓存记录失败',
1211
+
1112
1212
  'settings.trashConfig.title': '回收站配置',
1113
1213
  'settings.trashConfig.meta': '回收站开关与自动清理天数',
1114
1214
  'settings.deleteBehavior.title': '会话删除行为',
@@ -1240,7 +1340,7 @@ const zh = Object.freeze({
1240
1340
  'claude.health.title': '配置健康检查',
1241
1341
  'claude.health.run': '运行检查',
1242
1342
  'claude.health.running': '检查中...',
1243
- 'claude.health.hint': '会批量探测所有 Claude 配置可用性,并刷新延迟显示。',
1343
+ 'claude.health.hint': '仅探测当前选中的 Claude 配置;未选配置的远程异常不计入本次错误。',
1244
1344
  'claude.health.progress': '已完成 {done}/{total} · 失败 {failed}',
1245
1345
  'claude.md.title': 'CLAUDE.md',
1246
1346
  'claude.md.open': '打开 CLAUDE.md',
@@ -0,0 +1,25 @@
1
+ export function nextNumericProviderName(existingNames) {
2
+ const used = new Set();
3
+ const list = Array.isArray(existingNames) ? existingNames : [];
4
+ for (const item of list) {
5
+ const name = typeof item === 'string'
6
+ ? item.trim()
7
+ : (item && typeof item.name === 'string' ? item.name.trim() : '');
8
+ if (!/^\d+$/.test(name)) continue;
9
+ const value = Number(name);
10
+ if (Number.isSafeInteger(value) && value > 0) {
11
+ used.add(value);
12
+ }
13
+ }
14
+ let next = 1;
15
+ while (used.has(next)) next += 1;
16
+ return String(next);
17
+ }
18
+
19
+ export function nextCodexProviderName(providers) {
20
+ return nextNumericProviderName(Array.isArray(providers) ? providers : []);
21
+ }
22
+
23
+ export function nextClaudeConfigName(configs) {
24
+ return nextNumericProviderName(Object.keys(configs && typeof configs === 'object' ? configs : {}));
25
+ }
@@ -385,11 +385,24 @@
385
385
  </div>
386
386
 
387
387
  <div class="side-rail-lang">
388
- <button
389
- type="button"
390
- class="language-settings-link"
391
- :aria-label="t('settings.language.sideLabel', { language: currentLanguageLabel() })"
392
- @click="openLanguageSettings">{{ t('settings.language.sideLabel', { language: currentLanguageLabel() }) }}</button>
388
+ <div class="side-rail-lang-actions">
389
+ <button
390
+ type="button"
391
+ class="language-settings-link"
392
+ :aria-label="t('settings.language.sideLabel', { language: currentLanguageLabel() })"
393
+ @click="openLanguageSettings">{{ t('settings.language.sideLabel', { language: currentLanguageLabel() }) }}</button>
394
+ <button
395
+ type="button"
396
+ class="side-announcement-button"
397
+ :aria-label="t('announcement.providerCache.open')"
398
+ :title="t('announcement.providerCache.open')"
399
+ @click="openProviderCacheAnnouncementModal">
400
+ <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" width="16" height="16" aria-hidden="true">
401
+ <path d="M15 8.5a5 5 0 0 0-10 0c0 2.75-1 4.25-2 5.5h14c-1-1.25-2-2.75-2-5.5Z"/>
402
+ <path d="M8 16a2 2 0 0 0 4 0"/>
403
+ </svg>
404
+ </button>
405
+ </div>
393
406
  </div>
394
407
  </aside>
395
408
  <main class="main-panel">
@@ -1,9 +1,10 @@
1
1
  <div v-if="showHealthCheckModal" class="modal-overlay" @click.self="showHealthCheckModal = false">
2
- <div class="modal" role="dialog" aria-modal="true" aria-labelledby="health-check-modal-title">
2
+ <div class="modal modal-health-check" role="dialog" aria-modal="true" aria-labelledby="health-check-modal-title">
3
3
  <div class="modal-title" id="health-check-modal-title">{{ t('config.health.title') }}</div>
4
4
 
5
- <div v-if="!healthCheckResult" class="state-message">{{ t('common.notLoaded') }}</div>
6
- <template v-else>
5
+ <div class="health-check-modal-body">
6
+ <div v-if="!healthCheckResult" class="state-message">{{ t('common.notLoaded') }}</div>
7
+ <template v-else>
7
8
  <div class="form-hint">
8
9
  {{ healthCheckResult.ok ? t('config.health.ok') : t('config.health.fail') }}
9
10
  <span v-if="healthCheckResult.issues">({{ t('config.health.issues', { count: healthCheckResult.issues.length }) }})</span>
@@ -15,6 +16,62 @@
15
16
  <span v-if="healthCheckResult.remote.message"> · {{ healthCheckResult.remote.message }}</span>
16
17
  </div>
17
18
 
19
+ <div v-if="healthCheckResult.remote && healthCheckResult.remote.type === 'providers-health'" class="form-hint">
20
+ {{ t('config.health.providersSummary', healthCheckResult.remote.summary || { total: 0, green: 0, yellow: 0, red: 0 }) }}
21
+ </div>
22
+
23
+ <div v-if="getHealthCheckFailedProviderItems().length" class="model-list health-failed-provider-list">
24
+ <div class="model-item health-failed-provider-header">
25
+ <span class="health-failed-provider-main">
26
+ <input
27
+ type="checkbox"
28
+ :checked="areAllHealthCheckFailedProvidersSelected()"
29
+ :disabled="!getSelectableHealthCheckFailedProviderItems().length || healthCheckFailedProviderDeleting"
30
+ @change="setAllHealthCheckFailedProviderSelections($event.target.checked)"
31
+ :aria-label="t('config.health.failedProviders.selectAllAria')"
32
+ >
33
+ <span>{{ t('config.health.failedProviders.title') }}</span>
34
+ </span>
35
+ <span class="health-failed-provider-actions">
36
+ <button
37
+ type="button"
38
+ class="btn-link"
39
+ :disabled="!getSelectableHealthCheckFailedProviderItems().length || healthCheckFailedProviderDeleting"
40
+ @click="setAllHealthCheckFailedProviderSelections(true)"
41
+ >{{ t('config.health.failedProviders.selectAll') }}</button>
42
+ <button
43
+ type="button"
44
+ class="btn-link"
45
+ :disabled="!hasHealthCheckFailedProviderSelection() || healthCheckFailedProviderDeleting"
46
+ @click="setAllHealthCheckFailedProviderSelections(false)"
47
+ >{{ t('config.health.failedProviders.clearSelection') }}</button>
48
+ <span class="latency error">{{ getHealthCheckFailedProviderItems().length }}</span>
49
+ </span>
50
+ </div>
51
+ <label
52
+ v-for="item in getHealthCheckFailedProviderItems()"
53
+ :key="item.key"
54
+ class="model-item health-failed-provider-item"
55
+ :class="{ disabled: !item.deletable }"
56
+ >
57
+ <span class="health-failed-provider-main">
58
+ <input
59
+ type="checkbox"
60
+ :checked="item.selected"
61
+ :disabled="!item.deletable || healthCheckFailedProviderDeleting"
62
+ @change="toggleHealthCheckFailedProviderSelection(item, $event.target.checked)"
63
+ :aria-label="t('config.health.failedProviders.selectAria', { name: item.name })"
64
+ >
65
+ <span>
66
+ <strong>{{ item.name }}</strong>
67
+ <span class="form-hint" v-if="item.detail">{{ item.detail }}</span>
68
+ <span class="form-hint" v-if="!item.deletable">{{ item.blockedReason === 'readonly' ? t('config.health.failedProviders.writeRequired') : t('config.health.failedProviders.notDeletable') }}</span>
69
+ </span>
70
+ </span>
71
+ <span class="latency error">{{ item.status }}</span>
72
+ </label>
73
+ </div>
74
+
18
75
  <div v-if="healthCheckResult.remote && healthCheckResult.remote.speedTests" class="model-list">
19
76
  <div
20
77
  v-for="(result, name) in healthCheckResult.remote.speedTests"
@@ -36,9 +93,16 @@
36
93
  <span>{{ issue.message || issue.code || '' }}</span>
37
94
  </div>
38
95
  </div>
39
- </template>
96
+ </template>
97
+ </div>
40
98
 
41
- <div class="btn-group">
99
+ <div class="btn-group health-check-modal-actions">
100
+ <button
101
+ v-if="getHealthCheckFailedProviderItems().length"
102
+ class="btn btn-confirm btn-danger"
103
+ :disabled="healthCheckFailedProviderDeleting || !hasHealthCheckFailedProviderSelection()"
104
+ @click="deleteSelectedHealthCheckFailedProviders()"
105
+ >{{ healthCheckFailedProviderDeleting ? t('config.health.failedProviders.deleting') : t('config.health.failedProviders.deleteSelected') }}</button>
42
106
  <button class="btn btn-confirm" @click="showHealthCheckModal = false">{{ t('common.close') }}</button>
43
107
  </div>
44
108
  </div>