codexmate 0.1.28 → 0.1.29
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 +53 -44
- package/README.vi.md +45 -36
- package/README.zh.md +51 -42
- package/cli/session-usage.js +2 -2
- package/cli.js +369 -15
- package/package.json +1 -1
- package/web-ui/app.js +3 -0
- package/web-ui/index.html +3 -2
- package/web-ui/logic.sessions.mjs +6 -4
- package/web-ui/modules/app.computed.session.mjs +52 -33
- package/web-ui/modules/app.methods.agents.mjs +36 -0
- package/web-ui/modules/app.methods.codex-config.mjs +3 -1
- package/web-ui/modules/app.methods.providers.mjs +12 -4
- package/web-ui/modules/app.methods.session-actions.mjs +10 -8
- package/web-ui/modules/app.methods.session-browser.mjs +3 -1
- package/web-ui/modules/app.methods.session-trash.mjs +16 -16
- package/web-ui/modules/app.methods.theme.mjs +5 -4
- package/web-ui/modules/i18n/locales/en.mjs +74 -1
- package/web-ui/modules/i18n/locales/ja.mjs +74 -1
- package/web-ui/modules/i18n/locales/vi.mjs +74 -1
- package/web-ui/modules/i18n/locales/zh-tw.mjs +74 -1
- package/web-ui/modules/i18n/locales/zh.mjs +74 -1
- package/web-ui/modules/i18n.mjs +7 -0
- package/web-ui/partials/index/modal-config-template-agents.html +1 -1
- package/web-ui/partials/index/modal-health-check.html +1 -1
- package/web-ui/partials/index/modal-openclaw-config.html +50 -50
- package/web-ui/partials/index/panel-dashboard.html +1 -1
- package/web-ui/partials/index/panel-prompts.html +2 -2
- package/web-ui/partials/index/panel-sessions.html +45 -28
- package/web-ui/partials/index/panel-settings.html +6 -0
- package/web-ui/res/web-ui-render.precompiled.js +219 -156
- package/web-ui/session-helpers.mjs +3 -0
- package/web-ui/styles/base-theme.css +2 -2
- package/web-ui/styles/bridge-pool.css +1 -1
- package/web-ui/styles/layout-shell.css +0 -1
- package/web-ui/styles/sessions-preview.css +77 -0
- package/web-ui/styles/theme-dark-tokens.css +2 -2
- package/web-ui/styles/theme-dark.css +2 -2
- package/web-ui/styles/theme-ocean-tokens.css +88 -0
- package/web-ui/styles/theme-ocean.css +920 -0
- package/web-ui/styles.css +1 -0
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -333,6 +333,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
333
333
|
opencode: 0
|
|
334
334
|
},
|
|
335
335
|
sessionExporting: {},
|
|
336
|
+
sessionExportRange: 'default',
|
|
337
|
+
sessionExportNotice: null,
|
|
338
|
+
sessionSearchInfo: null,
|
|
336
339
|
sessionConverting: {},
|
|
337
340
|
sessionImportingNative: {},
|
|
338
341
|
sessionCloning: {},
|
package/web-ui/index.html
CHANGED
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
// server preference store loads. Kept in sync by applyDocumentTheme().
|
|
17
17
|
(function () {
|
|
18
18
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
var cachedTheme = localStorage.getItem('codexmateThemeCache');
|
|
20
|
+
if (cachedTheme === 'dark' || cachedTheme === 'ocean') {
|
|
21
|
+
document.documentElement.setAttribute('data-theme', cachedTheme);
|
|
21
22
|
}
|
|
22
23
|
} catch (_) {}
|
|
23
24
|
})();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export const DEFAULT_SESSION_CONTENT_SCAN_LIMIT = 50;
|
|
1
2
|
export const DEFAULT_SESSION_LIST_LIMIT = 200;
|
|
2
3
|
export const DEFAULT_SESSION_LIST_FAST_LIMIT = 20;
|
|
3
4
|
|
|
@@ -29,14 +30,14 @@ function shouldUseFastSessionBrowseLimit(options = {}) {
|
|
|
29
30
|
|
|
30
31
|
export function isSessionQueryEnabled(source) {
|
|
31
32
|
const normalized = normalizeSessionSource(source, '');
|
|
32
|
-
return normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'workbuddy' || normalized === 'pi' || normalized === 'opencode' || normalized === 'all';
|
|
33
|
+
return normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'workbuddy' || normalized === 'pi' || normalized === 'opencode' || normalized === 'zed' || normalized === 'all';
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export function normalizeSessionSource(source, fallback = 'all') {
|
|
36
37
|
const normalized = typeof source === 'string'
|
|
37
38
|
? source.trim().toLowerCase()
|
|
38
39
|
: '';
|
|
39
|
-
if (normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'workbuddy' || normalized === 'pi' || normalized === 'opencode' || normalized === 'all') {
|
|
40
|
+
if (normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'workbuddy' || normalized === 'pi' || normalized === 'opencode' || normalized === 'zed' || normalized === 'all') {
|
|
40
41
|
return normalized;
|
|
41
42
|
}
|
|
42
43
|
return fallback;
|
|
@@ -64,10 +65,11 @@ function getSessionSourceLabel(source) {
|
|
|
64
65
|
if (source === 'workbuddy') return 'WorkBuddy AI';
|
|
65
66
|
if (source === 'pi') return 'Pi';
|
|
66
67
|
if (source === 'opencode') return 'OpenCode';
|
|
68
|
+
if (source === 'zed') return 'Zed';
|
|
67
69
|
return 'Codex';
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
const SESSION_USAGE_SOURCE_ORDER = ['codex', 'claude', 'gemini', 'codebuddy', 'workbuddy', 'pi', 'opencode'];
|
|
72
|
+
const SESSION_USAGE_SOURCE_ORDER = ['codex', 'claude', 'gemini', 'codebuddy', 'workbuddy', 'pi', 'opencode', 'zed'];
|
|
71
73
|
|
|
72
74
|
function collectSessionModelNames(session) {
|
|
73
75
|
if (!session || typeof session !== 'object') {
|
|
@@ -154,7 +156,7 @@ export function buildSessionListParams(options = {}) {
|
|
|
154
156
|
query: queryValue,
|
|
155
157
|
queryMode: 'and',
|
|
156
158
|
queryScope: 'content',
|
|
157
|
-
contentScanLimit:
|
|
159
|
+
contentScanLimit: DEFAULT_SESSION_CONTENT_SCAN_LIMIT,
|
|
158
160
|
roleFilter,
|
|
159
161
|
timeRangePreset,
|
|
160
162
|
limit,
|
|
@@ -250,48 +250,65 @@ export function buildSessionWorkspaceSummary(session, messages, options = {}) {
|
|
|
250
250
|
return summary;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
const DURATION_UNIT_TABLES = {
|
|
254
|
+
en: {
|
|
255
|
+
zero: '0m',
|
|
256
|
+
underOne: '<1m',
|
|
257
|
+
spaced: true,
|
|
258
|
+
spaceCompact: false,
|
|
259
|
+
compact: [['d', 1440], ['h', 60], ['m', 1]],
|
|
260
|
+
long: [['day', 1440], ['hr', 60], ['min', 1]]
|
|
261
|
+
},
|
|
262
|
+
ja: {
|
|
263
|
+
zero: '0分',
|
|
264
|
+
underOne: '<1分',
|
|
265
|
+
spaced: false,
|
|
266
|
+
spaceCompact: false,
|
|
267
|
+
compact: [['日', 1440], ['時間', 60], ['分', 1]],
|
|
268
|
+
long: [['日', 1440], ['時間', 60], ['分', 1]]
|
|
269
|
+
},
|
|
270
|
+
vi: {
|
|
271
|
+
zero: '0 phút',
|
|
272
|
+
underOne: '<1 phút',
|
|
273
|
+
spaced: true,
|
|
274
|
+
spaceCompact: true,
|
|
275
|
+
compact: [['ngày', 1440], ['giờ', 60], ['phút', 1]],
|
|
276
|
+
long: [['ngày', 1440], ['giờ', 60], ['phút', 1]]
|
|
277
|
+
},
|
|
278
|
+
zh: {
|
|
279
|
+
zero: '0分',
|
|
280
|
+
underOne: '<1分',
|
|
281
|
+
spaced: false,
|
|
282
|
+
spaceCompact: false,
|
|
283
|
+
compact: [['天', 1440], ['时', 60], ['分', 1]],
|
|
284
|
+
long: [['天', 1440], ['小时', 60], ['分', 1]]
|
|
285
|
+
},
|
|
286
|
+
'zh-tw': {
|
|
287
|
+
zero: '0分',
|
|
288
|
+
underOne: '<1分',
|
|
289
|
+
spaced: false,
|
|
290
|
+
spaceCompact: false,
|
|
291
|
+
compact: [['天', 1440], ['時', 60], ['分', 1]],
|
|
292
|
+
long: [['天', 1440], ['小時', 60], ['分', 1]]
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
253
296
|
function formatUsageDuration(value, options = {}) {
|
|
254
297
|
const normalizedLang = typeof options.lang === 'string' ? options.lang.trim().toLowerCase() : '';
|
|
255
|
-
const
|
|
298
|
+
const unitSet = DURATION_UNIT_TABLES[normalizedLang] || DURATION_UNIT_TABLES.zh;
|
|
256
299
|
const numeric = Number(value);
|
|
257
300
|
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
258
|
-
return
|
|
301
|
+
return unitSet.zero;
|
|
259
302
|
}
|
|
260
303
|
const totalMinutes = Math.floor(numeric / 60000);
|
|
261
304
|
if (totalMinutes <= 0) {
|
|
262
|
-
return
|
|
305
|
+
return unitSet.underOne;
|
|
263
306
|
}
|
|
264
307
|
const maxParts = Number.isFinite(Number(options.maxParts))
|
|
265
308
|
? Math.max(1, Math.floor(Number(options.maxParts)))
|
|
266
309
|
: 2;
|
|
267
310
|
const compact = options.compact !== false;
|
|
268
|
-
const units = compact
|
|
269
|
-
? (
|
|
270
|
-
isEn
|
|
271
|
-
? [
|
|
272
|
-
{ label: 'd', value: 24 * 60 },
|
|
273
|
-
{ label: 'h', value: 60 },
|
|
274
|
-
{ label: 'm', value: 1 }
|
|
275
|
-
]
|
|
276
|
-
: [
|
|
277
|
-
{ label: '天', value: 24 * 60 },
|
|
278
|
-
{ label: '时', value: 60 },
|
|
279
|
-
{ label: '分', value: 1 }
|
|
280
|
-
]
|
|
281
|
-
)
|
|
282
|
-
: (
|
|
283
|
-
isEn
|
|
284
|
-
? [
|
|
285
|
-
{ label: 'day', value: 24 * 60 },
|
|
286
|
-
{ label: 'hr', value: 60 },
|
|
287
|
-
{ label: 'min', value: 1 }
|
|
288
|
-
]
|
|
289
|
-
: [
|
|
290
|
-
{ label: '天', value: 24 * 60 },
|
|
291
|
-
{ label: '小时', value: 60 },
|
|
292
|
-
{ label: '分', value: 1 }
|
|
293
|
-
]
|
|
294
|
-
);
|
|
311
|
+
const units = (compact ? unitSet.compact : unitSet.long).map(([label, minutes]) => ({ label, value: minutes }));
|
|
295
312
|
let remainingMinutes = totalMinutes;
|
|
296
313
|
const parts = [];
|
|
297
314
|
for (const unit of units) {
|
|
@@ -302,13 +319,14 @@ function formatUsageDuration(value, options = {}) {
|
|
|
302
319
|
if (count <= 0) {
|
|
303
320
|
continue;
|
|
304
321
|
}
|
|
305
|
-
parts.push(compact ?
|
|
322
|
+
parts.push((compact ? unitSet.spaceCompact : unitSet.spaced) ? `${count} ${unit.label}` : `${count}${unit.label}`);
|
|
306
323
|
remainingMinutes -= count * unit.value;
|
|
307
324
|
if (parts.length >= maxParts) {
|
|
308
325
|
break;
|
|
309
326
|
}
|
|
310
327
|
}
|
|
311
|
-
|
|
328
|
+
const separator = compact ? (unitSet.spaceCompact ? ' ' : '') : ' ';
|
|
329
|
+
return parts.length ? parts.join(separator) : unitSet.zero;
|
|
312
330
|
}
|
|
313
331
|
|
|
314
332
|
export function createSessionComputed() {
|
|
@@ -325,7 +343,8 @@ export function createSessionComputed() {
|
|
|
325
343
|
{ value: "codebuddy", label: this.t("sessions.source.codebuddy") },
|
|
326
344
|
{ value: "workbuddy", label: this.t("sessions.source.workbuddy") },
|
|
327
345
|
{ value: "pi", label: this.t("sessions.source.pi") },
|
|
328
|
-
{ value: "opencode", label: this.t("sessions.source.opencode") }
|
|
346
|
+
{ value: "opencode", label: this.t("sessions.source.opencode") },
|
|
347
|
+
{ value: "zed", label: this.t("sessions.source.zed") }
|
|
329
348
|
];
|
|
330
349
|
},
|
|
331
350
|
activeSessionExportKey() {
|
|
@@ -252,6 +252,38 @@ export function createAgentsMethods(options = {}) {
|
|
|
252
252
|
this.agentsDiffFingerprint = '';
|
|
253
253
|
this._agentsDiffPreviewRequestToken = null;
|
|
254
254
|
},
|
|
255
|
+
trapModalFocus(event) {
|
|
256
|
+
const overlays = typeof document !== 'undefined' && typeof document.querySelectorAll === 'function'
|
|
257
|
+
? Array.from(document.querySelectorAll('.modal-overlay'))
|
|
258
|
+
: [];
|
|
259
|
+
if (!overlays.length) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const modal = overlays[overlays.length - 1];
|
|
263
|
+
const focusableSelector = 'button:not(:disabled), [href], input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"])';
|
|
264
|
+
const focusables = Array.from(modal.querySelectorAll(focusableSelector))
|
|
265
|
+
.filter((el) => el.offsetParent !== null || el === document.activeElement);
|
|
266
|
+
if (!focusables.length) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const first = focusables[0];
|
|
270
|
+
const last = focusables[focusables.length - 1];
|
|
271
|
+
const active = document.activeElement;
|
|
272
|
+
if (!modal.contains(active)) {
|
|
273
|
+
event.preventDefault();
|
|
274
|
+
first.focus();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (event.shiftKey && active === first) {
|
|
278
|
+
event.preventDefault();
|
|
279
|
+
last.focus();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (!event.shiftKey && active === last) {
|
|
283
|
+
event.preventDefault();
|
|
284
|
+
first.focus();
|
|
285
|
+
}
|
|
286
|
+
},
|
|
255
287
|
handleGlobalKeydown(event) {
|
|
256
288
|
if (!event) {
|
|
257
289
|
return;
|
|
@@ -293,6 +325,10 @@ export function createAgentsMethods(options = {}) {
|
|
|
293
325
|
}
|
|
294
326
|
return;
|
|
295
327
|
}
|
|
328
|
+
if (key === 'Tab') {
|
|
329
|
+
this.trapModalFocus(event);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
296
332
|
if (key !== 'Escape') {
|
|
297
333
|
return;
|
|
298
334
|
}
|
|
@@ -75,7 +75,8 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
75
75
|
const res = await api('export-session', {
|
|
76
76
|
source: session.source,
|
|
77
77
|
sessionId: session.sessionId,
|
|
78
|
-
filePath: session.filePath
|
|
78
|
+
filePath: session.filePath,
|
|
79
|
+
...(this.sessionExportRange === 'all' ? { maxMessages: 'all' } : {})
|
|
79
80
|
});
|
|
80
81
|
if (res.error) {
|
|
81
82
|
this.showMessage(res.error, 'error');
|
|
@@ -84,6 +85,7 @@ export function createCodexConfigMethods(options = {}) {
|
|
|
84
85
|
|
|
85
86
|
const fileName = res.fileName || `${session.source || 'session'}-${session.sessionId || Date.now()}.md`;
|
|
86
87
|
this.downloadTextFile(fileName, res.content || '');
|
|
88
|
+
this.sessionExportNotice = res.truncated ? { key } : null;
|
|
87
89
|
if (res.truncated) {
|
|
88
90
|
const maxLabel = res.maxMessages === 'all' ? 'all' : res.maxMessages;
|
|
89
91
|
this.showMessage(`会话导出完成(已截断:最多 ${maxLabel} 条消息)`, 'info');
|
|
@@ -479,6 +479,14 @@ export function createProvidersMethods(options = {}) {
|
|
|
479
479
|
|
|
480
480
|
async resetConfig() {
|
|
481
481
|
if (this.resetConfigLoading) return;
|
|
482
|
+
const confirmed = await this.requestConfirmDialog({
|
|
483
|
+
title: this.t('settings.reset.confirmTitle'),
|
|
484
|
+
message: this.t('settings.reset.confirmMessage'),
|
|
485
|
+
confirmText: this.t('settings.reset.button'),
|
|
486
|
+
cancelText: this.t('confirm.cancel'),
|
|
487
|
+
danger: true
|
|
488
|
+
});
|
|
489
|
+
if (!confirmed) return;
|
|
482
490
|
this.resetConfigLoading = true;
|
|
483
491
|
try {
|
|
484
492
|
const res = await api('reset-config');
|
|
@@ -486,7 +494,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
486
494
|
this.showMessage(res.error, 'error');
|
|
487
495
|
return;
|
|
488
496
|
}
|
|
489
|
-
const backup = res.backupFile ?
|
|
497
|
+
const backup = res.backupFile ? this.t('toast.provider.resetBackup', { file: res.backupFile }) : '';
|
|
490
498
|
this.showMessage(this.t('toast.provider.resetSuccess', { backup }), 'success');
|
|
491
499
|
await this.loadAll();
|
|
492
500
|
} catch (e) {
|
|
@@ -498,7 +506,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
498
506
|
|
|
499
507
|
async addModel() {
|
|
500
508
|
if (!this.newModelName || !this.newModelName.trim()) {
|
|
501
|
-
return this.showMessage('
|
|
509
|
+
return this.showMessage(this.t('toast.provider.modelRequired'), 'error');
|
|
502
510
|
}
|
|
503
511
|
try {
|
|
504
512
|
const modelName = this.newModelName.trim();
|
|
@@ -523,7 +531,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
523
531
|
this.closeModelModal();
|
|
524
532
|
}
|
|
525
533
|
} catch (_) {
|
|
526
|
-
this.showMessage('
|
|
534
|
+
this.showMessage(this.t('toast.provider.addModelFail'), 'error');
|
|
527
535
|
}
|
|
528
536
|
},
|
|
529
537
|
|
|
@@ -546,7 +554,7 @@ export function createProvidersMethods(options = {}) {
|
|
|
546
554
|
this.showMessage(this.t('toast.operation.success'), 'success');
|
|
547
555
|
}
|
|
548
556
|
} catch (_) {
|
|
549
|
-
this.showMessage('
|
|
557
|
+
this.showMessage(this.t('toast.provider.deleteModelFail'), 'error');
|
|
550
558
|
}
|
|
551
559
|
},
|
|
552
560
|
|
|
@@ -28,8 +28,8 @@ export function createSessionActionMethods(options = {}) {
|
|
|
28
28
|
let error = '';
|
|
29
29
|
if (!source) {
|
|
30
30
|
error = '缺少 source 参数';
|
|
31
|
-
} else if (source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'workbuddy' && source !== 'pi' && source !== 'opencode') {
|
|
32
|
-
error = 'source 仅支持 codex、claude、gemini、codebuddy、workbuddy、pi 或
|
|
31
|
+
} else if (source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'workbuddy' && source !== 'pi' && source !== 'opencode' && source !== 'zed') {
|
|
32
|
+
error = 'source 仅支持 codex、claude、gemini、codebuddy、workbuddy、pi、opencode 或 zed';
|
|
33
33
|
}
|
|
34
34
|
if (!sessionId && !filePath) {
|
|
35
35
|
error = error ? `${error},还缺少 sessionId 或 filePath` : '缺少 sessionId 或 filePath 参数';
|
|
@@ -77,11 +77,13 @@ export function createSessionActionMethods(options = {}) {
|
|
|
77
77
|
? 'Pi'
|
|
78
78
|
: (context.params.source === 'opencode'
|
|
79
79
|
? 'OpenCode'
|
|
80
|
-
: (context.params.source === '
|
|
81
|
-
? '
|
|
82
|
-
: (context.params.source === '
|
|
83
|
-
? '
|
|
84
|
-
: context.params.source
|
|
80
|
+
: (context.params.source === 'zed'
|
|
81
|
+
? 'Zed'
|
|
82
|
+
: (context.params.source === 'codebuddy'
|
|
83
|
+
? 'CodeBuddy Code'
|
|
84
|
+
: (context.params.source === 'workbuddy'
|
|
85
|
+
? 'WorkBuddy AI'
|
|
86
|
+
: context.params.source)))))))
|
|
85
87
|
this.activeSession = {
|
|
86
88
|
source: context.params.source,
|
|
87
89
|
sourceLabel,
|
|
@@ -110,7 +112,7 @@ export function createSessionActionMethods(options = {}) {
|
|
|
110
112
|
buildSessionStandaloneUrl(session) {
|
|
111
113
|
if (!session) return '';
|
|
112
114
|
const source = typeof session.source === 'string' ? session.source.trim().toLowerCase() : '';
|
|
113
|
-
if (!source || (source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'workbuddy' && source !== 'pi' && source !== 'opencode')) return '';
|
|
115
|
+
if (!source || (source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'workbuddy' && source !== 'pi' && source !== 'opencode' && source !== 'zed')) return '';
|
|
114
116
|
const sessionId = typeof session.sessionId === 'string' ? session.sessionId.trim() : '';
|
|
115
117
|
const filePath = typeof session.filePath === 'string' ? session.filePath.trim() : '';
|
|
116
118
|
if (!sessionId && !filePath) return '';
|
|
@@ -524,7 +524,9 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
524
524
|
? this.t('sessions.source.pi')
|
|
525
525
|
: (this.sessionFilterSource === 'opencode'
|
|
526
526
|
? this.t('sessions.source.opencode')
|
|
527
|
-
: this.sessionFilterSource
|
|
527
|
+
: (this.sessionFilterSource === 'zed'
|
|
528
|
+
? this.t('sessions.source.zed')
|
|
529
|
+
: this.sessionFilterSource)))))));
|
|
528
530
|
chips.push({ key: 'source', title: this.t('sessions.filters.source'), value: label });
|
|
529
531
|
}
|
|
530
532
|
if (this.sessionPathFilter) {
|
|
@@ -292,7 +292,7 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
292
292
|
this.sessionTrashCountLoadedOnce = true;
|
|
293
293
|
} catch (e) {
|
|
294
294
|
if (this.isLatestSessionTrashCountRequestToken(requestToken) && options.silent !== true) {
|
|
295
|
-
this.showMessage('
|
|
295
|
+
this.showMessage(this.t('settings.trash.loadCountFail'), 'error');
|
|
296
296
|
}
|
|
297
297
|
} finally {
|
|
298
298
|
this.sessionTrashCountLoading = false;
|
|
@@ -365,7 +365,7 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
365
365
|
} catch (e) {
|
|
366
366
|
if (this.isLatestSessionTrashListRequestToken(requestToken)) {
|
|
367
367
|
this.sessionTrashLastLoadFailed = true;
|
|
368
|
-
this.showMessage('
|
|
368
|
+
this.showMessage(this.t('settings.trash.loadFail'), 'error');
|
|
369
369
|
}
|
|
370
370
|
} finally {
|
|
371
371
|
this.sessionTrashLoading = false;
|
|
@@ -392,7 +392,7 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
392
392
|
this.showMessage(res.error, 'error');
|
|
393
393
|
return;
|
|
394
394
|
}
|
|
395
|
-
this.showMessage('
|
|
395
|
+
this.showMessage(this.t('settings.trash.restored'), 'success');
|
|
396
396
|
if (typeof this.invalidateSessionsUsageData === 'function') {
|
|
397
397
|
this.invalidateSessionsUsageData({ preserveList: true });
|
|
398
398
|
}
|
|
@@ -402,7 +402,7 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
402
402
|
await this.loadSessions();
|
|
403
403
|
}
|
|
404
404
|
} catch (e) {
|
|
405
|
-
this.showMessage('
|
|
405
|
+
this.showMessage(this.t('settings.trash.restoreFail'), 'error');
|
|
406
406
|
} finally {
|
|
407
407
|
this.sessionTrashRestoring[key] = false;
|
|
408
408
|
}
|
|
@@ -414,10 +414,10 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
414
414
|
return;
|
|
415
415
|
}
|
|
416
416
|
const confirmed = await this.requestConfirmDialog({
|
|
417
|
-
title: '
|
|
418
|
-
message: '
|
|
419
|
-
confirmText: '
|
|
420
|
-
cancelText: '
|
|
417
|
+
title: this.t('settings.trash.purgeConfirmTitle'),
|
|
418
|
+
message: this.t('settings.trash.purgeConfirmMessage'),
|
|
419
|
+
confirmText: this.t('settings.trash.purge'),
|
|
420
|
+
cancelText: this.t('confirm.cancel'),
|
|
421
421
|
danger: true
|
|
422
422
|
});
|
|
423
423
|
if (!confirmed) {
|
|
@@ -430,11 +430,11 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
430
430
|
this.showMessage(res.error, 'error');
|
|
431
431
|
return;
|
|
432
432
|
}
|
|
433
|
-
this.showMessage('
|
|
433
|
+
this.showMessage(this.t('settings.trash.purged'), 'success');
|
|
434
434
|
this.invalidateSessionTrashRequests();
|
|
435
435
|
await this.loadSessionTrash({ forceRefresh: true });
|
|
436
436
|
} catch (e) {
|
|
437
|
-
this.showMessage('
|
|
437
|
+
this.showMessage(this.t('settings.trash.purgeFail'), 'error');
|
|
438
438
|
} finally {
|
|
439
439
|
this.sessionTrashPurging[key] = false;
|
|
440
440
|
}
|
|
@@ -446,10 +446,10 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
446
446
|
return;
|
|
447
447
|
}
|
|
448
448
|
const confirmed = await this.requestConfirmDialog({
|
|
449
|
-
title: '
|
|
450
|
-
message: '
|
|
451
|
-
confirmText: '
|
|
452
|
-
cancelText: '
|
|
449
|
+
title: this.t('settings.trash.clearConfirmTitle'),
|
|
450
|
+
message: this.t('settings.trash.clearConfirmMessage'),
|
|
451
|
+
confirmText: this.t('settings.trash.clearConfirmButton'),
|
|
452
|
+
cancelText: this.t('confirm.cancel'),
|
|
453
453
|
danger: true
|
|
454
454
|
});
|
|
455
455
|
if (!confirmed) {
|
|
@@ -462,11 +462,11 @@ export function createSessionTrashMethods(options = {}) {
|
|
|
462
462
|
this.showMessage(res.error, 'error');
|
|
463
463
|
return;
|
|
464
464
|
}
|
|
465
|
-
this.showMessage('
|
|
465
|
+
this.showMessage(this.t('settings.trash.cleared'), 'success');
|
|
466
466
|
this.invalidateSessionTrashRequests();
|
|
467
467
|
await this.loadSessionTrash({ forceRefresh: true });
|
|
468
468
|
} catch (e) {
|
|
469
|
-
this.showMessage('
|
|
469
|
+
this.showMessage(this.t('settings.trash.clearFail'), 'error');
|
|
470
470
|
} finally {
|
|
471
471
|
this.sessionTrashClearing = false;
|
|
472
472
|
}
|
|
@@ -3,14 +3,15 @@ const THEME_STORAGE_CACHE_KEY = 'codexmateThemeCache';
|
|
|
3
3
|
export function createThemeMethods() {
|
|
4
4
|
return {
|
|
5
5
|
normalizeTheme(value) {
|
|
6
|
-
|
|
6
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
7
|
+
return normalized === 'dark' || normalized === 'ocean' ? normalized : 'light';
|
|
7
8
|
},
|
|
8
9
|
applyDocumentTheme() {
|
|
9
10
|
try {
|
|
10
11
|
if (typeof document === 'undefined' || !document.documentElement) return;
|
|
11
12
|
const next = this.normalizeTheme(this.theme);
|
|
12
|
-
if (next === 'dark') {
|
|
13
|
-
document.documentElement.setAttribute('data-theme',
|
|
13
|
+
if (next === 'dark' || next === 'ocean') {
|
|
14
|
+
document.documentElement.setAttribute('data-theme', next);
|
|
14
15
|
} else {
|
|
15
16
|
document.documentElement.removeAttribute('data-theme');
|
|
16
17
|
}
|
|
@@ -19,7 +20,7 @@ export function createThemeMethods() {
|
|
|
19
20
|
// the server-side webUi preference store.
|
|
20
21
|
const storage = typeof globalThis !== 'undefined' ? globalThis.localStorage : null;
|
|
21
22
|
if (storage && typeof storage.setItem === 'function' && typeof storage.removeItem === 'function') {
|
|
22
|
-
if (next === 'dark') storage.setItem(THEME_STORAGE_CACHE_KEY,
|
|
23
|
+
if (next === 'dark' || next === 'ocean') storage.setItem(THEME_STORAGE_CACHE_KEY, next);
|
|
23
24
|
else storage.removeItem(THEME_STORAGE_CACHE_KEY);
|
|
24
25
|
}
|
|
25
26
|
} catch (_) {}
|
|
@@ -520,6 +520,11 @@ const en = Object.freeze({
|
|
|
520
520
|
'toast.provider.updateFail': 'Failed to update',
|
|
521
521
|
'toast.provider.resetSuccess': 'Config reset{backup}',
|
|
522
522
|
'toast.provider.resetFail': 'Reset failed',
|
|
523
|
+
|
|
524
|
+
'toast.provider.resetBackup': ' (backup: {file})',
|
|
525
|
+
'toast.provider.modelRequired': 'Please enter model',
|
|
526
|
+
'toast.provider.addModelFail': 'Failed to add model',
|
|
527
|
+
'toast.provider.deleteModelFail': 'Failed to delete model',
|
|
523
528
|
'toast.provider.fieldsRequired': 'Name, URL, API Key and model are required',
|
|
524
529
|
'toast.provider.urlRequired': 'URL is required',
|
|
525
530
|
'toast.claude.modelRequired': 'Please enter model',
|
|
@@ -690,12 +695,13 @@ const en = Object.freeze({
|
|
|
690
695
|
'sessions.source.workbuddy': 'WorkBuddy AI',
|
|
691
696
|
'sessions.source.pi': 'Pi',
|
|
692
697
|
'sessions.source.opencode': 'OpenCode',
|
|
698
|
+
'sessions.source.zed': 'Zed',
|
|
693
699
|
'sessions.loadingList': 'Loading sessions...',
|
|
694
700
|
'sessions.empty': 'No sessions found',
|
|
695
701
|
'sessions.unknownTime': 'unknown time',
|
|
696
702
|
|
|
697
703
|
|
|
698
|
-
'sessions.query.placeholder.enabled':
|
|
704
|
+
'sessions.query.placeholder.enabled': "Search keywords (e.g. claude code)",
|
|
699
705
|
'sessions.query.placeholder.disabled': 'Keyword search is not available for this source',
|
|
700
706
|
'sessions.pin': 'Pin',
|
|
701
707
|
'sessions.unpin': 'Unpin',
|
|
@@ -721,6 +727,17 @@ const en = Object.freeze({
|
|
|
721
727
|
'sessions.preview.moveToTrash': 'Move to trash',
|
|
722
728
|
'sessions.preview.deleting': 'Deleting...',
|
|
723
729
|
'sessions.preview.moving': 'Moving...',
|
|
730
|
+
'sessions.export.range': "Export range",
|
|
731
|
+
'sessions.export.default': "Latest 1000 messages",
|
|
732
|
+
'sessions.export.all': "All extractable messages",
|
|
733
|
+
'sessions.export.scopeHint': "All removes the message limit; it is not a complete state backup.",
|
|
734
|
+
'sessions.export.truncated': "This export was truncated. Choose all and export again for earlier messages.",
|
|
735
|
+
'sessions.search.coverage': "Scanned {scanned} / {candidates} candidate sessions",
|
|
736
|
+
'sessions.search.partial': "Some candidates remain unscanned. Narrow the source or project.",
|
|
737
|
+
'sessions.search.scopeHint': "Covers this candidate set only, not all historical records.",
|
|
738
|
+
'sessions.empty.filtered': "No records match the current filters.",
|
|
739
|
+
'sessions.empty.source': "No displayable records found for this source. Check local sessions and refresh.",
|
|
740
|
+
'sessions.empty.clearFilters': "Clear filters",
|
|
724
741
|
'sessions.preview.export': 'Export',
|
|
725
742
|
'sessions.preview.exporting': 'Exporting...',
|
|
726
743
|
'sessions.preview.convert': 'Create derived',
|
|
@@ -1137,6 +1154,7 @@ const en = Object.freeze({
|
|
|
1137
1154
|
'settings.theme.meta': 'Choose the Web UI color theme',
|
|
1138
1155
|
'settings.theme.light': 'Light',
|
|
1139
1156
|
'settings.theme.dark': 'Dark',
|
|
1157
|
+
'settings.theme.ocean': 'Ocean',
|
|
1140
1158
|
'settings.theme.hint': 'Theme preference is saved automatically and persists across reloads.',
|
|
1141
1159
|
'settings.sharePrefix.title': 'Share command prefix',
|
|
1142
1160
|
'settings.sharePrefix.meta': 'Used as the prefix for “Copy share command” in the Web UI',
|
|
@@ -1213,6 +1231,20 @@ const en = Object.freeze({
|
|
|
1213
1231
|
'settings.trash.retentionLabel': 'Retention days',
|
|
1214
1232
|
'settings.trash.retentionUnit': 'days',
|
|
1215
1233
|
'settings.trash.retentionHint': 'Range 1-365 days, default 30. Expired entries are purged on each trash load.',
|
|
1234
|
+
|
|
1235
|
+
'settings.trash.loadCountFail': 'Failed to load trash count',
|
|
1236
|
+
'settings.trash.loadFail': 'Failed to load trash',
|
|
1237
|
+
'settings.trash.restored': 'Session restored',
|
|
1238
|
+
'settings.trash.restoreFail': 'Restore failed',
|
|
1239
|
+
'settings.trash.purged': 'Purged',
|
|
1240
|
+
'settings.trash.purgeFail': 'Purge failed',
|
|
1241
|
+
'settings.trash.cleared': 'Trash emptied',
|
|
1242
|
+
'settings.trash.clearFail': 'Failed to empty trash',
|
|
1243
|
+
'settings.trash.purgeConfirmTitle': 'Purge trash entry',
|
|
1244
|
+
'settings.trash.purgeConfirmMessage': 'This session will be permanently deleted from the trash and cannot be restored.',
|
|
1245
|
+
'settings.trash.clearConfirmTitle': 'Empty trash',
|
|
1246
|
+
'settings.trash.clearConfirmMessage': 'All sessions in the trash will be permanently deleted and cannot be restored.',
|
|
1247
|
+
'settings.trash.clearConfirmButton': 'Empty all',
|
|
1216
1248
|
'settings.trash.count': '{count} item(s)',
|
|
1217
1249
|
'settings.trash.retentionShort': 'Kept {days} days',
|
|
1218
1250
|
'settings.trash.disabled': 'Trash is disabled',
|
|
@@ -1253,6 +1285,9 @@ const en = Object.freeze({
|
|
|
1253
1285
|
'settings.reset.button': 'Reset config',
|
|
1254
1286
|
'settings.reset.loading': 'Resetting...',
|
|
1255
1287
|
|
|
1288
|
+
'settings.reset.confirmTitle': 'Confirm config reset',
|
|
1289
|
+
'settings.reset.confirmMessage': 'This will back up config.toml, then overwrite the current config with defaults. Continue?',
|
|
1290
|
+
|
|
1256
1291
|
// Market (Skills)
|
|
1257
1292
|
'market.title': 'Skills Overview',
|
|
1258
1293
|
'market.subtitle': 'Switch target and inspect local skills.',
|
|
@@ -1363,6 +1398,44 @@ const en = Object.freeze({
|
|
|
1363
1398
|
'modal.openclaw.quick.subtitle': '3 steps: fill provider/model, write to editor, save & apply.',
|
|
1364
1399
|
'modal.openclaw.quick.step2': 'Write to editor',
|
|
1365
1400
|
'modal.openclaw.structured.writeHint': 'Writing to editor may reformat JSON and drop comments.',
|
|
1401
|
+
|
|
1402
|
+
'modal.openclaw.next': 'Next →',
|
|
1403
|
+
'modal.openclaw.prev': '← Previous',
|
|
1404
|
+
'modal.openclaw.finish': 'Finish ✓',
|
|
1405
|
+
'modal.openclaw.quick.advancedOptions': 'Advanced options',
|
|
1406
|
+
'modal.openclaw.quick.providerNamePlaceholder': 'e.g. custom-myapi',
|
|
1407
|
+
'modal.openclaw.quick.modelIdHint': 'Required, e.g. gpt-4',
|
|
1408
|
+
'modal.openclaw.quick.modelNameHint': 'Optional, used for display',
|
|
1409
|
+
'modal.openclaw.structured.title': 'Structured config (advanced)',
|
|
1410
|
+
'modal.openclaw.structured.primaryModel': 'Primary model',
|
|
1411
|
+
'modal.openclaw.structured.fallbackModels': 'Fallback models',
|
|
1412
|
+
'modal.openclaw.structured.providerModelPlaceholder': 'e.g. provider/model',
|
|
1413
|
+
'modal.openclaw.structured.addFallback': 'Add fallback',
|
|
1414
|
+
'modal.openclaw.structured.workspacePlaceholder': 'e.g. ~/.openclaw/workspace',
|
|
1415
|
+
'modal.openclaw.structured.timeoutPlaceholder': 'e.g. 600',
|
|
1416
|
+
'modal.openclaw.structured.contextTokensPlaceholder': 'e.g. 4096',
|
|
1417
|
+
'modal.openclaw.structured.maxConcurrentPlaceholder': 'e.g. 2',
|
|
1418
|
+
'modal.openclaw.structured.envVars': 'Environment variables',
|
|
1419
|
+
'modal.openclaw.structured.addEnvVar': 'Add variable',
|
|
1420
|
+
'modal.openclaw.structured.addAllow': 'Add allow',
|
|
1421
|
+
'modal.openclaw.structured.addDeny': 'Add deny',
|
|
1422
|
+
'modal.openclaw.structured.allowPlaceholder': 'e.g. fs.read*',
|
|
1423
|
+
'modal.openclaw.structured.denyPlaceholder': 'e.g. net.*',
|
|
1424
|
+
'modal.openclaw.structured.providersTitle': 'Providers (read-only)',
|
|
1425
|
+
'modal.openclaw.structured.noProviders': 'No providers config found (may use environment variables or external config).',
|
|
1426
|
+
'modal.openclaw.structured.sourceLabel': 'Source',
|
|
1427
|
+
'modal.openclaw.structured.active': 'Active',
|
|
1428
|
+
'modal.openclaw.structured.noFields': 'No fields configured',
|
|
1429
|
+
'modal.openclaw.structured.missingProviders': 'Active providers not shown in config: {list}.',
|
|
1430
|
+
'modal.openclaw.structured.agentsTitle': 'Agents (read-only)',
|
|
1431
|
+
'modal.openclaw.structured.noAgents': 'No agents.list config found.',
|
|
1432
|
+
'modal.openclaw.structured.themeLabel': 'Theme',
|
|
1433
|
+
'modal.openclaw.structured.emojiLabel': 'Emoji',
|
|
1434
|
+
'modal.openclaw.structured.avatarLabel': 'Avatar',
|
|
1435
|
+
'modal.openclaw.editorLabel': 'OpenClaw config (JSON5)',
|
|
1436
|
+
'modal.openclaw.editorPlaceholder': 'Edit OpenClaw config here (JSON5)',
|
|
1437
|
+
'modal.openclaw.defaultConfigHint': 'The default config always maps to the current openclaw.json. Use "Save & apply" directly.',
|
|
1438
|
+
'modal.openclaw.saveOnlyHint': 'Saving only writes to the local config library. Click "Save & apply" to write openclaw.json.',
|
|
1366
1439
|
'openclaw.action.applyAria': 'Apply OpenClaw config: {name}',
|
|
1367
1440
|
'openclaw.action.editAria': 'Edit OpenClaw config: {name}',
|
|
1368
1441
|
'openclaw.action.deleteAria': 'Delete OpenClaw config: {name}'
|