ec-web 0.0.28 → 0.0.29-test.0
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/dist/server.js +21 -3
- package/dist/sources/basic-settings.js +4 -3
- package/dist/sources/msg.js +9 -5
- package/dist/sources/relation-roles.js +5 -0
- package/dist/sources/role-definitions.js +7 -0
- package/dist/sources/session-origin.js +2 -2
- package/dist/sources/triggers.js +2 -12
- package/dist/static/app.js +728 -144
- package/dist/static/basic-settings.js +10 -9
- package/dist/static/index.html +33 -12
- package/dist/static/style.css +613 -38
- package/package.json +1 -1
package/dist/static/app.js
CHANGED
|
@@ -83,6 +83,12 @@ const translations = {
|
|
|
83
83
|
'common.loadingSuffix': '加载中',
|
|
84
84
|
'common.noChanges': '没有需要保存的改动',
|
|
85
85
|
'common.close': '关闭',
|
|
86
|
+
'common.back': '返回',
|
|
87
|
+
'common.openDetail': '打开详情',
|
|
88
|
+
'navigation.open': '打开导航',
|
|
89
|
+
'navigation.close': '关闭导航',
|
|
90
|
+
'navigation.pages': '页面导航',
|
|
91
|
+
'navigation.moreFilters': '更多筛选',
|
|
86
92
|
'common.rawJsonPreview': '原始 JSON 预览',
|
|
87
93
|
'common.inherited': '继承',
|
|
88
94
|
'common.noLimit': '无限制',
|
|
@@ -116,6 +122,7 @@ const translations = {
|
|
|
116
122
|
'theme.light': '浅色模式',
|
|
117
123
|
'theme.dark': '深色模式',
|
|
118
124
|
'theme.system': '跟随系统',
|
|
125
|
+
'theme.drawerLabel': '主题:{mode}',
|
|
119
126
|
'theme.buttonTitle': '主题:{mode};点击切换',
|
|
120
127
|
'language.buttonTitle': '切换语言',
|
|
121
128
|
|
|
@@ -133,6 +140,7 @@ const translations = {
|
|
|
133
140
|
'logs.lines.1000': '1000 行',
|
|
134
141
|
'logs.lines.2000': '2000 行',
|
|
135
142
|
'logs.follow': '跟随尾部',
|
|
143
|
+
'logs.moreFilters': '更多筛选',
|
|
136
144
|
'logs.live': '自动刷新',
|
|
137
145
|
'logs.meta': '{files} 个文件 · {size} · 更新于 {time}',
|
|
138
146
|
'logs.filtered': '筛选结果 {visible} 行(已加载 {total} 行)',
|
|
@@ -277,10 +285,13 @@ const translations = {
|
|
|
277
285
|
// Messages view
|
|
278
286
|
'messages.colTitle.aid': 'Agent',
|
|
279
287
|
'messages.colTitle.peers': 'Chats',
|
|
288
|
+
'messages.mobile.selection': '消息选择',
|
|
289
|
+
'messages.mobile.aid': 'Agent',
|
|
290
|
+
'messages.mobile.peer': 'Chats',
|
|
280
291
|
'messages.empty.selectAid': '← 选择一个 Agent',
|
|
281
292
|
'messages.empty.selectToView': '选择 Agent 查看消息',
|
|
282
293
|
'messages.empty.noAgents': '暂无 Agent',
|
|
283
|
-
'messages.empty.noPeers': '
|
|
294
|
+
'messages.empty.noPeers': '暂无 Chats',
|
|
284
295
|
'messages.empty.noMessages': '暂无消息',
|
|
285
296
|
'messages.loadOlder': '加载更早消息',
|
|
286
297
|
'messages.tag.group': '群聊',
|
|
@@ -304,7 +315,7 @@ const translations = {
|
|
|
304
315
|
'messages.msgType.command': '命令',
|
|
305
316
|
'messages.groupCount': '群 {count}',
|
|
306
317
|
'messages.privateCount': '单 {count}',
|
|
307
|
-
'messages.session.field.sessionId': '
|
|
318
|
+
'messages.session.field.sessionId': 'EC会话ID',
|
|
308
319
|
'messages.session.field.baseagent': 'Baseagent',
|
|
309
320
|
'messages.session.field.chatMode': '模式',
|
|
310
321
|
'messages.session.field.permissionMode': '权限',
|
|
@@ -315,6 +326,8 @@ const translations = {
|
|
|
315
326
|
'messages.session.status.idle': '空闲',
|
|
316
327
|
'messages.session.mode.interactive': '响应',
|
|
317
328
|
'messages.session.mode.proactive': '自主',
|
|
329
|
+
'messages.session.details.expand': '展开会话状态',
|
|
330
|
+
'messages.session.details.collapse': '收起会话状态',
|
|
318
331
|
|
|
319
332
|
// Sessions view
|
|
320
333
|
'sessions.filter.normal': '🔍 仅有效',
|
|
@@ -358,6 +371,8 @@ const translations = {
|
|
|
358
371
|
'sessions.fullView': '📜 完整视图',
|
|
359
372
|
'sessions.chatHint': '只看用户与 Agent 的对话,处理过程已折叠',
|
|
360
373
|
'sessions.fullHint': '显示全部消息',
|
|
374
|
+
'sessions.details.expand': '展开会话信息',
|
|
375
|
+
'sessions.details.collapse': '收起会话信息',
|
|
361
376
|
'sessions.procSummary': '⋯ {count} 条处理过程(思考·工具·结果)',
|
|
362
377
|
'sessions.empty.noDialogue': '该会话没有用户对话消息',
|
|
363
378
|
'sessions.turnType.userInput': '用户输入',
|
|
@@ -749,10 +764,13 @@ const translations = {
|
|
|
749
764
|
'triggers.lastUnrecorded': '未记录',
|
|
750
765
|
'triggers.collapse.expand': '展开 Agent 列表',
|
|
751
766
|
'triggers.collapse.collapse': '收起 Agent 列表',
|
|
767
|
+
'triggers.agent.daemon': 'DAEMON',
|
|
752
768
|
'triggers.empty.noAgents': '暂无 Agent',
|
|
753
769
|
'triggers.empty.noTriggers': '暂无触发器',
|
|
754
770
|
'triggers.empty.noAgentTriggers': '该 Agent 暂无触发器',
|
|
755
771
|
'triggers.th.status': '状态',
|
|
772
|
+
'triggers.th.agent': 'Agent',
|
|
773
|
+
'triggers.th.id': 'ID',
|
|
756
774
|
'triggers.th.subscription': '订阅',
|
|
757
775
|
'triggers.th.name': '名称',
|
|
758
776
|
'triggers.th.type': '类型',
|
|
@@ -1023,6 +1041,12 @@ const translations = {
|
|
|
1023
1041
|
'common.loadingSuffix': 'loading',
|
|
1024
1042
|
'common.noChanges': 'No changes to save',
|
|
1025
1043
|
'common.close': 'Close',
|
|
1044
|
+
'common.back': 'Back',
|
|
1045
|
+
'common.openDetail': 'Open details',
|
|
1046
|
+
'navigation.open': 'Open navigation',
|
|
1047
|
+
'navigation.close': 'Close navigation',
|
|
1048
|
+
'navigation.pages': 'Page navigation',
|
|
1049
|
+
'navigation.moreFilters': 'More filters',
|
|
1026
1050
|
'common.rawJsonPreview': 'Raw JSON Preview',
|
|
1027
1051
|
'common.inherited': 'Inherited',
|
|
1028
1052
|
'common.noLimit': 'No limit',
|
|
@@ -1056,6 +1080,7 @@ const translations = {
|
|
|
1056
1080
|
'theme.light': 'Light',
|
|
1057
1081
|
'theme.dark': 'Dark',
|
|
1058
1082
|
'theme.system': 'System',
|
|
1083
|
+
'theme.drawerLabel': 'Theme: {mode}',
|
|
1059
1084
|
'theme.buttonTitle': 'Theme: {mode}; click to switch',
|
|
1060
1085
|
'language.buttonTitle': 'Switch language',
|
|
1061
1086
|
|
|
@@ -1073,6 +1098,7 @@ const translations = {
|
|
|
1073
1098
|
'logs.lines.1000': '1000 Lines',
|
|
1074
1099
|
'logs.lines.2000': '2000 Lines',
|
|
1075
1100
|
'logs.follow': 'Follow tail',
|
|
1101
|
+
'logs.moreFilters': 'More filters',
|
|
1076
1102
|
'logs.live': 'Auto refresh',
|
|
1077
1103
|
'logs.meta': '{files} files · {size} · updated {time}',
|
|
1078
1104
|
'logs.filtered': '{visible} filtered ({total} loaded)',
|
|
@@ -1217,6 +1243,9 @@ const translations = {
|
|
|
1217
1243
|
// Messages view
|
|
1218
1244
|
'messages.colTitle.aid': 'Agent',
|
|
1219
1245
|
'messages.colTitle.peers': 'Chats',
|
|
1246
|
+
'messages.mobile.selection': 'Message selection',
|
|
1247
|
+
'messages.mobile.aid': 'Agent',
|
|
1248
|
+
'messages.mobile.peer': 'Chats',
|
|
1220
1249
|
'messages.empty.selectAid': '← Select Agent',
|
|
1221
1250
|
'messages.empty.selectToView': 'Select Agent to view messages',
|
|
1222
1251
|
'messages.empty.noAgents': 'No agents',
|
|
@@ -1244,7 +1273,7 @@ const translations = {
|
|
|
1244
1273
|
'messages.msgType.command': 'Command',
|
|
1245
1274
|
'messages.groupCount': '{count} groups',
|
|
1246
1275
|
'messages.privateCount': '{count} private',
|
|
1247
|
-
'messages.session.field.sessionId': '
|
|
1276
|
+
'messages.session.field.sessionId': 'EC Session ID',
|
|
1248
1277
|
'messages.session.field.baseagent': 'Baseagent',
|
|
1249
1278
|
'messages.session.field.chatMode': 'Mode',
|
|
1250
1279
|
'messages.session.field.permissionMode': 'Perm',
|
|
@@ -1255,6 +1284,8 @@ const translations = {
|
|
|
1255
1284
|
'messages.session.status.idle': 'Idle',
|
|
1256
1285
|
'messages.session.mode.interactive': 'Interactive',
|
|
1257
1286
|
'messages.session.mode.proactive': 'Proactive',
|
|
1287
|
+
'messages.session.details.expand': 'Expand session status',
|
|
1288
|
+
'messages.session.details.collapse': 'Collapse session status',
|
|
1258
1289
|
|
|
1259
1290
|
// Sessions view
|
|
1260
1291
|
'sessions.filter.normal': '🔍 Valid Only',
|
|
@@ -1298,6 +1329,8 @@ const translations = {
|
|
|
1298
1329
|
'sessions.fullView': '📜 Full View',
|
|
1299
1330
|
'sessions.chatHint': 'Only user and Agent messages; processing is collapsed',
|
|
1300
1331
|
'sessions.fullHint': 'Show all messages',
|
|
1332
|
+
'sessions.details.expand': 'Expand session details',
|
|
1333
|
+
'sessions.details.collapse': 'Collapse session details',
|
|
1301
1334
|
'sessions.procSummary': '⋯ {count} processing steps (thinking · tools · results)',
|
|
1302
1335
|
'sessions.empty.noDialogue': 'No user dialogue messages in this session',
|
|
1303
1336
|
'sessions.turnType.userInput': 'User Input',
|
|
@@ -1689,10 +1722,13 @@ const translations = {
|
|
|
1689
1722
|
'triggers.lastUnrecorded': 'Not recorded',
|
|
1690
1723
|
'triggers.collapse.expand': 'Expand Agent List',
|
|
1691
1724
|
'triggers.collapse.collapse': 'Collapse Agent List',
|
|
1725
|
+
'triggers.agent.daemon': 'DAEMON',
|
|
1692
1726
|
'triggers.empty.noAgents': 'No agents',
|
|
1693
1727
|
'triggers.empty.noTriggers': 'No triggers',
|
|
1694
1728
|
'triggers.empty.noAgentTriggers': 'No triggers for this agent',
|
|
1695
1729
|
'triggers.th.status': 'Status',
|
|
1730
|
+
'triggers.th.agent': 'Agent',
|
|
1731
|
+
'triggers.th.id': 'ID',
|
|
1696
1732
|
'triggers.th.subscription': 'Subscription',
|
|
1697
1733
|
'triggers.th.name': 'Name',
|
|
1698
1734
|
'triggers.th.type': 'Type',
|
|
@@ -1921,9 +1957,15 @@ function updateI18n() {
|
|
|
1921
1957
|
const key = el.getAttribute('data-i18n-title');
|
|
1922
1958
|
el.title = t(key);
|
|
1923
1959
|
});
|
|
1960
|
+
document.querySelectorAll('[data-i18n-aria-label]').forEach(el => {
|
|
1961
|
+
const key = el.getAttribute('data-i18n-aria-label');
|
|
1962
|
+
el.setAttribute('aria-label', t(key));
|
|
1963
|
+
});
|
|
1924
1964
|
// 更新 html lang 属性
|
|
1925
1965
|
document.documentElement.lang = currentLang;
|
|
1926
1966
|
updateThemeButton();
|
|
1967
|
+
updateMobilePageTitle();
|
|
1968
|
+
renderMobileNavigation();
|
|
1927
1969
|
window.refreshBasicSettingsI18n?.();
|
|
1928
1970
|
}
|
|
1929
1971
|
|
|
@@ -2282,7 +2324,13 @@ setInterval(() => {
|
|
|
2282
2324
|
|
|
2283
2325
|
// ── Tab 切换 ──
|
|
2284
2326
|
let msgSel = { aid: null, peer: null, before: null };
|
|
2327
|
+
let msgCollapsedAids = new Set();
|
|
2328
|
+
// 消息详情扩展信息只在当前页面生命周期内记忆,不写入 sessionStorage。
|
|
2329
|
+
// 以 Agent/Chats/EC 会话 ID 区分,切换会话时仍默认展开。
|
|
2330
|
+
const msgSessionDetailsExpanded = new Map();
|
|
2285
2331
|
let sessSel = { sessionId: null, project: null, baseagent: null, turnBefore: null };
|
|
2332
|
+
// 会话详情顶部信息只在当前页面生命周期内记忆展开/收起状态。
|
|
2333
|
+
const sessHeaderExpanded = new Map();
|
|
2286
2334
|
let trigSel = { agent: null };
|
|
2287
2335
|
let trigAgentsCollapsed = false;
|
|
2288
2336
|
const _trigOps = new Map(); // Map<triggerId, string>
|
|
@@ -2292,9 +2340,98 @@ let _triggerTest = null;
|
|
|
2292
2340
|
let sessSearch = '';
|
|
2293
2341
|
let sessShowAll = false; // false=只显示有效会话;true=显示全部
|
|
2294
2342
|
let sessSourceFilter = 'all'; // all / trigger / agent / cli / user / unknown
|
|
2343
|
+
let sessListScroll = { layout: 0, items: 0 };
|
|
2295
2344
|
let sessChatMode = false; // false=完整视图,true=对话视图(折叠处理过程)
|
|
2296
2345
|
let monRange = '2m'; // Monitor 时间窗口:2m / 10m / 1h
|
|
2297
2346
|
let logSel = { source: null, level: 'all', module: 'all', search: '', limit: 500, follow: true };
|
|
2347
|
+
const SESSION_UI_KEY = 'ecWebUiState';
|
|
2348
|
+
let mobileNavOpen = false;
|
|
2349
|
+
let detailViewState = { msg: false, session: false };
|
|
2350
|
+
let historySyncing = false;
|
|
2351
|
+
let sessScrollSaveFrame = null;
|
|
2352
|
+
|
|
2353
|
+
function readSessionUiState() {
|
|
2354
|
+
try {
|
|
2355
|
+
const raw = sessionStorage.getItem(SESSION_UI_KEY);
|
|
2356
|
+
if (!raw) return;
|
|
2357
|
+
const saved = JSON.parse(raw);
|
|
2358
|
+
if (saved.msg && typeof saved.msg === 'object') msgSel = { ...msgSel, ...saved.msg, before: null };
|
|
2359
|
+
if (saved.session && typeof saved.session === 'object') sessSel = { ...sessSel, ...saved.session, turnBefore: null };
|
|
2360
|
+
if (saved.trigger && typeof saved.trigger === 'object') trigSel = { ...trigSel, ...saved.trigger };
|
|
2361
|
+
if (typeof saved.sessShowAll === 'boolean') sessShowAll = saved.sessShowAll;
|
|
2362
|
+
if (typeof saved.sessSourceFilter === 'string') sessSourceFilter = saved.sessSourceFilter;
|
|
2363
|
+
if (saved.sessionListScroll && typeof saved.sessionListScroll === 'object') {
|
|
2364
|
+
sessListScroll = {
|
|
2365
|
+
layout: Math.max(0, Number(saved.sessionListScroll.layout) || 0),
|
|
2366
|
+
items: Math.max(0, Number(saved.sessionListScroll.items) || 0),
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
if (saved.log && typeof saved.log === 'object') logSel = { ...logSel, ...saved.log };
|
|
2370
|
+
if (saved.detail && typeof saved.detail === 'object') detailViewState = { ...detailViewState, ...saved.detail };
|
|
2371
|
+
} catch {
|
|
2372
|
+
// sessionStorage may be unavailable in private or embedded contexts.
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
function saveSessionUiState() {
|
|
2377
|
+
try {
|
|
2378
|
+
captureSessionListScroll();
|
|
2379
|
+
sessionStorage.setItem(SESSION_UI_KEY, JSON.stringify({
|
|
2380
|
+
msg: { aid: msgSel.aid, peer: msgSel.peer },
|
|
2381
|
+
session: { ...sessSel, turnBefore: null },
|
|
2382
|
+
trigger: { agent: trigSel.agent },
|
|
2383
|
+
sessShowAll,
|
|
2384
|
+
sessSourceFilter,
|
|
2385
|
+
sessionListScroll: sessListScroll,
|
|
2386
|
+
log: { ...logSel },
|
|
2387
|
+
detail: { ...detailViewState },
|
|
2388
|
+
}));
|
|
2389
|
+
} catch {
|
|
2390
|
+
// Keep the in-memory state when storage is unavailable.
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
function captureSessionListScroll() {
|
|
2395
|
+
const layout = document.querySelector('.sess-layout');
|
|
2396
|
+
const items = document.querySelector('#sess-list .sess-items');
|
|
2397
|
+
if (layout) sessListScroll.layout = Math.max(0, layout.scrollTop || 0);
|
|
2398
|
+
if (items) sessListScroll.items = Math.max(0, items.scrollTop || 0);
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
function restoreSessionListScroll() {
|
|
2402
|
+
const layout = document.querySelector('.sess-layout');
|
|
2403
|
+
const items = document.querySelector('#sess-list .sess-items');
|
|
2404
|
+
if (layout) layout.scrollTop = sessListScroll.layout;
|
|
2405
|
+
if (items) items.scrollTop = sessListScroll.items;
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
function bindSessionListScroll() {
|
|
2409
|
+
const layout = document.querySelector('.sess-layout');
|
|
2410
|
+
if (layout && layout.dataset.scrollBound !== '1') {
|
|
2411
|
+
layout.dataset.scrollBound = '1';
|
|
2412
|
+
layout.addEventListener('scroll', () => {
|
|
2413
|
+
captureSessionListScroll();
|
|
2414
|
+
scheduleSessionListStateSave();
|
|
2415
|
+
}, { passive: true });
|
|
2416
|
+
}
|
|
2417
|
+
const items = document.querySelector('#sess-list .sess-items');
|
|
2418
|
+
if (items) items.addEventListener('scroll', () => {
|
|
2419
|
+
captureSessionListScroll();
|
|
2420
|
+
scheduleSessionListStateSave();
|
|
2421
|
+
}, { passive: true });
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
function scheduleSessionListStateSave() {
|
|
2425
|
+
if (sessScrollSaveFrame != null) return;
|
|
2426
|
+
const flush = () => {
|
|
2427
|
+
sessScrollSaveFrame = null;
|
|
2428
|
+
saveSessionUiState();
|
|
2429
|
+
};
|
|
2430
|
+
if (typeof window.requestAnimationFrame === 'function') sessScrollSaveFrame = window.requestAnimationFrame(flush);
|
|
2431
|
+
else sessScrollSaveFrame = window.setTimeout(flush, 0);
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
readSessionUiState();
|
|
2298
2435
|
// ECWeb 界面的 base agent 展示名统一走这里的 i18n 键,而不是父包别名表:
|
|
2299
2436
|
// 别名表面向 CLI/日志(claude → "Claude Code"、ecagent → "ecagent"),
|
|
2300
2437
|
// 面板里应保持简洁一致(Claude / Codex / Gemini / EC Agent)。
|
|
@@ -2371,7 +2508,133 @@ function setRoleManagementSubview(subview, options = {}) {
|
|
|
2371
2508
|
if (options.render !== false) renderRoleManagementSubview();
|
|
2372
2509
|
}
|
|
2373
2510
|
|
|
2374
|
-
|
|
2511
|
+
const VIEW_LABEL_KEYS = {
|
|
2512
|
+
agents: 'tab.agents', msg: 'tab.messages', session: 'tab.sessions', triggers: 'tab.triggers',
|
|
2513
|
+
cache: 'tab.cache', system: 'tab.system', logs: 'tab.logs', usage: 'tab.usage', roleManagement: 'tab.roleManagement',
|
|
2514
|
+
};
|
|
2515
|
+
|
|
2516
|
+
function normalizedView(view) {
|
|
2517
|
+
if (view === 'monitor') return 'system';
|
|
2518
|
+
if (view === 'roles' || view === 'roleDefinitions') return 'roleManagement';
|
|
2519
|
+
return ['agents', 'msg', 'session', 'triggers', 'cache', 'system', 'logs', 'usage', 'roleManagement'].includes(view)
|
|
2520
|
+
? view
|
|
2521
|
+
: 'agents';
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
function historySnapshot() {
|
|
2525
|
+
return {
|
|
2526
|
+
ecweb: true,
|
|
2527
|
+
view: currentView,
|
|
2528
|
+
detail: { ...detailViewState },
|
|
2529
|
+
roleManagementSubview,
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
function updateMobilePageTitle() {
|
|
2534
|
+
const title = $('#mobile-page-title');
|
|
2535
|
+
if (title) title.textContent = t(VIEW_LABEL_KEYS[currentView] || 'tab.agents');
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
function applyDetailClasses() {
|
|
2539
|
+
['msg', 'session'].forEach(view => {
|
|
2540
|
+
const el = $('#view-' + view);
|
|
2541
|
+
if (!el) return;
|
|
2542
|
+
el.classList.toggle('detail-fullscreen', currentView === view && detailViewState[view] === true);
|
|
2543
|
+
});
|
|
2544
|
+
saveSessionUiState();
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
function enterDetail(view, options = {}) {
|
|
2548
|
+
if (!['msg', 'session'].includes(view) || currentView !== view) return;
|
|
2549
|
+
if (detailViewState[view]) return;
|
|
2550
|
+
detailViewState[view] = true;
|
|
2551
|
+
applyDetailClasses();
|
|
2552
|
+
if (options.history !== false && !historySyncing) history.pushState(historySnapshot(), '', location.href);
|
|
2553
|
+
renderView(view);
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
function leaveDetail(view, options = {}) {
|
|
2557
|
+
if (!['msg', 'session'].includes(view) || !detailViewState[view]) return;
|
|
2558
|
+
if (options.history !== false && !historySyncing && history.state?.ecweb && history.state.detail?.[view]) {
|
|
2559
|
+
history.back();
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
detailViewState[view] = false;
|
|
2563
|
+
applyDetailClasses();
|
|
2564
|
+
renderView(view);
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
function restoreHistoryState(snapshot) {
|
|
2568
|
+
const next = snapshot && snapshot.ecweb ? snapshot : { ecweb: true, view: currentView, detail: {} };
|
|
2569
|
+
historySyncing = true;
|
|
2570
|
+
if (next.roleManagementSubview) roleManagementSubview = next.roleManagementSubview;
|
|
2571
|
+
detailViewState = { msg: false, session: false };
|
|
2572
|
+
switchView(next.view || 'agents', { history: false });
|
|
2573
|
+
detailViewState = { msg: false, session: false, ...(next.detail || {}) };
|
|
2574
|
+
historySyncing = false;
|
|
2575
|
+
applyDetailClasses();
|
|
2576
|
+
if (currentView === 'msg' || currentView === 'session') renderView(currentView);
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
function initHistory() {
|
|
2580
|
+
const initial = history.state && history.state.ecweb ? history.state : historySnapshot();
|
|
2581
|
+
history.replaceState(initial, '', location.href);
|
|
2582
|
+
window.addEventListener('popstate', event => restoreHistoryState(event.state));
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
function renderMobileNavigation() {
|
|
2586
|
+
const list = $('.mobile-nav-list');
|
|
2587
|
+
if (!list) return;
|
|
2588
|
+
list.innerHTML = [...document.querySelectorAll('.tabs .tab')].map(tab => {
|
|
2589
|
+
const view = tab.dataset.view;
|
|
2590
|
+
const label = t(tab.dataset.i18n || VIEW_LABEL_KEYS[view] || view);
|
|
2591
|
+
return `<button type="button" class="mobile-nav-item${view === currentView ? ' active' : ''}" data-mobile-view="${esc(view)}">${esc(label)}</button>`;
|
|
2592
|
+
}).join('');
|
|
2593
|
+
list.querySelectorAll('[data-mobile-view]').forEach(button => {
|
|
2594
|
+
button.onclick = () => {
|
|
2595
|
+
closeMobileNavigation();
|
|
2596
|
+
switchView(button.dataset.mobileView);
|
|
2597
|
+
};
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
function setMobileNavigation(open) {
|
|
2602
|
+
mobileNavOpen = Boolean(open);
|
|
2603
|
+
const drawer = $('#mobile-nav-drawer');
|
|
2604
|
+
const backdrop = $('#mobile-nav-backdrop');
|
|
2605
|
+
const menu = $('#mobile-menu-btn');
|
|
2606
|
+
if (drawer) {
|
|
2607
|
+
drawer.classList.toggle('open', mobileNavOpen);
|
|
2608
|
+
drawer.setAttribute('aria-hidden', String(!mobileNavOpen));
|
|
2609
|
+
}
|
|
2610
|
+
if (backdrop) {
|
|
2611
|
+
backdrop.hidden = !mobileNavOpen;
|
|
2612
|
+
backdrop.classList.toggle('open', mobileNavOpen);
|
|
2613
|
+
}
|
|
2614
|
+
if (menu) menu.setAttribute('aria-expanded', String(mobileNavOpen));
|
|
2615
|
+
document.body.classList.toggle('mobile-nav-open', mobileNavOpen);
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
function closeMobileNavigation() { setMobileNavigation(false); }
|
|
2619
|
+
|
|
2620
|
+
function initMobileNavigation() {
|
|
2621
|
+
renderMobileNavigation();
|
|
2622
|
+
$('#mobile-menu-btn')?.addEventListener('click', () => setMobileNavigation(!mobileNavOpen));
|
|
2623
|
+
$('#mobile-nav-close')?.addEventListener('click', closeMobileNavigation);
|
|
2624
|
+
$('#mobile-nav-backdrop')?.addEventListener('click', closeMobileNavigation);
|
|
2625
|
+
document.querySelectorAll('[data-mobile-tool]').forEach(button => {
|
|
2626
|
+
button.addEventListener('click', () => {
|
|
2627
|
+
const tool = button.dataset.mobileTool;
|
|
2628
|
+
closeMobileNavigation();
|
|
2629
|
+
if (tool === 'lang') toggleLang();
|
|
2630
|
+
else if (tool === 'theme') $('#theme-btn')?.click();
|
|
2631
|
+
else if (tool === 'settings') $('#basic-settings-btn')?.click();
|
|
2632
|
+
else if (tool === 'logout') $('#logout-btn')?.click();
|
|
2633
|
+
});
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
function switchView(view, options = {}) {
|
|
2375
2638
|
if (view === 'monitor') {
|
|
2376
2639
|
systemContentMode = 'monitor';
|
|
2377
2640
|
view = 'system';
|
|
@@ -2380,10 +2643,19 @@ function switchView(view) {
|
|
|
2380
2643
|
roleManagementSubview = view === 'roles' ? 'assignments' : 'definitions';
|
|
2381
2644
|
view = 'roleManagement';
|
|
2382
2645
|
}
|
|
2646
|
+
view = normalizedView(view);
|
|
2647
|
+
if (currentView !== view) {
|
|
2648
|
+
detailViewState = { msg: false, session: false };
|
|
2649
|
+
}
|
|
2383
2650
|
currentView = view;
|
|
2384
2651
|
localStorage.setItem(VIEW_KEY, view);
|
|
2385
2652
|
document.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', t.dataset.view === view));
|
|
2386
2653
|
document.querySelectorAll('.view').forEach(v => v.classList.toggle('active', v.id === 'view-' + view));
|
|
2654
|
+
updateMobilePageTitle();
|
|
2655
|
+
renderMobileNavigation();
|
|
2656
|
+
applyDetailClasses();
|
|
2657
|
+
if (options.replaceHistory) history.replaceState(historySnapshot(), '', location.href);
|
|
2658
|
+
else if (options.history !== false && !historySyncing && options.fromHistory !== true) history.pushState(historySnapshot(), '', location.href);
|
|
2387
2659
|
// 切换时按当前选择恢复订阅
|
|
2388
2660
|
if (view === 'msg') subscribe('msg', msgSel);
|
|
2389
2661
|
else if (view === 'session') subscribe('session', sessSel);
|
|
@@ -2415,6 +2687,7 @@ function initTabs() {
|
|
|
2415
2687
|
document.querySelector(`[data-role-subview="${next}"]`)?.focus();
|
|
2416
2688
|
};
|
|
2417
2689
|
});
|
|
2690
|
+
initMobileNavigation();
|
|
2418
2691
|
}
|
|
2419
2692
|
|
|
2420
2693
|
function renderView(view) {
|
|
@@ -2518,37 +2791,49 @@ function bindLogControls() {
|
|
|
2518
2791
|
const search = $('#log-search');
|
|
2519
2792
|
const limit = $('#log-limit');
|
|
2520
2793
|
const follow = $('#log-follow');
|
|
2794
|
+
const moreFilters = $('#log-more-filters');
|
|
2521
2795
|
if (!source || source.dataset.bound === '1') return;
|
|
2522
2796
|
source.dataset.bound = '1';
|
|
2523
2797
|
source.addEventListener('change', () => {
|
|
2524
2798
|
logSel.source = source.value || null;
|
|
2799
|
+
saveSessionUiState();
|
|
2525
2800
|
$('#log-stream').innerHTML = `<div class="empty">${t('common.loading')}</div>`;
|
|
2526
2801
|
subscribe('logs', { source: logSel.source, limit: logSel.limit });
|
|
2527
2802
|
});
|
|
2528
2803
|
level.addEventListener('change', () => {
|
|
2529
2804
|
logSel.level = level.value || 'all';
|
|
2805
|
+
saveSessionUiState();
|
|
2530
2806
|
renderLogs(state.logs);
|
|
2531
2807
|
});
|
|
2532
2808
|
module.addEventListener('change', () => {
|
|
2533
2809
|
logSel.module = module.value || 'all';
|
|
2810
|
+
saveSessionUiState();
|
|
2534
2811
|
renderLogs(state.logs);
|
|
2535
2812
|
});
|
|
2536
2813
|
search.addEventListener('input', () => {
|
|
2537
2814
|
logSel.search = search.value || '';
|
|
2815
|
+
saveSessionUiState();
|
|
2538
2816
|
renderLogs(state.logs);
|
|
2539
2817
|
});
|
|
2540
2818
|
limit.addEventListener('change', () => {
|
|
2541
2819
|
logSel.limit = Number(limit.value) || 500;
|
|
2820
|
+
saveSessionUiState();
|
|
2542
2821
|
$('#log-stream').innerHTML = `<div class="empty">${t('common.loading')}</div>`;
|
|
2543
2822
|
subscribe('logs', { source: logSel.source, limit: logSel.limit });
|
|
2544
2823
|
});
|
|
2545
2824
|
follow.addEventListener('change', () => {
|
|
2546
2825
|
logSel.follow = follow.checked;
|
|
2826
|
+
saveSessionUiState();
|
|
2547
2827
|
if (logSel.follow) {
|
|
2548
2828
|
const stream = $('#log-stream');
|
|
2549
2829
|
stream.scrollTop = stream.scrollHeight;
|
|
2550
2830
|
}
|
|
2551
2831
|
});
|
|
2832
|
+
if (moreFilters) moreFilters.addEventListener('click', () => {
|
|
2833
|
+
const toolbar = moreFilters.closest('.log-toolbar');
|
|
2834
|
+
const expanded = toolbar?.classList.toggle('filters-expanded') || false;
|
|
2835
|
+
moreFilters.setAttribute('aria-expanded', String(expanded));
|
|
2836
|
+
});
|
|
2552
2837
|
}
|
|
2553
2838
|
|
|
2554
2839
|
function renderLogs(data) {
|
|
@@ -2558,6 +2843,7 @@ function renderLogs(data) {
|
|
|
2558
2843
|
const search = $('#log-search');
|
|
2559
2844
|
const limit = $('#log-limit');
|
|
2560
2845
|
const follow = $('#log-follow');
|
|
2846
|
+
const moreFilters = $('#log-more-filters');
|
|
2561
2847
|
const meta = $('#log-meta-info');
|
|
2562
2848
|
const filterResult = $('#log-filter-result');
|
|
2563
2849
|
const stream = $('#log-stream');
|
|
@@ -2568,6 +2854,7 @@ function renderLogs(data) {
|
|
|
2568
2854
|
search.value = logSel.search;
|
|
2569
2855
|
limit.value = String(logSel.limit);
|
|
2570
2856
|
follow.checked = logSel.follow;
|
|
2857
|
+
if (moreFilters) moreFilters.textContent = t('logs.moreFilters');
|
|
2571
2858
|
if (!data) {
|
|
2572
2859
|
stream.innerHTML = `<div class="empty">${t('common.loading')}</div>`;
|
|
2573
2860
|
return;
|
|
@@ -2891,6 +3178,13 @@ function initMsgTipFloat() {
|
|
|
2891
3178
|
if (to && (wrap.contains(to) || (floatEl && floatEl.contains(to)))) return;
|
|
2892
3179
|
scheduleHide();
|
|
2893
3180
|
});
|
|
3181
|
+
document.addEventListener('click', (e) => {
|
|
3182
|
+
if (!floatEl || !floatEl.classList.contains('show')) return;
|
|
3183
|
+
if (floatEl.contains(e.target)) return;
|
|
3184
|
+
const wrap = e.target.closest && e.target.closest('.ag-msg-wrap');
|
|
3185
|
+
if (wrap && wrap === curWrap) return;
|
|
3186
|
+
hideNow();
|
|
3187
|
+
}, true);
|
|
2894
3188
|
// 滚动时隐藏,避免浮层与行脱节
|
|
2895
3189
|
window.addEventListener('scroll', hideNow, true);
|
|
2896
3190
|
}
|
|
@@ -3140,7 +3434,9 @@ function renderAgents(data) {
|
|
|
3140
3434
|
if (!disabledAgents.length) {
|
|
3141
3435
|
html += `<div class="empty">${t('agents.empty.disabled')}</div>`;
|
|
3142
3436
|
} else {
|
|
3143
|
-
html +=
|
|
3437
|
+
html += '<table class="agents-table agents-table-disabled"><thead><tr>' +
|
|
3438
|
+
`<th>${t('agents.th.agent')}</th><th>${t('agents.th.operations')}</th>` +
|
|
3439
|
+
'</tr></thead><tbody>';
|
|
3144
3440
|
for (const ag of disabledAgents) {
|
|
3145
3441
|
const busy = _agentOps.has(ag.aid);
|
|
3146
3442
|
const ops = busy
|
|
@@ -3148,7 +3444,6 @@ function renderAgents(data) {
|
|
|
3148
3444
|
: agentDisabledOpsHtml(ag.aid);
|
|
3149
3445
|
html += `<tr class="ag-main">` +
|
|
3150
3446
|
`<td><div class="ag-id"><span class="dot off"></span><span class="ag-id-text"><span class="ag-name">${esc(ag.displayName || shortAid(ag.aid))}${agentLifecycleBadge(ag.lifecycle)}</span><span class="ag-aid">${esc(ag.aid)}</span></span></div></td>` +
|
|
3151
|
-
`<td style="font-size:11px;font-family:monospace">${esc(ag.projectPath || '—')}</td>` +
|
|
3152
3447
|
`<td class="agent-ops-cell">${ops}</td></tr>`;
|
|
3153
3448
|
}
|
|
3154
3449
|
html += '</tbody></table>';
|
|
@@ -3173,7 +3468,7 @@ function renderAgents(data) {
|
|
|
3173
3468
|
return;
|
|
3174
3469
|
}
|
|
3175
3470
|
|
|
3176
|
-
html += '<table><thead><tr>' +
|
|
3471
|
+
html += '<table class="agents-table agents-table-enabled"><thead><tr>' +
|
|
3177
3472
|
`<th>${t('agents.th.aid')}</th><th>${t('agents.th.work')}</th><th>${t('agents.th.queue')}</th><th>${t('agents.th.model')}</th><th>${t('agents.th.runtime')}</th>` +
|
|
3178
3473
|
`<th>${t('agents.th.received')}</th><th>${t('agents.th.sent')}</th><th>${t('agents.th.errors')}</th><th>${t('agents.th.interrupts')}</th><th>${t('agents.th.completed')}</th>` +
|
|
3179
3474
|
`<th>${t('agents.th.lastActivity')}</th><th>${t('agents.th.operations')}</th>` +
|
|
@@ -3214,10 +3509,11 @@ function renderAgents(data) {
|
|
|
3214
3509
|
const recent = (s.recentMessages || []);
|
|
3215
3510
|
const tipHtml = recentMsgTooltipHtml(recent);
|
|
3216
3511
|
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3512
|
+
if (preview) {
|
|
3513
|
+
html += `<tr class="ag-sub"><td colspan="12"><div class="ag-info">` +
|
|
3514
|
+
`<div class="ag-msg-wrap">${tipHtml}<div class="ag-msg">${preview}</div></div>` +
|
|
3515
|
+
'</div></td></tr>';
|
|
3516
|
+
}
|
|
3221
3517
|
}
|
|
3222
3518
|
html += '</tbody></table>';
|
|
3223
3519
|
if (data.daemonRunning) {
|
|
@@ -3360,6 +3656,83 @@ function msgChannelTone(channelType) {
|
|
|
3360
3656
|
: 'other';
|
|
3361
3657
|
}
|
|
3362
3658
|
|
|
3659
|
+
function renderMsgMobileControls(aids, peers, scopeMatches) {
|
|
3660
|
+
const el = $('#msg-mobile-controls');
|
|
3661
|
+
if (!el) return;
|
|
3662
|
+
const aidOptions = aids.map(item => {
|
|
3663
|
+
const name = item.displayName || (item.selfAID && item.selfAID !== 'unknown' ? shortAid(item.selfAID) : 'unknown');
|
|
3664
|
+
return `<option value="${esc(item.aid)}"${item.aid === msgSel.aid ? ' selected' : ''}>${esc(name)}</option>`;
|
|
3665
|
+
}).join('');
|
|
3666
|
+
const peerOptions = peers.map(item => {
|
|
3667
|
+
const name = item.chatType === 'group'
|
|
3668
|
+
? (item.groupName || item.peerName || item.groupId || item.peerId)
|
|
3669
|
+
: (item.peerName || item.peerId);
|
|
3670
|
+
return `<option value="${esc(item.peerId)}"${item.peerId === msgSel.peer ? ' selected' : ''}>${esc(shortId(name))}</option>`;
|
|
3671
|
+
}).join('');
|
|
3672
|
+
el.innerHTML = `<label><span>${esc(t('messages.mobile.aid'))}</span>` +
|
|
3673
|
+
`<select id="msg-mobile-aid"${aids.length ? '' : ' disabled'}>${aidOptions || '<option value="">—</option>'}</select>` +
|
|
3674
|
+
`</label><label><span>${esc(t('messages.mobile.peer'))}</span>` +
|
|
3675
|
+
`<select id="msg-mobile-peer"${scopeMatches && peers.length ? '' : ' disabled'}>${peerOptions || '<option value="">—</option>'}</select>` +
|
|
3676
|
+
'</label>';
|
|
3677
|
+
const aidSelect = $('#msg-mobile-aid');
|
|
3678
|
+
if (aidSelect) aidSelect.onchange = () => {
|
|
3679
|
+
msgSel = { aid: aidSelect.value || null, peer: null, before: null };
|
|
3680
|
+
detailViewState.msg = false;
|
|
3681
|
+
saveSessionUiState();
|
|
3682
|
+
subscribe('msg', msgSel);
|
|
3683
|
+
};
|
|
3684
|
+
const peerSelect = $('#msg-mobile-peer');
|
|
3685
|
+
if (peerSelect) peerSelect.onchange = () => {
|
|
3686
|
+
msgSel = { aid: msgSel.aid, peer: peerSelect.value || null, before: null };
|
|
3687
|
+
saveSessionUiState();
|
|
3688
|
+
subscribe('msg', msgSel);
|
|
3689
|
+
};
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
function msgAgentIsExpanded(aid) {
|
|
3693
|
+
return aid === msgSel.aid && !msgCollapsedAids.has(aid);
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
function renderMsgPeerItem(peer, extraClass = '') {
|
|
3697
|
+
const sel = peer.peerId === msgSel.peer ? ' sel' : '';
|
|
3698
|
+
const isGroup = peer.chatType === 'group';
|
|
3699
|
+
const displayName = peer.chatType === 'group'
|
|
3700
|
+
? (peer.groupName || peer.peerName || peer.groupId || peer.peerId)
|
|
3701
|
+
: (peer.peerName || peer.peerId);
|
|
3702
|
+
const chatTitle = peer.partition === 'thread'
|
|
3703
|
+
? (peer.threadName || peer.threadId || displayName)
|
|
3704
|
+
: peer.partition === 'legacy'
|
|
3705
|
+
? t('messages.legacy.title')
|
|
3706
|
+
: displayName;
|
|
3707
|
+
const channelLabel = peer.channelName && peer.channelName !== 'main'
|
|
3708
|
+
? `${peer.channelType}/${peer.channelName}`
|
|
3709
|
+
: (peer.channelType || '');
|
|
3710
|
+
const groupMark = isGroup ? `<span class="msg-group-mark">${esc(t('messages.tag.groupShort'))}</span>` : '';
|
|
3711
|
+
const partitionLabel = peer.partition === 'thread'
|
|
3712
|
+
? t('messages.tag.topic')
|
|
3713
|
+
: peer.partition === 'legacy' ? t('messages.tag.legacy') : t('messages.tag.main');
|
|
3714
|
+
const partitionMark = `<span class="msg-partition-mark msg-partition-${esc(peer.partition || 'main')}">${esc(partitionLabel)}</span>`;
|
|
3715
|
+
const channelTag = channelLabel
|
|
3716
|
+
? `<span class="msg-tag msg-channel msg-channel-${msgChannelTone(peer.channelType)}">${esc(channelLabel)}</span>`
|
|
3717
|
+
: '';
|
|
3718
|
+
const topicStatus = ({ pending: '待讨论', in_progress: '进行中', completed: '已完成', archived: '已归档' })[peer.topicStatus] || '待讨论';
|
|
3719
|
+
const sub = peer.partition === 'thread' ? `${esc(topicStatus)} · ` : '';
|
|
3720
|
+
const label = peer.label ? `${esc(peer.label)} · ` : '';
|
|
3721
|
+
const threadPeer = peer.partition === 'thread' ? `${esc(shortId(displayName))} · ` : '';
|
|
3722
|
+
return `<div class="list-item msg-peer-item ${extraClass}${sel}" data-peer="${esc(peer.peerId)}">` +
|
|
3723
|
+
`<div class="name">${groupMark}${partitionMark}<span class="msg-peer-title" title="${esc(chatTitle)}">${esc(shortId(chatTitle))}</span>${channelTag}</div>` +
|
|
3724
|
+
`<div class="sub">${sub}${label}${threadPeer}↓${peer.inbound} ↑${peer.outbound} · ${fmtAgo(peer.lastAt)}</div></div>`;
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
function bindDetailPreview(container, view) {
|
|
3728
|
+
if (!container) return;
|
|
3729
|
+
container.onclick = event => {
|
|
3730
|
+
if (event.target.closest('button, a, input, select, textarea, summary, details')) return;
|
|
3731
|
+
if (detailViewState[view]) leaveDetail(view);
|
|
3732
|
+
else enterDetail(view);
|
|
3733
|
+
};
|
|
3734
|
+
}
|
|
3735
|
+
|
|
3363
3736
|
function renderMsg(data) {
|
|
3364
3737
|
if (!data) return;
|
|
3365
3738
|
const aids = data.scopes || data.aids || [];
|
|
@@ -3371,6 +3744,7 @@ function renderMsg(data) {
|
|
|
3371
3744
|
const nextAid = aids[0]?.aid || null;
|
|
3372
3745
|
const selectionChanged = msgSel.aid !== nextAid || msgSel.peer !== null;
|
|
3373
3746
|
msgSel = { aid: nextAid, peer: null, before: null };
|
|
3747
|
+
saveSessionUiState();
|
|
3374
3748
|
if (selectionChanged && nextAid) subscribe('msg', msgSel);
|
|
3375
3749
|
}
|
|
3376
3750
|
const scopeMatches = data.scope === msgSel.aid;
|
|
@@ -3381,6 +3755,7 @@ function renderMsg(data) {
|
|
|
3381
3755
|
const nextPeer = peers[0]?.peerId || null;
|
|
3382
3756
|
const selectionChanged = msgSel.peer !== nextPeer;
|
|
3383
3757
|
msgSel = { aid: msgSel.aid, peer: nextPeer, before: null };
|
|
3758
|
+
saveSessionUiState();
|
|
3384
3759
|
if (selectionChanged && nextPeer) subscribe('msg', msgSel);
|
|
3385
3760
|
}
|
|
3386
3761
|
}
|
|
@@ -3388,24 +3763,78 @@ function renderMsg(data) {
|
|
|
3388
3763
|
const selectedAid = aids.find(item => item.aid === msgSel.aid);
|
|
3389
3764
|
const peerTotal = scopeMatches ? peers.length : (selectedAid?.peerCount || 0);
|
|
3390
3765
|
|
|
3391
|
-
//
|
|
3392
|
-
let
|
|
3766
|
+
// 左:桌面端 AID 列表;双列布局使用同一栏里的 Agent → Chats 树。
|
|
3767
|
+
let aidListHtml = `<div class="col-title"><span>${t('messages.colTitle.aid')}</span><span class="col-count">${aids.length}</span></div>`;
|
|
3393
3768
|
for (const a of aids) {
|
|
3394
3769
|
const sel = a.aid === msgSel.aid ? ' sel' : '';
|
|
3395
3770
|
const name = a.displayName || (a.selfAID && a.selfAID !== 'unknown' ? shortAid(a.selfAID) : 'unknown');
|
|
3396
3771
|
const privateCount = Math.max(0, (a.peerCount || 0) - (a.groupCount || 0));
|
|
3397
3772
|
const privateBit = tf('messages.privateCount', { count: privateCount });
|
|
3398
3773
|
const groupBit = a.groupCount ? ` · ${tf('messages.groupCount', { count: a.groupCount })}` : '';
|
|
3399
|
-
|
|
3774
|
+
aidListHtml += `<div class="list-item msg-aid-item${sel}" data-aid="${esc(a.aid)}">` +
|
|
3400
3775
|
`<div class="name">${esc(name)}</div>` +
|
|
3401
3776
|
`<div class="sub">↓${a.totalIn} ↑${a.totalOut} · ${privateBit}${groupBit} · ${fmtAgo(a.lastAt)}</div></div>`;
|
|
3402
3777
|
}
|
|
3403
|
-
if (!aids.length)
|
|
3404
|
-
|
|
3405
|
-
|
|
3778
|
+
if (!aids.length) aidListHtml += `<div class="empty">${t('messages.empty.noAgents')}</div>`;
|
|
3779
|
+
|
|
3780
|
+
let aidTreeHtml = `<div class="msg-tree-title"><span>${t('messages.colTitle.aid')}</span><span class="col-count">${aids.length}</span></div>`;
|
|
3781
|
+
for (const a of aids) {
|
|
3782
|
+
const selected = a.aid === msgSel.aid;
|
|
3783
|
+
const expanded = msgAgentIsExpanded(a.aid);
|
|
3784
|
+
const name = a.displayName || (a.selfAID && a.selfAID !== 'unknown' ? shortAid(a.selfAID) : 'unknown');
|
|
3785
|
+
const peerCount = a.peerCount || 0;
|
|
3786
|
+
const groupBit = a.groupCount ? ` · ${tf('messages.groupCount', { count: a.groupCount })}` : '';
|
|
3787
|
+
let chatsHtml = '';
|
|
3788
|
+
if (expanded) {
|
|
3789
|
+
if (!scopeMatches) chatsHtml = `<div class="msg-tree-empty">${t('common.loading')}</div>`;
|
|
3790
|
+
else if (!peers.length) chatsHtml = `<div class="msg-tree-empty">${t('messages.empty.noPeers')}</div>`;
|
|
3791
|
+
else chatsHtml = peers.map(peer => renderMsgPeerItem(peer, 'msg-tree-chat')).join('');
|
|
3792
|
+
}
|
|
3793
|
+
aidTreeHtml += `<div class="msg-tree-agent${selected ? ' sel' : ''}${expanded ? ' expanded' : ''}">` +
|
|
3794
|
+
`<button type="button" class="msg-tree-agent-toggle" data-aid="${esc(a.aid)}" aria-expanded="${expanded ? 'true' : 'false'}">` +
|
|
3795
|
+
`<span class="msg-tree-chevron" aria-hidden="true">${expanded ? '▾' : '▸'}</span>` +
|
|
3796
|
+
`<span class="msg-tree-agent-main"><span class="name">${esc(name)}</span>` +
|
|
3797
|
+
`<span class="sub">${t('messages.colTitle.peers')} ${peerCount} · ↓${a.totalIn} ↑${a.totalOut}${groupBit ? `${groupBit}` : ''} · ${fmtAgo(a.lastAt)}</span>` +
|
|
3798
|
+
`</span>` +
|
|
3799
|
+
`</button>` +
|
|
3800
|
+
`<div class="msg-tree-chats"${expanded ? '' : ' hidden'}>${chatsHtml}</div>` +
|
|
3801
|
+
`</div>`;
|
|
3802
|
+
}
|
|
3803
|
+
if (!aids.length) aidTreeHtml += `<div class="empty">${t('messages.empty.noAgents')}</div>`;
|
|
3804
|
+
$('#msg-aids').innerHTML = `<div class="msg-aids-list">${aidListHtml}</div><div class="msg-tree-view">${aidTreeHtml}</div>`;
|
|
3805
|
+
$('#msg-aids').querySelectorAll('.msg-aid-item').forEach(item => {
|
|
3406
3806
|
item.onclick = () => {
|
|
3407
3807
|
if (item.dataset.aid === msgSel.aid) return;
|
|
3808
|
+
msgCollapsedAids.delete(item.dataset.aid);
|
|
3408
3809
|
msgSel = { aid: item.dataset.aid, peer: null, before: null };
|
|
3810
|
+
detailViewState.msg = false;
|
|
3811
|
+
saveSessionUiState();
|
|
3812
|
+
subscribe('msg', msgSel);
|
|
3813
|
+
};
|
|
3814
|
+
});
|
|
3815
|
+
|
|
3816
|
+
$('#msg-aids').querySelectorAll('.msg-tree-agent-toggle').forEach(item => {
|
|
3817
|
+
item.onclick = () => {
|
|
3818
|
+
const aid = item.dataset.aid;
|
|
3819
|
+
if (aid !== msgSel.aid) {
|
|
3820
|
+
msgCollapsedAids.delete(aid);
|
|
3821
|
+
msgSel = { aid, peer: null, before: null };
|
|
3822
|
+
detailViewState.msg = false;
|
|
3823
|
+
saveSessionUiState();
|
|
3824
|
+
subscribe('msg', msgSel);
|
|
3825
|
+
return;
|
|
3826
|
+
}
|
|
3827
|
+
if (msgCollapsedAids.has(aid)) msgCollapsedAids.delete(aid);
|
|
3828
|
+
else msgCollapsedAids.add(aid);
|
|
3829
|
+
renderMsg(state.msg);
|
|
3830
|
+
};
|
|
3831
|
+
});
|
|
3832
|
+
$('#msg-aids').querySelectorAll('.msg-tree-chat').forEach(item => {
|
|
3833
|
+
item.onclick = event => {
|
|
3834
|
+
event.stopPropagation();
|
|
3835
|
+
if (item.dataset.peer === msgSel.peer) return;
|
|
3836
|
+
msgSel = { aid: msgSel.aid, peer: item.dataset.peer, before: null };
|
|
3837
|
+
saveSessionUiState();
|
|
3409
3838
|
subscribe('msg', msgSel);
|
|
3410
3839
|
};
|
|
3411
3840
|
});
|
|
@@ -3419,40 +3848,20 @@ function renderMsg(data) {
|
|
|
3419
3848
|
} else if (!peers.length) {
|
|
3420
3849
|
peersHtml += `<div class="empty">${t('messages.empty.noPeers')}</div>`;
|
|
3421
3850
|
} else {
|
|
3422
|
-
for (const p of peers)
|
|
3423
|
-
const sel = p.peerId === msgSel.peer ? ' sel' : '';
|
|
3424
|
-
const isGroup = p.chatType === 'group';
|
|
3425
|
-
const displayName = p.chatType === 'group'
|
|
3426
|
-
? (p.groupName || p.peerName || p.groupId || p.peerId)
|
|
3427
|
-
: (p.peerName || p.peerId);
|
|
3428
|
-
const chatTitle = p.partition === 'thread'
|
|
3429
|
-
? (p.threadName || p.threadId || displayName)
|
|
3430
|
-
: p.partition === 'legacy'
|
|
3431
|
-
? t('messages.legacy.title')
|
|
3432
|
-
: displayName;
|
|
3433
|
-
const channelLabel = p.channelName && p.channelName !== 'main' ? `${p.channelType}/${p.channelName}` : (p.channelType || '');
|
|
3434
|
-
const groupMark = isGroup ? `<span class="msg-group-mark">${esc(t('messages.tag.groupShort'))}</span>` : '';
|
|
3435
|
-
const partitionLabel = p.partition === 'thread'
|
|
3436
|
-
? t('messages.tag.topic')
|
|
3437
|
-
: p.partition === 'legacy' ? t('messages.tag.legacy') : t('messages.tag.main');
|
|
3438
|
-
const partitionMark = `<span class="msg-partition-mark msg-partition-${esc(p.partition || 'main')}">${esc(partitionLabel)}</span>`;
|
|
3439
|
-
const channelTag = channelLabel
|
|
3440
|
-
? `<span class="msg-tag msg-channel msg-channel-${msgChannelTone(p.channelType)}">${esc(channelLabel)}</span>`
|
|
3441
|
-
: '';
|
|
3442
|
-
peersHtml += `<div class="list-item${sel}" data-peer="${esc(p.peerId)}">` +
|
|
3443
|
-
`<div class="name">${groupMark}${partitionMark}<span class="msg-peer-title" title="${esc(chatTitle)}">${esc(shortId(chatTitle))}</span>${channelTag}</div>` +
|
|
3444
|
-
`<div class="sub">${p.lable ? `${esc(p.lable)} · ` : ''}${p.partition === 'thread' ? `${esc(shortId(displayName))} · ` : ''}↓${p.inbound} ↑${p.outbound} · ${fmtAgo(p.lastAt)}</div></div>`;
|
|
3445
|
-
}
|
|
3851
|
+
for (const p of peers) peersHtml += renderMsgPeerItem(p);
|
|
3446
3852
|
}
|
|
3447
3853
|
$('#msg-peers').innerHTML = peersHtml;
|
|
3448
|
-
$('#msg-peers').querySelectorAll('.
|
|
3854
|
+
$('#msg-peers').querySelectorAll('.msg-peer-item').forEach(item => {
|
|
3449
3855
|
item.onclick = () => {
|
|
3450
3856
|
if (item.dataset.peer === msgSel.peer) return;
|
|
3451
3857
|
msgSel = { aid: msgSel.aid, peer: item.dataset.peer, before: null };
|
|
3858
|
+
saveSessionUiState();
|
|
3452
3859
|
subscribe('msg', msgSel);
|
|
3453
3860
|
};
|
|
3454
3861
|
});
|
|
3455
3862
|
|
|
3863
|
+
renderMsgMobileControls(aids, peers, scopeMatches);
|
|
3864
|
+
|
|
3456
3865
|
// 右:消息流
|
|
3457
3866
|
const stream = $('#msg-stream');
|
|
3458
3867
|
if (!msgSel.aid) { stream.innerHTML = `<div class="empty">${t('messages.empty.selectToView')}</div>`; return; }
|
|
@@ -3491,16 +3900,83 @@ function renderMsg(data) {
|
|
|
3491
3900
|
}
|
|
3492
3901
|
msgHtml += messages.length ? '</div>' : `<div class="empty">${t('messages.empty.noMessages')}</div></div>`;
|
|
3493
3902
|
stream.innerHTML = renderMsgSessionHeader(sessionContext) + msgHtml;
|
|
3903
|
+
bindDetailPreview(stream, 'msg');
|
|
3904
|
+
const sessionDetailsRow = stream.querySelector('.msg-session-id-row');
|
|
3905
|
+
const sessionDetails = stream.querySelector('.msg-session-grid');
|
|
3906
|
+
if (sessionDetailsRow && sessionDetails) {
|
|
3907
|
+
const detailsKey = [msgSel.aid || '', msgSel.peer || '', sessionContext?.sessionId || ''].join('\u001f');
|
|
3908
|
+
const expanded = msgSessionDetailsExpanded.has(detailsKey)
|
|
3909
|
+
? msgSessionDetailsExpanded.get(detailsKey) !== false
|
|
3910
|
+
: true;
|
|
3911
|
+
sessionDetails.hidden = !expanded;
|
|
3912
|
+
sessionDetailsRow.setAttribute('aria-expanded', String(expanded));
|
|
3913
|
+
sessionDetails.setAttribute('aria-expanded', String(expanded));
|
|
3914
|
+
const initialLabel = t(`messages.session.details.${expanded ? 'collapse' : 'expand'}`);
|
|
3915
|
+
sessionDetailsRow.setAttribute('aria-label', initialLabel);
|
|
3916
|
+
sessionDetailsRow.title = initialLabel;
|
|
3917
|
+
sessionDetails.setAttribute('aria-label', initialLabel);
|
|
3918
|
+
const initialIcon = sessionDetailsRow.querySelector('.msg-session-toggle-icon');
|
|
3919
|
+
if (initialIcon) initialIcon.textContent = expanded ? '▾' : '▸';
|
|
3920
|
+
const setSessionDetailsExpanded = () => {
|
|
3921
|
+
const nextExpanded = sessionDetails.hidden;
|
|
3922
|
+
sessionDetails.hidden = !nextExpanded;
|
|
3923
|
+
msgSessionDetailsExpanded.set(detailsKey, nextExpanded);
|
|
3924
|
+
sessionDetailsRow.setAttribute('aria-expanded', String(nextExpanded));
|
|
3925
|
+
sessionDetails.setAttribute('aria-expanded', String(nextExpanded));
|
|
3926
|
+
const label = t(`messages.session.details.${nextExpanded ? 'collapse' : 'expand'}`);
|
|
3927
|
+
sessionDetailsRow.setAttribute('aria-label', label);
|
|
3928
|
+
sessionDetailsRow.title = label;
|
|
3929
|
+
sessionDetails.setAttribute('aria-label', label);
|
|
3930
|
+
const icon = sessionDetailsRow.querySelector('.msg-session-toggle-icon');
|
|
3931
|
+
if (icon) icon.textContent = nextExpanded ? '▾' : '▸';
|
|
3932
|
+
};
|
|
3933
|
+
sessionDetailsRow.onclick = event => {
|
|
3934
|
+
event.stopPropagation();
|
|
3935
|
+
setSessionDetailsExpanded();
|
|
3936
|
+
};
|
|
3937
|
+
sessionDetailsRow.onkeydown = event => {
|
|
3938
|
+
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
3939
|
+
event.preventDefault();
|
|
3940
|
+
event.stopPropagation();
|
|
3941
|
+
setSessionDetailsExpanded();
|
|
3942
|
+
};
|
|
3943
|
+
sessionDetails.onclick = event => {
|
|
3944
|
+
event.stopPropagation();
|
|
3945
|
+
setSessionDetailsExpanded();
|
|
3946
|
+
};
|
|
3947
|
+
sessionDetails.onkeydown = event => {
|
|
3948
|
+
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
3949
|
+
event.preventDefault();
|
|
3950
|
+
event.stopPropagation();
|
|
3951
|
+
setSessionDetailsExpanded();
|
|
3952
|
+
};
|
|
3953
|
+
}
|
|
3494
3954
|
const loadMore = stream.querySelector('.msg-load-more');
|
|
3495
3955
|
if (loadMore) loadMore.onclick = () => {
|
|
3496
3956
|
msgSel = { aid: msgSel.aid, peer: msgSel.peer, before: Number(loadMore.dataset.before) };
|
|
3957
|
+
saveSessionUiState();
|
|
3497
3958
|
subscribe('msg', msgSel);
|
|
3498
3959
|
};
|
|
3499
3960
|
if (atBottom) stream.scrollTop = stream.scrollHeight;
|
|
3500
3961
|
}
|
|
3501
3962
|
|
|
3963
|
+
function renderDetailToolbar(view, title = '') {
|
|
3964
|
+
return title ? `<div class="detail-toolbar"><span class="detail-toolbar-title">${esc(title)}</span></div>` : '';
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
function renderSessionDetailToolbar() {
|
|
3968
|
+
return '<div class="detail-toolbar sess-detail-toolbar">' +
|
|
3969
|
+
`<span class="detail-toolbar-title">${esc(t('tab.sessions'))}</span>` +
|
|
3970
|
+
'<div class="sess-view-control">' +
|
|
3971
|
+
`<button class="view-toggle${sessChatMode ? ' active' : ''}" id="chat-toggle">` +
|
|
3972
|
+
`${sessChatMode ? t('sessions.chatView') : t('sessions.fullView')}</button>` +
|
|
3973
|
+
`<span class="toolbar-hint">${sessChatMode ? t('sessions.chatHint') : t('sessions.fullHint')}</span>` +
|
|
3974
|
+
'</div>' +
|
|
3975
|
+
'</div>';
|
|
3976
|
+
}
|
|
3977
|
+
|
|
3502
3978
|
function renderMsgSessionHeader(context) {
|
|
3503
|
-
if (!context) return '';
|
|
3979
|
+
if (!context) return renderDetailToolbar('msg');
|
|
3504
3980
|
const status = context.status === 'processing' ? 'processing' : 'idle';
|
|
3505
3981
|
const statusLabel = t(`messages.session.status.${status}`);
|
|
3506
3982
|
const modeKey = context.chatMode === 'proactive' ? 'proactive' : (context.chatMode === 'interactive' ? 'interactive' : '');
|
|
@@ -3521,13 +3997,16 @@ function renderMsgSessionHeader(context) {
|
|
|
3521
3997
|
`${esc(value)}</span>` +
|
|
3522
3998
|
'</span>'
|
|
3523
3999
|
).join('');
|
|
3524
|
-
|
|
4000
|
+
const detailsLabel = t('messages.session.details.collapse');
|
|
4001
|
+
return renderDetailToolbar('msg') +
|
|
4002
|
+
'<div class="sess-header msg-session-header">' +
|
|
3525
4003
|
'<div class="msg-session-context-row">' +
|
|
3526
|
-
|
|
4004
|
+
`<div class="msg-session-id-row" role="button" tabindex="0" aria-expanded="false" aria-label="${esc(detailsLabel)}" title="${esc(detailsLabel)}">` +
|
|
3527
4005
|
`<span class="msg-session-label">${t('messages.session.field.sessionId')}</span>` +
|
|
3528
4006
|
`<span class="msg-session-id" title="${esc(context.sessionId || '—')}">${esc(context.sessionId || '—')}</span>` +
|
|
4007
|
+
'<span class="msg-session-toggle" aria-hidden="true"><span class="msg-session-toggle-icon">▸</span></span>' +
|
|
3529
4008
|
'</div>' +
|
|
3530
|
-
`<div class="msg-session-grid">${stats}</div>` +
|
|
4009
|
+
`<div class="msg-session-grid" role="button" tabindex="0" hidden>${stats}</div>` +
|
|
3531
4010
|
'</div>' +
|
|
3532
4011
|
'</div>';
|
|
3533
4012
|
}
|
|
@@ -3665,7 +4144,7 @@ function renderSession(data) {
|
|
|
3665
4144
|
// 有效筛选
|
|
3666
4145
|
const filtered = filterSessionTranscripts(transcripts, sessShowAll, sessSourceFilter);
|
|
3667
4146
|
|
|
3668
|
-
//
|
|
4147
|
+
// 会话列表顶部:过滤条 + 标题/计数;详情保持右侧第二列。
|
|
3669
4148
|
const projOpts = projects.map(p =>
|
|
3670
4149
|
`<option value="${esc(p.encoded)}"${p.encoded === sessSel.project ? ' selected' : ''}>${esc(p.label)} (${p.count})</option>`
|
|
3671
4150
|
).join('');
|
|
@@ -3680,7 +4159,7 @@ function renderSession(data) {
|
|
|
3680
4159
|
|
|
3681
4160
|
const filterLabelKey = sessShowAll ? 'sessions.filter.all' : 'sessions.filter.valid';
|
|
3682
4161
|
const filterTitleKey = sessShowAll ? 'sessions.filter.allTitle' : 'sessions.filter.validTitle';
|
|
3683
|
-
|
|
4162
|
+
const contextHtml = '<div class="sess-filter">' +
|
|
3684
4163
|
'<div class="sess-select-row">' +
|
|
3685
4164
|
`<select id="sess-baseagent" title="Base Agent">${baseagentOpts}</select>` +
|
|
3686
4165
|
`<select id="sess-project" title="${t('sessions.header.project')}">${projOpts}</select>` +
|
|
@@ -3693,8 +4172,9 @@ function renderSession(data) {
|
|
|
3693
4172
|
`<input id="sess-filter-toggle" type="checkbox"${sessShowAll ? ' checked' : ''}>` +
|
|
3694
4173
|
'<span class="sess-switch-track" aria-hidden="true"><span class="sess-switch-thumb"></span></span>' +
|
|
3695
4174
|
'</label>' +
|
|
3696
|
-
'</div></div>'
|
|
3697
|
-
|
|
4175
|
+
'</div></div>';
|
|
4176
|
+
|
|
4177
|
+
let listHtml = contextHtml + '<div class="sess-items">';
|
|
3698
4178
|
|
|
3699
4179
|
if (!filtered.length) {
|
|
3700
4180
|
const emptyText = data.noConfiguredBaseagent
|
|
@@ -3719,47 +4199,72 @@ function renderSession(data) {
|
|
|
3719
4199
|
}
|
|
3720
4200
|
listHtml += '</div>';
|
|
3721
4201
|
$('#sess-list').innerHTML = listHtml;
|
|
4202
|
+
bindSessionListScroll();
|
|
4203
|
+
restoreSessionListScroll();
|
|
4204
|
+
window.requestAnimationFrame?.(restoreSessionListScroll);
|
|
3722
4205
|
|
|
3723
4206
|
// 绑定交互
|
|
3724
4207
|
const baseagentSel = $('#sess-baseagent');
|
|
3725
4208
|
if (baseagentSel) baseagentSel.onchange = () => {
|
|
3726
4209
|
console.log('[ecweb] Baseagent changed to:', baseagentSel.value);
|
|
3727
4210
|
sessSel = { sessionId: null, project: null, baseagent: baseagentSel.value, turnBefore: null };
|
|
4211
|
+
detailViewState.session = false;
|
|
4212
|
+
saveSessionUiState();
|
|
3728
4213
|
console.log('[ecweb] Subscribing to session with baseagent:', sessSel.baseagent);
|
|
3729
4214
|
subscribe('session', { baseagent: sessSel.baseagent });
|
|
3730
4215
|
};
|
|
3731
4216
|
const projSel = $('#sess-project');
|
|
3732
4217
|
if (projSel) projSel.onchange = () => {
|
|
3733
4218
|
sessSel = { sessionId: null, project: projSel.value, baseagent: sessSel.baseagent, turnBefore: null };
|
|
4219
|
+
detailViewState.session = false;
|
|
4220
|
+
saveSessionUiState();
|
|
3734
4221
|
subscribe('session', { project: sessSel.project, baseagent: sessSel.baseagent });
|
|
3735
4222
|
};
|
|
3736
4223
|
const filterToggle = $('#sess-filter-toggle');
|
|
3737
|
-
if (filterToggle) filterToggle.onchange = () => { sessShowAll = filterToggle.checked; renderSession(state.session); };
|
|
4224
|
+
if (filterToggle) filterToggle.onchange = () => { sessShowAll = filterToggle.checked; saveSessionUiState(); renderSession(state.session); };
|
|
3738
4225
|
const sourceTypeSelect = $('#sess-source-type');
|
|
3739
4226
|
if (sourceTypeSelect) sourceTypeSelect.onchange = () => {
|
|
3740
4227
|
sessSourceFilter = sourceTypeSelect.value;
|
|
4228
|
+
saveSessionUiState();
|
|
3741
4229
|
renderSession(state.session);
|
|
3742
4230
|
};
|
|
3743
4231
|
$('#sess-list').querySelectorAll('.list-item').forEach(item => {
|
|
3744
|
-
item.onclick = () => {
|
|
4232
|
+
item.onclick = () => {
|
|
4233
|
+
sessSel = { sessionId: item.dataset.sid, project: sessSel.project, baseagent: sessSel.baseagent, turnBefore: null };
|
|
4234
|
+
saveSessionUiState();
|
|
4235
|
+
if (window.matchMedia?.('(max-width: 479px)').matches) enterDetail('session');
|
|
4236
|
+
subscribe('session', sessSel);
|
|
4237
|
+
};
|
|
3745
4238
|
});
|
|
3746
4239
|
|
|
3747
4240
|
// 右:transcript 详情
|
|
3748
4241
|
const detail = $('#sess-detail');
|
|
3749
|
-
if (!sessSel.sessionId) {
|
|
3750
|
-
|
|
4242
|
+
if (!sessSel.sessionId) {
|
|
4243
|
+
detail.innerHTML = `<div class="detail-toolbar"><span class="detail-placeholder">${t('sessions.selectLog')}</span></div><div class="empty">${t('sessions.selectLog')}</div>`;
|
|
4244
|
+
bindDetailPreview(detail, 'session');
|
|
4245
|
+
return;
|
|
4246
|
+
}
|
|
4247
|
+
if (!turns.length) {
|
|
4248
|
+
detail.innerHTML = renderDetailToolbar('session') + `<div class="empty">${t('sessions.empty.noContent')}</div>`;
|
|
4249
|
+
bindDetailPreview(detail, 'session');
|
|
4250
|
+
return;
|
|
4251
|
+
}
|
|
3751
4252
|
const h = data.header || {};
|
|
3752
4253
|
const atBottom = detail.scrollHeight - detail.scrollTop - detail.clientHeight < 60;
|
|
3753
|
-
let html =
|
|
4254
|
+
let html = renderSessionDetailToolbar();
|
|
4255
|
+
html += renderSessHeader(h, data.baseagent || sessSel.baseagent);
|
|
3754
4256
|
if (data.turnPage?.hasMore && data.turnPage.nextBefore != null) {
|
|
3755
4257
|
html += `<button class="ctrl-btn sess-load-more" type="button" data-before="${data.turnPage.nextBefore}">${t('sessions.loadOlder') || '加载更早内容'}</button>`;
|
|
3756
4258
|
}
|
|
3757
4259
|
html += '<div class="turn-list">' + (sessChatMode ? renderChatView(turns) : renderFullView(turns)) + '</div>';
|
|
3758
4260
|
detail.innerHTML = html;
|
|
4261
|
+
bindDetailPreview(detail, 'session');
|
|
4262
|
+
bindSessionHeaderToggle(detail, [sessSel.project || '', data.baseagent || sessSel.baseagent || '', h.sessionId || ''].join('\u001f'));
|
|
3759
4263
|
|
|
3760
4264
|
const loadMore = detail.querySelector('.sess-load-more');
|
|
3761
4265
|
if (loadMore) loadMore.onclick = () => {
|
|
3762
4266
|
sessSel = { ...sessSel, turnBefore: Number(loadMore.dataset.before) };
|
|
4267
|
+
saveSessionUiState();
|
|
3763
4268
|
subscribe('session', sessSel);
|
|
3764
4269
|
};
|
|
3765
4270
|
|
|
@@ -3770,6 +4275,41 @@ function renderSession(data) {
|
|
|
3770
4275
|
if (atBottom) detail.scrollTop = detail.scrollHeight;
|
|
3771
4276
|
}
|
|
3772
4277
|
|
|
4278
|
+
function bindSessionHeaderToggle(container, key) {
|
|
4279
|
+
const toggleRow = container?.querySelector('.sess-header-toggle-row');
|
|
4280
|
+
const details = container?.querySelector('.sess-header-details');
|
|
4281
|
+
if (!toggleRow || !details) return;
|
|
4282
|
+
const remembered = sessHeaderExpanded.get(key);
|
|
4283
|
+
const initialExpanded = remembered == null ? true : remembered !== false;
|
|
4284
|
+
const apply = expanded => {
|
|
4285
|
+
details.hidden = !expanded;
|
|
4286
|
+
const label = t(`sessions.details.${expanded ? 'collapse' : 'expand'}`);
|
|
4287
|
+
toggleRow.setAttribute('aria-expanded', String(expanded));
|
|
4288
|
+
toggleRow.setAttribute('aria-label', label);
|
|
4289
|
+
toggleRow.title = label;
|
|
4290
|
+
details.setAttribute('aria-expanded', String(expanded));
|
|
4291
|
+
details.setAttribute('aria-label', label);
|
|
4292
|
+
const icon = toggleRow.querySelector('.sess-header-toggle-icon');
|
|
4293
|
+
if (icon) icon.textContent = expanded ? '▾' : '▸';
|
|
4294
|
+
};
|
|
4295
|
+
const toggle = event => {
|
|
4296
|
+
event.preventDefault();
|
|
4297
|
+
event.stopPropagation();
|
|
4298
|
+
const expanded = details.hidden;
|
|
4299
|
+
sessHeaderExpanded.set(key, expanded);
|
|
4300
|
+
apply(expanded);
|
|
4301
|
+
};
|
|
4302
|
+
toggleRow.onclick = toggle;
|
|
4303
|
+
details.onclick = toggle;
|
|
4304
|
+
toggleRow.onkeydown = event => {
|
|
4305
|
+
if (event.key === 'Enter' || event.key === ' ') toggle(event);
|
|
4306
|
+
};
|
|
4307
|
+
details.onkeydown = event => {
|
|
4308
|
+
if (event.key === 'Enter' || event.key === ' ') toggle(event);
|
|
4309
|
+
};
|
|
4310
|
+
apply(initialExpanded);
|
|
4311
|
+
}
|
|
4312
|
+
|
|
3773
4313
|
// 完整视图:所有轮次按 4 类渲染
|
|
3774
4314
|
function renderFullView(turns) {
|
|
3775
4315
|
let html = '';
|
|
@@ -3899,24 +4439,23 @@ function renderSessHeader(h, baseagent) {
|
|
|
3899
4439
|
const dot = h.online ? `<span class="dot on"></span>${t('common.online')}` : `<span class="dot idle"></span>${t('common.offline')}`;
|
|
3900
4440
|
bind = `<span class="sh-stat">🔗 ${esc(h.boundChannel || '')} · ${esc(shortAid(h.boundSelf || ''))} ${dot}</span>`;
|
|
3901
4441
|
}
|
|
3902
|
-
|
|
3903
|
-
|
|
4442
|
+
const detailsLabel = t('sessions.details.collapse');
|
|
4443
|
+
return '<div class="sess-header sess-header-collapsible">' +
|
|
4444
|
+
`<div class="sess-header-toggle-row" role="button" tabindex="0" aria-expanded="true" aria-label="${esc(detailsLabel)}" title="${esc(detailsLabel)}">` +
|
|
3904
4445
|
`<div class="sh-title" title="${esc(title)}">${esc(title)}</div>` +
|
|
4446
|
+
'<span class="sess-header-toggle" aria-hidden="true"><span class="sess-header-toggle-icon">▾</span></span>' +
|
|
3905
4447
|
'</div>' +
|
|
3906
|
-
'<div class="
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
`<button class="view-toggle${sessChatMode ? ' active' : ''}" id="chat-toggle">` +
|
|
3918
|
-
`${sessChatMode ? t('sessions.chatView') : t('sessions.fullView')}</button>` +
|
|
3919
|
-
`<span class="toolbar-hint">${sessChatMode ? t('sessions.chatHint') : t('sessions.fullHint')}</span>` +
|
|
4448
|
+
'<div class="sess-header-details" role="button" tabindex="0" aria-expanded="true" aria-label="' + esc(detailsLabel) + '">' +
|
|
4449
|
+
'<div class="sh-stats">' +
|
|
4450
|
+
`<span class="sh-stat" title="${tf('sessions.messageCountTitle', { user: h.userMsgs || 0, total: h.totalMsgs || 0 })}">💬 ${h.userMsgs || 0}/${h.totalMsgs || 0} ${t('sessions.msgUnit')}</span>` +
|
|
4451
|
+
(h.model ? `<span class="sh-stat">🤖 ${esc(h.model)}</span>` : '') +
|
|
4452
|
+
tok + ctx + cost +
|
|
4453
|
+
(h.gitBranch ? `<span class="sh-stat">🌿 ${esc(h.gitBranch)}</span>` : '') +
|
|
4454
|
+
(h.version ? `<span class="sh-stat">cc ${esc(h.version)}</span>` : '') +
|
|
4455
|
+
bind +
|
|
4456
|
+
'</div>' +
|
|
4457
|
+
'<div class="sh-footer-row">' +
|
|
4458
|
+
renderCatBar(h.counts) +
|
|
3920
4459
|
'</div>' +
|
|
3921
4460
|
'</div>' +
|
|
3922
4461
|
'</div>';
|
|
@@ -5346,7 +5885,7 @@ function openAgentConfigDrawer(aid, data) {
|
|
|
5346
5885
|
const fullaccessState = enabled => currentLang === 'zh-CN'
|
|
5347
5886
|
? (enabled ? '开启' : '关闭')
|
|
5348
5887
|
: (enabled ? 'Enabled' : 'Disabled');
|
|
5349
|
-
const fullaccessSection =
|
|
5888
|
+
const fullaccessSection =
|
|
5350
5889
|
`<section class="ag-edit-section">` +
|
|
5351
5890
|
`<div class="ag-edit-fullaccess-row">` +
|
|
5352
5891
|
`<span class="ag-edit-field-label">Owner Fullaccess<span class="basic-settings-tip" tabindex="0" role="img" aria-label="${esc(fullaccessHelpLabel)}" data-tooltip="${esc(fullaccessHelp)}">?</span></span>` +
|
|
@@ -5356,7 +5895,7 @@ function openAgentConfigDrawer(aid, data) {
|
|
|
5356
5895
|
`<span class="sess-switch-track" aria-hidden="true"><span class="sess-switch-thumb"></span></span>` +
|
|
5357
5896
|
`</label>` +
|
|
5358
5897
|
`</div>` +
|
|
5359
|
-
`</section
|
|
5898
|
+
`</section>`;
|
|
5360
5899
|
|
|
5361
5900
|
const html = `<div class="ag-edit-backdrop" id="ag-edit-backdrop">` +
|
|
5362
5901
|
`<aside class="ag-edit-drawer" role="dialog" aria-modal="true" aria-labelledby="ag-edit-title">` +
|
|
@@ -5439,7 +5978,6 @@ function openAgentConfigDrawer(aid, data) {
|
|
|
5439
5978
|
document.addEventListener('keydown', ctx.onKeydown);
|
|
5440
5979
|
syncAgentBaseagentFields(ctx);
|
|
5441
5980
|
syncAgentBaseagentOptions(ctx);
|
|
5442
|
-
setTimeout(() => backdrop.querySelector('#ag-edit-owners')?.focus(), 0);
|
|
5443
5981
|
}
|
|
5444
5982
|
|
|
5445
5983
|
async function agentOpEdit(aid) {
|
|
@@ -5789,8 +6327,7 @@ const TRIGGER_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
|
5789
6327
|
const TRIGGER_PERMISSION_MODES = ['readonly', 'auto', 'request', 'bypass'];
|
|
5790
6328
|
function triggerPermissionModes(currentMode = '') {
|
|
5791
6329
|
const modes = [...TRIGGER_PERMISSION_MODES];
|
|
5792
|
-
if (
|
|
5793
|
-
&& state.triggers?.canApprovePersistentFullAccess === true)
|
|
6330
|
+
if (state.triggers?.canApprovePersistentFullAccess === true
|
|
5794
6331
|
|| currentMode === 'fullaccess') modes.push('fullaccess');
|
|
5795
6332
|
return modes;
|
|
5796
6333
|
}
|
|
@@ -6332,6 +6869,18 @@ function triggerAgentInfo(agentAid) {
|
|
|
6332
6869
|
return (state.triggers?.agents || []).find(item => item.aid === agentAid || item.value === agentAid) || null;
|
|
6333
6870
|
}
|
|
6334
6871
|
|
|
6872
|
+
function isDaemonTriggerAgent(aid, label, channels) {
|
|
6873
|
+
const normalizedAid = String(aid || '').trim().toLowerCase();
|
|
6874
|
+
const normalizedLabel = String(label || '').trim().toLowerCase();
|
|
6875
|
+
return normalizedAid === 'daemon'
|
|
6876
|
+
|| normalizedAid === '__daemon__'
|
|
6877
|
+
|| normalizedAid.endsWith('#daemon')
|
|
6878
|
+
|| normalizedAid.endsWith('.daemon')
|
|
6879
|
+
|| normalizedLabel === 'daemon'
|
|
6880
|
+
|| (Array.isArray(channels) && channels.some(channel => String(channel).trim().toLowerCase() === 'daemon'))
|
|
6881
|
+
|| /\(daemon\)$/i.test(normalizedLabel);
|
|
6882
|
+
}
|
|
6883
|
+
|
|
6335
6884
|
function triggerChannelOptions(agentAid, current) {
|
|
6336
6885
|
const channels = [...(triggerAgentInfo(agentAid)?.channels || [])].map(String).filter(Boolean);
|
|
6337
6886
|
if (current && !channels.includes(current)) channels.push(current);
|
|
@@ -6468,31 +7017,22 @@ function triggerRunJson(value) {
|
|
|
6468
7017
|
try { return JSON.stringify(value, null, 2); } catch { return String(value); }
|
|
6469
7018
|
}
|
|
6470
7019
|
|
|
6471
|
-
function
|
|
6472
|
-
return String(
|
|
6473
|
-
run?.error?.message
|
|
6474
|
-
|| run?.reply?.textPreview
|
|
6475
|
-
|| run?.script?.result?.text
|
|
6476
|
-
|| run?.script?.stderrPreview
|
|
6477
|
-
|| run?.script?.stdoutPreview
|
|
6478
|
-
|| run?.reason
|
|
6479
|
-
|| '',
|
|
6480
|
-
);
|
|
6481
|
-
}
|
|
6482
|
-
|
|
6483
|
-
function triggerHistoryListHtml(runs, selectedRunId) {
|
|
7020
|
+
function triggerHistoryListHtml(runs, selectedRunId, inlineDetails, renderDetail) {
|
|
6484
7021
|
if (!runs.length) return `<div class="trig-history-empty">${t('triggers.history.empty')}</div>`;
|
|
6485
7022
|
return runs.map(run => {
|
|
6486
7023
|
const runId = String(run.runId || '');
|
|
7024
|
+
const selected = runId === selectedRunId;
|
|
7025
|
+
const expanded = inlineDetails && selected;
|
|
6487
7026
|
const duration = Number(run.finishedAt || 0) - Number(run.startedAt || 0);
|
|
6488
7027
|
const source = run.source?.eventName || run.source?.type || '—';
|
|
6489
7028
|
const execution = run.processing?.mode || run.definition?.name || '—';
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
`<div class="trig-history-item-head"><span>${run.startedAt ? fmtTime(run.startedAt) : '—'}</span>${triggerRunStatusBadge(run.status)}
|
|
7029
|
+
return `<article class="trig-history-entry${expanded ? ' expanded' : ''}">` +
|
|
7030
|
+
`<button type="button" class="trig-history-item${selected ? ' selected' : ''}" data-runid="${esc(runId)}" aria-expanded="${expanded}">` +
|
|
7031
|
+
`<div class="trig-history-item-head"><span>${run.startedAt ? fmtTime(run.startedAt) : '—'}</span>${triggerRunStatusBadge(run.status)}<span class="trig-history-chevron" aria-hidden="true">›</span></div>` +
|
|
6493
7032
|
`<div class="trig-history-item-meta"><span>${esc(source)}</span><span>${esc(execution)}</span><span>${esc(triggerRunDurationLabel(duration))}</span></div>` +
|
|
6494
|
-
|
|
6495
|
-
|
|
7033
|
+
`</button>` +
|
|
7034
|
+
(expanded ? `<div class="trig-history-inline-detail">${renderDetail(run)}</div>` : '') +
|
|
7035
|
+
`</article>`;
|
|
6496
7036
|
}).join('');
|
|
6497
7037
|
}
|
|
6498
7038
|
|
|
@@ -6843,29 +7383,38 @@ function triggerHistoryDetailHtml(run, definition) {
|
|
|
6843
7383
|
function renderTriggerHistoryDrawer(ctx) {
|
|
6844
7384
|
if (_triggerHistory !== ctx) return;
|
|
6845
7385
|
const list = ctx.backdrop.querySelector('#trig-history-list');
|
|
6846
|
-
const detail = ctx.backdrop.querySelector('#trig-history-detail');
|
|
6847
7386
|
const count = ctx.backdrop.querySelector('#trig-history-count');
|
|
7387
|
+
const detail = ctx.backdrop.querySelector('#trig-history-detail');
|
|
7388
|
+
const inlineDetails = ctx.layoutQuery.matches;
|
|
6848
7389
|
if (count) count.textContent = tf('triggers.history.runCount', { count: ctx.runs.length });
|
|
7390
|
+
const renderDetail = run => {
|
|
7391
|
+
const revision = String(run?.definition?.revision || '');
|
|
7392
|
+
const historicalDefinition = ctx.definitionsByRevision.get(revision)
|
|
7393
|
+
|| (revision && revision === String(ctx.currentRevision || '') ? ctx.currentDefinition : undefined);
|
|
7394
|
+
return triggerHistoryDetailHtml(run, historicalDefinition);
|
|
7395
|
+
};
|
|
6849
7396
|
if (list) {
|
|
6850
|
-
list.innerHTML = triggerHistoryListHtml(ctx.runs, ctx.selectedRunId);
|
|
7397
|
+
list.innerHTML = triggerHistoryListHtml(ctx.runs, ctx.selectedRunId, inlineDetails, renderDetail);
|
|
6851
7398
|
list.querySelectorAll('.trig-history-item').forEach(item => {
|
|
6852
7399
|
item.addEventListener('click', () => {
|
|
6853
|
-
|
|
7400
|
+
const runId = item.dataset.runid || '';
|
|
7401
|
+
ctx.selectedRunId = inlineDetails && ctx.selectedRunId === runId ? '' : runId;
|
|
6854
7402
|
renderTriggerHistoryDrawer(ctx);
|
|
6855
7403
|
});
|
|
6856
7404
|
});
|
|
6857
7405
|
}
|
|
6858
|
-
const
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
if (detail) detail.innerHTML = triggerHistoryDetailHtml(selected, historicalDefinition);
|
|
7406
|
+
const selectedRun = ctx.runs.find(run => String(run.runId || '') === ctx.selectedRunId) || null;
|
|
7407
|
+
if (detail) detail.innerHTML = !inlineDetails && selectedRun
|
|
7408
|
+
? `<div class="trig-history-detail">${renderDetail(selectedRun)}</div>`
|
|
7409
|
+
: `<div class="trig-history-detail-empty">${t('triggers.history.selectRun')}</div>`;
|
|
6863
7410
|
}
|
|
6864
7411
|
|
|
6865
7412
|
function closeTriggerHistoryDrawer() {
|
|
6866
7413
|
const ctx = _triggerHistory;
|
|
6867
7414
|
if (!ctx) return;
|
|
6868
7415
|
document.removeEventListener('keydown', ctx.onKeydown);
|
|
7416
|
+
if (ctx.layoutQuery?.removeEventListener) ctx.layoutQuery.removeEventListener('change', ctx.onLayoutChange);
|
|
7417
|
+
else if (ctx.layoutQuery?.removeListener) ctx.layoutQuery.removeListener(ctx.onLayoutChange);
|
|
6869
7418
|
try { ctx.backdrop.remove(); } catch {}
|
|
6870
7419
|
_triggerHistory = null;
|
|
6871
7420
|
}
|
|
@@ -6884,10 +7433,10 @@ async function openTriggerHistoryDrawer(id, agent) {
|
|
|
6884
7433
|
`<button class="ag-edit-close" id="trig-history-close" type="button" aria-label="${t('common.close')}">×</button>` +
|
|
6885
7434
|
`</div>` +
|
|
6886
7435
|
`<div class="trig-history-content">` +
|
|
6887
|
-
`<div class="trig-history-list-pane"><div id="trig-history-
|
|
6888
|
-
`<div id="trig-history-detail" class="trig-history-detail"><div class="trig-history-detail-empty">${t('triggers.history.selectRun')}</div></div>` +
|
|
7436
|
+
`<div class="trig-history-list-pane"><div id="trig-history-list" class="trig-history-list"><div class="trig-history-empty">${t('triggers.history.loading')}</div></div></div>` +
|
|
7437
|
+
`<div id="trig-history-detail" class="trig-history-detail-pane"><div class="trig-history-detail-empty">${t('triggers.history.selectRun')}</div></div>` +
|
|
6889
7438
|
`</div>` +
|
|
6890
|
-
`<div class="ag-edit-actions"><button class="ctrl-btn" id="trig-history-done" type="button">${t('common.close')}</button></div>` +
|
|
7439
|
+
`<div class="ag-edit-actions trig-history-actions"><span id="trig-history-count" class="trig-history-count" aria-live="polite">${t('triggers.history.loading')}</span><button class="ctrl-btn" id="trig-history-done" type="button">${t('common.close')}</button></div>` +
|
|
6891
7440
|
`</aside>` +
|
|
6892
7441
|
`</div>`;
|
|
6893
7442
|
const wrap = document.createElement('div');
|
|
@@ -6898,16 +7447,20 @@ async function openTriggerHistoryDrawer(id, agent) {
|
|
|
6898
7447
|
backdrop,
|
|
6899
7448
|
runs: [],
|
|
6900
7449
|
selectedRunId: '',
|
|
7450
|
+
layoutQuery: window.matchMedia('(orientation: portrait)'),
|
|
6901
7451
|
currentDefinition: item.definition,
|
|
6902
7452
|
currentRevision: item.revision,
|
|
6903
7453
|
definitionsByRevision: new Map(),
|
|
6904
7454
|
onKeydown: e => { if (e.key === 'Escape') closeTriggerHistoryDrawer(); },
|
|
7455
|
+
onLayoutChange: () => renderTriggerHistoryDrawer(ctx),
|
|
6905
7456
|
};
|
|
6906
7457
|
_triggerHistory = ctx;
|
|
6907
7458
|
backdrop.addEventListener('click', e => { if (e.target === backdrop) closeTriggerHistoryDrawer(); });
|
|
6908
7459
|
backdrop.querySelector('#trig-history-close').addEventListener('click', closeTriggerHistoryDrawer);
|
|
6909
7460
|
backdrop.querySelector('#trig-history-done').addEventListener('click', closeTriggerHistoryDrawer);
|
|
6910
7461
|
document.addEventListener('keydown', ctx.onKeydown);
|
|
7462
|
+
if (ctx.layoutQuery.addEventListener) ctx.layoutQuery.addEventListener('change', ctx.onLayoutChange);
|
|
7463
|
+
else if (ctx.layoutQuery.addListener) ctx.layoutQuery.addListener(ctx.onLayoutChange);
|
|
6911
7464
|
|
|
6912
7465
|
try {
|
|
6913
7466
|
const response = await ipcSend({ type: 'trigger.history', agentAid: agent, triggerId: id, limit: 500 }, 10000);
|
|
@@ -7760,7 +8313,6 @@ function openTriggerEditDrawer(id, agent) {
|
|
|
7760
8313
|
if (_triggerEdit === ctx) toast(e.message || String(e), true);
|
|
7761
8314
|
});
|
|
7762
8315
|
if (triggerUsesModelOptions(ctx)) syncTriggerOptionFields(ctx, original);
|
|
7763
|
-
setTimeout(() => backdrop.querySelector('#trig-edit-name')?.focus(), 0);
|
|
7764
8316
|
}
|
|
7765
8317
|
|
|
7766
8318
|
function renderTriggers(data) {
|
|
@@ -7771,7 +8323,7 @@ function renderTriggers(data) {
|
|
|
7771
8323
|
|
|
7772
8324
|
// 左列:agent 列表(仿 msg list-item 风格)
|
|
7773
8325
|
let aHtml = '<div class="col-title trig-title">' +
|
|
7774
|
-
'
|
|
8326
|
+
`<span>${t('triggers.th.agent')}</span>` +
|
|
7775
8327
|
`<button id="trig-agents-toggle" class="trig-collapse-btn" title="${trigAgentsCollapsed ? t('triggers.collapse.expand') : t('triggers.collapse.collapse')}">${trigAgentsCollapsed ? '›' : '‹'}</button>` +
|
|
7776
8328
|
'</div>';
|
|
7777
8329
|
if (!trigAgentsCollapsed) {
|
|
@@ -7779,9 +8331,12 @@ function renderTriggers(data) {
|
|
|
7779
8331
|
for (const ag of agents) {
|
|
7780
8332
|
const aid = ag.aid || ag.value || '';
|
|
7781
8333
|
const sel = aid === selAid ? ' sel' : '';
|
|
8334
|
+
const isDaemonAgent = isDaemonTriggerAgent(aid, ag.label, ag.channels);
|
|
8335
|
+
const label = isDaemonAgent ? t('triggers.agent.daemon') : ag.label;
|
|
7782
8336
|
aHtml += `<div class="list-item${sel}" data-aid="${esc(aid)}">` +
|
|
7783
|
-
`<div class="name">${esc(
|
|
7784
|
-
`<div class="sub">${esc(aid)}</div
|
|
8337
|
+
`<div class="name">${esc(label)}</div>` +
|
|
8338
|
+
(isDaemonAgent ? '' : `<div class="sub">${esc(aid)}</div>`) +
|
|
8339
|
+
'</div>';
|
|
7785
8340
|
}
|
|
7786
8341
|
}
|
|
7787
8342
|
const agentCol = $('#trig-agents');
|
|
@@ -7799,11 +8354,13 @@ function renderTriggers(data) {
|
|
|
7799
8354
|
if (trigAgentsCollapsed) return;
|
|
7800
8355
|
if (!trigSel.agent) return;
|
|
7801
8356
|
trigSel.agent = null;
|
|
8357
|
+
saveSessionUiState();
|
|
7802
8358
|
subscribe('triggers', {});
|
|
7803
8359
|
};
|
|
7804
8360
|
agentCol.querySelectorAll('.list-item').forEach(item => {
|
|
7805
8361
|
item.onclick = () => {
|
|
7806
8362
|
trigSel.agent = item.dataset.aid;
|
|
8363
|
+
saveSessionUiState();
|
|
7807
8364
|
subscribe('triggers', { agent: trigSel.agent });
|
|
7808
8365
|
};
|
|
7809
8366
|
});
|
|
@@ -7811,14 +8368,14 @@ function renderTriggers(data) {
|
|
|
7811
8368
|
// 右列:table,每字段一列
|
|
7812
8369
|
const el = $('#trig-table');
|
|
7813
8370
|
if (!triggers.length) {
|
|
7814
|
-
el.innerHTML = `<div class="empty" style="padding:16px">${selAid ? t('triggers.empty.noAgentTriggers') : t('triggers.empty.noTriggers')}</div>`;
|
|
8371
|
+
el.innerHTML = `<div class="trig-table-empty empty" style="padding:16px">${selAid ? t('triggers.empty.noAgentTriggers') : t('triggers.empty.noTriggers')}</div>`;
|
|
7815
8372
|
return;
|
|
7816
8373
|
}
|
|
7817
8374
|
|
|
7818
8375
|
let html = '<table><thead><tr>' +
|
|
7819
|
-
|
|
8376
|
+
`<th class="trig-agent-col">${t('triggers.th.agent')}</th><th>${t('triggers.th.status')}</th><th class="trig-subscription-col">${t('triggers.th.subscription')}</th><th>${t('triggers.th.name')}</th><th class="trig-id-col">${t('triggers.th.id')}</th><th>${t('triggers.th.type')}</th><th>${t('triggers.th.expression')}</th>` +
|
|
7820
8377
|
`<th>${t('triggers.th.lastFire')}</th><th>${t('triggers.th.nextFire')}</th><th>${t('triggers.th.fireCount')}</th><th>${t('triggers.th.failCount')}</th><th>${t('triggers.th.lastResult')}</th><th>${t('triggers.field.executionType')}</th>` +
|
|
7821
|
-
`<th>${t('triggers.th.targetChannel')}</th><th>${t('triggers.field.channelId')}</th><th>${t('triggers.th.channelType')}</th>` +
|
|
8378
|
+
`<th class="trig-target-channel-col">${t('triggers.th.targetChannel')}</th><th class="trig-channel-id-col">${t('triggers.field.channelId')}</th><th class="trig-channel-type-col">${t('triggers.th.channelType')}</th>` +
|
|
7822
8379
|
`<th>${t('triggers.th.createdBy')}</th><th>${t('triggers.th.createdChannel')}</th><th>${t('triggers.th.createdAt')}</th><th class="trig-ops-col">${t('triggers.th.operations')}</th>` +
|
|
7823
8380
|
'</tr></thead><tbody>';
|
|
7824
8381
|
for (const trigger of triggers) {
|
|
@@ -7827,42 +8384,62 @@ function renderTriggers(data) {
|
|
|
7827
8384
|
const lastFireTime = trigger.lastFiredAt || trigger.lastScheduledAt;
|
|
7828
8385
|
const lastResult = trigger.lastResult || (trigger.lastScheduledAt ? t('triggers.lastUnrecorded') : '');
|
|
7829
8386
|
const agentAid = trigger.agentAid || trigger.schedulerAid || selAid || '';
|
|
7830
|
-
const
|
|
8387
|
+
const agentInfo = triggerAgentInfo(agentAid);
|
|
8388
|
+
const agentLabel = isDaemonTriggerAgent(agentAid, trigger.agentLabel, agentInfo?.channels)
|
|
8389
|
+
? t('triggers.agent.daemon')
|
|
8390
|
+
: (trigger.agentLabel || (agentAid ? shortAid(agentAid) : ''));
|
|
7831
8391
|
const sourceType = triggerSourceType(trigger);
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
`<td>${
|
|
7835
|
-
`<td
|
|
7836
|
-
`<td>${
|
|
7837
|
-
`<td>${esc(trigger.
|
|
7838
|
-
`<td>${esc(
|
|
7839
|
-
`<td>${esc(
|
|
7840
|
-
`<td
|
|
7841
|
-
`<td>${
|
|
7842
|
-
`<td>${trigger.
|
|
7843
|
-
`<td
|
|
7844
|
-
`<td>${
|
|
7845
|
-
`<td
|
|
7846
|
-
`<td>${esc(trigger.
|
|
7847
|
-
`<td>${esc(trigger.
|
|
7848
|
-
`<td>${esc(trigger.
|
|
7849
|
-
`<td>${esc(trigger.
|
|
7850
|
-
`<td>${esc(trigger.
|
|
7851
|
-
`<td>${
|
|
7852
|
-
`<td
|
|
8392
|
+
const mobileLabel = key => ` data-mobile-label="${esc(t(key))}"`;
|
|
8393
|
+
html += `<tr class="trig-row${active ? '' : ' trig-disabled'}" tabindex="0" aria-expanded="false">` +
|
|
8394
|
+
`<td class="trig-agent-col"${mobileLabel('triggers.th.agent')} title="${esc(agentAid)}">${esc(agentLabel)}</td>` +
|
|
8395
|
+
`<td${mobileLabel('triggers.th.status')}>${trigStatusBadge(status)}</td>` +
|
|
8396
|
+
`<td class="trig-subscription-col"${mobileLabel('triggers.th.subscription')}>${trigSubscriptionBadge(trigger.subscription, sourceType)}</td>` +
|
|
8397
|
+
`<td${mobileLabel('triggers.th.name')}>${esc(trigger.name ?? trigger.label ?? '')}</td>` +
|
|
8398
|
+
`<td class="trig-id-col"${mobileLabel('triggers.th.id')}>${esc(trigger.id ?? trigger.value ?? '')}</td>` +
|
|
8399
|
+
`<td${mobileLabel('triggers.th.type')}>${esc(triggerScheduleLabel(sourceType || trigger.scheduleType || ''))}</td>` +
|
|
8400
|
+
`<td${mobileLabel('triggers.th.expression')}>${esc(triggerScheduleValueLabel(trigger.scheduleType, trigger.scheduleValue))}</td>` +
|
|
8401
|
+
`<td${mobileLabel('triggers.th.lastFire')}>${lastFireTime ? fmtTime(lastFireTime) : '—'}</td>` +
|
|
8402
|
+
`<td${mobileLabel('triggers.th.nextFire')}>${trigger.nextFireAt ? fmtTime(trigger.nextFireAt) : '—'}</td>` +
|
|
8403
|
+
`<td${mobileLabel('triggers.th.fireCount')}>${trigger.fireCount ?? 0}</td>` +
|
|
8404
|
+
`<td${mobileLabel('triggers.th.failCount')}>${trigger.failCount ? `<span style="color:var(--red)">${trigger.failCount}</span>` : '0'}</td>` +
|
|
8405
|
+
`<td${mobileLabel('triggers.th.lastResult')}>${lastResult ? esc(lastResult) : '—'}</td>` +
|
|
8406
|
+
`<td${mobileLabel('triggers.field.executionType')}>${esc(triggerExecutionTypeLabel(trigger.definition?.execution?.type ?? trigger.executionType ?? trigger.execution?.type))}</td>` +
|
|
8407
|
+
`<td class="trig-target-channel-col"${mobileLabel('triggers.th.targetChannel')}>${esc(trigger.targetChannel ?? '')}</td>` +
|
|
8408
|
+
`<td class="trig-channel-id-col"${mobileLabel('triggers.field.channelId')}>${esc(trigger.targetChannelId ?? '')}</td>` +
|
|
8409
|
+
`<td class="trig-channel-type-col"${mobileLabel('triggers.th.channelType')}>${esc(trigger.targetChannelType ?? '')}</td>` +
|
|
8410
|
+
`<td${mobileLabel('triggers.th.createdBy')}>${esc(trigger.createdByPeerId ?? '')}</td>` +
|
|
8411
|
+
`<td${mobileLabel('triggers.th.createdChannel')}>${esc(trigger.createdByChannel ?? '')}</td>` +
|
|
8412
|
+
`<td${mobileLabel('triggers.th.createdAt')}>${trigger.createdAt ? fmtTime(trigger.createdAt) : '—'}</td>` +
|
|
8413
|
+
`<td class="trig-ops-col"${mobileLabel('triggers.th.operations')}>${triggerOpsHtml(trigger)}</td>` +
|
|
7853
8414
|
'</tr>';
|
|
7854
8415
|
}
|
|
7855
8416
|
html += '</tbody></table>';
|
|
7856
|
-
el.innerHTML = html
|
|
8417
|
+
el.innerHTML = `<div class="trig-table-wide">${html}</div>`;
|
|
7857
8418
|
ensureOpsOutsideClose();
|
|
8419
|
+
bindTriggerOps(el);
|
|
8420
|
+
el.querySelectorAll('.trig-row').forEach(row => {
|
|
8421
|
+
const toggle = event => {
|
|
8422
|
+
if (event.target.closest('button, a, input, select, textarea, summary, details')) return;
|
|
8423
|
+
const expanded = row.classList.toggle('trig-expanded');
|
|
8424
|
+
row.setAttribute('aria-expanded', String(expanded));
|
|
8425
|
+
};
|
|
8426
|
+
row.addEventListener('click', toggle);
|
|
8427
|
+
row.addEventListener('keydown', event => {
|
|
8428
|
+
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
8429
|
+
event.preventDefault();
|
|
8430
|
+
toggle(event);
|
|
8431
|
+
});
|
|
8432
|
+
});
|
|
8433
|
+
}
|
|
7858
8434
|
|
|
7859
|
-
|
|
8435
|
+
function bindTriggerOps(container) {
|
|
8436
|
+
container.querySelectorAll('.trig-ops button[data-trig-op]').forEach(btn => {
|
|
7860
8437
|
btn.addEventListener('click', async (e) => {
|
|
7861
8438
|
const op = btn.dataset.trigOp;
|
|
7862
8439
|
if (op === 'more') {
|
|
7863
8440
|
const more = btn.closest('.ops-more');
|
|
7864
8441
|
const wasOpen = more?.classList.contains('open');
|
|
7865
|
-
document.querySelectorAll('.ops-more.open').forEach(
|
|
8442
|
+
document.querySelectorAll('.ops-more.open').forEach(item => item.classList.remove('open'));
|
|
7866
8443
|
if (more && !wasOpen) more.classList.add('open');
|
|
7867
8444
|
e.stopPropagation();
|
|
7868
8445
|
return;
|
|
@@ -7883,8 +8460,9 @@ function startApp() {
|
|
|
7883
8460
|
initTabs();
|
|
7884
8461
|
initRolesTab();
|
|
7885
8462
|
initRoleDefinitionsTab();
|
|
8463
|
+
initHistory();
|
|
7886
8464
|
// 恢复保存的 tab 视图
|
|
7887
|
-
switchView(currentView);
|
|
8465
|
+
switchView(currentView, { history: false });
|
|
7888
8466
|
connect();
|
|
7889
8467
|
$('#logout-btn').onclick = () => {
|
|
7890
8468
|
localStorage.removeItem(TOKEN_KEY);
|
|
@@ -7912,13 +8490,17 @@ function resolveTheme(preference) {
|
|
|
7912
8490
|
|
|
7913
8491
|
function updateThemeButton() {
|
|
7914
8492
|
const btn = $('#theme-btn');
|
|
7915
|
-
if (!btn) return;
|
|
7916
8493
|
const icons = { light: '☀️', dark: '🌙', system: '🖥️' };
|
|
7917
|
-
const
|
|
7918
|
-
|
|
7919
|
-
btn
|
|
7920
|
-
|
|
7921
|
-
|
|
8494
|
+
const modeLabel = t('theme.' + themePreference);
|
|
8495
|
+
const label = tf('theme.buttonTitle', { mode: modeLabel });
|
|
8496
|
+
if (btn) {
|
|
8497
|
+
btn.textContent = icons[themePreference] || icons.light;
|
|
8498
|
+
btn.title = label;
|
|
8499
|
+
btn.setAttribute('aria-label', label);
|
|
8500
|
+
btn.dataset.themeMode = themePreference;
|
|
8501
|
+
}
|
|
8502
|
+
const mobileLabel = $('[data-mobile-theme-label]');
|
|
8503
|
+
if (mobileLabel) mobileLabel.textContent = tf('theme.drawerLabel', { mode: modeLabel });
|
|
7922
8504
|
}
|
|
7923
8505
|
|
|
7924
8506
|
function disposeWindowChart(key) {
|
|
@@ -10039,6 +10621,7 @@ const COMMAND_PERMISSION_CATEGORY_ORDER = ['read', 'diagnose', 'write-own', 'wri
|
|
|
10039
10621
|
const COMMAND_PERMISSION_BOOLEAN_CONSTRAINTS = [
|
|
10040
10622
|
'ownPeerOnly',
|
|
10041
10623
|
'ownAgentOnly',
|
|
10624
|
+
'currentRelationOnly',
|
|
10042
10625
|
'privateOnly',
|
|
10043
10626
|
'groupOnly',
|
|
10044
10627
|
'requireDaemonOwner',
|
|
@@ -10106,6 +10689,7 @@ function commandConstraintLabel(key) {
|
|
|
10106
10689
|
const labels = {
|
|
10107
10690
|
ownPeerOnly: currentLang === 'zh-CN' ? '仅本人 Peer' : 'Own peer only',
|
|
10108
10691
|
ownAgentOnly: currentLang === 'zh-CN' ? '仅当前 Agent' : 'Own agent only',
|
|
10692
|
+
currentRelationOnly: currentLang === 'zh-CN' ? '仅当前关系' : 'Current relation only',
|
|
10109
10693
|
privateOnly: currentLang === 'zh-CN' ? '仅私聊' : 'Private only',
|
|
10110
10694
|
groupOnly: currentLang === 'zh-CN' ? '仅群聊' : 'Group only',
|
|
10111
10695
|
requireDaemonOwner: currentLang === 'zh-CN' ? '需要 daemon owner' : 'Require daemon owner',
|