codexmate 0.1.24 → 0.1.27
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/cli/session-usage.js +10 -4
- package/cli.js +737 -200
- package/lib/cli-sessions.js +1 -1
- package/package.json +3 -3
- package/plugins/prompt-templates/computed.mjs +1 -1
- package/plugins/prompt-templates/index.mjs +3 -3
- package/plugins/prompt-templates/manifest.mjs +5 -3
- package/plugins/prompt-templates/methods.mjs +9 -2
- package/plugins/registry.mjs +5 -2
- package/plugins/text-tools/computed.mjs +23 -0
- package/plugins/text-tools/index.mjs +14 -0
- package/plugins/text-tools/manifest.mjs +20 -0
- package/plugins/text-tools/methods.mjs +24 -0
- package/plugins/text-tools/overview.mjs +12 -0
- package/plugins/text-tools/ownership.mjs +7 -0
- package/tools/dev/launch.js +147 -0
- package/web-ui/app.js +15 -5
- package/web-ui/index.html +1 -2
- package/web-ui/logic.markdown-editor.mjs +5 -57
- package/web-ui/logic.prompts-token-count.mjs +49 -0
- package/web-ui/logic.sessions.mjs +30 -15
- package/web-ui/modules/app.computed.dashboard.mjs +1 -0
- package/web-ui/modules/app.computed.prompts.mjs +18 -0
- package/web-ui/modules/app.computed.session.mjs +3 -1
- package/web-ui/modules/app.methods.navigation.mjs +79 -3
- package/web-ui/modules/app.methods.prompts-editor.mjs +6 -64
- package/web-ui/modules/app.methods.session-actions.mjs +10 -4
- package/web-ui/modules/app.methods.session-browser.mjs +34 -45
- package/web-ui/modules/app.methods.session-timeline.mjs +1 -0
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +7 -1
- package/web-ui/modules/i18n/locales/en.mjs +20 -1
- package/web-ui/modules/i18n/locales/ja.mjs +20 -1
- package/web-ui/modules/i18n/locales/vi.mjs +20 -1
- package/web-ui/modules/i18n/locales/zh-tw.mjs +20 -1
- package/web-ui/modules/i18n/locales/zh.mjs +20 -1
- package/web-ui/modules/plugins.computed.mjs +8 -2
- package/web-ui/modules/plugins.methods.mjs +8 -2
- package/web-ui/partials/index/layout-header.html +23 -6
- package/web-ui/partials/index/panel-plugins.html +45 -5
- package/web-ui/partials/index/panel-prompts.html +16 -17
- package/web-ui/partials/index/panel-sessions.html +19 -4
- package/web-ui/res/web-ui-render.precompiled.js +193 -93
- package/web-ui/session-helpers.mjs +1 -2
- package/web-ui/source-bundle.cjs +1 -0
- package/web-ui/styles/base-theme.css +24 -0
- package/web-ui/styles/modals-core.css +48 -0
- package/web-ui/styles/prompts-editor.css +3 -66
- package/web-ui/styles/sessions-list.css +1 -1
- package/web-ui/styles/sessions-preview.css +56 -7
- package/web-ui/styles/sessions-toolbar-trash.css +1 -7
|
@@ -29,14 +29,14 @@ function shouldUseFastSessionBrowseLimit(options = {}) {
|
|
|
29
29
|
|
|
30
30
|
export function isSessionQueryEnabled(source) {
|
|
31
31
|
const normalized = normalizeSessionSource(source, '');
|
|
32
|
-
return normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'pi' || normalized === 'all';
|
|
32
|
+
return normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'workbuddy' || normalized === 'pi' || normalized === 'opencode' || normalized === 'all';
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export function normalizeSessionSource(source, fallback = 'all') {
|
|
36
36
|
const normalized = typeof source === 'string'
|
|
37
37
|
? source.trim().toLowerCase()
|
|
38
38
|
: '';
|
|
39
|
-
if (normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'pi' || normalized === 'all') {
|
|
39
|
+
if (normalized === 'codex' || normalized === 'claude' || normalized === 'gemini' || normalized === 'codebuddy' || normalized === 'workbuddy' || normalized === 'pi' || normalized === 'opencode' || normalized === 'all') {
|
|
40
40
|
return normalized;
|
|
41
41
|
}
|
|
42
42
|
return fallback;
|
|
@@ -57,6 +57,18 @@ function isConcreteSessionModelName(value) {
|
|
|
57
57
|
return normalized.toLowerCase() !== '<synthetic>';
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function getSessionSourceLabel(source) {
|
|
61
|
+
if (source === 'claude') return 'Claude Code';
|
|
62
|
+
if (source === 'gemini') return 'Gemini CLI';
|
|
63
|
+
if (source === 'codebuddy') return 'CodeBuddy Code';
|
|
64
|
+
if (source === 'workbuddy') return 'WorkBuddy AI';
|
|
65
|
+
if (source === 'pi') return 'Pi';
|
|
66
|
+
if (source === 'opencode') return 'OpenCode';
|
|
67
|
+
return 'Codex';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const SESSION_USAGE_SOURCE_ORDER = ['codex', 'claude', 'gemini', 'codebuddy', 'workbuddy', 'pi', 'opencode'];
|
|
71
|
+
|
|
60
72
|
function collectSessionModelNames(session) {
|
|
61
73
|
if (!session || typeof session !== 'object') {
|
|
62
74
|
return [];
|
|
@@ -209,7 +221,7 @@ export function buildUsageHeatmap(sessions = [], options = {}) {
|
|
|
209
221
|
for (const session of list) {
|
|
210
222
|
if (!session || typeof session !== 'object') continue;
|
|
211
223
|
const source = normalizeSessionSource(session.source, '');
|
|
212
|
-
if (source
|
|
224
|
+
if (!SESSION_USAGE_SOURCE_ORDER.includes(source)) continue;
|
|
213
225
|
const updatedAtMs = Date.parse(session.updatedAt || '');
|
|
214
226
|
if (!Number.isFinite(updatedAtMs)) continue;
|
|
215
227
|
normalized.push({
|
|
@@ -309,7 +321,7 @@ export function buildUsageHourlyHeatmap(sessions = [], options = {}) {
|
|
|
309
321
|
for (const session of list) {
|
|
310
322
|
if (!session || typeof session !== 'object') continue;
|
|
311
323
|
const source = normalizeSessionSource(session.source, '');
|
|
312
|
-
if (source
|
|
324
|
+
if (!SESSION_USAGE_SOURCE_ORDER.includes(source)) continue;
|
|
313
325
|
const updatedAtMs = Date.parse(session.updatedAt || '');
|
|
314
326
|
if (!Number.isFinite(updatedAtMs)) continue;
|
|
315
327
|
const dayStart = toUtcDayStartMs(updatedAtMs);
|
|
@@ -402,7 +414,7 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
402
414
|
for (const [sessionIndex, session] of list.entries()) {
|
|
403
415
|
if (!session || typeof session !== 'object') continue;
|
|
404
416
|
const source = normalizeSessionSource(session.source, '');
|
|
405
|
-
if (source
|
|
417
|
+
if (!SESSION_USAGE_SOURCE_ORDER.includes(source)) continue;
|
|
406
418
|
const updatedAtMs = Date.parse(session.updatedAt || '');
|
|
407
419
|
if (!Number.isFinite(updatedAtMs)) continue;
|
|
408
420
|
const createdAtMs = Date.parse(session.createdAt || '');
|
|
@@ -437,6 +449,7 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
437
449
|
return ns.updatedAtMs >= rangeStartMs && ns.updatedAtMs <= rangeEndMs;
|
|
438
450
|
});
|
|
439
451
|
|
|
452
|
+
const sourceSessionTotals = Object.fromEntries(SESSION_USAGE_SOURCE_ORDER.map((source) => [source, 0]));
|
|
440
453
|
let codexTotal = 0;
|
|
441
454
|
let claudeTotal = 0;
|
|
442
455
|
let messageTotal = 0;
|
|
@@ -449,8 +462,8 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
449
462
|
const modelMap = new Map();
|
|
450
463
|
const missingModelProviderMap = new Map();
|
|
451
464
|
const missingModelSessionMap = new Map();
|
|
452
|
-
const sourceMessageTotals =
|
|
453
|
-
const missingModelSourceTotals =
|
|
465
|
+
const sourceMessageTotals = Object.fromEntries(SESSION_USAGE_SOURCE_ORDER.map((source) => [source, 0]));
|
|
466
|
+
const missingModelSourceTotals = Object.fromEntries(SESSION_USAGE_SOURCE_ORDER.map((source) => [source, 0]));
|
|
454
467
|
let missingModelSessions = 0;
|
|
455
468
|
let providerOnlySessions = 0;
|
|
456
469
|
const hourCounts = Array.from({ length: 24 }, (_, hour) => ({
|
|
@@ -485,10 +498,11 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
485
498
|
: 0;
|
|
486
499
|
bucket.totalSessions += 1;
|
|
487
500
|
bucket.totalMessages += messageCount;
|
|
501
|
+
sourceSessionTotals[source] = (sourceSessionTotals[source] || 0) + 1;
|
|
488
502
|
if (source === 'codex') {
|
|
489
503
|
bucket.codex += 1;
|
|
490
504
|
codexTotal += 1;
|
|
491
|
-
} else {
|
|
505
|
+
} else if (source === 'claude') {
|
|
492
506
|
bucket.claude += 1;
|
|
493
507
|
claudeTotal += 1;
|
|
494
508
|
}
|
|
@@ -519,7 +533,7 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
519
533
|
});
|
|
520
534
|
}
|
|
521
535
|
|
|
522
|
-
const sourceLabel = source
|
|
536
|
+
const sourceLabel = getSessionSourceLabel(source);
|
|
523
537
|
const normalizedTitle = typeof session.title === 'string' && session.title.trim()
|
|
524
538
|
? session.title.trim()
|
|
525
539
|
: (typeof session.sessionId === 'string' && session.sessionId.trim() ? session.sessionId.trim() : '未命名会话');
|
|
@@ -564,11 +578,12 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
564
578
|
topSessionsByMessages.push({ ...sessionEntry });
|
|
565
579
|
}
|
|
566
580
|
|
|
567
|
-
const totalSessions =
|
|
568
|
-
const sourceShare =
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
581
|
+
const totalSessions = SESSION_USAGE_SOURCE_ORDER.reduce((sum, source) => sum + (sourceSessionTotals[source] || 0), 0);
|
|
582
|
+
const sourceShare = SESSION_USAGE_SOURCE_ORDER.map((source) => ({
|
|
583
|
+
key: source,
|
|
584
|
+
label: getSessionSourceLabel(source),
|
|
585
|
+
value: sourceSessionTotals[source] || 0
|
|
586
|
+
})).map((item) => ({
|
|
572
587
|
...item,
|
|
573
588
|
percent: totalSessions > 0 ? Math.round((item.value / totalSessions) * 100) : 0,
|
|
574
589
|
messageTotal: sourceMessageTotals[item.key] || 0,
|
|
@@ -591,7 +606,7 @@ export function buildUsageChartGroups(sessions = [], options = {}) {
|
|
|
591
606
|
.map(([modelId, meta]) => {
|
|
592
607
|
const sourceLabels = [...meta.sources]
|
|
593
608
|
.sort((a, b) => a.localeCompare(b, 'en-US'))
|
|
594
|
-
.map((source) => (source
|
|
609
|
+
.map((source) => getSessionSourceLabel(source));
|
|
595
610
|
return {
|
|
596
611
|
key: modelId,
|
|
597
612
|
model: modelId,
|
|
@@ -196,6 +196,7 @@ export function createDashboardComputed() {
|
|
|
196
196
|
if (this.sessionFilterSource === 'claude') return this.t('dashboard.sessionSource.claude');
|
|
197
197
|
if (this.sessionFilterSource === 'gemini') return this.t('dashboard.sessionSource.gemini');
|
|
198
198
|
if (this.sessionFilterSource === 'codebuddy') return this.t('dashboard.sessionSource.codebuddy');
|
|
199
|
+
if (this.sessionFilterSource === 'workbuddy') return this.t('dashboard.sessionSource.workbuddy');
|
|
199
200
|
return this.t('dashboard.sessionSource.all');
|
|
200
201
|
},
|
|
201
202
|
inspectorSessionPathLabel() {
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
+
import { estimatePromptTokenCount, buildPromptTokenDiffStats } from '../logic.prompts-token-count.mjs';
|
|
2
|
+
|
|
1
3
|
export function createPromptsComputed() {
|
|
2
4
|
return {
|
|
5
|
+
agentsTokenCount() {
|
|
6
|
+
return estimatePromptTokenCount(this.agentsContent);
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
sysPromptTokenCount() {
|
|
10
|
+
return estimatePromptTokenCount(this.sysPromptContent);
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
agentsDiffTokenStats() {
|
|
14
|
+
return buildPromptTokenDiffStats(this.agentsOriginalContent, this.agentsContent);
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
sysPromptDiffTokenStats() {
|
|
18
|
+
return buildPromptTokenDiffStats(this.sysPromptOriginalContent, this.sysPromptContent);
|
|
19
|
+
},
|
|
20
|
+
|
|
3
21
|
promptsContextHint() {
|
|
4
22
|
if (!this.agentsLoading && !this.agentsDiffVisible && this.hasAgentsContentChanged()) {
|
|
5
23
|
return { text: this.t('modal.agents.unsaved.detectedHint'), warn: true };
|
|
@@ -323,7 +323,9 @@ export function createSessionComputed() {
|
|
|
323
323
|
{ value: "claude", label: this.t("sessions.source.claudeCode") },
|
|
324
324
|
{ value: "gemini", label: this.t("sessions.source.gemini") },
|
|
325
325
|
{ value: "codebuddy", label: this.t("sessions.source.codebuddy") },
|
|
326
|
-
{ value: "
|
|
326
|
+
{ value: "workbuddy", label: this.t("sessions.source.workbuddy") },
|
|
327
|
+
{ value: "pi", label: this.t("sessions.source.pi") },
|
|
328
|
+
{ value: "opencode", label: this.t("sessions.source.opencode") }
|
|
327
329
|
];
|
|
328
330
|
},
|
|
329
331
|
activeSessionExportKey() {
|
|
@@ -110,12 +110,17 @@
|
|
|
110
110
|
const settingsTab = typeof vm.settingsTab === 'string' ? vm.settingsTab.trim().toLowerCase() : 'general';
|
|
111
111
|
const skillsTargetApp = typeof vm.skillsTargetApp === 'string' && (vm.skillsTargetApp === 'codex' || vm.skillsTargetApp === 'claude' || vm.skillsTargetApp === 'pi') ? vm.skillsTargetApp : 'codex';
|
|
112
112
|
const promptTemplatesMode = typeof vm.promptTemplatesMode === 'string' && (vm.promptTemplatesMode === 'compose' || vm.promptTemplatesMode === 'manage') ? vm.promptTemplatesMode : 'compose';
|
|
113
|
+
const pluginsActiveIdSource = resolvedOverrides && typeof resolvedOverrides.pluginsActiveId === 'string'
|
|
114
|
+
? resolvedOverrides.pluginsActiveId
|
|
115
|
+
: vm.pluginsActiveId;
|
|
116
|
+
const pluginsActiveId = typeof pluginsActiveIdSource === 'string' ? pluginsActiveIdSource.trim() : '';
|
|
113
117
|
const snapshot = {
|
|
114
118
|
settingsTab: settingsTab === 'data' ? 'data' : 'general',
|
|
115
119
|
mainTab: resolveSelectableMainTab(mainTab),
|
|
116
120
|
configMode: configModeSet && configModeSet.has(configMode) ? configMode : 'codex',
|
|
117
121
|
skillsTargetApp,
|
|
118
|
-
promptTemplatesMode
|
|
122
|
+
promptTemplatesMode,
|
|
123
|
+
pluginsActiveId: pluginsActiveId || 'prompt-templates'
|
|
119
124
|
};
|
|
120
125
|
if (typeof vm.persistWebUiPreferences === 'function') {
|
|
121
126
|
vm.persistWebUiPreferences({ navigation: snapshot });
|
|
@@ -192,6 +197,7 @@
|
|
|
192
197
|
intent: '',
|
|
193
198
|
pendingTarget: '',
|
|
194
199
|
pendingConfigMode: '',
|
|
200
|
+
pendingPluginsId: '',
|
|
195
201
|
ticket: 0
|
|
196
202
|
};
|
|
197
203
|
return this.__mainTabSwitchState;
|
|
@@ -242,7 +248,7 @@
|
|
|
242
248
|
if (!normalizedKind || !normalizedValue) {
|
|
243
249
|
return;
|
|
244
250
|
}
|
|
245
|
-
const expectedIntent = normalizedKind === 'config'
|
|
251
|
+
const expectedIntent = normalizedKind === 'config' || normalizedKind === 'plugins' ? normalizedKind : normalizedValue;
|
|
246
252
|
this.cancelTouchNavIntentReset();
|
|
247
253
|
const token = (Number(this.__touchNavIntentResetToken) || 0) + 1;
|
|
248
254
|
this.__touchNavIntentResetToken = token;
|
|
@@ -259,21 +265,26 @@
|
|
|
259
265
|
this.clearMainTabSwitchIntent(expectedIntent);
|
|
260
266
|
}, 1000);
|
|
261
267
|
},
|
|
262
|
-
applyImmediateNavIntent(tab, configMode = '') {
|
|
268
|
+
applyImmediateNavIntent(tab, configMode = '', pluginsId = '') {
|
|
263
269
|
if (typeof document === 'undefined') return;
|
|
264
270
|
const normalizedTab = typeof tab === 'string' ? tab.trim().toLowerCase() : '';
|
|
265
271
|
if (!normalizedTab) return;
|
|
266
272
|
const normalizedMode = typeof configMode === 'string' ? configMode.trim().toLowerCase() : '';
|
|
273
|
+
const normalizedPluginId = typeof pluginsId === 'string' ? pluginsId.trim().toLowerCase() : '';
|
|
267
274
|
const domState = this.ensureImmediateNavDomState();
|
|
268
275
|
const nodes = Array.isArray(domState.navNodes) ? domState.navNodes : [];
|
|
269
276
|
for (const node of nodes) {
|
|
270
277
|
if (!node || !node.classList) continue;
|
|
271
278
|
const nodeTab = String(node.getAttribute('data-main-tab') || '').trim().toLowerCase();
|
|
272
279
|
const nodeMode = String(node.getAttribute('data-config-mode') || '').trim().toLowerCase();
|
|
280
|
+
const nodePluginId = String(node.getAttribute('data-plugins-id') || '').trim().toLowerCase();
|
|
273
281
|
let shouldActivate = nodeTab === normalizedTab;
|
|
274
282
|
if (shouldActivate && normalizedTab === 'config') {
|
|
275
283
|
shouldActivate = nodeMode ? nodeMode === normalizedMode : false;
|
|
276
284
|
}
|
|
285
|
+
if (shouldActivate && normalizedTab === 'plugins') {
|
|
286
|
+
shouldActivate = nodePluginId ? nodePluginId === normalizedPluginId : !normalizedPluginId;
|
|
287
|
+
}
|
|
277
288
|
node.classList.toggle('nav-intent-active', !!shouldActivate);
|
|
278
289
|
node.classList.toggle('nav-intent-inactive', !shouldActivate);
|
|
279
290
|
}
|
|
@@ -395,6 +406,67 @@
|
|
|
395
406
|
if (this.consumePointerNavCommit('config', normalizedMode)) return;
|
|
396
407
|
this.switchConfigMode(normalizedMode);
|
|
397
408
|
},
|
|
409
|
+
|
|
410
|
+
switchPluginsTab(pluginId) {
|
|
411
|
+
const normalizedId = typeof pluginId === 'string' ? pluginId.trim().toLowerCase() : '';
|
|
412
|
+
if (!normalizedId) return;
|
|
413
|
+
if (typeof this.selectPlugin === 'function') {
|
|
414
|
+
this.selectPlugin(normalizedId);
|
|
415
|
+
} else {
|
|
416
|
+
this.pluginsActiveId = normalizedId;
|
|
417
|
+
}
|
|
418
|
+
this.switchMainTab('plugins');
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
onPluginsTabPointerDown(pluginId) {
|
|
422
|
+
const event = arguments.length > 1 ? arguments[1] : null;
|
|
423
|
+
if (event && typeof event.button === 'number' && event.button !== 0) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const normalizedId = typeof pluginId === 'string' ? pluginId.trim().toLowerCase() : '';
|
|
427
|
+
if (!normalizedId) return;
|
|
428
|
+
persistNavState(this, { mainTab: 'plugins', pluginsActiveId: normalizedId });
|
|
429
|
+
this.setMainTabSwitchIntent('plugins');
|
|
430
|
+
if (typeof this.ensureMainTabSwitchState === 'function') {
|
|
431
|
+
this.ensureMainTabSwitchState().pendingPluginsId = normalizedId;
|
|
432
|
+
}
|
|
433
|
+
this.applyImmediateNavIntent('plugins', '', normalizedId);
|
|
434
|
+
const shouldHideSessionPanel = this.mainTab === 'sessions';
|
|
435
|
+
this.setSessionPanelFastHidden(shouldHideSessionPanel);
|
|
436
|
+
const pointerType = event && typeof event.pointerType === 'string'
|
|
437
|
+
? event.pointerType.trim().toLowerCase()
|
|
438
|
+
: '';
|
|
439
|
+
if (pointerType === 'touch') {
|
|
440
|
+
this.scheduleTouchNavIntentReset('plugins', normalizedId);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
this.recordPointerNavCommit('plugins', normalizedId);
|
|
444
|
+
this.switchPluginsTab(normalizedId);
|
|
445
|
+
},
|
|
446
|
+
|
|
447
|
+
onPluginsTabClick(pluginId) {
|
|
448
|
+
const normalizedId = typeof pluginId === 'string' ? pluginId.trim().toLowerCase() : '';
|
|
449
|
+
if (!normalizedId) return;
|
|
450
|
+
if (this.consumePointerNavCommit('plugins', normalizedId)) return;
|
|
451
|
+
this.switchPluginsTab(normalizedId);
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
isPluginsIdNavActive(pluginId) {
|
|
455
|
+
const normalizedId = typeof pluginId === 'string' ? pluginId.trim().toLowerCase() : '';
|
|
456
|
+
if (!normalizedId) return false;
|
|
457
|
+
if (!this.isMainTabNavActive('plugins')) return false;
|
|
458
|
+
const state = this.ensureMainTabSwitchState();
|
|
459
|
+
const pendingId = typeof state.pendingPluginsId === 'string'
|
|
460
|
+
? state.pendingPluginsId.trim().toLowerCase()
|
|
461
|
+
: '';
|
|
462
|
+
if (state.intent === 'plugins' && pendingId) {
|
|
463
|
+
return pendingId === normalizedId;
|
|
464
|
+
}
|
|
465
|
+
const currentId = typeof this.pluginsActiveId === 'string'
|
|
466
|
+
? this.pluginsActiveId.trim().toLowerCase()
|
|
467
|
+
: '';
|
|
468
|
+
return currentId === normalizedId;
|
|
469
|
+
},
|
|
398
470
|
clearMainTabSwitchIntent(expectedTab = '') {
|
|
399
471
|
const state = this.ensureMainTabSwitchState();
|
|
400
472
|
if (expectedTab && state.intent && state.intent !== expectedTab) {
|
|
@@ -404,6 +476,7 @@
|
|
|
404
476
|
state.intent = '';
|
|
405
477
|
state.pendingTarget = '';
|
|
406
478
|
state.pendingConfigMode = '';
|
|
479
|
+
state.pendingPluginsId = '';
|
|
407
480
|
this.clearImmediateNavIntent();
|
|
408
481
|
this.setSessionPanelFastHidden(false);
|
|
409
482
|
},
|
|
@@ -463,6 +536,9 @@
|
|
|
463
536
|
if (targetTab !== 'config') {
|
|
464
537
|
switchState.pendingConfigMode = '';
|
|
465
538
|
}
|
|
539
|
+
if (targetTab !== 'plugins') {
|
|
540
|
+
switchState.pendingPluginsId = '';
|
|
541
|
+
}
|
|
466
542
|
if (targetTab === previousTab) {
|
|
467
543
|
switchState.ticket += 1;
|
|
468
544
|
switchState.pendingTarget = '';
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Prompts-panel Markdown editor methods:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* and the sysPromptContent editor.
|
|
2
|
+
* Prompts-panel Markdown editor methods: mobile view switching and the
|
|
3
|
+
* live split preview (marked + DOMPurify, debounced). One implementation
|
|
4
|
+
* is shared by the agentsContent editor and the sysPromptContent editor.
|
|
6
5
|
*
|
|
7
6
|
* State contract (declared in app.js data()):
|
|
8
7
|
* promptsPreviewEnabled, promptsPreviewCollapsed, promptsMobileView,
|
|
9
|
-
* promptsPreviewLibsMissing,
|
|
10
|
-
*
|
|
8
|
+
* promptsPreviewLibsMissing, agentsPreviewHtml, sysPromptPreviewHtml,
|
|
9
|
+
* agentsChangeAxis, sysChangeAxis,
|
|
11
10
|
* promptsPathReferencePaths, promptsPathReferenceLoading,
|
|
12
11
|
* promptsPathReferenceCacheKey (path-reference dropdown)
|
|
13
12
|
* Template contract:
|
|
14
13
|
* refs promptsAgentsTextarea / promptsSysTextarea on the two textareas,
|
|
15
|
-
* refs promptsAgentsHighlight / promptsSysHighlight on the backdrop <pre>,
|
|
16
14
|
* refs promptsAgentsDiffView / promptsSysDiffView on the two diff row containers.
|
|
17
15
|
*/
|
|
18
16
|
|
|
19
|
-
import {
|
|
20
|
-
buildMarkdownPreviewHtml,
|
|
21
|
-
highlightMarkdownText
|
|
22
|
-
} from '../logic.markdown-editor.mjs';
|
|
17
|
+
import { buildMarkdownPreviewHtml } from '../logic.markdown-editor.mjs';
|
|
23
18
|
import {
|
|
24
19
|
buildPromptPathReferenceOptions as buildPromptPathReferenceOptionList,
|
|
25
20
|
buildPromptPathReferenceSentence,
|
|
@@ -29,7 +24,6 @@ import {
|
|
|
29
24
|
buildPromptsDiffAxis
|
|
30
25
|
} from '../logic.prompts-change-axis.mjs';
|
|
31
26
|
|
|
32
|
-
const PROMPTS_HIGHLIGHT_DEBOUNCE_MS = 150;
|
|
33
27
|
const PROMPTS_PREVIEW_DEBOUNCE_MS = 300;
|
|
34
28
|
const PROMPTS_AXIS_DEBOUNCE_MS = 300;
|
|
35
29
|
|
|
@@ -37,7 +31,6 @@ const PROMPTS_EDITOR_META = {
|
|
|
37
31
|
agents: {
|
|
38
32
|
contentField: 'agentsContent',
|
|
39
33
|
originalField: 'agentsOriginalContent',
|
|
40
|
-
highlightField: 'agentsHighlightHtml',
|
|
41
34
|
previewField: 'agentsPreviewHtml',
|
|
42
35
|
axisField: 'agentsChangeAxis',
|
|
43
36
|
diffLinesField: 'agentsDiffLines',
|
|
@@ -48,13 +41,11 @@ const PROMPTS_EDITOR_META = {
|
|
|
48
41
|
diffLoadingField: 'agentsDiffLoading',
|
|
49
42
|
prepareDiffMethod: 'prepareAgentsDiff',
|
|
50
43
|
textareaRef: 'promptsAgentsTextarea',
|
|
51
|
-
highlightRef: 'promptsAgentsHighlight',
|
|
52
44
|
diffViewRef: 'promptsAgentsDiffView'
|
|
53
45
|
},
|
|
54
46
|
sys: {
|
|
55
47
|
contentField: 'sysPromptContent',
|
|
56
48
|
originalField: 'sysPromptOriginalContent',
|
|
57
|
-
highlightField: 'sysPromptHighlightHtml',
|
|
58
49
|
previewField: 'sysPromptPreviewHtml',
|
|
59
50
|
axisField: 'sysChangeAxis',
|
|
60
51
|
diffLinesField: 'sysPromptDiffLines',
|
|
@@ -65,7 +56,6 @@ const PROMPTS_EDITOR_META = {
|
|
|
65
56
|
diffLoadingField: 'sysPromptDiffLoading',
|
|
66
57
|
prepareDiffMethod: 'prepareSysPromptDiff',
|
|
67
58
|
textareaRef: 'promptsSysTextarea',
|
|
68
|
-
highlightRef: 'promptsSysHighlight',
|
|
69
59
|
diffViewRef: 'promptsSysDiffView'
|
|
70
60
|
}
|
|
71
61
|
};
|
|
@@ -74,13 +64,6 @@ function emptyPromptsChangeAxis() {
|
|
|
74
64
|
return { mode: 'edit', ticks: [], truncated: false, totalLines: 0, added: 0, removed: 0 };
|
|
75
65
|
}
|
|
76
66
|
|
|
77
|
-
function resolvePromptsHljs() {
|
|
78
|
-
if (typeof window !== 'undefined' && window.hljs) {
|
|
79
|
-
return window.hljs;
|
|
80
|
-
}
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
67
|
function resolvePromptsPreviewLibs() {
|
|
85
68
|
if (typeof window === 'undefined') {
|
|
86
69
|
return { marked: null, purify: null };
|
|
@@ -97,46 +80,6 @@ export function createPromptsEditorMethods(options = {}) {
|
|
|
97
80
|
},
|
|
98
81
|
|
|
99
82
|
|
|
100
|
-
refreshPromptsHighlight(key) {
|
|
101
|
-
const meta = this.promptsEditorMeta(key);
|
|
102
|
-
if (!meta) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
this[meta.highlightField] = highlightMarkdownText(this[meta.contentField], resolvePromptsHljs());
|
|
106
|
-
this.syncPromptsOverlayScroll(key);
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
schedulePromptsHighlight(key) {
|
|
110
|
-
const meta = this.promptsEditorMeta(key);
|
|
111
|
-
if (!meta) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (!this._promptsHighlightTimers) {
|
|
115
|
-
this._promptsHighlightTimers = {};
|
|
116
|
-
}
|
|
117
|
-
if (this._promptsHighlightTimers[key]) {
|
|
118
|
-
clearTimeout(this._promptsHighlightTimers[key]);
|
|
119
|
-
}
|
|
120
|
-
this._promptsHighlightTimers[key] = setTimeout(() => {
|
|
121
|
-
this._promptsHighlightTimers[key] = null;
|
|
122
|
-
this.refreshPromptsHighlight(key);
|
|
123
|
-
}, PROMPTS_HIGHLIGHT_DEBOUNCE_MS);
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
syncPromptsOverlayScroll(key) {
|
|
127
|
-
const meta = this.promptsEditorMeta(key);
|
|
128
|
-
if (!meta || typeof this.$refs !== 'object' || !this.$refs) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
const textarea = this.$refs[meta.textareaRef];
|
|
132
|
-
const backdrop = this.$refs[meta.highlightRef];
|
|
133
|
-
if (!textarea || !backdrop) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
backdrop.scrollTop = textarea.scrollTop;
|
|
137
|
-
backdrop.scrollLeft = textarea.scrollLeft;
|
|
138
|
-
},
|
|
139
|
-
|
|
140
83
|
refreshPromptsPreview(key) {
|
|
141
84
|
const meta = this.promptsEditorMeta(key);
|
|
142
85
|
if (!meta) {
|
|
@@ -174,7 +117,6 @@ export function createPromptsEditorMethods(options = {}) {
|
|
|
174
117
|
},
|
|
175
118
|
|
|
176
119
|
schedulePromptsEditorRefresh(key) {
|
|
177
|
-
this.schedulePromptsHighlight(key);
|
|
178
120
|
this.schedulePromptsPreview(key);
|
|
179
121
|
this.schedulePromptsChangeAxis(key);
|
|
180
122
|
},
|
|
@@ -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 !== 'pi') {
|
|
32
|
-
error = 'source 仅支持 codex、claude、gemini、codebuddy 或
|
|
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 或 opencode';
|
|
33
33
|
}
|
|
34
34
|
if (!sessionId && !filePath) {
|
|
35
35
|
error = error ? `${error},还缺少 sessionId 或 filePath` : '缺少 sessionId 或 filePath 参数';
|
|
@@ -75,7 +75,13 @@ export function createSessionActionMethods(options = {}) {
|
|
|
75
75
|
? 'Gemini CLI'
|
|
76
76
|
: (context.params.source === 'pi'
|
|
77
77
|
? 'Pi'
|
|
78
|
-
: (context.params.source === '
|
|
78
|
+
: (context.params.source === 'opencode'
|
|
79
|
+
? 'OpenCode'
|
|
80
|
+
: (context.params.source === 'codebuddy'
|
|
81
|
+
? 'CodeBuddy Code'
|
|
82
|
+
: (context.params.source === 'workbuddy'
|
|
83
|
+
? 'WorkBuddy AI'
|
|
84
|
+
: context.params.source))))));
|
|
79
85
|
this.activeSession = {
|
|
80
86
|
source: context.params.source,
|
|
81
87
|
sourceLabel,
|
|
@@ -104,7 +110,7 @@ export function createSessionActionMethods(options = {}) {
|
|
|
104
110
|
buildSessionStandaloneUrl(session) {
|
|
105
111
|
if (!session) return '';
|
|
106
112
|
const source = typeof session.source === 'string' ? session.source.trim().toLowerCase() : '';
|
|
107
|
-
if (!source || (source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'pi')) return '';
|
|
113
|
+
if (!source || (source !== 'codex' && source !== 'claude' && source !== 'gemini' && source !== 'codebuddy' && source !== 'workbuddy' && source !== 'pi' && source !== 'opencode')) return '';
|
|
108
114
|
const sessionId = typeof session.sessionId === 'string' ? session.sessionId.trim() : '';
|
|
109
115
|
const filePath = typeof session.filePath === 'string' ? session.filePath.trim() : '';
|
|
110
116
|
if (!sessionId && !filePath) return '';
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
buildSessionFilterCacheState,
|
|
3
3
|
isSessionQueryEnabled,
|
|
4
4
|
normalizeSessionMessageRole,
|
|
5
|
-
normalizeSessionPathFilter
|
|
5
|
+
normalizeSessionPathFilter,
|
|
6
|
+
normalizeSessionSource
|
|
6
7
|
} from '../logic.mjs';
|
|
7
8
|
import {
|
|
8
9
|
applySessionsFilterUrlState,
|
|
9
|
-
buildSessionsFilterShareUrl,
|
|
10
10
|
normalizeSessionRoleFilter,
|
|
11
11
|
normalizeSessionTimePreset,
|
|
12
12
|
readSessionsFilterUrlState,
|
|
@@ -118,9 +118,7 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
118
118
|
},
|
|
119
119
|
|
|
120
120
|
syncSessionPathOptionsForSource(source, nextOptions, mergeWithExisting = false) {
|
|
121
|
-
const targetSource = source
|
|
122
|
-
? 'claude'
|
|
123
|
-
: (source === 'gemini' ? 'gemini' : (source === 'pi' ? 'pi' : (source === 'all' ? 'all' : 'codex')));
|
|
121
|
+
const targetSource = normalizeSessionSource(source, 'all');
|
|
124
122
|
const current = Array.isArray(this.sessionPathOptionsMap[targetSource])
|
|
125
123
|
? this.sessionPathOptionsMap[targetSource]
|
|
126
124
|
: [];
|
|
@@ -135,9 +133,7 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
135
133
|
},
|
|
136
134
|
|
|
137
135
|
refreshSessionPathOptions(source) {
|
|
138
|
-
const targetSource = source
|
|
139
|
-
? 'claude'
|
|
140
|
-
: (source === 'gemini' ? 'gemini' : (source === 'pi' ? 'pi' : (source === 'all' ? 'all' : 'codex')));
|
|
136
|
+
const targetSource = normalizeSessionSource(source, 'all');
|
|
141
137
|
const base = Array.isArray(this.sessionPathOptionsMap[targetSource])
|
|
142
138
|
? [...this.sessionPathOptionsMap[targetSource]]
|
|
143
139
|
: [];
|
|
@@ -159,9 +155,7 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
159
155
|
},
|
|
160
156
|
|
|
161
157
|
async loadSessionPathOptions(options = {}) {
|
|
162
|
-
const source = options.source
|
|
163
|
-
? 'claude'
|
|
164
|
-
: (options.source === 'gemini' ? 'gemini' : (options.source === 'pi' ? 'pi' : (options.source === 'all' ? 'all' : 'codex')));
|
|
158
|
+
const source = normalizeSessionSource(options.source, 'all');
|
|
165
159
|
const forceRefresh = !!options.forceRefresh;
|
|
166
160
|
const loaded = !!this.sessionPathOptionsLoadedMap[source];
|
|
167
161
|
if (!forceRefresh && loaded) {
|
|
@@ -454,17 +448,28 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
454
448
|
this.loadSessions();
|
|
455
449
|
},
|
|
456
450
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
451
|
+
getSessionPreviewScrollContainer() {
|
|
452
|
+
return this.sessionPreviewScrollEl || (this.$refs && this.$refs.sessionPreviewScroll) || null;
|
|
453
|
+
},
|
|
454
|
+
|
|
455
|
+
scrollSessionPreviewToTop() {
|
|
456
|
+
const scrollEl = this.getSessionPreviewScrollContainer();
|
|
457
|
+
if (!scrollEl) return;
|
|
458
|
+
if (typeof scrollEl.scrollTo === 'function') {
|
|
459
|
+
scrollEl.scrollTo({ top: 0, behavior: 'smooth' });
|
|
460
|
+
} else {
|
|
461
|
+
scrollEl.scrollTop = 0;
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
scrollSessionPreviewToBottom() {
|
|
466
|
+
const scrollEl = this.getSessionPreviewScrollContainer();
|
|
467
|
+
if (!scrollEl) return;
|
|
468
|
+
if (typeof scrollEl.scrollTo === 'function') {
|
|
469
|
+
scrollEl.scrollTo({ top: scrollEl.scrollHeight, behavior: 'smooth' });
|
|
470
|
+
} else {
|
|
471
|
+
scrollEl.scrollTop = scrollEl.scrollHeight;
|
|
466
472
|
}
|
|
467
|
-
return escaped;
|
|
468
473
|
},
|
|
469
474
|
|
|
470
475
|
async onSessionSourceChange(event) {
|
|
@@ -513,9 +518,13 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
513
518
|
? this.t('sessions.source.gemini')
|
|
514
519
|
: (this.sessionFilterSource === 'codebuddy'
|
|
515
520
|
? this.t('sessions.source.codebuddy')
|
|
516
|
-
: (this.sessionFilterSource === '
|
|
517
|
-
? this.t('sessions.source.
|
|
518
|
-
: this.sessionFilterSource
|
|
521
|
+
: (this.sessionFilterSource === 'workbuddy'
|
|
522
|
+
? this.t('sessions.source.workbuddy')
|
|
523
|
+
: (this.sessionFilterSource === 'pi'
|
|
524
|
+
? this.t('sessions.source.pi')
|
|
525
|
+
: (this.sessionFilterSource === 'opencode'
|
|
526
|
+
? this.t('sessions.source.opencode')
|
|
527
|
+
: this.sessionFilterSource))))));
|
|
519
528
|
chips.push({ key: 'source', title: this.t('sessions.filters.source'), value: label });
|
|
520
529
|
}
|
|
521
530
|
if (this.sessionPathFilter) {
|
|
@@ -567,27 +576,6 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
567
576
|
await this.onSessionSourceChange();
|
|
568
577
|
},
|
|
569
578
|
|
|
570
|
-
async copySessionsFilterShareUrl() {
|
|
571
|
-
const url = buildSessionsFilterShareUrl(this);
|
|
572
|
-
if (!url) {
|
|
573
|
-
this.showMessage(typeof this.t === 'function' ? this.t('sessions.filters.urlBuildFail') : 'Failed to build link', 'error');
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
try {
|
|
577
|
-
if (navigator.clipboard && window.isSecureContext) {
|
|
578
|
-
await navigator.clipboard.writeText(url);
|
|
579
|
-
this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.ok') : 'Copied', 'success');
|
|
580
|
-
return;
|
|
581
|
-
}
|
|
582
|
-
} catch (_) {}
|
|
583
|
-
const ok = typeof this.fallbackCopyText === 'function' ? this.fallbackCopyText(url) : false;
|
|
584
|
-
if (ok) {
|
|
585
|
-
this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.ok') : 'Copied', 'success');
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
this.showMessage(typeof this.t === 'function' ? this.t('toast.copy.fail') : 'Copy failed', 'error');
|
|
589
|
-
},
|
|
590
|
-
|
|
591
579
|
normalizeSessionMessage(message) {
|
|
592
580
|
const fallback = {
|
|
593
581
|
role: 'assistant',
|
|
@@ -934,6 +922,7 @@ export function createSessionBrowserMethods(options = {}) {
|
|
|
934
922
|
return;
|
|
935
923
|
}
|
|
936
924
|
this.activeSession = session;
|
|
925
|
+
this.sessionPreviewScrollAtTop = true;
|
|
937
926
|
emitSessionLoadNativeDialog(this, 'selectSession:activate', `sessionId=${session.sessionId || ''}`);
|
|
938
927
|
if (typeof this.expandVisibleSessionList === 'function') {
|
|
939
928
|
this.expandVisibleSessionList(0, { ensureActive: true });
|
|
@@ -262,6 +262,7 @@ export function createSessionTimelineMethods() {
|
|
|
262
262
|
) return;
|
|
263
263
|
const scrollEl = this.sessionPreviewScrollEl || this.$refs.sessionPreviewScroll;
|
|
264
264
|
if (!scrollEl) return;
|
|
265
|
+
this.sessionPreviewScrollAtTop = Number(scrollEl.scrollTop || 0) <= 8;
|
|
265
266
|
|
|
266
267
|
if (
|
|
267
268
|
this.canLoadMoreSessionMessages
|