@co0ontty/wand 1.22.0 → 1.23.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.
@@ -1423,15 +1423,22 @@
1423
1423
  '<div class="composer-top-row">' +
1424
1424
  '<div id="todo-progress" class="todo-progress hidden">' +
1425
1425
  '<div class="todo-progress-header" id="todo-progress-toggle">' +
1426
- '<span class="todo-progress-spinner" aria-hidden="true"></span>' +
1427
- '<span class="todo-progress-message" id="todo-progress-message"></span>' +
1428
- '<span class="todo-progress-status" id="todo-progress-status">执行中</span>' +
1429
- '<svg class="todo-progress-chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>' +
1430
- '</div>' +
1431
- '<div class="todo-progress-body hidden" id="todo-progress-body">' +
1432
- '<ul class="todo-progress-list" id="todo-progress-list"></ul>' +
1426
+ '<div class="todo-progress-left">' +
1427
+ '<span class="todo-progress-ring" id="todo-progress-ring" aria-hidden="true" style="--progress:0">' +
1428
+ '<svg width="16" height="16" viewBox="0 0 36 36">' +
1429
+ '<circle class="todo-ring-track" cx="18" cy="18" r="15.5" fill="none" stroke-width="4"/>' +
1430
+ '<circle class="todo-ring-fill" cx="18" cy="18" r="15.5" fill="none" stroke-width="4" stroke-linecap="round"/>' +
1431
+ '</svg>' +
1432
+ '</span>' +
1433
+ '<span class="todo-progress-counter" id="todo-progress-counter"></span>' +
1434
+ '<span class="todo-progress-task" id="todo-progress-task"></span>' +
1435
+ '</div>' +
1436
+ '<svg class="todo-progress-chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 15 12 9 18 15"/></svg>' +
1433
1437
  '</div>' +
1434
1438
  '</div>' +
1439
+ '<div class="todo-progress-body hidden" id="todo-progress-body">' +
1440
+ '<ul class="todo-progress-list" id="todo-progress-list"></ul>' +
1441
+ '</div>' +
1435
1442
  '</div>' +
1436
1443
  '<div class="input-composer">' +
1437
1444
  '<button id="prompt-optimize-btn" class="prompt-optimize-btn" type="button" title="提示词优化(AI)" aria-label="提示词优化">' +
@@ -7588,6 +7595,14 @@
7588
7595
  // Reset todo progress bar
7589
7596
  var todoEl = document.getElementById("todo-progress");
7590
7597
  if (todoEl) todoEl.classList.add("hidden");
7598
+ // 同时清掉上一会话残留的 "回复中 N.Ns" 状态条以及它的计时器/glow。
7599
+ // 不清就会出现:切到新建的空会话,底部仍显示前一会话的 todolist + 回复中。
7600
+ var staleStatusBar = document.querySelector(".structured-status-bar");
7601
+ if (staleStatusBar) staleStatusBar.remove();
7602
+ if (_statusBarTimerId) { clearInterval(_statusBarTimerId); _statusBarTimerId = null; }
7603
+ _statusBarStartTime = 0;
7604
+ var staleComposer = document.querySelector(".input-composer");
7605
+ if (staleComposer) staleComposer.classList.remove("in-flight");
7591
7606
  var session = foundSession;
7592
7607
  state.preferredCommand = getPreferredTool();
7593
7608
  state.chatMode = getSafeModeForTool("claude", session && session.mode ? session.mode : state.chatMode);
@@ -13795,6 +13810,10 @@
13795
13810
  state.lastRenderedEmpty = "empty";
13796
13811
  state.lastRenderedMsgCount = 0;
13797
13812
  }
13813
+ // 空会话进入空状态前,把上一会话残留的状态条 / todo 进度条清掉。
13814
+ // 这里是 selectSession 之外的兜底:WS init 等异步路径也会落到这条空分支。
13815
+ renderStructuredStatusBar(null, selectedSession);
13816
+ updateTodoProgress([]);
13798
13817
  return;
13799
13818
  }
13800
13819
 
@@ -14132,10 +14151,10 @@
14132
14151
  if (prog && body) {
14133
14152
  if (todoExpanded) {
14134
14153
  prog.classList.add("expanded");
14135
- body.classList.remove("hidden");
14154
+ body.classList.add("expanded");
14136
14155
  } else {
14137
14156
  prog.classList.remove("expanded");
14138
- body.classList.add("hidden");
14157
+ body.classList.remove("expanded");
14139
14158
  }
14140
14159
  }
14141
14160
  });
@@ -14157,14 +14176,17 @@
14157
14176
  }
14158
14177
 
14159
14178
  var container = document.getElementById("todo-progress");
14179
+ var bodyEl = document.getElementById("todo-progress-body");
14160
14180
  if (!container) return;
14161
14181
 
14162
14182
  if (!todos || todos.length === 0) {
14163
14183
  container.classList.add("hidden");
14184
+ if (bodyEl) bodyEl.classList.add("hidden");
14164
14185
  return;
14165
14186
  }
14166
14187
 
14167
14188
  container.classList.remove("hidden");
14189
+ if (bodyEl) bodyEl.classList.remove("hidden");
14168
14190
 
14169
14191
  var completed = 0;
14170
14192
  var inProgress = 0;
@@ -14186,6 +14208,7 @@
14186
14208
  if (allDone) {
14187
14209
  // Hide todo when all tasks are completed
14188
14210
  container.classList.add("hidden");
14211
+ if (bodyEl) bodyEl.classList.add("hidden");
14189
14212
  return;
14190
14213
  } else {
14191
14214
  container.classList.remove("all-done");
@@ -14197,6 +14220,14 @@
14197
14220
  var task = document.getElementById("todo-progress-task");
14198
14221
  if (task) task.textContent = activeTask;
14199
14222
 
14223
+ // Drive the circular progress ring with the honest "completed / total" fraction
14224
+ // (counter text shows the 1-indexed current step, ring shows actual done ratio).
14225
+ var ring = document.getElementById("todo-progress-ring");
14226
+ if (ring) {
14227
+ var ratio = todos.length > 0 ? completed / todos.length : 0;
14228
+ ring.style.setProperty("--progress", ratio.toFixed(3));
14229
+ }
14230
+
14200
14231
  // Render expanded list
14201
14232
  var list = document.getElementById("todo-progress-list");
14202
14233
  if (list) {
@@ -5345,10 +5345,12 @@
5345
5345
  .input-label { font-size: 0.6875rem; color: var(--text-muted); font-weight: 500; }
5346
5346
  .input-textarea-wrap { position: relative; width: 100%; }
5347
5347
 
5348
- /* Composer top row: holds todo collapse bar (left) + reply status bar (right) on one line */
5348
+ /* Composer top row: holds todo collapse bar (left) + reply status bar (right) on one line.
5349
+ position: relative anchors the upward-floating .todo-progress-body to the full row width. */
5349
5350
  .composer-top-row {
5351
+ position: relative;
5350
5352
  display: flex;
5351
- align-items: flex-start;
5353
+ align-items: center;
5352
5354
  gap: 8px;
5353
5355
  min-width: 0;
5354
5356
  }
@@ -5391,31 +5393,52 @@
5391
5393
  min-width: 0;
5392
5394
  flex: 1;
5393
5395
  }
5394
- .todo-progress-spinner {
5395
- width: 14px;
5396
- height: 14px;
5397
- min-width: 14px;
5398
- border: 2px solid var(--accent-muted);
5399
- border-top-color: var(--accent);
5400
- border-radius: 50%;
5401
- animation: spin 0.8s linear infinite;
5396
+ /* Circular progress ring (replaces the old plain spinner) */
5397
+ .todo-progress-ring {
5398
+ position: relative;
5399
+ display: inline-flex;
5400
+ align-items: center;
5401
+ justify-content: center;
5402
+ width: 16px;
5403
+ height: 16px;
5404
+ min-width: 16px;
5405
+ flex-shrink: 0;
5402
5406
  }
5403
- .todo-progress.all-done .todo-progress-spinner {
5404
- display: none;
5407
+ .todo-progress-ring svg {
5408
+ width: 100%;
5409
+ height: 100%;
5410
+ transform: rotate(-90deg);
5405
5411
  }
5406
- .todo-progress.all-done .todo-progress-left::before {
5407
- content: "✓";
5408
- font-size: 0.8125rem;
5409
- font-weight: 700;
5410
- color: #4a7a4f;
5411
- width: 14px;
5412
- text-align: center;
5412
+ .todo-ring-track {
5413
+ stroke: rgba(197, 101, 61, 0.18);
5414
+ }
5415
+ .todo-ring-fill {
5416
+ stroke: var(--accent);
5417
+ /* circumference = 2 * π * 15.5 ≈ 97.39 */
5418
+ stroke-dasharray: 97.39;
5419
+ stroke-dashoffset: calc(97.39 * (1 - var(--progress, 0)));
5420
+ transition: stroke-dashoffset 0.35s ease;
5421
+ }
5422
+ .todo-progress-ring::after {
5423
+ content: "";
5424
+ position: absolute;
5425
+ width: 4px;
5426
+ height: 4px;
5427
+ border-radius: 50%;
5428
+ background: var(--accent);
5429
+ opacity: 0.6;
5430
+ animation: ringPulse 1.4s ease-in-out infinite;
5431
+ }
5432
+ @keyframes ringPulse {
5433
+ 0%, 100% { opacity: 0.35; transform: scale(0.85); }
5434
+ 50% { opacity: 0.95; transform: scale(1.05); }
5413
5435
  }
5414
5436
  .todo-progress-counter {
5415
5437
  font-size: 0.75rem;
5416
5438
  font-weight: 600;
5417
5439
  color: var(--text-primary);
5418
5440
  white-space: nowrap;
5441
+ font-variant-numeric: tabular-nums;
5419
5442
  }
5420
5443
  .todo-progress-task {
5421
5444
  font-size: 0.75rem;
@@ -5432,44 +5455,84 @@
5432
5455
  .todo-progress.expanded .todo-progress-chevron {
5433
5456
  transform: rotate(180deg);
5434
5457
  }
5458
+
5459
+ /* Expanded body floats UPWARD above the composer, spans the full row width,
5460
+ and is decoupled from the flex row so the status bar on the right does not
5461
+ animate together with the expansion. */
5435
5462
  .todo-progress-body {
5436
- border-top: 1px solid var(--border-subtle);
5437
- padding: 6px 10px 8px;
5463
+ position: absolute;
5464
+ left: 0;
5465
+ right: 0;
5466
+ bottom: calc(100% + 6px);
5467
+ background: rgba(255, 251, 246, 0.98);
5468
+ border: 1px solid var(--border-default);
5469
+ border-radius: 12px;
5470
+ padding: 8px 10px;
5471
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08), 0 2px 6px rgba(0, 0, 0, 0.04);
5472
+ backdrop-filter: blur(8px);
5473
+ -webkit-backdrop-filter: blur(8px);
5474
+ max-height: 320px;
5475
+ overflow-y: auto;
5476
+ z-index: 30;
5477
+ opacity: 0;
5478
+ transform: translateY(6px);
5479
+ pointer-events: none;
5480
+ transition: opacity 0.18s ease, transform 0.18s ease;
5438
5481
  }
5439
5482
  .todo-progress-body.hidden { display: none; }
5483
+ .todo-progress-body.expanded {
5484
+ opacity: 1;
5485
+ transform: translateY(0);
5486
+ pointer-events: auto;
5487
+ }
5440
5488
  .todo-progress-list {
5441
5489
  list-style: none;
5442
5490
  margin: 0;
5443
5491
  padding: 0;
5444
5492
  display: flex;
5445
5493
  flex-direction: column;
5446
- gap: 3px;
5494
+ gap: 1px;
5447
5495
  }
5448
5496
  .todo-progress-item {
5449
5497
  display: flex;
5450
5498
  align-items: center;
5451
- gap: 8px;
5452
- padding: 3px 4px;
5453
- border-radius: 6px;
5454
- font-size: 0.75rem;
5499
+ gap: 10px;
5500
+ padding: 6px 8px;
5501
+ border-radius: 8px;
5502
+ font-size: 0.8125rem;
5455
5503
  color: var(--text-secondary);
5504
+ line-height: 1.35;
5456
5505
  transition: background var(--transition-fast);
5457
5506
  }
5507
+ .todo-progress-item + .todo-progress-item {
5508
+ border-top: 1px solid var(--border-subtle);
5509
+ border-radius: 0;
5510
+ }
5511
+ .todo-progress-item:first-child { border-top-left-radius: 8px; border-top-right-radius: 8px; }
5512
+ .todo-progress-item:last-child { border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; }
5458
5513
  .todo-progress-item.active {
5459
5514
  color: var(--text-primary);
5460
5515
  font-weight: 500;
5461
- background: rgba(197, 101, 61, 0.06);
5516
+ background: rgba(197, 101, 61, 0.07);
5462
5517
  }
5463
5518
  .todo-progress-item.done {
5464
5519
  color: var(--text-muted);
5465
5520
  text-decoration: line-through;
5466
5521
  text-decoration-color: var(--text-muted);
5467
5522
  }
5523
+ .todo-progress-item > span:last-child {
5524
+ flex: 1;
5525
+ min-width: 0;
5526
+ overflow: hidden;
5527
+ text-overflow: ellipsis;
5528
+ white-space: nowrap;
5529
+ }
5468
5530
  .todo-item-icon {
5469
5531
  width: 16px;
5470
5532
  min-width: 16px;
5471
5533
  text-align: center;
5472
- font-size: 0.6875rem;
5534
+ font-size: 0.75rem;
5535
+ flex-shrink: 0;
5473
5536
  }
5474
5537
  .todo-item-icon.pending { color: var(--text-muted); }
5475
5538
  .todo-item-icon.active { color: var(--accent); }
@@ -12015,7 +12078,9 @@
12015
12078
  100% { background-position: 100% center; }
12016
12079
  }
12017
12080
 
12018
- /* ── 结构化会话状态条(与 todo 折叠栏共处一行,靠右) ── */
12081
+ /* ── 结构化会话状态条(与 todo 折叠栏共处一行,靠右) ──
12082
+ Only opacity/color transition — `transition: all` would cause this bar to
12083
+ animate together with the todo bar's expansion, which looks broken. */
12019
12084
  .structured-status-bar {
12020
12085
  display: flex;
12021
12086
  align-items: center;
@@ -12028,7 +12093,7 @@
12028
12093
  border: none;
12029
12094
  font-size: 0.6875rem;
12030
12095
  color: var(--text-muted);
12031
- transition: all 0.3s ease;
12096
+ transition: opacity 0.3s ease, color 0.3s ease;
12032
12097
  overflow: hidden;
12033
12098
  flex-shrink: 0;
12034
12099
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {