codexmate 0.0.26 → 0.0.28
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 +7 -2
- package/README.zh.md +7 -2
- package/cli/builtin-proxy.js +636 -95
- package/cli/openai-bridge.js +497 -5
- package/cli.js +75 -29
- package/lib/cli-models-utils.js +71 -10
- package/package.json +3 -1
- package/plugins/prompt-templates/computed.mjs +1 -1
- package/plugins/prompt-templates/methods.mjs +0 -66
- package/plugins/prompt-templates/overview.mjs +1 -0
- package/web-ui/app.js +16 -16
- package/web-ui/logic.codex.mjs +56 -0
- package/web-ui/logic.sessions.mjs +56 -0
- package/web-ui/modules/app.computed.dashboard.mjs +54 -0
- package/web-ui/modules/app.computed.session.mjs +48 -0
- package/web-ui/modules/app.methods.claude-config.mjs +18 -7
- package/web-ui/modules/app.methods.codex-config.mjs +35 -3
- package/web-ui/modules/app.methods.providers.mjs +9 -1
- package/web-ui/modules/app.methods.session-actions.mjs +2 -5
- package/web-ui/modules/app.methods.session-browser.mjs +4 -5
- package/web-ui/modules/app.methods.session-trash.mjs +19 -4
- package/web-ui/modules/app.methods.startup-claude.mjs +12 -1
- package/web-ui/modules/i18n.dict.mjs +28 -32
- package/web-ui/modules/provider-url-display.mjs +17 -0
- package/web-ui/partials/index/panel-config-claude.html +5 -1
- package/web-ui/partials/index/panel-config-codex.html +33 -4
- package/web-ui/partials/index/panel-plugins.html +3 -29
- package/web-ui/partials/index/panel-sessions.html +0 -10
- package/web-ui/partials/index/panel-settings.html +62 -67
- package/web-ui/partials/index/panel-usage.html +31 -2
- package/web-ui/session-helpers.mjs +2 -2
- package/web-ui/styles/base-theme.css +47 -34
- package/web-ui/styles/controls-forms.css +27 -28
- package/web-ui/styles/layout-shell.css +37 -34
- package/web-ui/styles/modals-core.css +12 -10
- package/web-ui/styles/navigation-panels.css +36 -35
- package/web-ui/styles/responsive.css +4 -4
- package/web-ui/styles/sessions-list.css +10 -6
- package/web-ui/styles/sessions-usage.css +95 -0
- package/web-ui/styles/settings-panel.css +19 -0
- package/web-ui/styles/titles-cards.css +90 -26
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getProviderDisplayUrl, checkIsTransformProvider } from './provider-url-display.mjs';
|
|
2
|
+
import { getCodexModelCatalogForProvider, CODEX_PROVIDER_TEMPLATES } from '../logic.codex.mjs';
|
|
1
3
|
export function createDashboardComputed() {
|
|
2
4
|
return {
|
|
3
5
|
agentsDiffHasChanges() {
|
|
@@ -29,6 +31,50 @@ export function createDashboardComputed() {
|
|
|
29
31
|
}
|
|
30
32
|
return list;
|
|
31
33
|
},
|
|
34
|
+
activeProviderModel() {
|
|
35
|
+
return (name) => {
|
|
36
|
+
const target = String(name || '').trim();
|
|
37
|
+
if (!target) return '';
|
|
38
|
+
const dict = this.currentModels && typeof this.currentModels === 'object' ? this.currentModels : {};
|
|
39
|
+
const fromDict = typeof dict[target] === 'string' ? dict[target].trim() : '';
|
|
40
|
+
if (fromDict) return fromDict;
|
|
41
|
+
const activeName = String(this.currentProvider || '').trim();
|
|
42
|
+
if (target === activeName) {
|
|
43
|
+
const top = typeof this.currentModel === 'string' ? this.currentModel.trim() : '';
|
|
44
|
+
if (top && top !== '未设置') return top;
|
|
45
|
+
}
|
|
46
|
+
return '';
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
codexModelOptions() {
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
const out = [];
|
|
52
|
+
const push = (val) => {
|
|
53
|
+
const s = typeof val === 'string' ? val.trim() : '';
|
|
54
|
+
if (!s || seen.has(s)) return;
|
|
55
|
+
seen.add(s);
|
|
56
|
+
out.push(s);
|
|
57
|
+
};
|
|
58
|
+
const activeName = String(this.displayCurrentProvider || '').trim();
|
|
59
|
+
const current = typeof this.currentModel === 'string' ? this.currentModel.trim() : '';
|
|
60
|
+
if (current && current !== '未设置') push(current);
|
|
61
|
+
const dict = this.currentModels && typeof this.currentModels === 'object' ? this.currentModels : {};
|
|
62
|
+
if (activeName && typeof dict[activeName] === 'string') push(dict[activeName]);
|
|
63
|
+
const remote = Array.isArray(this.models) ? this.models : [];
|
|
64
|
+
for (const m of remote) push(m);
|
|
65
|
+
const list = Array.isArray(this.providersList) ? this.providersList : [];
|
|
66
|
+
const activeProvider = list.find((p) => p && p.name === activeName);
|
|
67
|
+
if (activeProvider) {
|
|
68
|
+
for (const m of getCodexModelCatalogForProvider(activeProvider)) push(m);
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
},
|
|
72
|
+
codexModelHasList() {
|
|
73
|
+
return this.codexModelOptions.length > 0;
|
|
74
|
+
},
|
|
75
|
+
codexProviderTemplates() {
|
|
76
|
+
return CODEX_PROVIDER_TEMPLATES;
|
|
77
|
+
},
|
|
32
78
|
displayCurrentProvider() {
|
|
33
79
|
const switching = String(this.providerSwitchDisplayTarget || '').trim();
|
|
34
80
|
if (switching) return switching;
|
|
@@ -39,6 +85,14 @@ export function createDashboardComputed() {
|
|
|
39
85
|
const list = Array.isArray(this.providersList) ? this.providersList : [];
|
|
40
86
|
return list;
|
|
41
87
|
},
|
|
88
|
+
|
|
89
|
+
displayProviderUrl() {
|
|
90
|
+
return (provider) => getProviderDisplayUrl(provider);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
isTransformProvider() {
|
|
94
|
+
return (provider) => checkIsTransformProvider(provider);
|
|
95
|
+
},
|
|
42
96
|
installTargetCards() {
|
|
43
97
|
const targets = Array.isArray(this.installStatusTargets) && this.installStatusTargets.length
|
|
44
98
|
? this.installStatusTargets
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
buildSessionTimelineNodes,
|
|
3
3
|
buildUsageChartGroups,
|
|
4
4
|
buildUsageHeatmap,
|
|
5
|
+
buildUsageHourlyHeatmap,
|
|
5
6
|
isSessionQueryEnabled
|
|
6
7
|
} from '../logic.mjs';
|
|
7
8
|
import { SESSION_TRASH_PAGE_SIZE } from './app.constants.mjs';
|
|
@@ -580,6 +581,53 @@ export function createSessionComputed() {
|
|
|
580
581
|
weekdayAxis
|
|
581
582
|
};
|
|
582
583
|
},
|
|
584
|
+
sessionUsageHourlyHeatmap() {
|
|
585
|
+
const sessions = this.sessionUsageCharts && Array.isArray(this.sessionUsageCharts.filteredSessions)
|
|
586
|
+
? this.sessionUsageCharts.filteredSessions
|
|
587
|
+
: this.sessionsUsageList;
|
|
588
|
+
const result = buildUsageHourlyHeatmap(sessions, { range: this.sessionsUsageTimeRange });
|
|
589
|
+
const t = typeof this.t === 'function' ? this.t : null;
|
|
590
|
+
const lang = typeof this.lang === 'string' ? this.lang.trim().toLowerCase() : '';
|
|
591
|
+
const weekdayLabelsZh = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
|
592
|
+
const weekdayLabelsEn = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
593
|
+
const weekdayLabels = lang === 'en' ? weekdayLabelsEn : weekdayLabelsZh;
|
|
594
|
+
const max = Math.max(1, result.maxSessionCount);
|
|
595
|
+
const grid = Array.isArray(result.grid) ? result.grid : [];
|
|
596
|
+
const rows = grid.map((cells, dayIndex) => ({
|
|
597
|
+
weekday: weekdayLabels[dayIndex] || '',
|
|
598
|
+
cells: cells.map((cell, hourIndex) => {
|
|
599
|
+
const ratio = cell.sessionCount > 0 ? (cell.sessionCount / max) : 0;
|
|
600
|
+
const level = cell.sessionCount <= 0
|
|
601
|
+
? 0
|
|
602
|
+
: (ratio <= 0.25 ? 1 : (ratio <= 0.5 ? 2 : (ratio <= 0.75 ? 3 : 4)));
|
|
603
|
+
const hourLabel = String(hourIndex).padStart(2, '0');
|
|
604
|
+
const tooltipText = t
|
|
605
|
+
? t('usage.hourlyHeatmap.tooltip', {
|
|
606
|
+
weekday: weekdayLabels[dayIndex],
|
|
607
|
+
hour: hourLabel,
|
|
608
|
+
sessions: cell.sessionCount,
|
|
609
|
+
messages: cell.messageCount,
|
|
610
|
+
tokens: (cell.tokenTotal || 0).toLocaleString('en-US')
|
|
611
|
+
})
|
|
612
|
+
: `${weekdayLabels[dayIndex] || ''} ${hourLabel}:00 · ${cell.sessionCount} sessions · ${cell.messageCount} messages · ${(cell.tokenTotal || 0).toLocaleString('en-US')} tokens`;
|
|
613
|
+
return {
|
|
614
|
+
hour: hourIndex,
|
|
615
|
+
hourLabel,
|
|
616
|
+
sessionCount: cell.sessionCount,
|
|
617
|
+
messageCount: cell.messageCount,
|
|
618
|
+
tokenTotal: cell.tokenTotal,
|
|
619
|
+
level,
|
|
620
|
+
tooltip: tooltipText
|
|
621
|
+
};
|
|
622
|
+
})
|
|
623
|
+
}));
|
|
624
|
+
return {
|
|
625
|
+
range: result.range,
|
|
626
|
+
rows,
|
|
627
|
+
hourLabels: result.hourLabels,
|
|
628
|
+
maxSessionCount: result.maxSessionCount
|
|
629
|
+
};
|
|
630
|
+
},
|
|
583
631
|
sessionUsageSummaryCards() {
|
|
584
632
|
const summary = this.sessionUsageCharts && this.sessionUsageCharts.summary
|
|
585
633
|
? this.sessionUsageCharts.summary
|
|
@@ -4,6 +4,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
4
4
|
return {
|
|
5
5
|
switchClaudeConfig(name) {
|
|
6
6
|
this.currentClaudeConfig = name;
|
|
7
|
+
try { localStorage.setItem('currentClaudeConfig', name || ''); } catch (_) {}
|
|
7
8
|
this.refreshClaudeModelContext();
|
|
8
9
|
},
|
|
9
10
|
|
|
@@ -36,7 +37,10 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
36
37
|
},
|
|
37
38
|
|
|
38
39
|
saveClaudeConfigs() {
|
|
39
|
-
localStorage.setItem('claudeConfigs', JSON.stringify(this.claudeConfigs));
|
|
40
|
+
try { localStorage.setItem('claudeConfigs', JSON.stringify(this.claudeConfigs)); } catch (_) {}
|
|
41
|
+
if (this.currentClaudeConfig) {
|
|
42
|
+
try { localStorage.setItem('currentClaudeConfig', this.currentClaudeConfig); } catch (_) {}
|
|
43
|
+
}
|
|
40
44
|
},
|
|
41
45
|
|
|
42
46
|
openEditConfigModal(name) {
|
|
@@ -73,7 +77,7 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
73
77
|
|
|
74
78
|
const config = this.claudeConfigs[name];
|
|
75
79
|
if (!config.apiKey) {
|
|
76
|
-
this.showMessage('
|
|
80
|
+
this.showMessage('已保存(未填写 API Key)', 'info');
|
|
77
81
|
this.closeEditConfigModal();
|
|
78
82
|
if (name === this.currentClaudeConfig) {
|
|
79
83
|
this.refreshClaudeModelContext();
|
|
@@ -81,14 +85,17 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
81
85
|
return;
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
const _claudeKey = `${name}|${config.apiKey || ""}|${config.baseUrl || ""}|${config.model || ""}`;
|
|
84
89
|
try {
|
|
85
90
|
const res = await api('apply-claude-config', { config });
|
|
86
91
|
if (res.error || res.success === false) {
|
|
87
92
|
this.showMessage(res.error || '应用配置失败', 'error');
|
|
88
93
|
} else {
|
|
89
94
|
this.currentClaudeConfig = name;
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
if (this._lastAppliedClaudeKey !== _claudeKey) {
|
|
96
|
+
this.showMessage('Claude 配置已生效', 'success');
|
|
97
|
+
this._lastAppliedClaudeKey = _claudeKey;
|
|
98
|
+
}
|
|
92
99
|
this.closeEditConfigModal();
|
|
93
100
|
this.refreshClaudeModelContext();
|
|
94
101
|
}
|
|
@@ -143,23 +150,27 @@ export function createClaudeConfigMethods(options = {}) {
|
|
|
143
150
|
|
|
144
151
|
async applyClaudeConfig(name) {
|
|
145
152
|
this.currentClaudeConfig = name;
|
|
153
|
+
try { localStorage.setItem('currentClaudeConfig', name || ''); } catch (_) {}
|
|
146
154
|
this.refreshClaudeModelContext();
|
|
147
155
|
const config = this.claudeConfigs[name];
|
|
148
156
|
|
|
149
157
|
if (!config.apiKey) {
|
|
150
158
|
if (config.externalCredentialType) {
|
|
151
|
-
return this.showMessage('
|
|
159
|
+
return this.showMessage('使用外部认证,无需 API Key', 'info');
|
|
152
160
|
}
|
|
153
161
|
return this.showMessage('请先配置 API Key', 'error');
|
|
154
162
|
}
|
|
155
163
|
|
|
164
|
+
const _claudeKey2 = `${name}|${config.apiKey || ""}|${config.baseUrl || ""}|${config.model || ""}`;
|
|
156
165
|
try {
|
|
157
166
|
const res = await api('apply-claude-config', { config });
|
|
158
167
|
if (res.error || res.success === false) {
|
|
159
168
|
this.showMessage(res.error || '应用配置失败', 'error');
|
|
160
169
|
} else {
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
if (this._lastAppliedClaudeKey !== _claudeKey2) {
|
|
171
|
+
this.showMessage('配置已应用', 'success');
|
|
172
|
+
this._lastAppliedClaudeKey = _claudeKey2;
|
|
173
|
+
}
|
|
163
174
|
}
|
|
164
175
|
} catch (_) {
|
|
165
176
|
this.showMessage('应用配置失败', 'error');
|
|
@@ -180,7 +180,19 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
180
180
|
const previousModels = Array.isArray(this.models) ? [...this.models] : [];
|
|
181
181
|
const previousModelsSource = this.modelsSource;
|
|
182
182
|
const previousModelsHasCurrent = this.modelsHasCurrent;
|
|
183
|
+
// 切走前把上一个 provider 的 model 落到内存字典,避免切回时显示抖动。
|
|
184
|
+
if (previousProvider && typeof previousModel === 'string' && previousModel.trim() && previousModel !== '未设置') {
|
|
185
|
+
if (!this.currentModels || typeof this.currentModels !== 'object') this.currentModels = {};
|
|
186
|
+
this.currentModels[previousProvider] = previousModel.trim();
|
|
187
|
+
}
|
|
183
188
|
this.currentProvider = name;
|
|
189
|
+
// 立即按字典预填,让 UI 不出现空白;远端 /models 后台异步补齐。
|
|
190
|
+
const dictModel = typeof this.activeProviderModel === 'function'
|
|
191
|
+
? this.activeProviderModel(name)
|
|
192
|
+
: '';
|
|
193
|
+
if (dictModel) {
|
|
194
|
+
this.currentModel = dictModel;
|
|
195
|
+
}
|
|
184
196
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
185
197
|
|
|
186
198
|
// 不要把“切换提供商”强绑定到 /models 成功与否:
|
|
@@ -195,7 +207,11 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
195
207
|
|
|
196
208
|
await Promise.race([modelsTask, delay(250)]);
|
|
197
209
|
|
|
198
|
-
|
|
210
|
+
// 只在“字典中没有该 provider 记录”时才允许 remote 首项覆盖,否则尊重用户上次选择。
|
|
211
|
+
if (!dictModel
|
|
212
|
+
&& this.modelsSource === 'remote'
|
|
213
|
+
&& this.models.length > 0
|
|
214
|
+
&& !this.models.includes(this.currentModel)) {
|
|
199
215
|
this.currentModel = this.models[0];
|
|
200
216
|
this.modelsHasCurrent = true;
|
|
201
217
|
}
|
|
@@ -208,7 +224,10 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
208
224
|
await modelsTask;
|
|
209
225
|
|
|
210
226
|
if (this.currentProvider === name) {
|
|
211
|
-
if (
|
|
227
|
+
if (!dictModel
|
|
228
|
+
&& this.modelsSource === 'remote'
|
|
229
|
+
&& this.models.length > 0
|
|
230
|
+
&& !this.models.includes(this.currentModel)) {
|
|
212
231
|
this.currentModel = this.models[0];
|
|
213
232
|
this.modelsHasCurrent = true;
|
|
214
233
|
if (getProviderConfigModeMeta(this.configMode)) {
|
|
@@ -261,6 +280,12 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
261
280
|
},
|
|
262
281
|
|
|
263
282
|
async onModelChange() {
|
|
283
|
+
const name = String(this.currentProvider || '').trim();
|
|
284
|
+
const model = typeof this.currentModel === 'string' ? this.currentModel.trim() : '';
|
|
285
|
+
if (name && model) {
|
|
286
|
+
if (!this.currentModels || typeof this.currentModels !== 'object') this.currentModels = {};
|
|
287
|
+
this.currentModels[name] = model;
|
|
288
|
+
}
|
|
264
289
|
await this.applyCodexConfigDirect();
|
|
265
290
|
},
|
|
266
291
|
|
|
@@ -629,6 +654,8 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
629
654
|
this.modelContextWindowInput = modelContextWindow.text;
|
|
630
655
|
this.modelAutoCompactTokenLimitInput = modelAutoCompactTokenLimit.text;
|
|
631
656
|
|
|
657
|
+
const _codexKey = `${provider}|${model}|${this.serviceTier || ""}|${this.modelReasoningEffort || ""}|${modelContextWindow.value}|${modelAutoCompactTokenLimit.value}`;
|
|
658
|
+
|
|
632
659
|
this.codexApplying = true;
|
|
633
660
|
try {
|
|
634
661
|
const tplRes = await api('get-config-template', {
|
|
@@ -665,7 +692,12 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
665
692
|
}
|
|
666
693
|
|
|
667
694
|
if (options.silent !== true) {
|
|
668
|
-
this.
|
|
695
|
+
if (this._lastAppliedCodexKey !== _codexKey) {
|
|
696
|
+
this.showMessage('配置已应用', 'success');
|
|
697
|
+
this._lastAppliedCodexKey = _codexKey;
|
|
698
|
+
}
|
|
699
|
+
} else {
|
|
700
|
+
this._lastAppliedCodexKey = _codexKey;
|
|
669
701
|
}
|
|
670
702
|
|
|
671
703
|
const refreshOptions = options.silent === true
|
|
@@ -149,6 +149,9 @@ export function createProvidersMethods(options = {}) {
|
|
|
149
149
|
if (this.newProvider && this.newProvider.useTransform) {
|
|
150
150
|
payload.useTransform = true;
|
|
151
151
|
}
|
|
152
|
+
const suggestedModel = typeof this.newProvider._suggestedModel === 'string'
|
|
153
|
+
? this.newProvider._suggestedModel.trim()
|
|
154
|
+
: '';
|
|
152
155
|
const res = await api('add-provider', payload);
|
|
153
156
|
if (res.error) {
|
|
154
157
|
this.showMessage(res.error, 'error');
|
|
@@ -158,6 +161,11 @@ export function createProvidersMethods(options = {}) {
|
|
|
158
161
|
this.showMessage('操作成功', 'success');
|
|
159
162
|
this.closeAddModal();
|
|
160
163
|
await this.loadAll();
|
|
164
|
+
// loadAll 会重拉 status 覆盖字典,所以暗示模型在 loadAll 之后再写。
|
|
165
|
+
if (suggestedModel) {
|
|
166
|
+
if (!this.currentModels || typeof this.currentModels !== 'object') this.currentModels = {};
|
|
167
|
+
this.currentModels[validation.name] = suggestedModel;
|
|
168
|
+
}
|
|
161
169
|
} catch (e) {
|
|
162
170
|
this.showMessage('添加失败', 'error');
|
|
163
171
|
}
|
|
@@ -377,7 +385,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
377
385
|
|
|
378
386
|
closeAddModal() {
|
|
379
387
|
this.showAddModal = false;
|
|
380
|
-
this.newProvider = { name: '', url: '', key: '', useTransform: false };
|
|
388
|
+
this.newProvider = { name: '', url: '', key: '', useTransform: false, _suggestedModel: '' };
|
|
381
389
|
},
|
|
382
390
|
|
|
383
391
|
closeModelModal() {
|
|
@@ -181,12 +181,9 @@ export function createSessionActionMethods(options = {}) {
|
|
|
181
181
|
return `gemini -r ${arg}`;
|
|
182
182
|
}
|
|
183
183
|
if (source === 'claude') {
|
|
184
|
-
return `claude -r ${arg}`;
|
|
184
|
+
return `claude --dangerously-skip-permissions -r ${arg}`;
|
|
185
185
|
}
|
|
186
|
-
|
|
187
|
-
return `codex --yolo resume ${arg}`;
|
|
188
|
-
}
|
|
189
|
-
return `codex resume ${arg}`;
|
|
186
|
+
return `codex --yolo resume ${arg}`;
|
|
190
187
|
},
|
|
191
188
|
|
|
192
189
|
extractClaudeResumeKeyFromFilePath(filePath) {
|
|
@@ -249,11 +249,6 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
249
249
|
}
|
|
250
250
|
},
|
|
251
251
|
|
|
252
|
-
onSessionResumeYoloChange() {
|
|
253
|
-
const value = this.sessionResumeWithYolo ? '1' : '0';
|
|
254
|
-
localStorage.setItem('codexmateSessionResumeYolo', value);
|
|
255
|
-
},
|
|
256
|
-
|
|
257
252
|
normalizeSessionSortMode(value) {
|
|
258
253
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
259
254
|
return normalized === 'hot' ? 'hot' : 'time';
|
|
@@ -778,6 +773,7 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
778
773
|
const normalized = typeof nextRange === 'string' ? nextRange.trim().toLowerCase() : '';
|
|
779
774
|
const range = normalized === 'all' ? 'all' : (normalized === '30d' ? '30d' : '7d');
|
|
780
775
|
this.sessionsUsageTimeRange = range;
|
|
776
|
+
try { localStorage.setItem('sessionsUsageTimeRange', range); } catch (_) {}
|
|
781
777
|
if (range === 'all') {
|
|
782
778
|
this.sessionsUsageCompareEnabled = false;
|
|
783
779
|
}
|
|
@@ -848,6 +844,9 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
848
844
|
if (loadSucceeded) {
|
|
849
845
|
this.sessionsUsageLoadedOnce = true;
|
|
850
846
|
this.sessionsUsageLoadedLimit = limit;
|
|
847
|
+
if (!this.sessionsUsageSelectedDayKey && Array.isArray(this.sessionUsageDailyTableRows) && this.sessionUsageDailyTableRows.length > 0) {
|
|
848
|
+
this.sessionsUsageSelectedDayKey = this.sessionUsageDailyTableRows[0].key;
|
|
849
|
+
}
|
|
851
850
|
}
|
|
852
851
|
}
|
|
853
852
|
},
|
|
@@ -208,10 +208,10 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
208
208
|
},
|
|
209
209
|
|
|
210
210
|
normalizeSettingsTab(tab) {
|
|
211
|
-
if (tab === '
|
|
211
|
+
if (tab === 'general' || tab === 'data') {
|
|
212
212
|
return tab;
|
|
213
213
|
}
|
|
214
|
-
return '
|
|
214
|
+
return 'general';
|
|
215
215
|
},
|
|
216
216
|
|
|
217
217
|
async onSettingsTabClick(tab) {
|
|
@@ -221,7 +221,7 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
221
221
|
async switchSettingsTab(tab, options = {}) {
|
|
222
222
|
const nextTab = this.normalizeSettingsTab(tab);
|
|
223
223
|
this.settingsTab = nextTab;
|
|
224
|
-
if (nextTab !== '
|
|
224
|
+
if (nextTab !== 'data') {
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
227
|
const forceRefresh = options.forceRefresh === true;
|
|
@@ -270,6 +270,20 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
270
270
|
}
|
|
271
271
|
},
|
|
272
272
|
|
|
273
|
+
normalizeSessionTrashRetentionDays(value) {
|
|
274
|
+
const numeric = Number(value);
|
|
275
|
+
if (!Number.isFinite(numeric) || numeric < 1) {
|
|
276
|
+
return 30;
|
|
277
|
+
}
|
|
278
|
+
return Math.min(365, Math.max(1, Math.floor(numeric)));
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
setSessionTrashRetentionDays(days) {
|
|
282
|
+
const normalized = this.normalizeSessionTrashRetentionDays(days);
|
|
283
|
+
this.sessionTrashRetentionDays = normalized;
|
|
284
|
+
try { localStorage.setItem('codexmateSessionTrashRetentionDays', String(normalized)); } catch (_) {}
|
|
285
|
+
},
|
|
286
|
+
|
|
273
287
|
getSessionTrashActionKey(item) {
|
|
274
288
|
return item && typeof item.trashId === 'string' ? item.trashId : '';
|
|
275
289
|
},
|
|
@@ -294,7 +308,8 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
294
308
|
try {
|
|
295
309
|
const res = await api('list-session-trash', {
|
|
296
310
|
limit: sessionTrashListLimit,
|
|
297
|
-
forceRefresh: !!options.forceRefresh
|
|
311
|
+
forceRefresh: !!options.forceRefresh,
|
|
312
|
+
retentionDays: this.sessionTrashRetentionDays
|
|
298
313
|
});
|
|
299
314
|
if (!this.isLatestSessionTrashListRequestToken(requestToken)) {
|
|
300
315
|
return;
|
|
@@ -60,7 +60,14 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
60
60
|
return false;
|
|
61
61
|
}
|
|
62
62
|
this.currentProvider = statusRes.provider;
|
|
63
|
-
this.
|
|
63
|
+
this.currentModels = statusRes.currentModels && typeof statusRes.currentModels === 'object'
|
|
64
|
+
? { ...statusRes.currentModels }
|
|
65
|
+
: {};
|
|
66
|
+
const dictModelForCurrent = this.currentProvider
|
|
67
|
+
&& typeof this.currentModels[this.currentProvider] === 'string'
|
|
68
|
+
? this.currentModels[this.currentProvider].trim()
|
|
69
|
+
: '';
|
|
70
|
+
this.currentModel = dictModelForCurrent || statusRes.model;
|
|
64
71
|
try {
|
|
65
72
|
const installRes = await withTimeout(api('install-status'), Math.max(0, Math.min(1200, timeLeftMs())));
|
|
66
73
|
if (installRes && !installRes.error) {
|
|
@@ -315,6 +322,7 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
315
322
|
if (matchName) {
|
|
316
323
|
if (this.currentClaudeConfig !== matchName) {
|
|
317
324
|
this.currentClaudeConfig = matchName;
|
|
325
|
+
try { localStorage.setItem('currentClaudeConfig', matchName); } catch (_) {}
|
|
318
326
|
}
|
|
319
327
|
this.refreshClaudeModelContext({ silentError: silentModelError });
|
|
320
328
|
return;
|
|
@@ -323,6 +331,7 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
323
331
|
if (importedName) {
|
|
324
332
|
if (this.currentClaudeConfig !== importedName) {
|
|
325
333
|
this.currentClaudeConfig = importedName;
|
|
334
|
+
try { localStorage.setItem('currentClaudeConfig', importedName); } catch (_) {}
|
|
326
335
|
}
|
|
327
336
|
this.refreshClaudeModelContext({ silentError: silentModelError });
|
|
328
337
|
if (!silent) {
|
|
@@ -338,12 +347,14 @@ export function createStartupClaudeMethods(options = {}) {
|
|
|
338
347
|
: (configNames[0] || '');
|
|
339
348
|
if (!fallback) {
|
|
340
349
|
this.currentClaudeConfig = '';
|
|
350
|
+
try { localStorage.setItem('currentClaudeConfig', ''); } catch (_) {}
|
|
341
351
|
this.currentClaudeModel = '';
|
|
342
352
|
this.resetClaudeModelsState();
|
|
343
353
|
return;
|
|
344
354
|
}
|
|
345
355
|
if (this.currentClaudeConfig !== fallback) {
|
|
346
356
|
this.currentClaudeConfig = fallback;
|
|
357
|
+
try { localStorage.setItem('currentClaudeConfig', fallback); } catch (_) {}
|
|
347
358
|
}
|
|
348
359
|
this.refreshClaudeModelContext({ silentError: silentModelError });
|
|
349
360
|
}
|