@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.
@@ -164,10 +164,13 @@
164
164
  var _statusBarStartTime = 0;
165
165
 
166
166
  function renderStructuredStatusBar(chatMessages, session) {
167
- // Remove stale bar if session changed or not structured
168
- var existing = chatMessages.querySelector(".structured-status-bar");
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
- if (!existing) {
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
- // column-reverse: first child = visual bottom
192
- chatMessages.insertBefore(bar, chatMessages.firstChild);
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 ? (isStructuredSession(selectedSession) ? 'Structured' : 'PTY') : 'PTY') + '</span>' +
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
- return session.status;
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
- function fetchRecentPaths(callback) {
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) ? "Structured" : "PTY";
3516
+ return isStructuredSession(session) ? "结构化" : "终端";
3494
3517
  }
3495
3518
 
3496
3519
  function getSessionKindDescription(session) {
3497
3520
  return isStructuredSession(session)
3498
- ? "Structured · block transcript"
3499
- : "PTY · terminal session";
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) : "PTY";
3834
- var exitText = "exit=" + (selectedSession && selectedSession.exitCode !== undefined ? selectedSession.exitCode : "n/a");
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.getElementById("inline-keyboard");
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 ? false : state.currentView === "terminal");
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
- for (var i = 0; i < msg.content.length; i++) {
8977
- var block = msg.content[i];
9094
+ var groups = groupConsecutiveTools(msg.content);
9095
+ for (var g = 0; g < groups.length; g++) {
9096
+ var grp = groups[g];
8978
9097
  try {
8979
- blocksHtml += renderContentBlock(block, role, toolResults, i);
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
- gap: 8px;
7529
- margin: 0;
7530
- padding: 4px 10px;
7531
- border-radius: var(--radius-sm);
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-label {
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
- .structured-status-bar .status-bar-fill {
7556
- position: absolute;
7557
- top: 0;
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
- @keyframes marqueeScroll {
7567
- 0% { left: -40%; }
7568
- 100% { left: 100%; }
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: 30px; margin: 0; padding: 4px 10px; }
7604
- 70% { opacity: 0; max-height: 30px; margin: 0; padding: 4px 10px; }
7605
- 100% { opacity: 0; max-height: 0; margin: 0; padding: 0 10px; border-width: 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
  /* 结束标记 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "1.5.4",
3
+ "version": "1.5.5",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {