@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" ? "codex exec --json" : "claude -p --output-format stream-json",
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
- // 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");
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
- _statusBarTimerId = null;
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
- // Start timer if not already running
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 (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
- }
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-counter" id="todo-progress-counter">0/0</span>' +
1429
- '<span class="todo-progress-task" id="todo-progress-task"></span>' +
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
- if (!todos || todos.length === 0) {
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
- container.classList.remove("hidden");
14047
-
14048
- var completed = 0;
14049
- var inProgress = 0;
14050
- var activeTask = "";
14051
- for (var k = 0; k < todos.length; k++) {
14052
- if (todos[k].status === "completed") completed++;
14053
- if (todos[k].status === "in_progress") {
14054
- inProgress++;
14055
- if (!activeTask) {
14056
- activeTask = todos[k].activeForm || todos[k].content || "";
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
- var currentStep = completed + inProgress;
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
- var counter = document.getElementById("todo-progress-counter");
14073
- if (counter) counter.textContent = currentStep + "/" + todos.length;
14054
+ // ── 写入 DOM ──
14055
+ var msgEl = document.getElementById("todo-progress-message");
14056
+ if (msgEl) msgEl.textContent = lastUserText;
14074
14057
 
14075
- var task = document.getElementById("todo-progress-task");
14076
- if (task) task.textContent = activeTask;
14058
+ var statusEl = document.getElementById("todo-progress-status");
14059
+ if (statusEl) statusEl.textContent = statusText + stepLabel;
14077
14060
 
14078
- // Render expanded list
14061
+ // ── 渲染展开后的 todo 列表(仅当有 todos 时) ──
14079
14062
  var list = document.getElementById("todo-progress-list");
14080
14063
  if (list) {
14081
- var html = "";
14082
- for (var m = 0; m < todos.length; m++) {
14083
- var t = todos[m];
14084
- var st = t.status || "pending";
14085
- var itemClass = st === "in_progress" ? "active" : st === "completed" ? "done" : "";
14086
- var iconClass = st === "in_progress" ? "active" : st === "completed" ? "done" : "pending";
14087
- var icon = st === "completed" ? "✓" : st === "in_progress" ? "›" : "○";
14088
- html += '<li class="todo-progress-item ' + itemClass + '">' +
14089
- '<span class="todo-item-icon ' + iconClass + '">' + icon + '</span>' +
14090
- '<span>' + escapeHtml(t.content || "") + '</span>' +
14091
- '</li>';
14092
- }
14093
- list.innerHTML = html;
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "1.21.13",
3
+ "version": "1.21.14",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {