codexmate 0.1.27 → 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/config-bootstrap.js +2 -9
- package/cli/session-usage.js +2 -2
- package/cli.js +407 -40
- package/package.json +1 -1
- package/web-ui/app.js +7 -0
- package/web-ui/index.html +12 -0
- 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.index.mjs +2 -0
- 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 +39 -0
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +9 -0
- package/web-ui/modules/i18n/locales/en.mjs +80 -2
- package/web-ui/modules/i18n/locales/ja.mjs +80 -2
- package/web-ui/modules/i18n/locales/vi.mjs +80 -2
- package/web-ui/modules/i18n/locales/zh-tw.mjs +80 -2
- package/web-ui/modules/i18n/locales/zh.mjs +80 -2
- 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 +47 -30
- package/web-ui/partials/index/panel-settings.html +30 -0
- package/web-ui/res/web-ui-render.precompiled.js +244 -157
- package/web-ui/session-helpers.mjs +3 -0
- package/web-ui/styles/base-theme.css +3 -2
- package/web-ui/styles/bridge-pool.css +1 -1
- package/web-ui/styles/controls-forms.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/sessions-toolbar-trash.css +1 -1
- package/web-ui/styles/sessions-usage.css +1 -1
- package/web-ui/styles/settings-panel.css +2 -2
- package/web-ui/styles/theme-dark-tokens.css +83 -0
- package/web-ui/styles/theme-dark.css +1355 -0
- package/web-ui/styles/theme-ocean-tokens.css +88 -0
- package/web-ui/styles/theme-ocean.css +920 -0
- package/web-ui/styles.css +2 -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: {},
|
|
@@ -575,6 +578,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
575
578
|
kilocodeAutoSaveSignature: '',
|
|
576
579
|
forceCompactLayout: false,
|
|
577
580
|
sidebarCollapsed: false,
|
|
581
|
+
theme: 'light',
|
|
578
582
|
sessionLoadNativeDialog: false,
|
|
579
583
|
starPrompted: false,
|
|
580
584
|
webhookConfig: { enabled: false, url: '', events: ['provider-switch', 'claude-md-edit'] },
|
|
@@ -604,6 +608,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
604
608
|
if (typeof this.initI18n === 'function') {
|
|
605
609
|
this.initI18n();
|
|
606
610
|
}
|
|
611
|
+
if (typeof this.applyDocumentTheme === 'function') {
|
|
612
|
+
this.applyDocumentTheme();
|
|
613
|
+
}
|
|
607
614
|
if (typeof this.loadWebhookSettings === 'function') {
|
|
608
615
|
this.loadWebhookSettings();
|
|
609
616
|
}
|
package/web-ui/index.html
CHANGED
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
<script src="/res/marked.min.js"></script>
|
|
12
12
|
<script src="/res/purify.min.js"></script>
|
|
13
13
|
<link rel="stylesheet" href="/web-ui/styles.css">
|
|
14
|
+
<script>
|
|
15
|
+
// Render hint: skip the light flash for dark-theme users before the
|
|
16
|
+
// server preference store loads. Kept in sync by applyDocumentTheme().
|
|
17
|
+
(function () {
|
|
18
|
+
try {
|
|
19
|
+
var cachedTheme = localStorage.getItem('codexmateThemeCache');
|
|
20
|
+
if (cachedTheme === 'dark' || cachedTheme === 'ocean') {
|
|
21
|
+
document.documentElement.setAttribute('data-theme', cachedTheme);
|
|
22
|
+
}
|
|
23
|
+
} catch (_) {}
|
|
24
|
+
})();
|
|
25
|
+
</script>
|
|
14
26
|
</head>
|
|
15
27
|
<body>
|
|
16
28
|
<!-- @include ./partials/index/layout-header.html -->
|
|
@@ -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');
|
|
@@ -38,6 +38,7 @@ import { createPluginsMethods } from './plugins.methods.mjs';
|
|
|
38
38
|
import { createI18nMethods } from './i18n.mjs';
|
|
39
39
|
import { createWebhookMethods } from './app.methods.webhook.mjs';
|
|
40
40
|
import { createWebUiPreferencesMethods } from './app.methods.web-ui-preferences.mjs';
|
|
41
|
+
import { createThemeMethods } from './app.methods.theme.mjs';
|
|
41
42
|
import {
|
|
42
43
|
CONFIG_MODE_SET,
|
|
43
44
|
getProviderConfigModeMeta
|
|
@@ -92,6 +93,7 @@ export function createAppMethods() {
|
|
|
92
93
|
...createProvidersMethods({ api }),
|
|
93
94
|
...createProviderCacheMethods({ api }),
|
|
94
95
|
...createWebUiPreferencesMethods({ api }),
|
|
96
|
+
...createThemeMethods(),
|
|
95
97
|
...createClaudeConfigMethods({ api }),
|
|
96
98
|
...createToolConfigPermissionMethods({ api }),
|
|
97
99
|
...createOpenclawCoreMethods(),
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const THEME_STORAGE_CACHE_KEY = 'codexmateThemeCache';
|
|
2
|
+
|
|
3
|
+
export function createThemeMethods() {
|
|
4
|
+
return {
|
|
5
|
+
normalizeTheme(value) {
|
|
6
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
7
|
+
return normalized === 'dark' || normalized === 'ocean' ? normalized : 'light';
|
|
8
|
+
},
|
|
9
|
+
applyDocumentTheme() {
|
|
10
|
+
try {
|
|
11
|
+
if (typeof document === 'undefined' || !document.documentElement) return;
|
|
12
|
+
const next = this.normalizeTheme(this.theme);
|
|
13
|
+
if (next === 'dark' || next === 'ocean') {
|
|
14
|
+
document.documentElement.setAttribute('data-theme', next);
|
|
15
|
+
} else {
|
|
16
|
+
document.documentElement.removeAttribute('data-theme');
|
|
17
|
+
}
|
|
18
|
+
// Render hint only: lets the inline head script skip the light
|
|
19
|
+
// flash before server preferences arrive. Source of truth is
|
|
20
|
+
// the server-side webUi preference store.
|
|
21
|
+
const storage = typeof globalThis !== 'undefined' ? globalThis.localStorage : null;
|
|
22
|
+
if (storage && typeof storage.setItem === 'function' && typeof storage.removeItem === 'function') {
|
|
23
|
+
if (next === 'dark' || next === 'ocean') storage.setItem(THEME_STORAGE_CACHE_KEY, next);
|
|
24
|
+
else storage.removeItem(THEME_STORAGE_CACHE_KEY);
|
|
25
|
+
}
|
|
26
|
+
} catch (_) {}
|
|
27
|
+
},
|
|
28
|
+
setTheme(theme) {
|
|
29
|
+
const next = this.normalizeTheme(theme);
|
|
30
|
+
if (next !== this.theme) {
|
|
31
|
+
this.theme = next;
|
|
32
|
+
this.applyDocumentTheme();
|
|
33
|
+
}
|
|
34
|
+
if (typeof this.persistWebUiPreferences === 'function') {
|
|
35
|
+
this.persistWebUiPreferences({ theme: next });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -352,6 +352,9 @@ export function createWebUiPreferencesMethods(options = {}) {
|
|
|
352
352
|
? source.projectClaudeMdPath
|
|
353
353
|
: (typeof this.projectClaudeMdPath === 'string' ? this.projectClaudeMdPath : ''),
|
|
354
354
|
sidebarCollapsed: normalizeBoolean(hasOwn(source, 'sidebarCollapsed') ? source.sidebarCollapsed : this.sidebarCollapsed, false),
|
|
355
|
+
theme: typeof this.normalizeTheme === 'function'
|
|
356
|
+
? this.normalizeTheme(hasOwn(source, 'theme') ? source.theme : this.theme)
|
|
357
|
+
: 'light',
|
|
355
358
|
configModeVisibility: typeof this.normalizeConfigModeVisibility === 'function'
|
|
356
359
|
? this.normalizeConfigModeVisibility(hasOwn(source, 'configModeVisibility') ? source.configModeVisibility : this.configModeVisibility)
|
|
357
360
|
: { codex: true, claude: true, openclaw: true, opencode: true, kilocode: true },
|
|
@@ -423,6 +426,12 @@ export function createWebUiPreferencesMethods(options = {}) {
|
|
|
423
426
|
if (hasOwn(source, 'sidebarCollapsed')) {
|
|
424
427
|
this.sidebarCollapsed = normalizeBoolean(source.sidebarCollapsed, false);
|
|
425
428
|
}
|
|
429
|
+
if (typeof source.theme === 'string') {
|
|
430
|
+
this.theme = typeof this.normalizeTheme === 'function'
|
|
431
|
+
? this.normalizeTheme(source.theme)
|
|
432
|
+
: 'light';
|
|
433
|
+
if (typeof this.applyDocumentTheme === 'function') this.applyDocumentTheme();
|
|
434
|
+
}
|
|
426
435
|
if (hasOwn(source, 'configModeVisibility') && typeof this.normalizeConfigModeVisibility === 'function') {
|
|
427
436
|
this.configModeVisibility = this.normalizeConfigModeVisibility(source.configModeVisibility);
|
|
428
437
|
}
|