@co0ontty/wand 1.5.4 → 1.5.5
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/web-ui/content/scripts.js +172 -60
- package/dist/web-ui/content/styles.css +128 -45
- package/package.json +1 -1
|
@@ -164,10 +164,13 @@
|
|
|
164
164
|
var _statusBarStartTime = 0;
|
|
165
165
|
|
|
166
166
|
function renderStructuredStatusBar(chatMessages, session) {
|
|
167
|
-
//
|
|
168
|
-
var
|
|
167
|
+
// Status bar now lives above the input-composer, inside .input-panel
|
|
168
|
+
var inputPanel = document.querySelector(".input-panel");
|
|
169
|
+
var existing = document.querySelector(".structured-status-bar");
|
|
170
|
+
var composer = document.querySelector(".input-composer");
|
|
169
171
|
if (!session || !isStructuredSession(session)) {
|
|
170
172
|
if (existing) existing.remove();
|
|
173
|
+
if (composer) composer.classList.remove("in-flight");
|
|
171
174
|
clearInterval(_statusBarTimerId);
|
|
172
175
|
_statusBarTimerId = null;
|
|
173
176
|
return;
|
|
@@ -181,21 +184,26 @@
|
|
|
181
184
|
_statusBarStartTime = Date.now();
|
|
182
185
|
}
|
|
183
186
|
|
|
184
|
-
|
|
187
|
+
// Add glow to input composer
|
|
188
|
+
if (composer) composer.classList.add("in-flight");
|
|
189
|
+
|
|
190
|
+
if (!existing && inputPanel && composer) {
|
|
185
191
|
var bar = document.createElement("div");
|
|
186
192
|
bar.className = "structured-status-bar";
|
|
187
193
|
bar.innerHTML =
|
|
194
|
+
'<span class="status-bar-dot"></span>' +
|
|
188
195
|
'<span class="status-bar-label">回复中</span>' +
|
|
189
|
-
'<div class="status-bar-track"><div class="status-bar-fill"></div></div>' +
|
|
190
196
|
'<span class="status-bar-timer">0.0s</span>';
|
|
191
|
-
//
|
|
192
|
-
|
|
197
|
+
// Insert right before the input-composer element
|
|
198
|
+
inputPanel.insertBefore(bar, composer);
|
|
193
199
|
existing = bar;
|
|
194
|
-
} else if (existing.classList.contains("completed")) {
|
|
200
|
+
} else if (existing && existing.classList.contains("completed")) {
|
|
195
201
|
// Was completed, now in-flight again — reset
|
|
196
202
|
existing.classList.remove("completed");
|
|
197
203
|
existing.style.animation = "none";
|
|
198
204
|
existing.querySelector(".status-bar-label").textContent = "回复中";
|
|
205
|
+
var dot = existing.querySelector(".status-bar-dot");
|
|
206
|
+
if (dot) dot.style.display = "";
|
|
199
207
|
_statusBarStartTime = Date.now();
|
|
200
208
|
}
|
|
201
209
|
|
|
@@ -214,12 +222,17 @@
|
|
|
214
222
|
clearInterval(_statusBarTimerId);
|
|
215
223
|
_statusBarTimerId = null;
|
|
216
224
|
|
|
225
|
+
// Remove glow from input composer
|
|
226
|
+
if (composer) composer.classList.remove("in-flight");
|
|
227
|
+
|
|
217
228
|
if (existing && !existing.classList.contains("completed")) {
|
|
218
229
|
// Just finished — transition to completed state
|
|
219
230
|
var elapsed = _statusBarStartTime ? ((Date.now() - _statusBarStartTime) / 1000).toFixed(1) : "0.0";
|
|
220
231
|
existing.classList.add("completed");
|
|
221
232
|
existing.querySelector(".status-bar-label").textContent = "完成";
|
|
222
233
|
existing.querySelector(".status-bar-timer").textContent = elapsed + "s";
|
|
234
|
+
var dot = existing.querySelector(".status-bar-dot");
|
|
235
|
+
if (dot) dot.style.display = "none";
|
|
223
236
|
_statusBarStartTime = 0;
|
|
224
237
|
// Remove after animation ends
|
|
225
238
|
setTimeout(function() {
|
|
@@ -471,6 +484,8 @@
|
|
|
471
484
|
if (!state.selectedId) return "";
|
|
472
485
|
var isTerminal = state.currentView === "terminal";
|
|
473
486
|
if (!isTerminal) return "";
|
|
487
|
+
var sel = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
488
|
+
if (sel && isStructuredSession(sel)) return "";
|
|
474
489
|
var keys = renderShortcutKeys();
|
|
475
490
|
var arrow = state.shortcutsExpanded ? '›' : '‹';
|
|
476
491
|
return '<div class="inline-shortcuts-wrap' + (state.shortcutsExpanded ? ' expanded' : '') + '">' +
|
|
@@ -484,6 +499,8 @@
|
|
|
484
499
|
if (!state.selectedId) return "";
|
|
485
500
|
var isTerminal = state.currentView === "terminal";
|
|
486
501
|
if (!isTerminal) return "";
|
|
502
|
+
var sel = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
503
|
+
if (sel && isStructuredSession(sel)) return "";
|
|
487
504
|
return '<div class="inline-shortcuts-expanded-row' + (state.shortcutsExpanded ? ' visible' : '') + '">' + renderShortcutKeys() + '</div>';
|
|
488
505
|
}
|
|
489
506
|
|
|
@@ -700,12 +717,11 @@
|
|
|
700
717
|
'<span id="session-mode-display" class="session-mode-display">' + (selectedSession ? getModeLabel(selectedSession.mode) : '默认') + '</span>' +
|
|
701
718
|
(selectedSession && selectedSession.autoApprovePermissions ? '<span class="session-info-separator">|</span><span id="auto-approve-toggle" class="auto-approve-indicator active" title="自动批准已启用 — 点击关闭">🛡 自动批准</span>' : '<span class="session-info-separator">|</span><span id="auto-approve-toggle" class="auto-approve-indicator" title="自动批准已关闭 — 点击开启">🛡 手动</span>') +
|
|
702
719
|
'<span class="session-info-separator">|</span>' +
|
|
703
|
-
'<span id="session-kind-display" class="session-kind-display">' + (selectedSession ? (
|
|
720
|
+
'<span id="session-kind-display" class="session-kind-display">' + (selectedSession ? getSessionKindLabel(selectedSession) : '终端') + '</span>' +
|
|
704
721
|
'<span class="session-info-separator">|</span>' +
|
|
705
722
|
'<span id="session-status-display" class="session-status-display">' + (selectedSession ? getSessionStatusLabel(selectedSession) : '-') + '</span>' +
|
|
706
723
|
(selectedSession && selectedSession.claudeSessionId ? '<span class="session-info-separator">|</span><span id="claude-session-id-badge" class="claude-session-id-badge" data-claude-id="' + escapeHtml(selectedSession.claudeSessionId) + '" title="点击复制 Claude 会话 ID">☁ ' + escapeHtml(selectedSession.claudeSessionId.slice(0, 8)) + '</span>' : '') +
|
|
707
|
-
'<span class="session-info-separator">|</span>' +
|
|
708
|
-
'<span id="session-exit-display" class="session-exit-display">exit=' + (selectedSession && selectedSession.exitCode !== undefined ? selectedSession.exitCode : 'n/a') + '</span>' +
|
|
724
|
+
(selectedSession && !isStructuredSession(selectedSession) ? '<span class="session-info-separator">|</span><span id="session-exit-display" class="session-exit-display">退出码=' + (selectedSession.exitCode !== undefined ? selectedSession.exitCode : 'n/a') + '</span>' : '') +
|
|
709
725
|
'</div>' +
|
|
710
726
|
'</div>' +
|
|
711
727
|
'<p id="action-error" class="error-message hidden"></p>' +
|
|
@@ -1743,7 +1759,15 @@
|
|
|
1743
1759
|
if (!session) return "";
|
|
1744
1760
|
if (session.archived) return "已归档";
|
|
1745
1761
|
if (session.permissionBlocked) return "等待授权";
|
|
1746
|
-
|
|
1762
|
+
var statusMap = {
|
|
1763
|
+
"stopped": "已停止",
|
|
1764
|
+
"running": "运行中",
|
|
1765
|
+
"idle": "空闲",
|
|
1766
|
+
"thinking": "思考中",
|
|
1767
|
+
"waiting-input": "等待输入",
|
|
1768
|
+
"initializing": "启动中"
|
|
1769
|
+
};
|
|
1770
|
+
return statusMap[session.status] || session.status;
|
|
1747
1771
|
}
|
|
1748
1772
|
|
|
1749
1773
|
function getSessionStatusClass(session) {
|
|
@@ -2312,19 +2336,6 @@
|
|
|
2312
2336
|
var selectedIndex = -1;
|
|
2313
2337
|
var folderItems = [];
|
|
2314
2338
|
|
|
2315
|
-
function saveWorkingDir(path) {
|
|
2316
|
-
state.workingDir = path;
|
|
2317
|
-
try {
|
|
2318
|
-
localStorage.setItem("wand-working-dir", path);
|
|
2319
|
-
} catch (e) {
|
|
2320
|
-
// Ignore localStorage errors
|
|
2321
|
-
}
|
|
2322
|
-
// Also add to recent paths (defined later, will be called after function is available)
|
|
2323
|
-
if (typeof addRecentPath === "function") {
|
|
2324
|
-
addRecentPath(path);
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
2339
|
// Helper functions for path validation feedback
|
|
2329
2340
|
function showValidationError(message) {
|
|
2330
2341
|
if (folderPickerInput) {
|
|
@@ -2349,21 +2360,7 @@
|
|
|
2349
2360
|
}
|
|
2350
2361
|
|
|
2351
2362
|
// Helper functions for recent paths (single source: backend API)
|
|
2352
|
-
|
|
2353
|
-
fetch("/api/recent-paths", { credentials: "same-origin" })
|
|
2354
|
-
.then(function(res) { return res.json(); })
|
|
2355
|
-
.then(function(items) { callback(items || []); })
|
|
2356
|
-
.catch(function() { callback([]); });
|
|
2357
|
-
}
|
|
2358
|
-
|
|
2359
|
-
function addRecentPath(path) {
|
|
2360
|
-
return fetch("/api/recent-paths", {
|
|
2361
|
-
method: "POST",
|
|
2362
|
-
headers: { "Content-Type": "application/json" },
|
|
2363
|
-
credentials: "same-origin",
|
|
2364
|
-
body: JSON.stringify({ path: path })
|
|
2365
|
-
}).catch(function() {});
|
|
2366
|
-
}
|
|
2363
|
+
// NOTE: fetchRecentPaths and addRecentPath are defined at outer scope
|
|
2367
2364
|
|
|
2368
2365
|
function renderRecentPathsHtml(items) {
|
|
2369
2366
|
if (!items.length) return "";
|
|
@@ -2690,6 +2687,32 @@
|
|
|
2690
2687
|
setupVisualViewportHandlers();
|
|
2691
2688
|
}
|
|
2692
2689
|
|
|
2690
|
+
function saveWorkingDir(path) {
|
|
2691
|
+
state.workingDir = path;
|
|
2692
|
+
try {
|
|
2693
|
+
localStorage.setItem("wand-working-dir", path);
|
|
2694
|
+
} catch (e) {
|
|
2695
|
+
// Ignore localStorage errors
|
|
2696
|
+
}
|
|
2697
|
+
addRecentPath(path);
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
function fetchRecentPaths(callback) {
|
|
2701
|
+
fetch("/api/recent-paths", { credentials: "same-origin" })
|
|
2702
|
+
.then(function(res) { return res.json(); })
|
|
2703
|
+
.then(function(items) { callback(items || []); })
|
|
2704
|
+
.catch(function() { callback([]); });
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
function addRecentPath(path) {
|
|
2708
|
+
return fetch("/api/recent-paths", {
|
|
2709
|
+
method: "POST",
|
|
2710
|
+
headers: { "Content-Type": "application/json" },
|
|
2711
|
+
credentials: "same-origin",
|
|
2712
|
+
body: JSON.stringify({ path: path })
|
|
2713
|
+
}).catch(function() {});
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2693
2716
|
function activateSessionItem(sessionId) {
|
|
2694
2717
|
var session = state.sessions.find(function(s) { return s.id === sessionId; });
|
|
2695
2718
|
if (session && session.status !== "running" && !isStructuredSession(session)) {
|
|
@@ -3490,13 +3513,13 @@
|
|
|
3490
3513
|
}
|
|
3491
3514
|
|
|
3492
3515
|
function getSessionKindLabel(session) {
|
|
3493
|
-
return isStructuredSession(session) ? "
|
|
3516
|
+
return isStructuredSession(session) ? "结构化" : "终端";
|
|
3494
3517
|
}
|
|
3495
3518
|
|
|
3496
3519
|
function getSessionKindDescription(session) {
|
|
3497
3520
|
return isStructuredSession(session)
|
|
3498
|
-
? "
|
|
3499
|
-
: "
|
|
3521
|
+
? "结构化 · 块级记录"
|
|
3522
|
+
: "终端 · PTY 会话";
|
|
3500
3523
|
}
|
|
3501
3524
|
|
|
3502
3525
|
function isRecoverableToolError(toolResult, nextResult) {
|
|
@@ -3830,8 +3853,9 @@
|
|
|
3830
3853
|
var exitEl = document.getElementById("session-exit-display");
|
|
3831
3854
|
var cwdText = selectedSession && selectedSession.cwd ? selectedSession.cwd : "未设置目录";
|
|
3832
3855
|
var modeText = selectedSession ? getModeLabel(selectedSession.mode) : "默认";
|
|
3833
|
-
var kindText = selectedSession ? getSessionKindLabel(selectedSession) : "
|
|
3834
|
-
var
|
|
3856
|
+
var kindText = selectedSession ? getSessionKindLabel(selectedSession) : "终端";
|
|
3857
|
+
var isStructured = selectedSession && isStructuredSession(selectedSession);
|
|
3858
|
+
var exitText = isStructured ? "" : "退出码=" + (selectedSession && selectedSession.exitCode !== undefined ? selectedSession.exitCode : "n/a");
|
|
3835
3859
|
if (cwdEl && cwdEl.textContent !== cwdText) cwdEl.textContent = cwdText;
|
|
3836
3860
|
if (modeEl && modeEl.textContent !== modeText) modeEl.textContent = modeText;
|
|
3837
3861
|
if (kindEl && kindEl.textContent !== kindText) kindEl.textContent = kindText;
|
|
@@ -5626,10 +5650,12 @@
|
|
|
5626
5650
|
}
|
|
5627
5651
|
});
|
|
5628
5652
|
// Inline keyboard visibility follows current view
|
|
5629
|
-
var inlineKeyboard = document.
|
|
5653
|
+
var inlineKeyboard = document.querySelector(".inline-shortcuts-wrap");
|
|
5630
5654
|
if (inlineKeyboard) inlineKeyboard.classList.toggle("hidden", structured || state.currentView !== "terminal");
|
|
5655
|
+
var expandedRow = document.querySelector(".inline-shortcuts-expanded-row");
|
|
5656
|
+
if (expandedRow) expandedRow.classList.toggle("hidden", structured || state.currentView !== "terminal");
|
|
5631
5657
|
var inputHint = document.querySelector(".input-hint");
|
|
5632
|
-
if (inputHint) inputHint.classList.toggle("hidden", structured ?
|
|
5658
|
+
if (inputHint) inputHint.classList.toggle("hidden", structured ? true : state.currentView === "terminal");
|
|
5633
5659
|
var container = document.getElementById("output");
|
|
5634
5660
|
if (container) container.classList.toggle("interactive", !structured && state.terminalInteractive);
|
|
5635
5661
|
}
|
|
@@ -8955,6 +8981,98 @@
|
|
|
8955
8981
|
return '<div class="structured-tool-hint">已自动恢复一次 ' + escapeHtml(getToolDisplayName(toolName)) + ' 参数问题</div>';
|
|
8956
8982
|
}
|
|
8957
8983
|
|
|
8984
|
+
// ── 连续同类工具调用分组 ──
|
|
8985
|
+
var GROUPABLE_TOOLS = { Read: 1, Glob: 1, Grep: 1, WebFetch: 1, WebSearch: 1, TodoRead: 1 };
|
|
8986
|
+
|
|
8987
|
+
function groupConsecutiveTools(content) {
|
|
8988
|
+
var groups = [];
|
|
8989
|
+
var i = 0;
|
|
8990
|
+
while (i < content.length) {
|
|
8991
|
+
var block = content[i];
|
|
8992
|
+
if (block.type === "tool_result") { i++; continue; }
|
|
8993
|
+
if (block.type === "tool_use" && GROUPABLE_TOOLS[block.name]) {
|
|
8994
|
+
var run = [{ block: block, index: i }];
|
|
8995
|
+
var j = i + 1;
|
|
8996
|
+
while (j < content.length) {
|
|
8997
|
+
if (content[j].type === "tool_result") { j++; continue; }
|
|
8998
|
+
if (content[j].type === "tool_use" && GROUPABLE_TOOLS[content[j].name]) {
|
|
8999
|
+
run.push({ block: content[j], index: j });
|
|
9000
|
+
j++;
|
|
9001
|
+
} else { break; }
|
|
9002
|
+
}
|
|
9003
|
+
if (run.length >= 2) {
|
|
9004
|
+
groups.push({ type: "group", items: run, endIndex: j });
|
|
9005
|
+
} else {
|
|
9006
|
+
groups.push({ type: "single", block: block, index: i });
|
|
9007
|
+
}
|
|
9008
|
+
i = j;
|
|
9009
|
+
} else {
|
|
9010
|
+
groups.push({ type: "single", block: block, index: i });
|
|
9011
|
+
i++;
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
return groups;
|
|
9015
|
+
}
|
|
9016
|
+
|
|
9017
|
+
var TOOL_GROUP_LABELS = { Read: "读取", Glob: "搜索", Grep: "搜索", WebFetch: "抓取", WebSearch: "搜索", TodoRead: "待办" };
|
|
9018
|
+
|
|
9019
|
+
function renderToolGroup(items, role, toolResults) {
|
|
9020
|
+
// Count by tool name
|
|
9021
|
+
var counts = {};
|
|
9022
|
+
for (var k = 0; k < items.length; k++) {
|
|
9023
|
+
var n = items[k].block.name;
|
|
9024
|
+
counts[n] = (counts[n] || 0) + 1;
|
|
9025
|
+
}
|
|
9026
|
+
// Check if all done or still pending
|
|
9027
|
+
var allDone = true;
|
|
9028
|
+
var anyError = false;
|
|
9029
|
+
for (var k = 0; k < items.length; k++) {
|
|
9030
|
+
var b = items[k].block;
|
|
9031
|
+
var tr = pickToolResultForDisplay(toolResults, b.id);
|
|
9032
|
+
if (!tr) { allDone = false; }
|
|
9033
|
+
else if (tr.is_error) { anyError = true; }
|
|
9034
|
+
}
|
|
9035
|
+
var statusIcon = !allDone ? "…" : (anyError ? "✗" : "✓");
|
|
9036
|
+
var statusClass = !allDone ? "pending" : (anyError ? "error" : "done");
|
|
9037
|
+
// Summary text
|
|
9038
|
+
var parts = [];
|
|
9039
|
+
for (var name in counts) {
|
|
9040
|
+
parts.push(counts[name] + " " + (TOOL_GROUP_LABELS[name] || name));
|
|
9041
|
+
}
|
|
9042
|
+
var summaryText = parts.join(" · ");
|
|
9043
|
+
|
|
9044
|
+
// Render each item's inline-tool card
|
|
9045
|
+
var innerHtml = "";
|
|
9046
|
+
for (var k = 0; k < items.length; k++) {
|
|
9047
|
+
try {
|
|
9048
|
+
innerHtml += renderContentBlock(items[k].block, role, toolResults, items[k].index);
|
|
9049
|
+
} catch (e) {
|
|
9050
|
+
innerHtml += '<div class="render-error">工具渲染失败</div>';
|
|
9051
|
+
}
|
|
9052
|
+
}
|
|
9053
|
+
|
|
9054
|
+
return '<div class="tool-group" data-expanded="false" data-status="' + statusClass + '">' +
|
|
9055
|
+
'<div class="tool-group-summary" onclick="__toolGroupToggle(this.parentNode)">' +
|
|
9056
|
+
'<span class="tool-group-status">' + statusIcon + '</span>' +
|
|
9057
|
+
'<span class="tool-group-text">' + escapeHtml(summaryText) + '</span>' +
|
|
9058
|
+
'<span class="tool-group-count">' + items.length + ' 个调用</span>' +
|
|
9059
|
+
'<svg class="tool-group-chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>' +
|
|
9060
|
+
'</div>' +
|
|
9061
|
+
'<div class="tool-group-body">' + innerHtml + '</div>' +
|
|
9062
|
+
'</div>';
|
|
9063
|
+
}
|
|
9064
|
+
|
|
9065
|
+
// global toggle
|
|
9066
|
+
window.__toolGroupToggle = function(el) {
|
|
9067
|
+
if (!el) return;
|
|
9068
|
+
var expanded = el.getAttribute("data-expanded") === "true";
|
|
9069
|
+
el.setAttribute("data-expanded", expanded ? "false" : "true");
|
|
9070
|
+
var body = el.querySelector(".tool-group-body");
|
|
9071
|
+
if (body) body.style.display = expanded ? "none" : "block";
|
|
9072
|
+
var chevron = el.querySelector(".tool-group-chevron");
|
|
9073
|
+
if (chevron) chevron.style.transform = expanded ? "" : "rotate(180deg)";
|
|
9074
|
+
};
|
|
9075
|
+
|
|
8958
9076
|
function renderStructuredMessage(msg, roundUsage) {
|
|
8959
9077
|
var role = msg.role;
|
|
8960
9078
|
var avatar = role === "assistant" ? '<div class="chat-message-avatar">赛博虎妞</div>' : "";
|
|
@@ -8973,10 +9091,15 @@
|
|
|
8973
9091
|
var blocksHtml = "";
|
|
8974
9092
|
|
|
8975
9093
|
try {
|
|
8976
|
-
|
|
8977
|
-
|
|
9094
|
+
var groups = groupConsecutiveTools(msg.content);
|
|
9095
|
+
for (var g = 0; g < groups.length; g++) {
|
|
9096
|
+
var grp = groups[g];
|
|
8978
9097
|
try {
|
|
8979
|
-
|
|
9098
|
+
if (grp.type === "group") {
|
|
9099
|
+
blocksHtml += renderToolGroup(grp.items, role, toolResults);
|
|
9100
|
+
} else {
|
|
9101
|
+
blocksHtml += renderContentBlock(grp.block, role, toolResults, grp.index);
|
|
9102
|
+
}
|
|
8980
9103
|
} catch (e) {
|
|
8981
9104
|
blocksHtml += '<div class="render-error">消息块渲染失败</div>';
|
|
8982
9105
|
}
|
|
@@ -8989,17 +9112,6 @@
|
|
|
8989
9112
|
}
|
|
8990
9113
|
|
|
8991
9114
|
var usageHtml = "";
|
|
8992
|
-
if (role === "assistant" && roundUsage) {
|
|
8993
|
-
var u = roundUsage;
|
|
8994
|
-
var parts = [];
|
|
8995
|
-
if (u.inputTokens > 0) parts.push("输入 " + u.inputTokens);
|
|
8996
|
-
if (u.outputTokens > 0) parts.push("输出 " + u.outputTokens);
|
|
8997
|
-
if (u.cacheReadInputTokens > 0) parts.push("缓存 " + u.cacheReadInputTokens);
|
|
8998
|
-
if (u.totalCostUsd > 0) parts.push("$" + u.totalCostUsd.toFixed(4));
|
|
8999
|
-
if (parts.length > 0) {
|
|
9000
|
-
usageHtml = '<div class="message-usage">' + parts.join(" · ") + '</div>';
|
|
9001
|
-
}
|
|
9002
|
-
}
|
|
9003
9115
|
|
|
9004
9116
|
return '<div class="chat-message ' + role + '">' +
|
|
9005
9117
|
avatar +
|
|
@@ -2876,6 +2876,65 @@
|
|
|
2876
2876
|
border-radius: 2px;
|
|
2877
2877
|
}
|
|
2878
2878
|
|
|
2879
|
+
/* ── Tool Group (连续同类调用折叠) ── */
|
|
2880
|
+
.tool-group {
|
|
2881
|
+
margin: 2px 0;
|
|
2882
|
+
border-radius: 6px;
|
|
2883
|
+
border: 1px solid var(--border-subtle, rgba(127,127,127,0.1));
|
|
2884
|
+
overflow: hidden;
|
|
2885
|
+
}
|
|
2886
|
+
.tool-group-summary {
|
|
2887
|
+
display: flex;
|
|
2888
|
+
align-items: center;
|
|
2889
|
+
gap: 6px;
|
|
2890
|
+
padding: 5px 10px;
|
|
2891
|
+
cursor: pointer;
|
|
2892
|
+
font-size: 0.75rem;
|
|
2893
|
+
color: var(--text-secondary);
|
|
2894
|
+
user-select: none;
|
|
2895
|
+
transition: background var(--transition-fast);
|
|
2896
|
+
}
|
|
2897
|
+
.tool-group-summary:hover {
|
|
2898
|
+
background: var(--bg-hover, rgba(127,127,127,0.06));
|
|
2899
|
+
}
|
|
2900
|
+
.tool-group-status {
|
|
2901
|
+
font-size: 0.6875rem;
|
|
2902
|
+
flex-shrink: 0;
|
|
2903
|
+
width: 14px;
|
|
2904
|
+
text-align: center;
|
|
2905
|
+
}
|
|
2906
|
+
.tool-group[data-status="done"] .tool-group-status { color: var(--success, #22c55e); }
|
|
2907
|
+
.tool-group[data-status="error"] .tool-group-status { color: var(--error, #ef4444); }
|
|
2908
|
+
.tool-group[data-status="pending"] .tool-group-status { color: var(--text-muted); }
|
|
2909
|
+
.tool-group-text {
|
|
2910
|
+
flex: 1;
|
|
2911
|
+
min-width: 0;
|
|
2912
|
+
overflow: hidden;
|
|
2913
|
+
text-overflow: ellipsis;
|
|
2914
|
+
white-space: nowrap;
|
|
2915
|
+
}
|
|
2916
|
+
.tool-group-count {
|
|
2917
|
+
flex-shrink: 0;
|
|
2918
|
+
font-size: 0.625rem;
|
|
2919
|
+
color: var(--text-muted);
|
|
2920
|
+
}
|
|
2921
|
+
.tool-group-chevron {
|
|
2922
|
+
flex-shrink: 0;
|
|
2923
|
+
transition: transform 0.2s ease;
|
|
2924
|
+
color: var(--text-muted);
|
|
2925
|
+
}
|
|
2926
|
+
.tool-group[data-expanded="true"] .tool-group-chevron {
|
|
2927
|
+
transform: rotate(180deg);
|
|
2928
|
+
}
|
|
2929
|
+
.tool-group-body {
|
|
2930
|
+
display: none;
|
|
2931
|
+
padding: 2px 6px 4px;
|
|
2932
|
+
border-top: 1px solid var(--border-subtle, rgba(127,127,127,0.08));
|
|
2933
|
+
}
|
|
2934
|
+
.tool-group[data-expanded="true"] .tool-group-body {
|
|
2935
|
+
display: block;
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2879
2938
|
/* ── Inline Tool Display (Read, Glob, Grep, WebFetch, WebSearch, TodoRead) ── */
|
|
2880
2939
|
.inline-tool {
|
|
2881
2940
|
display: flex;
|
|
@@ -7522,13 +7581,61 @@
|
|
|
7522
7581
|
}
|
|
7523
7582
|
|
|
7524
7583
|
/* ── 结构化会话状态条 ── */
|
|
7584
|
+
/* ── 输入框顶部波浪脉冲(回复中) ── */
|
|
7585
|
+
.input-composer.in-flight {
|
|
7586
|
+
border-color: transparent;
|
|
7587
|
+
}
|
|
7588
|
+
.input-composer.in-flight::before {
|
|
7589
|
+
content: "";
|
|
7590
|
+
position: absolute;
|
|
7591
|
+
top: -2px;
|
|
7592
|
+
left: -60%;
|
|
7593
|
+
width: 220%;
|
|
7594
|
+
height: 3px;
|
|
7595
|
+
border-radius: 14px 14px 0 0;
|
|
7596
|
+
background:
|
|
7597
|
+
radial-gradient(ellipse 280px 4px,
|
|
7598
|
+
rgba(var(--accent-rgb, 197, 101, 61), 0.45) 0%,
|
|
7599
|
+
rgba(var(--accent-rgb, 197, 101, 61), 0.12) 35%,
|
|
7600
|
+
transparent 60%)
|
|
7601
|
+
no-repeat;
|
|
7602
|
+
background-size: 280px 4px;
|
|
7603
|
+
animation: composerWaveSlide 7s cubic-bezier(0.35, 0, 0.65, 1) infinite;
|
|
7604
|
+
z-index: 2;
|
|
7605
|
+
pointer-events: none;
|
|
7606
|
+
}
|
|
7607
|
+
.input-composer.in-flight::after {
|
|
7608
|
+
content: "";
|
|
7609
|
+
position: absolute;
|
|
7610
|
+
top: -2px;
|
|
7611
|
+
left: -60%;
|
|
7612
|
+
width: 220%;
|
|
7613
|
+
height: 3px;
|
|
7614
|
+
border-radius: 14px 14px 0 0;
|
|
7615
|
+
background:
|
|
7616
|
+
radial-gradient(ellipse 220px 3px,
|
|
7617
|
+
rgba(var(--accent-rgb, 197, 101, 61), 0.25) 0%,
|
|
7618
|
+
rgba(var(--accent-rgb, 197, 101, 61), 0.06) 35%,
|
|
7619
|
+
transparent 60%)
|
|
7620
|
+
no-repeat;
|
|
7621
|
+
background-size: 220px 3px;
|
|
7622
|
+
animation: composerWaveSlide 9s cubic-bezier(0.35, 0, 0.65, 1) infinite reverse;
|
|
7623
|
+
z-index: 2;
|
|
7624
|
+
pointer-events: none;
|
|
7625
|
+
}
|
|
7626
|
+
@keyframes composerWaveSlide {
|
|
7627
|
+
0% { background-position: 0% center; }
|
|
7628
|
+
100% { background-position: 100% center; }
|
|
7629
|
+
}
|
|
7630
|
+
|
|
7631
|
+
/* ── 结构化会话状态条(输入框右上角) ── */
|
|
7525
7632
|
.structured-status-bar {
|
|
7526
7633
|
display: flex;
|
|
7527
7634
|
align-items: center;
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7635
|
+
justify-content: flex-end;
|
|
7636
|
+
gap: 5px;
|
|
7637
|
+
margin: 0 4px 2px 0;
|
|
7638
|
+
padding: 0;
|
|
7532
7639
|
background: transparent;
|
|
7533
7640
|
border: none;
|
|
7534
7641
|
font-size: 0.6875rem;
|
|
@@ -7537,35 +7644,25 @@
|
|
|
7537
7644
|
overflow: hidden;
|
|
7538
7645
|
}
|
|
7539
7646
|
|
|
7540
|
-
.structured-status-bar .status-bar-
|
|
7647
|
+
.structured-status-bar .status-bar-dot {
|
|
7648
|
+
width: 5px;
|
|
7649
|
+
height: 5px;
|
|
7650
|
+
border-radius: 50%;
|
|
7651
|
+
background: rgba(var(--accent-rgb, 197, 101, 61), 0.8);
|
|
7652
|
+
animation: statusDotPulse 1.2s ease-in-out infinite;
|
|
7541
7653
|
flex-shrink: 0;
|
|
7542
|
-
font-weight: 500;
|
|
7543
|
-
color: var(--text-muted);
|
|
7544
|
-
}
|
|
7545
|
-
|
|
7546
|
-
.structured-status-bar .status-bar-track {
|
|
7547
|
-
flex: 1;
|
|
7548
|
-
height: 2px;
|
|
7549
|
-
border-radius: 1px;
|
|
7550
|
-
background: rgba(var(--accent-rgb, 99, 102, 241), 0.08);
|
|
7551
|
-
overflow: hidden;
|
|
7552
|
-
position: relative;
|
|
7553
7654
|
}
|
|
7554
7655
|
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
left: 0;
|
|
7559
|
-
width: 30%;
|
|
7560
|
-
height: 100%;
|
|
7561
|
-
border-radius: 1px;
|
|
7562
|
-
background: linear-gradient(90deg, transparent, var(--accent-soft), transparent);
|
|
7563
|
-
animation: marqueeScroll 1.5s ease-in-out infinite;
|
|
7656
|
+
@keyframes statusDotPulse {
|
|
7657
|
+
0%, 100% { opacity: 0.4; transform: scale(0.9); }
|
|
7658
|
+
50% { opacity: 1; transform: scale(1.1); }
|
|
7564
7659
|
}
|
|
7565
7660
|
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7661
|
+
.structured-status-bar .status-bar-label {
|
|
7662
|
+
flex-shrink: 0;
|
|
7663
|
+
font-weight: 500;
|
|
7664
|
+
color: var(--text-muted);
|
|
7665
|
+
font-size: 0.625rem;
|
|
7569
7666
|
}
|
|
7570
7667
|
|
|
7571
7668
|
.structured-status-bar .status-bar-timer {
|
|
@@ -7578,8 +7675,6 @@
|
|
|
7578
7675
|
|
|
7579
7676
|
/* 完成态 */
|
|
7580
7677
|
.structured-status-bar.completed {
|
|
7581
|
-
background: transparent;
|
|
7582
|
-
border-color: transparent;
|
|
7583
7678
|
animation: statusBarFadeOut 2s ease-out 1s forwards;
|
|
7584
7679
|
}
|
|
7585
7680
|
|
|
@@ -7587,22 +7682,10 @@
|
|
|
7587
7682
|
color: var(--success);
|
|
7588
7683
|
}
|
|
7589
7684
|
|
|
7590
|
-
.structured-status-bar.completed .status-bar-track {
|
|
7591
|
-
background: rgba(79, 122, 88, 0.1);
|
|
7592
|
-
}
|
|
7593
|
-
|
|
7594
|
-
.structured-status-bar.completed .status-bar-fill {
|
|
7595
|
-
width: 100%;
|
|
7596
|
-
background: var(--success);
|
|
7597
|
-
opacity: 0.5;
|
|
7598
|
-
animation: none;
|
|
7599
|
-
left: 0;
|
|
7600
|
-
}
|
|
7601
|
-
|
|
7602
7685
|
@keyframes statusBarFadeOut {
|
|
7603
|
-
0% { opacity: 1; max-height:
|
|
7604
|
-
70% { opacity: 0; max-height:
|
|
7605
|
-
100% { opacity: 0; max-height: 0; margin: 0;
|
|
7686
|
+
0% { opacity: 1; max-height: 20px; }
|
|
7687
|
+
70% { opacity: 0; max-height: 20px; }
|
|
7688
|
+
100% { opacity: 0; max-height: 0; margin: 0; }
|
|
7606
7689
|
}
|
|
7607
7690
|
|
|
7608
7691
|
/* 结束标记 */
|