@co0ontty/wand 1.21.14 → 1.21.16
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 +131 -115
- package/dist/web-ui/content/styles.css +0 -45
- package/package.json +1 -1
|
@@ -328,29 +328,81 @@
|
|
|
328
328
|
// 先驱动跨视图的运行指示器(顶部进度条/徽章计时/气泡呼吸条)
|
|
329
329
|
updateRunningIndicators(session);
|
|
330
330
|
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
// 顶部徽章计时复用。
|
|
335
|
-
var legacy = document.querySelector(".structured-status-bar");
|
|
336
|
-
if (legacy) legacy.remove();
|
|
337
|
-
|
|
331
|
+
// Status bar now lives in .composer-top-row alongside the todo-progress collapse bar
|
|
332
|
+
var topRow = document.querySelector(".composer-top-row");
|
|
333
|
+
var existing = document.querySelector(".structured-status-bar");
|
|
338
334
|
var composer = document.querySelector(".input-composer");
|
|
339
335
|
if (!session || !isStructuredSession(session)) {
|
|
336
|
+
if (existing) existing.remove();
|
|
340
337
|
if (composer) composer.classList.remove("in-flight");
|
|
341
|
-
|
|
342
|
-
|
|
338
|
+
clearInterval(_statusBarTimerId);
|
|
339
|
+
_statusBarTimerId = null;
|
|
343
340
|
return;
|
|
344
341
|
}
|
|
345
342
|
|
|
346
|
-
var isInFlight =
|
|
343
|
+
var isInFlight = session.structuredState && session.structuredState.inFlight;
|
|
344
|
+
|
|
347
345
|
if (isInFlight) {
|
|
348
|
-
if
|
|
346
|
+
// Start timer if not already running
|
|
347
|
+
if (!_statusBarTimerId) {
|
|
348
|
+
_statusBarStartTime = Date.now();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Add glow to input composer
|
|
349
352
|
if (composer) composer.classList.add("in-flight");
|
|
353
|
+
|
|
354
|
+
if (!existing && topRow) {
|
|
355
|
+
var bar = document.createElement("div");
|
|
356
|
+
bar.className = "structured-status-bar";
|
|
357
|
+
bar.innerHTML =
|
|
358
|
+
'<span class="status-bar-dot"></span>' +
|
|
359
|
+
'<span class="status-bar-label">回复中</span>' +
|
|
360
|
+
'<span class="status-bar-timer">0.0s</span>';
|
|
361
|
+
// Append as last child of the top row so it sits to the right of the todo bar
|
|
362
|
+
topRow.appendChild(bar);
|
|
363
|
+
existing = bar;
|
|
364
|
+
} else if (existing && existing.classList.contains("completed")) {
|
|
365
|
+
// Was completed, now in-flight again — reset
|
|
366
|
+
existing.classList.remove("completed");
|
|
367
|
+
existing.style.animation = "none";
|
|
368
|
+
existing.querySelector(".status-bar-label").textContent = "回复中";
|
|
369
|
+
var dot = existing.querySelector(".status-bar-dot");
|
|
370
|
+
if (dot) dot.style.display = "";
|
|
371
|
+
_statusBarStartTime = Date.now();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Start interval to update timer
|
|
375
|
+
if (!_statusBarTimerId) {
|
|
376
|
+
_statusBarTimerId = setInterval(function() {
|
|
377
|
+
var bar = document.querySelector(".structured-status-bar:not(.completed)");
|
|
378
|
+
if (!bar) { clearInterval(_statusBarTimerId); _statusBarTimerId = null; return; }
|
|
379
|
+
var elapsed = ((Date.now() - _statusBarStartTime) / 1000).toFixed(1);
|
|
380
|
+
var timerEl = bar.querySelector(".status-bar-timer");
|
|
381
|
+
if (timerEl) timerEl.textContent = elapsed + "s";
|
|
382
|
+
}, 100);
|
|
383
|
+
}
|
|
350
384
|
} else {
|
|
385
|
+
// Not in-flight: show completion or remove
|
|
386
|
+
clearInterval(_statusBarTimerId);
|
|
387
|
+
_statusBarTimerId = null;
|
|
388
|
+
|
|
389
|
+
// Remove glow from input composer
|
|
351
390
|
if (composer) composer.classList.remove("in-flight");
|
|
352
|
-
|
|
353
|
-
if (
|
|
391
|
+
|
|
392
|
+
if (existing && !existing.classList.contains("completed")) {
|
|
393
|
+
// Just finished — transition to completed state
|
|
394
|
+
var elapsed = _statusBarStartTime ? ((Date.now() - _statusBarStartTime) / 1000).toFixed(1) : "0.0";
|
|
395
|
+
existing.classList.add("completed");
|
|
396
|
+
existing.querySelector(".status-bar-label").textContent = "完成";
|
|
397
|
+
existing.querySelector(".status-bar-timer").textContent = elapsed + "s";
|
|
398
|
+
var dot = existing.querySelector(".status-bar-dot");
|
|
399
|
+
if (dot) dot.style.display = "none";
|
|
400
|
+
_statusBarStartTime = 0;
|
|
401
|
+
// Remove after animation ends
|
|
402
|
+
setTimeout(function() {
|
|
403
|
+
if (existing.parentNode) existing.remove();
|
|
404
|
+
}, 3000);
|
|
405
|
+
}
|
|
354
406
|
}
|
|
355
407
|
}
|
|
356
408
|
|
|
@@ -1373,12 +1425,10 @@
|
|
|
1373
1425
|
'<div class="todo-progress-header" id="todo-progress-toggle">' +
|
|
1374
1426
|
'<div class="todo-progress-left">' +
|
|
1375
1427
|
'<span class="todo-progress-spinner"></span>' +
|
|
1376
|
-
'<span class="todo-progress-
|
|
1377
|
-
|
|
1378
|
-
'<div class="todo-progress-right">' +
|
|
1379
|
-
'<span class="todo-progress-status" id="todo-progress-status">运行中</span>' +
|
|
1380
|
-
'<svg class="todo-progress-chevron" width="14" height="14" 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>' +
|
|
1428
|
+
'<span class="todo-progress-counter" id="todo-progress-counter">0/0</span>' +
|
|
1429
|
+
'<span class="todo-progress-task" id="todo-progress-task"></span>' +
|
|
1381
1430
|
'</div>' +
|
|
1431
|
+
'<svg class="todo-progress-chevron" width="14" height="14" 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>' +
|
|
1382
1432
|
'</div>' +
|
|
1383
1433
|
'<div class="todo-progress-body hidden" id="todo-progress-body">' +
|
|
1384
1434
|
'<ul class="todo-progress-list" id="todo-progress-list"></ul>' +
|
|
@@ -13746,6 +13796,17 @@
|
|
|
13746
13796
|
}
|
|
13747
13797
|
|
|
13748
13798
|
chatMessages.innerHTML = html;
|
|
13799
|
+
// 会话切换 / 首次渲染后,浏览器会把旧的 scrollTop 钳制到新内容
|
|
13800
|
+
// 的最大值——column-reverse 下这意味着视觉上跳到最上面(最旧消息),
|
|
13801
|
+
// 也就是用户反馈的"退出再回来时被重定向到最上面"。这里在
|
|
13802
|
+
// prevMsgCount === 0(resetChatRenderCache 后或刚从空状态进入)
|
|
13803
|
+
// 强制 scrollTop=0(视觉底部 = 最新消息),避免错位。
|
|
13804
|
+
// 注意:仅在"换会话/初次渲染"时强制重置;同一会话内的全量重渲染
|
|
13805
|
+
// (prevMsgCount > 0,比如 forceFullRender 或消息数减少)继续走
|
|
13806
|
+
// smartScrollToBottom,尊重用户的 chatAutoFollow 选择。
|
|
13807
|
+
if (prevMsgCount === 0) {
|
|
13808
|
+
chatMessages.scrollTop = 0;
|
|
13809
|
+
}
|
|
13749
13810
|
attachAllCopyHandlers(chatMessages);
|
|
13750
13811
|
bindChatScrollListener();
|
|
13751
13812
|
applyPersistedExpandState(chatMessages);
|
|
@@ -13970,122 +14031,77 @@
|
|
|
13970
14031
|
});
|
|
13971
14032
|
|
|
13972
14033
|
function updateTodoProgress(messages) {
|
|
13973
|
-
var
|
|
13974
|
-
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
var lastUserText = "";
|
|
13984
|
-
if (Array.isArray(messages)) {
|
|
13985
|
-
for (var i = messages.length - 1; i >= 0; i--) {
|
|
13986
|
-
var umsg = messages[i];
|
|
13987
|
-
if (!umsg || umsg.role !== "user" || !Array.isArray(umsg.content)) continue;
|
|
13988
|
-
for (var uj = 0; uj < umsg.content.length; uj++) {
|
|
13989
|
-
var ublock = umsg.content[uj];
|
|
13990
|
-
if (ublock && ublock.type === "text" && ublock.text && ublock.text.trim()) {
|
|
13991
|
-
lastUserText = ublock.text.trim().replace(/\s+/g, " ");
|
|
13992
|
-
break;
|
|
13993
|
-
}
|
|
14034
|
+
var todos = null;
|
|
14035
|
+
// Scan all messages for latest TodoWrite tool_use
|
|
14036
|
+
for (var i = messages.length - 1; i >= 0; i--) {
|
|
14037
|
+
var msg = messages[i];
|
|
14038
|
+
if (!msg.content || !Array.isArray(msg.content)) continue;
|
|
14039
|
+
for (var j = msg.content.length - 1; j >= 0; j--) {
|
|
14040
|
+
var block = msg.content[j];
|
|
14041
|
+
if (block.type === "tool_use" && block.name === "TodoWrite" && block.input && block.input.todos) {
|
|
14042
|
+
todos = block.input.todos;
|
|
14043
|
+
break;
|
|
13994
14044
|
}
|
|
13995
|
-
if (lastUserText) break;
|
|
13996
14045
|
}
|
|
14046
|
+
if (todos) break;
|
|
13997
14047
|
}
|
|
13998
14048
|
|
|
13999
|
-
|
|
14000
|
-
if (!
|
|
14049
|
+
var container = document.getElementById("todo-progress");
|
|
14050
|
+
if (!container) return;
|
|
14051
|
+
|
|
14052
|
+
if (!todos || todos.length === 0) {
|
|
14001
14053
|
container.classList.add("hidden");
|
|
14002
14054
|
return;
|
|
14003
14055
|
}
|
|
14004
|
-
container.classList.remove("hidden");
|
|
14005
14056
|
|
|
14006
|
-
|
|
14007
|
-
var sig = computeRunningSignal(session);
|
|
14008
|
-
var isStructured = isStructuredSession(session);
|
|
14009
|
-
var isRunning = isStructured ? !!sig.inFlight : !!sig.ptyRunning;
|
|
14010
|
-
var statusText = isRunning
|
|
14011
|
-
? (isStructured ? "运行中" : "正在运行")
|
|
14012
|
-
: "已完成";
|
|
14057
|
+
container.classList.remove("hidden");
|
|
14013
14058
|
|
|
14014
|
-
|
|
14015
|
-
var
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
|
|
14019
|
-
|
|
14020
|
-
|
|
14021
|
-
|
|
14022
|
-
|
|
14023
|
-
todos = tblock.input.todos;
|
|
14024
|
-
break;
|
|
14025
|
-
}
|
|
14059
|
+
var completed = 0;
|
|
14060
|
+
var inProgress = 0;
|
|
14061
|
+
var activeTask = "";
|
|
14062
|
+
for (var k = 0; k < todos.length; k++) {
|
|
14063
|
+
if (todos[k].status === "completed") completed++;
|
|
14064
|
+
if (todos[k].status === "in_progress") {
|
|
14065
|
+
inProgress++;
|
|
14066
|
+
if (!activeTask) {
|
|
14067
|
+
activeTask = todos[k].activeForm || todos[k].content || "";
|
|
14026
14068
|
}
|
|
14027
|
-
if (todos) break;
|
|
14028
14069
|
}
|
|
14029
14070
|
}
|
|
14030
14071
|
|
|
14031
|
-
|
|
14032
|
-
var
|
|
14033
|
-
var
|
|
14034
|
-
if (
|
|
14035
|
-
|
|
14036
|
-
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
|
|
14040
|
-
}
|
|
14041
|
-
allTodosDone = completedCount === todos.length;
|
|
14042
|
-
if (isRunning && !allTodosDone) {
|
|
14043
|
-
var stepIdx = Math.max(1, completedCount + (inProgressCount > 0 ? 1 : 0));
|
|
14044
|
-
stepLabel = " · 第 " + stepIdx + "/" + todos.length + " 步";
|
|
14045
|
-
}
|
|
14072
|
+
// 显示当前执行步骤 = 已完成 + 正在进行(如果有)
|
|
14073
|
+
var currentStep = completed + inProgress;
|
|
14074
|
+
var allDone = completed === todos.length;
|
|
14075
|
+
if (allDone) {
|
|
14076
|
+
// Hide todo when all tasks are completed
|
|
14077
|
+
container.classList.add("hidden");
|
|
14078
|
+
return;
|
|
14079
|
+
} else {
|
|
14080
|
+
container.classList.remove("all-done");
|
|
14046
14081
|
}
|
|
14047
14082
|
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
// 复用已有 .all-done 样式(隐藏 spinner + 显示绿色 ✓)
|
|
14051
|
-
container.classList.toggle("all-done", !isRunning);
|
|
14052
|
-
container.classList.toggle("has-todos", hasTodos);
|
|
14053
|
-
|
|
14054
|
-
// ── 写入 DOM ──
|
|
14055
|
-
var msgEl = document.getElementById("todo-progress-message");
|
|
14056
|
-
if (msgEl) msgEl.textContent = lastUserText;
|
|
14083
|
+
var counter = document.getElementById("todo-progress-counter");
|
|
14084
|
+
if (counter) counter.textContent = currentStep + "/" + todos.length;
|
|
14057
14085
|
|
|
14058
|
-
var
|
|
14059
|
-
if (
|
|
14086
|
+
var task = document.getElementById("todo-progress-task");
|
|
14087
|
+
if (task) task.textContent = activeTask;
|
|
14060
14088
|
|
|
14061
|
-
//
|
|
14089
|
+
// Render expanded list
|
|
14062
14090
|
var list = document.getElementById("todo-progress-list");
|
|
14063
14091
|
if (list) {
|
|
14064
|
-
|
|
14065
|
-
|
|
14066
|
-
|
|
14067
|
-
var
|
|
14068
|
-
|
|
14069
|
-
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
'</li>';
|
|
14078
|
-
}
|
|
14079
|
-
list.innerHTML = html;
|
|
14080
|
-
}
|
|
14081
|
-
}
|
|
14082
|
-
|
|
14083
|
-
// 没有 todos 时强制收起 body(没东西可展开)
|
|
14084
|
-
if (!hasTodos) {
|
|
14085
|
-
todoExpanded = false;
|
|
14086
|
-
container.classList.remove("expanded");
|
|
14087
|
-
var body = document.getElementById("todo-progress-body");
|
|
14088
|
-
if (body) body.classList.add("hidden");
|
|
14092
|
+
var html = "";
|
|
14093
|
+
for (var m = 0; m < todos.length; m++) {
|
|
14094
|
+
var t = todos[m];
|
|
14095
|
+
var st = t.status || "pending";
|
|
14096
|
+
var itemClass = st === "in_progress" ? "active" : st === "completed" ? "done" : "";
|
|
14097
|
+
var iconClass = st === "in_progress" ? "active" : st === "completed" ? "done" : "pending";
|
|
14098
|
+
var icon = st === "completed" ? "✓" : st === "in_progress" ? "›" : "○";
|
|
14099
|
+
html += '<li class="todo-progress-item ' + itemClass + '">' +
|
|
14100
|
+
'<span class="todo-item-icon ' + iconClass + '">' + icon + '</span>' +
|
|
14101
|
+
'<span>' + escapeHtml(t.content || "") + '</span>' +
|
|
14102
|
+
'</li>';
|
|
14103
|
+
}
|
|
14104
|
+
list.innerHTML = html;
|
|
14089
14105
|
}
|
|
14090
14106
|
|
|
14091
14107
|
// Sync todo progress to native notification
|
|
@@ -5390,38 +5390,6 @@
|
|
|
5390
5390
|
text-overflow: ellipsis;
|
|
5391
5391
|
white-space: nowrap;
|
|
5392
5392
|
}
|
|
5393
|
-
/* 左侧:用户最新发出的消息 */
|
|
5394
|
-
.todo-progress-message {
|
|
5395
|
-
flex: 1;
|
|
5396
|
-
min-width: 0;
|
|
5397
|
-
font-size: 0.75rem;
|
|
5398
|
-
font-weight: 500;
|
|
5399
|
-
color: var(--text-primary);
|
|
5400
|
-
overflow: hidden;
|
|
5401
|
-
text-overflow: ellipsis;
|
|
5402
|
-
white-space: nowrap;
|
|
5403
|
-
}
|
|
5404
|
-
/* 右侧:状态文字 + 折叠箭头 */
|
|
5405
|
-
.todo-progress-right {
|
|
5406
|
-
display: flex;
|
|
5407
|
-
align-items: center;
|
|
5408
|
-
gap: 6px;
|
|
5409
|
-
flex-shrink: 0;
|
|
5410
|
-
min-width: 0;
|
|
5411
|
-
}
|
|
5412
|
-
.todo-progress-status {
|
|
5413
|
-
font-size: 0.75rem;
|
|
5414
|
-
font-weight: 500;
|
|
5415
|
-
color: var(--text-secondary);
|
|
5416
|
-
white-space: nowrap;
|
|
5417
|
-
letter-spacing: 0.01em;
|
|
5418
|
-
}
|
|
5419
|
-
.todo-progress.is-running .todo-progress-status {
|
|
5420
|
-
color: var(--accent);
|
|
5421
|
-
}
|
|
5422
|
-
.todo-progress.all-done .todo-progress-status {
|
|
5423
|
-
color: #4a7a4f;
|
|
5424
|
-
}
|
|
5425
5393
|
.todo-progress-chevron {
|
|
5426
5394
|
color: var(--text-muted);
|
|
5427
5395
|
flex-shrink: 0;
|
|
@@ -5430,16 +5398,6 @@
|
|
|
5430
5398
|
.todo-progress.expanded .todo-progress-chevron {
|
|
5431
5399
|
transform: rotate(180deg);
|
|
5432
5400
|
}
|
|
5433
|
-
/* 没有 todo 列表时,没什么可展开的——隐藏箭头并去掉指针手势 */
|
|
5434
|
-
.todo-progress:not(.has-todos) .todo-progress-chevron {
|
|
5435
|
-
display: none;
|
|
5436
|
-
}
|
|
5437
|
-
.todo-progress:not(.has-todos) .todo-progress-header {
|
|
5438
|
-
cursor: default;
|
|
5439
|
-
}
|
|
5440
|
-
.todo-progress:not(.has-todos) .todo-progress-header:hover {
|
|
5441
|
-
background: transparent;
|
|
5442
|
-
}
|
|
5443
5401
|
.todo-progress-body {
|
|
5444
5402
|
border-top: 1px solid var(--border-subtle);
|
|
5445
5403
|
padding: 6px 10px 8px;
|
|
@@ -8580,9 +8538,6 @@
|
|
|
8580
8538
|
.todo-progress-header { padding: 3px 6px; gap: 4px; }
|
|
8581
8539
|
.todo-progress-counter { font-size: 0.5625rem; }
|
|
8582
8540
|
.todo-progress-task { font-size: 0.5625rem; }
|
|
8583
|
-
.todo-progress-message { font-size: 0.625rem; }
|
|
8584
|
-
.todo-progress-status { font-size: 0.5625rem; }
|
|
8585
|
-
.todo-progress-right { gap: 4px; }
|
|
8586
8541
|
.todo-progress-chevron { width: 12px; height: 12px; }
|
|
8587
8542
|
.todo-progress-body { padding: 4px 6px; }
|
|
8588
8543
|
|