@co0ontty/wand 1.36.0 → 1.39.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/claude-sdk-runner.d.ts +7 -0
- package/dist/claude-sdk-runner.js +3 -0
- package/dist/git-quick-commit.js +5 -5
- package/dist/language-prompt.d.ts +17 -0
- package/dist/language-prompt.js +68 -0
- package/dist/process-manager.d.ts +16 -0
- package/dist/process-manager.js +187 -5
- package/dist/prompt-optimizer.js +3 -3
- package/dist/pwa.js +36 -5
- package/dist/server-session-routes.js +110 -0
- package/dist/server.js +3 -0
- package/dist/structured-session-manager.d.ts +48 -0
- package/dist/structured-session-manager.js +297 -33
- package/dist/types.d.ts +2 -0
- package/dist/web-ui/content/scripts.js +430 -82
- package/dist/web-ui/content/styles.css +328 -102
- package/dist/web-ui/styles.js +18 -4
- package/package.json +1 -1
|
@@ -370,6 +370,10 @@
|
|
|
370
370
|
sessionsManageMode: false,
|
|
371
371
|
selectedSessionIds: {},
|
|
372
372
|
selectedClaudeHistoryIds: {},
|
|
373
|
+
codexHistory: [],
|
|
374
|
+
codexHistoryLoaded: false,
|
|
375
|
+
codexHistoryExpandedDirs: {},
|
|
376
|
+
selectedCodexHistoryIds: {},
|
|
373
377
|
askUserSelections: {}, // { toolUseId: { 0: [optIdx...], submitted: false } }
|
|
374
378
|
queueEpoch: 0, // Monotonic counter for queue state freshness
|
|
375
379
|
pendingAttachments: [], // [{ file, previewUrl, name, size }]
|
|
@@ -384,6 +388,81 @@
|
|
|
384
388
|
})()
|
|
385
389
|
};
|
|
386
390
|
|
|
391
|
+
// ── 前端 i18n(最小化)──
|
|
392
|
+
// 后端 config.language 是给 Claude 用的"回答语言"偏好("中文" / "English" / 任意字符串),
|
|
393
|
+
// 之前 frontend 完全没收 → UI label 一直 hardcoded 中文 + 个别英文("SUBAGENT" 那个 tag)。
|
|
394
|
+
// 用户设的是中文时,"SUBAGENT" 这类英文残留就和"配置语言不一致"。
|
|
395
|
+
//
|
|
396
|
+
// 设计取舍:
|
|
397
|
+
// - 只维护两套:中文(默认) + 英文。其它取值("日本語"、"Français"等)回退到英文,
|
|
398
|
+
// 因为 Claude 会按用户语言回答,UI 至少不卡在中文上让英语圈用户看不懂。
|
|
399
|
+
// - 不引入 i18n 库,几十个 key 用平铺对象,t(key, params) 是个十行 helper。
|
|
400
|
+
// - params 支持 "{name}" 占位符替换,避免在调用点拼字符串。
|
|
401
|
+
// - 缺 key 时回退到中文表,再没有就返回 key 本身(debug 友好)。
|
|
402
|
+
var I18N_DEFAULT_LANG = "中文";
|
|
403
|
+
var I18N = {
|
|
404
|
+
"中文": {
|
|
405
|
+
"subagent.tag": "子代理",
|
|
406
|
+
"subagent.handoff": "{parent} 让 {sub} 帮忙",
|
|
407
|
+
"subagent.handoff.with_desc": "{parent} 让 {sub} 帮忙:",
|
|
408
|
+
"subagent.continued": "继续输出",
|
|
409
|
+
"subagent.task.done": "任务完成",
|
|
410
|
+
"subagent.task.failed": "任务失败",
|
|
411
|
+
"subagent.running": "运行中",
|
|
412
|
+
"subagent.no_output": "(无输出)",
|
|
413
|
+
"subagent.helper_fallback_prefix": "协作猫·",
|
|
414
|
+
"subagent.title_aria": "点击展开 / 收起子代理输出",
|
|
415
|
+
"subagent.tag_title": "子代理 / subagent",
|
|
416
|
+
"ui.expand": "展开",
|
|
417
|
+
"ui.collapse": "收起",
|
|
418
|
+
"ui.expand_panel_aria": "展开子代理输出",
|
|
419
|
+
"ui.collapse_panel_aria": "收起子代理输出"
|
|
420
|
+
},
|
|
421
|
+
"English": {
|
|
422
|
+
"subagent.tag": "Subagent",
|
|
423
|
+
"subagent.handoff": "{parent} asked {sub} for help",
|
|
424
|
+
"subagent.handoff.with_desc": "{parent} asked {sub} for help with: ",
|
|
425
|
+
"subagent.continued": "continued",
|
|
426
|
+
"subagent.task.done": "Task complete",
|
|
427
|
+
"subagent.task.failed": "Task failed",
|
|
428
|
+
"subagent.running": "Running",
|
|
429
|
+
"subagent.no_output": "(no output)",
|
|
430
|
+
"subagent.helper_fallback_prefix": "Helper·",
|
|
431
|
+
"subagent.title_aria": "Click to expand / collapse subagent output",
|
|
432
|
+
"subagent.tag_title": "Subagent",
|
|
433
|
+
"ui.expand": "Expand",
|
|
434
|
+
"ui.collapse": "Collapse",
|
|
435
|
+
"ui.expand_panel_aria": "Expand subagent output",
|
|
436
|
+
"ui.collapse_panel_aria": "Collapse subagent output"
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
function getActiveLang() {
|
|
440
|
+
var raw = state.config && typeof state.config.language === "string" ? state.config.language.trim() : "";
|
|
441
|
+
if (!raw) return I18N_DEFAULT_LANG;
|
|
442
|
+
if (I18N[raw]) return raw;
|
|
443
|
+
// 模糊匹配:用户可能写 "english" / "en" / "ENG"
|
|
444
|
+
var lower = raw.toLowerCase();
|
|
445
|
+
if (lower === "english" || lower === "en" || lower.indexOf("english") === 0 || lower.indexOf("英") === 0) return "English";
|
|
446
|
+
if (lower === "中文" || lower === "zh" || lower.indexOf("zh") === 0 || lower.indexOf("中") === 0 || lower.indexOf("chinese") === 0) return "中文";
|
|
447
|
+
return "English"; // 其它语言走英文 fallback(Claude 会按 raw 回答,UI 至少英文不卡)
|
|
448
|
+
}
|
|
449
|
+
function t(key, params) {
|
|
450
|
+
var lang = getActiveLang();
|
|
451
|
+
var table = I18N[lang] || I18N[I18N_DEFAULT_LANG];
|
|
452
|
+
var template = table && key in table ? table[key] : null;
|
|
453
|
+
if (template == null) {
|
|
454
|
+
var def = I18N[I18N_DEFAULT_LANG];
|
|
455
|
+
template = def && key in def ? def[key] : key;
|
|
456
|
+
}
|
|
457
|
+
if (params && typeof template === "string") {
|
|
458
|
+
for (var k in params) {
|
|
459
|
+
if (!Object.prototype.hasOwnProperty.call(params, k)) continue;
|
|
460
|
+
template = template.split("{" + k + "}").join(params[k]);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return template;
|
|
464
|
+
}
|
|
465
|
+
|
|
387
466
|
// ── 统一线性图标库 ──
|
|
388
467
|
// 替代页面里散落的 emoji(🛡 / ⌨ / 📁 / 🔔 …)。这些 emoji 在系统字体里渲染成
|
|
389
468
|
// 彩色卡通形态,与项目温暖米色 + 棕橙的复古主题视觉冲突明显。这里集中维护
|
|
@@ -1070,6 +1149,10 @@
|
|
|
1070
1149
|
}
|
|
1071
1150
|
case "tool-group":
|
|
1072
1151
|
return el.getAttribute("data-expanded") === "true";
|
|
1152
|
+
case "subagent-reply":
|
|
1153
|
+
return el.getAttribute("data-expanded") === "true";
|
|
1154
|
+
case "subagent-panel":
|
|
1155
|
+
return el.getAttribute("data-expanded") === "true";
|
|
1073
1156
|
default:
|
|
1074
1157
|
return false;
|
|
1075
1158
|
}
|
|
@@ -1118,6 +1201,35 @@
|
|
|
1118
1201
|
if (chevron) chevron.style.transform = expanded ? "rotate(180deg)" : "";
|
|
1119
1202
|
break;
|
|
1120
1203
|
}
|
|
1204
|
+
case "subagent-reply": {
|
|
1205
|
+
el.setAttribute("data-expanded", expanded ? "true" : "false");
|
|
1206
|
+
var subLabel = el.querySelector(".subagent-reply-toggle-label");
|
|
1207
|
+
if (subLabel) subLabel.textContent = expanded ? "收起" : "展开";
|
|
1208
|
+
var subToggleBtn = el.querySelector(".subagent-reply-toggle");
|
|
1209
|
+
if (subToggleBtn) {
|
|
1210
|
+
subToggleBtn.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
1211
|
+
subToggleBtn.setAttribute("aria-label", expanded ? "收起子代理回复" : "展开子代理回复全文");
|
|
1212
|
+
}
|
|
1213
|
+
break;
|
|
1214
|
+
}
|
|
1215
|
+
case "subagent-panel": {
|
|
1216
|
+
el.setAttribute("data-expanded", expanded ? "true" : "false");
|
|
1217
|
+
// 头/尾两个按钮都得同步——label、aria-expanded、aria-label
|
|
1218
|
+
var panelBtns = el.querySelectorAll(".subagent-panel-toggle");
|
|
1219
|
+
for (var pbi = 0; pbi < panelBtns.length; pbi++) {
|
|
1220
|
+
var pb = panelBtns[pbi];
|
|
1221
|
+
pb.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
1222
|
+
pb.setAttribute("aria-label", expanded ? "收起子代理输出" : "展开子代理输出");
|
|
1223
|
+
var pblbl = pb.querySelector(".subagent-panel-toggle-label");
|
|
1224
|
+
if (pblbl) pblbl.textContent = expanded ? "收起" : "展开";
|
|
1225
|
+
}
|
|
1226
|
+
// 展开时把 body 滚到顶,避免延续上次的滚动位置造成"展开后看到一半"
|
|
1227
|
+
if (expanded) {
|
|
1228
|
+
var pbody = el.querySelector(".subagent-panel-body");
|
|
1229
|
+
if (pbody) pbody.scrollTop = 0;
|
|
1230
|
+
}
|
|
1231
|
+
break;
|
|
1232
|
+
}
|
|
1121
1233
|
}
|
|
1122
1234
|
}
|
|
1123
1235
|
|
|
@@ -2250,6 +2362,10 @@
|
|
|
2250
2362
|
state.quickCommitForm.makeTag = tagCb.checked;
|
|
2251
2363
|
var row = document.getElementById("quick-commit-tag-row");
|
|
2252
2364
|
if (row) row.classList.toggle("hidden", !tagCb.checked);
|
|
2365
|
+
if (tagCb.checked) {
|
|
2366
|
+
var input = document.getElementById("quick-commit-tag");
|
|
2367
|
+
if (input) setTimeout(function() { input.focus(); }, 0);
|
|
2368
|
+
}
|
|
2253
2369
|
});
|
|
2254
2370
|
var tagInput = document.getElementById("quick-commit-tag");
|
|
2255
2371
|
if (tagInput) tagInput.addEventListener("input", function() {
|
|
@@ -2377,9 +2493,8 @@
|
|
|
2377
2493
|
state.quickCommitForm.customMessage = aiMessage;
|
|
2378
2494
|
}
|
|
2379
2495
|
var currentTag = (state.quickCommitForm.tag || "").trim();
|
|
2380
|
-
if (!currentTag && aiTag) {
|
|
2496
|
+
if (state.quickCommitForm.makeTag && !currentTag && aiTag) {
|
|
2381
2497
|
state.quickCommitForm.tag = aiTag;
|
|
2382
|
-
state.quickCommitForm.makeTag = true;
|
|
2383
2498
|
}
|
|
2384
2499
|
})
|
|
2385
2500
|
.catch(function(error) {
|
|
@@ -2740,19 +2855,21 @@
|
|
|
2740
2855
|
'<div class="qc-files-wrap">' + fileRows + '</div>' +
|
|
2741
2856
|
'<div class="qc-message-row" id="quick-commit-message-row">' +
|
|
2742
2857
|
'<div class="qc-message-header"><label class="field-label" for="quick-commit-message">commit message</label>' +
|
|
2743
|
-
'<
|
|
2858
|
+
'<div class="qc-ai-controls">' +
|
|
2859
|
+
'<button type="button" id="quick-commit-ai-btn" class="btn btn-ghost btn-sm"' + (state.quickCommitGenerating ? ' disabled' : '') + '>' + (state.quickCommitGenerating ? '生成中…' : 'AI 生成') + '</button>' +
|
|
2860
|
+
'<label class="qc-ai-tag-toggle" title="开启后,AI 会一并建议 tag,提交时会为本次 commit 打 tag">' +
|
|
2861
|
+
'<span class="qc-ai-tag-label">含 tag</span>' +
|
|
2862
|
+
'<span class="qc-switch qc-switch--compact">' +
|
|
2863
|
+
'<input type="checkbox" id="quick-commit-make-tag" class="switch-toggle" aria-label="同时为本次 commit 打 tag"' + (f.makeTag ? ' checked' : '') + ((state.quickCommitSubmitting || state.quickCommitGenerating) ? ' disabled' : '') + '>' +
|
|
2864
|
+
'<span class="switch-slider"></span>' +
|
|
2865
|
+
'</span>' +
|
|
2866
|
+
'</label>' +
|
|
2867
|
+
'</div>' +
|
|
2744
2868
|
'</div>' +
|
|
2745
2869
|
'<textarea id="quick-commit-message" class="field-input" rows="2" placeholder="输入 commit message 或点击 AI 生成"' + (state.quickCommitSubmitting ? ' disabled' : '') + '>' + escapeHtml(f.customMessage || "") + '</textarea>' +
|
|
2746
2870
|
'</div>' +
|
|
2747
|
-
'<div class="qc-checkbox-row">' +
|
|
2748
|
-
'<label class="qc-checkbox-label" for="quick-commit-make-tag">同时为本次 commit 打 tag</label>' +
|
|
2749
|
-
'<label class="qc-switch">' +
|
|
2750
|
-
'<input type="checkbox" id="quick-commit-make-tag" class="switch-toggle"' + (f.makeTag ? ' checked' : '') + '>' +
|
|
2751
|
-
'<span class="switch-slider"></span>' +
|
|
2752
|
-
'</label>' +
|
|
2753
|
-
'</div>' +
|
|
2754
2871
|
'<div class="qc-tag-row' + (f.makeTag ? '' : ' hidden') + '" id="quick-commit-tag-row">' +
|
|
2755
|
-
'<input type="text" id="quick-commit-tag" class="field-input" placeholder="输入 tag
|
|
2872
|
+
'<input type="text" id="quick-commit-tag" class="field-input" placeholder="输入 tag 名称;留空则提交时由 AI 自动生成" value="' + escapeHtml(f.tag || "") + '"' + (state.quickCommitSubmitting ? ' disabled' : '') + '>' +
|
|
2756
2873
|
'</div>' +
|
|
2757
2874
|
(state.quickCommitError ? '<p class="error-message">' + escapeHtml(state.quickCommitError) + '</p>' : '') +
|
|
2758
2875
|
'<div class="qc-section-actions">' +
|
|
@@ -3532,7 +3649,9 @@
|
|
|
3532
3649
|
var visibleHistory = getClaudeHistoryRegionItems();
|
|
3533
3650
|
var expanded = !!state.claudeHistoryExpanded;
|
|
3534
3651
|
var loaded = !!state.claudeHistoryLoaded;
|
|
3535
|
-
var
|
|
3652
|
+
var codexVisible = getVisibleCodexHistorySessions();
|
|
3653
|
+
var codexLoaded = !!state.codexHistoryLoaded;
|
|
3654
|
+
var count = (loaded ? visibleHistory.length : 0) + (codexLoaded ? codexVisible.length : 0);
|
|
3536
3655
|
|
|
3537
3656
|
var badgeCls = "history-bubble";
|
|
3538
3657
|
var badgeContent;
|
|
@@ -3558,7 +3677,7 @@
|
|
|
3558
3677
|
'</button>';
|
|
3559
3678
|
|
|
3560
3679
|
var body = expanded
|
|
3561
|
-
? '<div class="sidebar-history-body" id="sidebar-history-body">' + renderClaudeHistoryBodyContent(visibleHistory) + '</div>'
|
|
3680
|
+
? '<div class="sidebar-history-body" id="sidebar-history-body">' + renderClaudeHistoryBodyContent(visibleHistory) + renderCodexHistoryBodyContent(codexVisible) + '</div>'
|
|
3562
3681
|
: '';
|
|
3563
3682
|
|
|
3564
3683
|
return header + body;
|
|
@@ -3789,6 +3908,44 @@
|
|
|
3789
3908
|
});
|
|
3790
3909
|
}
|
|
3791
3910
|
|
|
3911
|
+
function renderCodexHistoryDirectoryHeader(cwd, cwdShort, count, isExpanded) {
|
|
3912
|
+
var chevron = isExpanded ? "\u25be" : "\u25b8";
|
|
3913
|
+
return '<div class="claude-history-directory-header codex-history-directory-header" data-action="toggle-codex-history-directory" data-cwd="' + escapeHtml(cwd) + '" role="button" tabindex="0">' +
|
|
3914
|
+
'<div class="session-group-title claude-history-directory-title">' +
|
|
3915
|
+
'<span class="chevron">' + chevron + '</span>' +
|
|
3916
|
+
'<span class="claude-history-directory-label">' + escapeHtml(cwdShort) + ' (' + count + ')</span>' +
|
|
3917
|
+
'</div>' +
|
|
3918
|
+
'</div>';
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
function renderCodexHistoryBodyContent(visibleHistory) {
|
|
3922
|
+
if (!state.codexHistoryLoaded) {
|
|
3923
|
+
return '<div class="claude-history-loading">\u626b\u63cf Codex \u5386\u53f2\u4f1a\u8bdd\u4e2d\u2026</div>';
|
|
3924
|
+
}
|
|
3925
|
+
if (visibleHistory.length === 0) {
|
|
3926
|
+
return '';
|
|
3927
|
+
}
|
|
3928
|
+
var groups = {};
|
|
3929
|
+
var groupOrder = [];
|
|
3930
|
+
visibleHistory.forEach(function(s) {
|
|
3931
|
+
if (!groups[s.cwd]) {
|
|
3932
|
+
groups[s.cwd] = [];
|
|
3933
|
+
groupOrder.push(s.cwd);
|
|
3934
|
+
}
|
|
3935
|
+
groups[s.cwd].push(s);
|
|
3936
|
+
});
|
|
3937
|
+
var listHtml = '<div class="sidebar-history-section-label">Codex</div>';
|
|
3938
|
+
groupOrder.forEach(function(cwd) {
|
|
3939
|
+
var cwdShort = cwd.split("/").filter(Boolean).slice(-3).join("/");
|
|
3940
|
+
var isDirExpanded = !!state.codexHistoryExpandedDirs[cwd];
|
|
3941
|
+
listHtml += renderCodexHistoryDirectoryHeader(cwd, cwdShort, groups[cwd].length, isDirExpanded);
|
|
3942
|
+
if (isDirExpanded) {
|
|
3943
|
+
listHtml += groups[cwd].map(function(session) { return renderClaudeHistoryItem(session, "codex"); }).join("");
|
|
3944
|
+
}
|
|
3945
|
+
});
|
|
3946
|
+
return '<div class="sidebar-history-scroll codex-history-scroll">' + listHtml + '</div>';
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3792
3949
|
function renderClaudeHistoryDirectoryHeader(cwd, cwdShort, count, isExpanded) {
|
|
3793
3950
|
var chevron = isExpanded ? "▾" : "▸";
|
|
3794
3951
|
return '<div class="claude-history-directory-header" data-action="toggle-history-directory" data-cwd="' + escapeHtml(cwd) + '" role="button" tabindex="0">' +
|
|
@@ -3802,19 +3959,23 @@
|
|
|
3802
3959
|
}
|
|
3803
3960
|
|
|
3804
3961
|
function renderClaudeHistoryItem(session, kind) {
|
|
3962
|
+
var isCodex = kind === "codex";
|
|
3963
|
+
var rAct = isCodex ? "resume-codex-history" : "resume-history";
|
|
3964
|
+
var dAct = isCodex ? "delete-codex-history" : "delete-history";
|
|
3965
|
+
var selMap = isCodex ? state.selectedCodexHistoryIds : state.selectedClaudeHistoryIds;
|
|
3805
3966
|
var shortId = session.claudeSessionId.slice(0, 8);
|
|
3806
3967
|
var preview = session.firstUserMessage || "(空会话)";
|
|
3807
3968
|
var timeStr = formatHistoryTime(session.timestamp);
|
|
3808
3969
|
var checkbox = renderManageCheckbox(kind, session.claudeSessionId, "选择历史会话 " + preview);
|
|
3809
3970
|
var deleteButton = state.sessionsManageMode ? '' :
|
|
3810
|
-
'<button class="session-action-btn delete-btn" data-action="
|
|
3971
|
+
'<button class="session-action-btn delete-btn" data-action="' + dAct + '" data-claude-session-id="' +
|
|
3811
3972
|
session.claudeSessionId + '" type="button" aria-label="删除会话" title="隐藏此历史会话"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/></svg></button>';
|
|
3812
3973
|
var resumeButton = state.sessionsManageMode ? '' :
|
|
3813
|
-
'<button class="session-action-btn" data-action="
|
|
3974
|
+
'<button class="session-action-btn" data-action="' + rAct + '" data-claude-session-id="' +
|
|
3814
3975
|
session.claudeSessionId + '" data-cwd="' + escapeHtml(session.cwd) +
|
|
3815
|
-
'" type="button" aria-label="恢复会话" title="恢复此 Claude 历史会话"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 4v6h6"/><path d="M3.51 15a9 9 0 105.64-11.36L3 10"/></svg></button>';
|
|
3976
|
+
'" type="button" aria-label="恢复会话" title="' + (isCodex ? "恢复此 Codex 历史会话" : "恢复此 Claude 历史会话") + '"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 4v6h6"/><path d="M3.51 15a9 9 0 105.64-11.36L3 10"/></svg></button>';
|
|
3816
3977
|
|
|
3817
|
-
return '<div class="session-item claude-history-item' + (state.sessionsManageMode &&
|
|
3978
|
+
return '<div class="session-item claude-history-item' + (state.sessionsManageMode && selMap[session.claudeSessionId] ? ' selected' : '') + '" data-claude-history-id="' + session.claudeSessionId + '" data-cwd="' + escapeHtml(session.cwd) + '" role="button" tabindex="0">' +
|
|
3818
3979
|
'<div class="session-item-content">' +
|
|
3819
3980
|
'<div class="session-item-row">' +
|
|
3820
3981
|
checkbox +
|
|
@@ -3872,6 +4033,7 @@
|
|
|
3872
4033
|
// 且在已加载时立即 resolve。
|
|
3873
4034
|
var _claudeHistoryLoadingPromise = null;
|
|
3874
4035
|
function ensureClaudeHistoryLoaded() {
|
|
4036
|
+
ensureCodexHistoryLoaded();
|
|
3875
4037
|
if (state.claudeHistoryLoaded) return Promise.resolve();
|
|
3876
4038
|
if (_claudeHistoryLoadingPromise) return _claudeHistoryLoadingPromise;
|
|
3877
4039
|
_claudeHistoryLoadingPromise = loadClaudeHistory().then(function() {
|
|
@@ -3882,6 +4044,46 @@
|
|
|
3882
4044
|
return _claudeHistoryLoadingPromise;
|
|
3883
4045
|
}
|
|
3884
4046
|
|
|
4047
|
+
function loadCodexHistory() {
|
|
4048
|
+
return fetch("/api/codex-history", { credentials: "same-origin" })
|
|
4049
|
+
.then(function(res) {
|
|
4050
|
+
if (!res.ok) return [];
|
|
4051
|
+
return res.json();
|
|
4052
|
+
})
|
|
4053
|
+
.then(function(sessions) {
|
|
4054
|
+
state.codexHistory = sessions || [];
|
|
4055
|
+
state.codexHistoryLoaded = true;
|
|
4056
|
+
updateSessionsList();
|
|
4057
|
+
})
|
|
4058
|
+
.catch(function() {
|
|
4059
|
+
state.codexHistoryLoaded = true;
|
|
4060
|
+
state.codexHistory = [];
|
|
4061
|
+
updateSessionsList();
|
|
4062
|
+
});
|
|
4063
|
+
}
|
|
4064
|
+
|
|
4065
|
+
var _codexHistoryLoadingPromise = null;
|
|
4066
|
+
function ensureCodexHistoryLoaded() {
|
|
4067
|
+
if (state.codexHistoryLoaded) return Promise.resolve();
|
|
4068
|
+
if (_codexHistoryLoadingPromise) return _codexHistoryLoadingPromise;
|
|
4069
|
+
_codexHistoryLoadingPromise = loadCodexHistory().then(function() {
|
|
4070
|
+
_codexHistoryLoadingPromise = null;
|
|
4071
|
+
}, function() {
|
|
4072
|
+
_codexHistoryLoadingPromise = null;
|
|
4073
|
+
});
|
|
4074
|
+
return _codexHistoryLoadingPromise;
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4077
|
+
function getVisibleCodexHistorySessions() {
|
|
4078
|
+
var managedIds = new Set();
|
|
4079
|
+
state.sessions.forEach(function(s) {
|
|
4080
|
+
if (s.claudeSessionId) managedIds.add(s.claudeSessionId);
|
|
4081
|
+
});
|
|
4082
|
+
return state.codexHistory.filter(function(s) {
|
|
4083
|
+
return s.hasConversation && !s.managedByWand && !managedIds.has(s.claudeSessionId);
|
|
4084
|
+
});
|
|
4085
|
+
}
|
|
4086
|
+
|
|
3885
4087
|
function isMobileLayout() {
|
|
3886
4088
|
return window.innerWidth <= 768;
|
|
3887
4089
|
}
|
|
@@ -5658,35 +5860,28 @@
|
|
|
5658
5860
|
}
|
|
5659
5861
|
persistElementExpandState(el, "thinking");
|
|
5660
5862
|
};
|
|
5661
|
-
// Toggle function for subagent reply bubbles —
|
|
5662
|
-
//
|
|
5663
|
-
|
|
5863
|
+
// Toggle function for subagent reply bubbles — simple two-state preview/expanded.
|
|
5864
|
+
// 参考 opencode 的折叠面板:默认固定高度预览(含底部渐隐 mask),点击切到全文展开。
|
|
5865
|
+
// 状态写在 data-expanded 上,配套 CSS 控制 max-height + mask;用 data-expand-key
|
|
5866
|
+
// 走通用持久化通道(applyPersistedExpandState 会自动恢复用户上次的选择)。
|
|
5867
|
+
window.__subagentReplyToggle = function(e, target) {
|
|
5664
5868
|
if (e) { e.preventDefault(); e.stopPropagation(); }
|
|
5665
|
-
var bubble =
|
|
5869
|
+
var bubble = target && target.closest ? target.closest(".subagent-reply") : null;
|
|
5666
5870
|
if (!bubble) return;
|
|
5667
|
-
var
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
if (
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
icon.textContent = next === "collapsed" ? "▸"
|
|
5682
|
-
: next === "expanded" ? "▴"
|
|
5683
|
-
: "▾";
|
|
5684
|
-
}
|
|
5685
|
-
btn.setAttribute("aria-label",
|
|
5686
|
-
next === "preview" ? "点击展开全部" :
|
|
5687
|
-
next === "expanded" ? "点击完全收起" :
|
|
5688
|
-
"点击切回预览"
|
|
5689
|
-
);
|
|
5871
|
+
var expanded = bubble.getAttribute("data-expanded") === "true";
|
|
5872
|
+
applyExpandedState(bubble, "subagent-reply", !expanded);
|
|
5873
|
+
persistElementExpandState(bubble, "subagent-reply");
|
|
5874
|
+
};
|
|
5875
|
+
// Toggle function for whole subagent panel (handoff + body + footer)。
|
|
5876
|
+
// 头部 / 尾部按钮、整条 header 都绑这个;data-expanded 一变,CSS 切 body max-height
|
|
5877
|
+
// + 旋转 chevron,applyExpandedState 走通用通道写持久化。
|
|
5878
|
+
window.__subagentPanelToggle = function(e, target) {
|
|
5879
|
+
if (e) { e.preventDefault(); e.stopPropagation(); }
|
|
5880
|
+
var panel = target && target.closest ? target.closest(".subagent-panel") : null;
|
|
5881
|
+
if (!panel) return;
|
|
5882
|
+
var expanded = panel.getAttribute("data-expanded") === "true";
|
|
5883
|
+
applyExpandedState(panel, "subagent-panel", !expanded);
|
|
5884
|
+
persistElementExpandState(panel, "subagent-panel");
|
|
5690
5885
|
};
|
|
5691
5886
|
// Toggle function for inline tool rows (Read, Glob, Grep, etc.)
|
|
5692
5887
|
window.__inlineToolToggle = function(el) {
|
|
@@ -7143,6 +7338,14 @@
|
|
|
7143
7338
|
handleResumeAction(actionButton);
|
|
7144
7339
|
} else if (actionButton.dataset.action === "resume-history" && actionButton.dataset.claudeSessionId) {
|
|
7145
7340
|
handleResumeHistoryAction(actionButton);
|
|
7341
|
+
} else if (actionButton.dataset.action === "resume-codex-history" && actionButton.dataset.claudeSessionId) {
|
|
7342
|
+
handleResumeCodexHistoryAction(actionButton);
|
|
7343
|
+
} else if (actionButton.dataset.action === "delete-codex-history" && actionButton.dataset.claudeSessionId) {
|
|
7344
|
+
handleDeleteCodexHistoryAction(actionButton);
|
|
7345
|
+
} else if (actionButton.dataset.action === "toggle-codex-history-directory" && actionButton.dataset.cwd) {
|
|
7346
|
+
var codexDirCwd = actionButton.dataset.cwd;
|
|
7347
|
+
state.codexHistoryExpandedDirs[codexDirCwd] = !state.codexHistoryExpandedDirs[codexDirCwd];
|
|
7348
|
+
updateSessionsList();
|
|
7146
7349
|
} else if (actionButton.dataset.action === "worktree-merge" && actionButton.dataset.sessionId) {
|
|
7147
7350
|
openWorktreeMergeModal(actionButton.dataset.sessionId);
|
|
7148
7351
|
} else if (actionButton.dataset.action === "worktree-cleanup" && actionButton.dataset.sessionId) {
|
|
@@ -14543,6 +14746,81 @@
|
|
|
14543
14746
|
});
|
|
14544
14747
|
}
|
|
14545
14748
|
|
|
14749
|
+
function handleResumeCodexHistoryAction(actionButton) {
|
|
14750
|
+
var threadId = actionButton.dataset.claudeSessionId;
|
|
14751
|
+
var cwd = actionButton.dataset.cwd;
|
|
14752
|
+
console.log("[WAND] handleResumeCodexHistoryAction threadId:", threadId, "cwd:", cwd);
|
|
14753
|
+
if (!threadId) return;
|
|
14754
|
+
actionButton.disabled = true;
|
|
14755
|
+
resumeCodexHistorySession(threadId, cwd)
|
|
14756
|
+
.then(function(data) {
|
|
14757
|
+
if (data && data.id) {
|
|
14758
|
+
state.codexHistory = state.codexHistory.filter(function(s) {
|
|
14759
|
+
return s.claudeSessionId !== threadId;
|
|
14760
|
+
});
|
|
14761
|
+
state.selectedId = data.id;
|
|
14762
|
+
persistSelectedId();
|
|
14763
|
+
state.drafts[data.id] = "";
|
|
14764
|
+
activateSession(data).then(function() {
|
|
14765
|
+
dismissDrawerIfOverlay();
|
|
14766
|
+
});
|
|
14767
|
+
}
|
|
14768
|
+
})
|
|
14769
|
+
.finally(function() {
|
|
14770
|
+
actionButton.disabled = false;
|
|
14771
|
+
});
|
|
14772
|
+
}
|
|
14773
|
+
|
|
14774
|
+
function resumeCodexHistorySession(threadId, cwd) {
|
|
14775
|
+
return fetch("/api/codex-sessions/" + encodeURIComponent(threadId) + "/resume", {
|
|
14776
|
+
method: "POST",
|
|
14777
|
+
headers: { "Content-Type": "application/json" },
|
|
14778
|
+
credentials: "same-origin",
|
|
14779
|
+
body: JSON.stringify(withTerminalDimensions({
|
|
14780
|
+
mode: state.chatMode || (state.config && state.config.defaultMode) || "default",
|
|
14781
|
+
cwd: cwd
|
|
14782
|
+
}))
|
|
14783
|
+
})
|
|
14784
|
+
.then(function(res) { return res.json(); })
|
|
14785
|
+
.then(function(data) {
|
|
14786
|
+
if (data.error) {
|
|
14787
|
+
showToast(data.error, "error");
|
|
14788
|
+
return null;
|
|
14789
|
+
}
|
|
14790
|
+
return data;
|
|
14791
|
+
})
|
|
14792
|
+
.catch(function(error) {
|
|
14793
|
+
showToast((error && error.message) || "无法恢复历史会话。", "error");
|
|
14794
|
+
return null;
|
|
14795
|
+
});
|
|
14796
|
+
}
|
|
14797
|
+
|
|
14798
|
+
function handleDeleteCodexHistoryAction(actionButton) {
|
|
14799
|
+
var threadId = actionButton.dataset.claudeSessionId;
|
|
14800
|
+
if (!threadId) return;
|
|
14801
|
+
console.log("[WAND] handleDeleteCodexHistoryAction threadId:", threadId);
|
|
14802
|
+
var item = actionButton.closest(".claude-history-item");
|
|
14803
|
+
if (item) item.style.opacity = "0.5";
|
|
14804
|
+
fetch("/api/codex-history/" + encodeURIComponent(threadId), {
|
|
14805
|
+
method: "DELETE",
|
|
14806
|
+
credentials: "same-origin"
|
|
14807
|
+
})
|
|
14808
|
+
.then(function(res) { return res.json(); })
|
|
14809
|
+
.then(function(data) {
|
|
14810
|
+
if (data && data.ok) {
|
|
14811
|
+
state.codexHistory = state.codexHistory.filter(function(s) {
|
|
14812
|
+
return s.claudeSessionId !== threadId;
|
|
14813
|
+
});
|
|
14814
|
+
updateSessionsList();
|
|
14815
|
+
} else if (item) {
|
|
14816
|
+
item.style.opacity = "1";
|
|
14817
|
+
}
|
|
14818
|
+
})
|
|
14819
|
+
.catch(function() {
|
|
14820
|
+
if (item) item.style.opacity = "1";
|
|
14821
|
+
});
|
|
14822
|
+
}
|
|
14823
|
+
|
|
14546
14824
|
function handleResumeHistoryAction(actionButton) {
|
|
14547
14825
|
var claudeSessionId = actionButton.dataset.claudeSessionId;
|
|
14548
14826
|
var cwd = actionButton.dataset.cwd;
|
|
@@ -18905,8 +19183,8 @@
|
|
|
18905
19183
|
var agentType = sub.agentType || "";
|
|
18906
19184
|
if (agentType && SUBAGENT_NAME_MAP[agentType]) return SUBAGENT_NAME_MAP[agentType];
|
|
18907
19185
|
if (agentType) return agentType;
|
|
18908
|
-
var tail = (sub.taskId || "").slice(-4) || "未知";
|
|
18909
|
-
return "
|
|
19186
|
+
var tail = (sub.taskId || "").slice(-4) || (getActiveLang() === "English" ? "?" : "未知");
|
|
19187
|
+
return t("subagent.helper_fallback_prefix") + tail;
|
|
18910
19188
|
}
|
|
18911
19189
|
function getSubagentPalette(sub) {
|
|
18912
19190
|
// 哈希优先用 agentType,让同类型 agent 跨 turn 颜色稳定;没有 agentType 时
|
|
@@ -18924,33 +19202,35 @@
|
|
|
18924
19202
|
'</div>';
|
|
18925
19203
|
}
|
|
18926
19204
|
|
|
18927
|
-
// subagent
|
|
18928
|
-
//
|
|
19205
|
+
// subagent 最终回复(父 Task 的 tool_result)——现在外层 .subagent-panel 已经
|
|
19206
|
+
// 负责整段折叠 / 滚动,这里只需把"任务完成 / 失败"做个轻量标记块,markdown
|
|
19207
|
+
// 内容平铺,让 panel 的 body 滚动条统一接管。
|
|
18929
19208
|
function renderSubagentReplyBubble(block, role) {
|
|
18930
19209
|
if (!block || block.type !== "tool_result") return "";
|
|
18931
19210
|
var text = extractToolResultText(block.content);
|
|
18932
19211
|
var isError = block.is_error === true;
|
|
18933
|
-
|
|
18934
|
-
|
|
18935
|
-
|
|
18936
|
-
|
|
18937
|
-
|
|
19212
|
+
var rawText = typeof text === "string" ? text : (text == null ? "" : String(text));
|
|
19213
|
+
|
|
19214
|
+
// pending:subagent 还在跑,没收到结果。在 panel body 里画一个 typing 指示器
|
|
19215
|
+
// 占位,告诉用户"还在跑"。
|
|
19216
|
+
if (!isError && !rawText.trim()) {
|
|
19217
|
+
return '<div class="subagent-reply pending">' +
|
|
19218
|
+
'<span class="subagent-reply-marker pending">' + escapeHtml(t("subagent.running")) + '</span>' +
|
|
19219
|
+
'<span class="typing-indicator"><span></span><span></span><span></span></span>' +
|
|
18938
19220
|
'</div>';
|
|
18939
19221
|
}
|
|
18940
19222
|
|
|
18941
|
-
|
|
18942
|
-
|
|
18943
|
-
|
|
19223
|
+
var displayText = rawText.trim() ? rawText : t("subagent.no_output");
|
|
19224
|
+
var bodyHtml = rawText.trim() ? renderMarkdown(displayText) : escapeHtml(displayText);
|
|
19225
|
+
var markerLabel = isError ? t("subagent.task.failed") : t("subagent.task.done");
|
|
19226
|
+
var markerSymbol = isError ? "✗" : "✓";
|
|
18944
19227
|
|
|
18945
|
-
|
|
18946
|
-
|
|
18947
|
-
|
|
18948
|
-
|
|
18949
|
-
'
|
|
18950
|
-
'<
|
|
18951
|
-
'<span class="subagent-reply-cycle-label">展开</span>' +
|
|
18952
|
-
'<span class="subagent-reply-cycle-icon" aria-hidden="true">▾</span>' +
|
|
18953
|
-
'</button>' +
|
|
19228
|
+
return '<div class="subagent-reply final' + (isError ? ' error' : '') + '">' +
|
|
19229
|
+
'<div class="subagent-reply-marker ' + (isError ? 'error' : 'done') + '">' +
|
|
19230
|
+
'<span class="subagent-reply-marker-icon" aria-hidden="true">' + markerSymbol + '</span>' +
|
|
19231
|
+
'<span class="subagent-reply-marker-label">' + escapeHtml(markerLabel) + '</span>' +
|
|
19232
|
+
'</div>' +
|
|
19233
|
+
'<div class="subagent-reply-content">' + bodyHtml + '</div>' +
|
|
18954
19234
|
'</div>';
|
|
18955
19235
|
}
|
|
18956
19236
|
var PIXEL_AVATAR = {
|
|
@@ -19301,23 +19581,12 @@
|
|
|
19301
19581
|
// 跳过整段。否则会渲染出"只有头像没内容"的空气泡。
|
|
19302
19582
|
if (!segHtml || !segHtml.trim()) continue;
|
|
19303
19583
|
if (seg.subagent) {
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
html += '<div class="chat-handoff" style="--agent-color:' + subPalette.primary + '">' +
|
|
19311
|
-
'<span class="chat-handoff-arrow">↳</span> ' +
|
|
19312
|
-
escapeHtml(parentPersonaName) + ' 让 <strong>' + escapeHtml(subName) + '</strong>' +
|
|
19313
|
-
'<span class="chat-handoff-tag" title="子代理 / subagent">subagent</span>' +
|
|
19314
|
-
'帮忙' + desc +
|
|
19315
|
-
'</div>';
|
|
19316
|
-
}
|
|
19317
|
-
html += '<div class="chat-message-segment subagent" data-agent-id="' + escapeHtml(seg.subagent.taskId) + '" style="--agent-color:' + subPalette.primary + '">' +
|
|
19318
|
-
subagentAvatarHtml(seg.subagent) +
|
|
19319
|
-
'<div class="chat-message-content">' + segHtml + '</div>' +
|
|
19320
|
-
'</div>';
|
|
19584
|
+
// 整段 subagent 输出包成一个统一的可折叠面板:
|
|
19585
|
+
// 头部 = handoff title + 展开按钮;body = segHtml(所有工具卡、文本、最终回复
|
|
19586
|
+
// 都在 body 里);footer = 同款按钮。同一 taskId 的连续段(极少出现的
|
|
19587
|
+
// parent→sub→parent→sub 交错)只在第一次露 handoff title。
|
|
19588
|
+
var includeHandoff = showHandoff && lastSubId !== seg.subagent.taskId;
|
|
19589
|
+
html += buildSubagentPanelHtml(seg, parentPersonaName, segHtml, messageKey, includeHandoff);
|
|
19321
19590
|
lastSubId = seg.subagent.taskId;
|
|
19322
19591
|
} else {
|
|
19323
19592
|
html += '<div class="chat-message-segment parent">' +
|
|
@@ -19330,6 +19599,85 @@
|
|
|
19330
19599
|
return html;
|
|
19331
19600
|
}
|
|
19332
19601
|
|
|
19602
|
+
// 渲染整段 subagent 输出为一个可折叠面板:
|
|
19603
|
+
// ┌─ subagent-panel ──────────────────────────────────┐
|
|
19604
|
+
// │ [🐱] ↳ 勤劳初二 让 协作猫 帮忙:xxx [展开 ▾] │ ← header (always visible)
|
|
19605
|
+
// ├───────────────────────────────────────────────────┤
|
|
19606
|
+
// │ <tool 卡 1> │
|
|
19607
|
+
// │ <text> │ ← body
|
|
19608
|
+
// │ <tool 卡 2> │ 默认 max-height 22em
|
|
19609
|
+
// │ <... 最终回复 ...> │ + 内部 overflow-y:auto
|
|
19610
|
+
// ├───────────────────────────────────────────────────┤
|
|
19611
|
+
// │ [展开 ▾] │ ← footer (always visible)
|
|
19612
|
+
// └───────────────────────────────────────────────────┘
|
|
19613
|
+
// 状态写在 .subagent-panel[data-expanded],按 messageKey + taskId 持久化。
|
|
19614
|
+
// 默认折叠(preview);用户点头/尾按钮或头部标题条都能切。
|
|
19615
|
+
function buildSubagentPanelHtml(seg, parentPersonaName, segHtml, messageKey, includeHandoff) {
|
|
19616
|
+
var sub = seg.subagent;
|
|
19617
|
+
var subPalette = getSubagentPalette(sub);
|
|
19618
|
+
var subName = getSubagentDisplayName(sub);
|
|
19619
|
+
var taskId = sub.taskId || "";
|
|
19620
|
+
var avatarSvg = buildPixelSvg(buildCatGrid(subPalette));
|
|
19621
|
+
|
|
19622
|
+
var titleHtml;
|
|
19623
|
+
if (includeHandoff) {
|
|
19624
|
+
// 用 i18n 模板拼 handoff title。{parent}/{sub} 用占位符避免在调用点拼字符串导致
|
|
19625
|
+
// 顺序错乱(中文 "X 让 Y 帮忙",英文 "X asked Y for help",词序完全不同)。
|
|
19626
|
+
// tag 是单独的彩色 chip,所以把模板里的 {sub} 替换成空 span 占位,然后再 splice
|
|
19627
|
+
// 进 <strong> + chip——保证模板可读、调用点不脏。
|
|
19628
|
+
var subInlineHtml = '<strong class="subagent-panel-name">' + escapeHtml(subName) + '</strong>' +
|
|
19629
|
+
'<span class="subagent-panel-tag" title="' + escapeHtml(t("subagent.tag_title")) + '">' + escapeHtml(t("subagent.tag")) + '</span>';
|
|
19630
|
+
var hasDesc = !!(sub.taskDescription && String(sub.taskDescription).trim());
|
|
19631
|
+
var handoffTpl = hasDesc ? t("subagent.handoff.with_desc", { parent: escapeHtml(parentPersonaName), sub: subInlineHtml })
|
|
19632
|
+
: t("subagent.handoff", { parent: escapeHtml(parentPersonaName), sub: subInlineHtml });
|
|
19633
|
+
var descSpan = hasDesc
|
|
19634
|
+
? '<span class="subagent-panel-task-desc">' + escapeHtml(sub.taskDescription) + '</span>'
|
|
19635
|
+
: '';
|
|
19636
|
+
titleHtml = '<span class="subagent-panel-arrow" aria-hidden="true">↳</span>' +
|
|
19637
|
+
'<span class="subagent-panel-attribution">' + handoffTpl + descSpan + '</span>';
|
|
19638
|
+
} else {
|
|
19639
|
+
titleHtml = '<span class="subagent-panel-attribution">' +
|
|
19640
|
+
'<strong class="subagent-panel-name">' + escapeHtml(subName) + '</strong>' +
|
|
19641
|
+
'<span class="subagent-panel-task-desc"> ' + escapeHtml(t("subagent.continued")) + '</span>' +
|
|
19642
|
+
'</span>';
|
|
19643
|
+
}
|
|
19644
|
+
|
|
19645
|
+
var expandKey = buildExpandKey("subagent-panel", [messageKey, taskId]);
|
|
19646
|
+
var persisted = getPersistedExpandState(expandKey);
|
|
19647
|
+
var expanded = persisted === null ? false : !!persisted;
|
|
19648
|
+
|
|
19649
|
+
function toggleBtnHtml(position) {
|
|
19650
|
+
return '<button type="button" class="subagent-panel-toggle" ' +
|
|
19651
|
+
'data-position="' + position + '" ' +
|
|
19652
|
+
'onclick="__subagentPanelToggle(event, this)" ' +
|
|
19653
|
+
'aria-expanded="' + (expanded ? "true" : "false") + '" ' +
|
|
19654
|
+
'aria-label="' + escapeHtml(expanded ? t("ui.collapse_panel_aria") : t("ui.expand_panel_aria")) + '">' +
|
|
19655
|
+
'<span class="subagent-panel-toggle-label">' + escapeHtml(expanded ? t("ui.collapse") : t("ui.expand")) + '</span>' +
|
|
19656
|
+
'<svg class="subagent-panel-toggle-icon" width="10" height="10" viewBox="0 0 10 10" fill="none" aria-hidden="true">' +
|
|
19657
|
+
'<path d="M2.5 3.75L5 6.25L7.5 3.75" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>' +
|
|
19658
|
+
'</svg>' +
|
|
19659
|
+
'</button>';
|
|
19660
|
+
}
|
|
19661
|
+
|
|
19662
|
+
return '<div class="subagent-panel" ' +
|
|
19663
|
+
'data-expand-kind="subagent-panel" ' +
|
|
19664
|
+
'data-expand-key="' + escapeHtml(expandKey) + '" ' +
|
|
19665
|
+
'data-agent-id="' + escapeHtml(taskId) + '" ' +
|
|
19666
|
+
'data-expanded="' + (expanded ? "true" : "false") + '" ' +
|
|
19667
|
+
'style="--agent-color:' + subPalette.primary + '">' +
|
|
19668
|
+
'<div class="subagent-panel-header" onclick="__subagentPanelToggle(event, this)" role="button" tabindex="0" aria-label="' + escapeHtml(t("subagent.title_aria")) + '">' +
|
|
19669
|
+
'<span class="subagent-panel-avatar" aria-hidden="true">' + avatarSvg + '</span>' +
|
|
19670
|
+
titleHtml +
|
|
19671
|
+
toggleBtnHtml("top") +
|
|
19672
|
+
'</div>' +
|
|
19673
|
+
'<div class="subagent-panel-body">' + segHtml + '</div>' +
|
|
19674
|
+
'<div class="subagent-panel-footer">' +
|
|
19675
|
+
'<span class="subagent-panel-footer-hint" aria-hidden="true">— ' + escapeHtml(subName) + ' —</span>' +
|
|
19676
|
+
toggleBtnHtml("bottom") +
|
|
19677
|
+
'</div>' +
|
|
19678
|
+
'</div>';
|
|
19679
|
+
}
|
|
19680
|
+
|
|
19333
19681
|
function renderStructuredMessage(msg, roundUsage, messageIndex, legacyTaskMap) {
|
|
19334
19682
|
var role = msg.role;
|
|
19335
19683
|
var messageKey = getMessageKey(msg, messageIndex);
|