@co0ontty/wand 1.21.13 → 1.21.14
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.
|
@@ -332,7 +332,11 @@ export class StructuredSessionManager {
|
|
|
332
332
|
sessionKind: "structured",
|
|
333
333
|
provider,
|
|
334
334
|
runner,
|
|
335
|
-
command: provider === "codex"
|
|
335
|
+
command: provider === "codex"
|
|
336
|
+
? "codex exec --json"
|
|
337
|
+
: runner === "claude-sdk"
|
|
338
|
+
? "claude-agent-sdk (stream-json)"
|
|
339
|
+
: "claude -p --output-format stream-json",
|
|
336
340
|
cwd: worktreeSetup?.cwd ?? options.cwd,
|
|
337
341
|
mode: options.mode,
|
|
338
342
|
worktreeEnabled: Boolean(worktreeSetup),
|
|
@@ -328,81 +328,29 @@
|
|
|
328
328
|
// 先驱动跨视图的运行指示器(顶部进度条/徽章计时/气泡呼吸条)
|
|
329
329
|
updateRunningIndicators(session);
|
|
330
330
|
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
331
|
+
// 旧的 .structured-status-bar(在 composer-top-row 里独立的小条)
|
|
332
|
+
// 已经合并进 #todo-progress 顶部状态条。这里只清理可能残留的旧节点 +
|
|
333
|
+
// 维护 composer in-flight 的发光态 + 同步 _statusBarStartTime 给
|
|
334
|
+
// 顶部徽章计时复用。
|
|
335
|
+
var legacy = document.querySelector(".structured-status-bar");
|
|
336
|
+
if (legacy) legacy.remove();
|
|
337
|
+
|
|
334
338
|
var composer = document.querySelector(".input-composer");
|
|
335
339
|
if (!session || !isStructuredSession(session)) {
|
|
336
|
-
if (existing) existing.remove();
|
|
337
340
|
if (composer) composer.classList.remove("in-flight");
|
|
338
|
-
clearInterval(_statusBarTimerId);
|
|
339
|
-
|
|
341
|
+
if (_statusBarTimerId) { clearInterval(_statusBarTimerId); _statusBarTimerId = null; }
|
|
342
|
+
_statusBarStartTime = 0;
|
|
340
343
|
return;
|
|
341
344
|
}
|
|
342
345
|
|
|
343
|
-
var isInFlight = session.structuredState && session.structuredState.inFlight;
|
|
344
|
-
|
|
346
|
+
var isInFlight = !!(session.structuredState && session.structuredState.inFlight);
|
|
345
347
|
if (isInFlight) {
|
|
346
|
-
|
|
347
|
-
if (!_statusBarTimerId) {
|
|
348
|
-
_statusBarStartTime = Date.now();
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Add glow to input composer
|
|
348
|
+
if (!_statusBarStartTime) _statusBarStartTime = Date.now();
|
|
352
349
|
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
|
-
}
|
|
384
350
|
} else {
|
|
385
|
-
// Not in-flight: show completion or remove
|
|
386
|
-
clearInterval(_statusBarTimerId);
|
|
387
|
-
_statusBarTimerId = null;
|
|
388
|
-
|
|
389
|
-
// Remove glow from input composer
|
|
390
351
|
if (composer) composer.classList.remove("in-flight");
|
|
391
|
-
|
|
392
|
-
if (
|
|
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
|
-
}
|
|
352
|
+
_statusBarStartTime = 0;
|
|
353
|
+
if (_statusBarTimerId) { clearInterval(_statusBarTimerId); _statusBarTimerId = null; }
|
|
406
354
|
}
|
|
407
355
|
}
|
|
408
356
|
|
|
@@ -1425,10 +1373,12 @@
|
|
|
1425
1373
|
'<div class="todo-progress-header" id="todo-progress-toggle">' +
|
|
1426
1374
|
'<div class="todo-progress-left">' +
|
|
1427
1375
|
'<span class="todo-progress-spinner"></span>' +
|
|
1428
|
-
'<span class="todo-progress-
|
|
1429
|
-
|
|
1376
|
+
'<span class="todo-progress-message" id="todo-progress-message"></span>' +
|
|
1377
|
+
'</div>' +
|
|
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>' +
|
|
1430
1381
|
'</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>' +
|
|
1432
1382
|
'</div>' +
|
|
1433
1383
|
'<div class="todo-progress-body hidden" id="todo-progress-body">' +
|
|
1434
1384
|
'<ul class="todo-progress-list" id="todo-progress-list"></ul>' +
|
|
@@ -14020,77 +13970,122 @@
|
|
|
14020
13970
|
});
|
|
14021
13971
|
|
|
14022
13972
|
function updateTodoProgress(messages) {
|
|
14023
|
-
var todos = null;
|
|
14024
|
-
// Scan all messages for latest TodoWrite tool_use
|
|
14025
|
-
for (var i = messages.length - 1; i >= 0; i--) {
|
|
14026
|
-
var msg = messages[i];
|
|
14027
|
-
if (!msg.content || !Array.isArray(msg.content)) continue;
|
|
14028
|
-
for (var j = msg.content.length - 1; j >= 0; j--) {
|
|
14029
|
-
var block = msg.content[j];
|
|
14030
|
-
if (block.type === "tool_use" && block.name === "TodoWrite" && block.input && block.input.todos) {
|
|
14031
|
-
todos = block.input.todos;
|
|
14032
|
-
break;
|
|
14033
|
-
}
|
|
14034
|
-
}
|
|
14035
|
-
if (todos) break;
|
|
14036
|
-
}
|
|
14037
|
-
|
|
14038
13973
|
var container = document.getElementById("todo-progress");
|
|
14039
13974
|
if (!container) return;
|
|
14040
13975
|
|
|
14041
|
-
|
|
13976
|
+
var session = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
13977
|
+
if (!session) {
|
|
14042
13978
|
container.classList.add("hidden");
|
|
14043
13979
|
return;
|
|
14044
13980
|
}
|
|
14045
13981
|
|
|
14046
|
-
|
|
14047
|
-
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
|
|
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
|
+
}
|
|
14057
13994
|
}
|
|
13995
|
+
if (lastUserText) break;
|
|
14058
13996
|
}
|
|
14059
13997
|
}
|
|
14060
13998
|
|
|
14061
|
-
//
|
|
14062
|
-
|
|
14063
|
-
var allDone = completed === todos.length;
|
|
14064
|
-
if (allDone) {
|
|
14065
|
-
// Hide todo when all tasks are completed
|
|
13999
|
+
// 没有任何用户消息(例如刚新建的空会话)才隐藏
|
|
14000
|
+
if (!lastUserText) {
|
|
14066
14001
|
container.classList.add("hidden");
|
|
14067
14002
|
return;
|
|
14068
|
-
} else {
|
|
14069
|
-
container.classList.remove("all-done");
|
|
14070
14003
|
}
|
|
14004
|
+
container.classList.remove("hidden");
|
|
14005
|
+
|
|
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
|
+
: "已完成";
|
|
14013
|
+
|
|
14014
|
+
// ── 步骤数:来自最近一次 TodoWrite ──
|
|
14015
|
+
var todos = null;
|
|
14016
|
+
if (Array.isArray(messages)) {
|
|
14017
|
+
for (var ti = messages.length - 1; ti >= 0; ti--) {
|
|
14018
|
+
var tmsg = messages[ti];
|
|
14019
|
+
if (!tmsg.content || !Array.isArray(tmsg.content)) continue;
|
|
14020
|
+
for (var tj = tmsg.content.length - 1; tj >= 0; tj--) {
|
|
14021
|
+
var tblock = tmsg.content[tj];
|
|
14022
|
+
if (tblock && tblock.type === "tool_use" && tblock.name === "TodoWrite" && tblock.input && tblock.input.todos) {
|
|
14023
|
+
todos = tblock.input.todos;
|
|
14024
|
+
break;
|
|
14025
|
+
}
|
|
14026
|
+
}
|
|
14027
|
+
if (todos) break;
|
|
14028
|
+
}
|
|
14029
|
+
}
|
|
14030
|
+
|
|
14031
|
+
var hasTodos = !!(todos && todos.length > 0);
|
|
14032
|
+
var allTodosDone = false;
|
|
14033
|
+
var stepLabel = "";
|
|
14034
|
+
if (hasTodos) {
|
|
14035
|
+
var completedCount = 0;
|
|
14036
|
+
var inProgressCount = 0;
|
|
14037
|
+
for (var k = 0; k < todos.length; k++) {
|
|
14038
|
+
if (todos[k].status === "completed") completedCount++;
|
|
14039
|
+
if (todos[k].status === "in_progress") inProgressCount++;
|
|
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
|
+
}
|
|
14046
|
+
}
|
|
14047
|
+
|
|
14048
|
+
// ── 应用状态类 ──
|
|
14049
|
+
container.classList.toggle("is-running", isRunning);
|
|
14050
|
+
// 复用已有 .all-done 样式(隐藏 spinner + 显示绿色 ✓)
|
|
14051
|
+
container.classList.toggle("all-done", !isRunning);
|
|
14052
|
+
container.classList.toggle("has-todos", hasTodos);
|
|
14071
14053
|
|
|
14072
|
-
|
|
14073
|
-
|
|
14054
|
+
// ── 写入 DOM ──
|
|
14055
|
+
var msgEl = document.getElementById("todo-progress-message");
|
|
14056
|
+
if (msgEl) msgEl.textContent = lastUserText;
|
|
14074
14057
|
|
|
14075
|
-
var
|
|
14076
|
-
if (
|
|
14058
|
+
var statusEl = document.getElementById("todo-progress-status");
|
|
14059
|
+
if (statusEl) statusEl.textContent = statusText + stepLabel;
|
|
14077
14060
|
|
|
14078
|
-
//
|
|
14061
|
+
// ── 渲染展开后的 todo 列表(仅当有 todos 时) ──
|
|
14079
14062
|
var list = document.getElementById("todo-progress-list");
|
|
14080
14063
|
if (list) {
|
|
14081
|
-
|
|
14082
|
-
|
|
14083
|
-
|
|
14084
|
-
var
|
|
14085
|
-
var
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14064
|
+
if (!hasTodos) {
|
|
14065
|
+
list.innerHTML = "";
|
|
14066
|
+
} else {
|
|
14067
|
+
var html = "";
|
|
14068
|
+
for (var m = 0; m < todos.length; m++) {
|
|
14069
|
+
var t = todos[m];
|
|
14070
|
+
var st = t.status || "pending";
|
|
14071
|
+
var itemClass = st === "in_progress" ? "active" : st === "completed" ? "done" : "";
|
|
14072
|
+
var iconClass = st === "in_progress" ? "active" : st === "completed" ? "done" : "pending";
|
|
14073
|
+
var icon = st === "completed" ? "✓" : st === "in_progress" ? "›" : "○";
|
|
14074
|
+
html += '<li class="todo-progress-item ' + itemClass + '">' +
|
|
14075
|
+
'<span class="todo-item-icon ' + iconClass + '">' + icon + '</span>' +
|
|
14076
|
+
'<span>' + escapeHtml(t.content || "") + '</span>' +
|
|
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");
|
|
14094
14089
|
}
|
|
14095
14090
|
|
|
14096
14091
|
// Sync todo progress to native notification
|
|
@@ -5390,6 +5390,38 @@
|
|
|
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
|
+
}
|
|
5393
5425
|
.todo-progress-chevron {
|
|
5394
5426
|
color: var(--text-muted);
|
|
5395
5427
|
flex-shrink: 0;
|
|
@@ -5398,6 +5430,16 @@
|
|
|
5398
5430
|
.todo-progress.expanded .todo-progress-chevron {
|
|
5399
5431
|
transform: rotate(180deg);
|
|
5400
5432
|
}
|
|
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
|
+
}
|
|
5401
5443
|
.todo-progress-body {
|
|
5402
5444
|
border-top: 1px solid var(--border-subtle);
|
|
5403
5445
|
padding: 6px 10px 8px;
|
|
@@ -8538,6 +8580,9 @@
|
|
|
8538
8580
|
.todo-progress-header { padding: 3px 6px; gap: 4px; }
|
|
8539
8581
|
.todo-progress-counter { font-size: 0.5625rem; }
|
|
8540
8582
|
.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; }
|
|
8541
8586
|
.todo-progress-chevron { width: 12px; height: 12px; }
|
|
8542
8587
|
.todo-progress-body { padding: 4px 6px; }
|
|
8543
8588
|
|