@co0ontty/wand 1.21.19 → 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.
- package/dist/cli.js +111 -2
- package/dist/pidfile.d.ts +38 -0
- package/dist/pidfile.js +117 -0
- package/dist/tui/attach.d.ts +18 -0
- package/dist/tui/attach.js +306 -0
- package/dist/tui/commands.d.ts +60 -0
- package/dist/tui/commands.js +505 -0
- package/dist/tui/index.js +171 -3
- package/dist/tui/ipc-client.d.ts +27 -0
- package/dist/tui/ipc-client.js +153 -0
- package/dist/tui/ipc-protocol.d.ts +50 -0
- package/dist/tui/ipc-protocol.js +7 -0
- package/dist/tui/ipc-server.d.ts +17 -0
- package/dist/tui/ipc-server.js +100 -0
- package/dist/tui/layout.d.ts +44 -0
- package/dist/tui/layout.js +365 -11
- package/dist/tui/service-panel.d.ts +19 -0
- package/dist/tui/service-panel.js +108 -0
- package/dist/tui/snapshot.d.ts +26 -0
- package/dist/tui/snapshot.js +58 -0
- package/dist/web-ui/content/scripts.js +109 -13
- package/dist/web-ui/content/styles.css +192 -31
- package/package.json +1 -1
|
@@ -1350,7 +1350,7 @@
|
|
|
1350
1350
|
) +
|
|
1351
1351
|
'</div>' +
|
|
1352
1352
|
'<div class="topbar-right">' +
|
|
1353
|
-
|
|
1353
|
+
'<button id="topbar-file-button" class="topbar-btn square' + (state.filePanelOpen ? ' active' : '') + '" type="button" aria-label="文件" title="查看文件(可修改路径)"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg></button>' +
|
|
1354
1354
|
'<span id="topbar-git-slot" class="topbar-git-slot">' + renderTopbarGitBadgeHtml() + '</span>' +
|
|
1355
1355
|
(selectedSession ? renderTopbarMoreMenuHtml(selectedSession) : '') +
|
|
1356
1356
|
'</div>' +
|
|
@@ -1366,7 +1366,7 @@
|
|
|
1366
1366
|
'<div class="file-side-panel-body">' +
|
|
1367
1367
|
'<div class="file-explorer-header">' +
|
|
1368
1368
|
'<button class="file-explorer-up" id="file-explorer-up" type="button" title="返回上级目录" aria-label="返回上级目录">⬆</button>' +
|
|
1369
|
-
'<
|
|
1369
|
+
'<input type="text" class="file-explorer-path" id="file-explorer-cwd" value="' + escapeHtml(selectedSession && selectedSession.cwd ? selectedSession.cwd : getConfigCwd()) + '" title="' + escapeHtml(selectedSession && selectedSession.cwd ? selectedSession.cwd : getConfigCwd()) + '" placeholder="输入路径并回车..." spellcheck="false" autocomplete="off" autocapitalize="off" autocorrect="off" aria-label="当前路径,可直接修改后回车" />' +
|
|
1370
1370
|
'<button class="file-explorer-toggle-hidden' + (state.fileExplorerShowHidden ? ' active' : '') + '" id="file-explorer-toggle-hidden" type="button" title="' + (state.fileExplorerShowHidden ? "隐藏点开头文件" : "显示隐藏文件") + '" aria-pressed="' + (state.fileExplorerShowHidden ? "true" : "false") + '">' + (state.fileExplorerShowHidden ? "👁" : "👁🗨") + '</button>' +
|
|
1371
1371
|
'<button class="file-explorer-refresh" id="file-explorer-refresh" title="刷新" aria-label="刷新文件列表">↻</button>' +
|
|
1372
1372
|
'</div>' +
|
|
@@ -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
|
-
'<
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
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="提示词优化">' +
|
|
@@ -2877,7 +2884,15 @@
|
|
|
2877
2884
|
var cwdEl = document.getElementById("file-explorer-cwd");
|
|
2878
2885
|
if (!cwdEl) return;
|
|
2879
2886
|
var cwd = session && session.cwd ? session.cwd : getConfigCwd();
|
|
2880
|
-
|
|
2887
|
+
// Don't clobber the user's in-progress edit when the input is focused.
|
|
2888
|
+
if (cwdEl.tagName === "INPUT") {
|
|
2889
|
+
if (document.activeElement !== cwdEl) {
|
|
2890
|
+
cwdEl.value = cwd;
|
|
2891
|
+
}
|
|
2892
|
+
} else {
|
|
2893
|
+
cwdEl.textContent = cwd;
|
|
2894
|
+
}
|
|
2895
|
+
cwdEl.title = cwd;
|
|
2881
2896
|
}
|
|
2882
2897
|
|
|
2883
2898
|
function closeFilePanel() {
|
|
@@ -3029,7 +3044,14 @@
|
|
|
3029
3044
|
state.fileExplorerTotal = 0;
|
|
3030
3045
|
explorer.innerHTML = '<div class="file-explorer"><div class="tree-loading" style="padding:12px;color:var(--text-muted);font-size:0.8125rem;">加载中…</div></div>';
|
|
3031
3046
|
if (cwdEl) {
|
|
3032
|
-
cwdEl.
|
|
3047
|
+
if (cwdEl.tagName === "INPUT") {
|
|
3048
|
+
// Avoid clobbering in-progress text while the user is typing.
|
|
3049
|
+
if (document.activeElement !== cwdEl) {
|
|
3050
|
+
cwdEl.value = cwd;
|
|
3051
|
+
}
|
|
3052
|
+
} else {
|
|
3053
|
+
cwdEl.textContent = cwd;
|
|
3054
|
+
}
|
|
3033
3055
|
cwdEl.title = cwd;
|
|
3034
3056
|
}
|
|
3035
3057
|
var url = "/api/directory?q=" + encodeURIComponent(cwd) +
|
|
@@ -5084,6 +5106,56 @@
|
|
|
5084
5106
|
var fileToggleHidden = document.getElementById("file-explorer-toggle-hidden");
|
|
5085
5107
|
if (fileToggleHidden) fileToggleHidden.addEventListener("click", toggleExplorerHidden);
|
|
5086
5108
|
|
|
5109
|
+
// 路径输入框:支持点击修改路径,回车跳转,Esc 撤销。
|
|
5110
|
+
var fileCwdInput = document.getElementById("file-explorer-cwd");
|
|
5111
|
+
if (fileCwdInput && fileCwdInput.tagName === "INPUT") {
|
|
5112
|
+
var lastCommittedCwd = fileCwdInput.value;
|
|
5113
|
+
var normalizeCwdInput = function(raw) {
|
|
5114
|
+
var s = (raw || "").trim();
|
|
5115
|
+
if (!s) return "";
|
|
5116
|
+
// 折叠重复斜杠,去掉尾随斜杠(根目录除外)。
|
|
5117
|
+
s = s.replace(/\/{2,}/g, "/");
|
|
5118
|
+
if (s.length > 1) s = s.replace(/\/+$/, "");
|
|
5119
|
+
return s;
|
|
5120
|
+
};
|
|
5121
|
+
fileCwdInput.addEventListener("focus", function() {
|
|
5122
|
+
lastCommittedCwd = fileCwdInput.value;
|
|
5123
|
+
// Select all on focus so the user can immediately overwrite.
|
|
5124
|
+
setTimeout(function() {
|
|
5125
|
+
try { fileCwdInput.select(); } catch (e) {}
|
|
5126
|
+
}, 0);
|
|
5127
|
+
});
|
|
5128
|
+
fileCwdInput.addEventListener("keydown", function(e) {
|
|
5129
|
+
if (e.key === "Enter") {
|
|
5130
|
+
e.preventDefault();
|
|
5131
|
+
var next = normalizeCwdInput(fileCwdInput.value);
|
|
5132
|
+
if (!next) return;
|
|
5133
|
+
lastCommittedCwd = next;
|
|
5134
|
+
fileCwdInput.value = next;
|
|
5135
|
+
refreshFileExplorer({ cwd: next });
|
|
5136
|
+
fileCwdInput.blur();
|
|
5137
|
+
} else if (e.key === "Escape") {
|
|
5138
|
+
e.preventDefault();
|
|
5139
|
+
fileCwdInput.value = lastCommittedCwd;
|
|
5140
|
+
fileCwdInput.blur();
|
|
5141
|
+
}
|
|
5142
|
+
});
|
|
5143
|
+
fileCwdInput.addEventListener("blur", function() {
|
|
5144
|
+
var next = normalizeCwdInput(fileCwdInput.value);
|
|
5145
|
+
if (!next) {
|
|
5146
|
+
fileCwdInput.value = lastCommittedCwd;
|
|
5147
|
+
return;
|
|
5148
|
+
}
|
|
5149
|
+
if (next === lastCommittedCwd) {
|
|
5150
|
+
fileCwdInput.value = next;
|
|
5151
|
+
return;
|
|
5152
|
+
}
|
|
5153
|
+
lastCommittedCwd = next;
|
|
5154
|
+
fileCwdInput.value = next;
|
|
5155
|
+
refreshFileExplorer({ cwd: next });
|
|
5156
|
+
});
|
|
5157
|
+
}
|
|
5158
|
+
|
|
5087
5159
|
// File search
|
|
5088
5160
|
var fileSearchInput = document.getElementById("file-search-input");
|
|
5089
5161
|
var fileSearchClear = document.getElementById("file-search-clear");
|
|
@@ -7523,6 +7595,14 @@
|
|
|
7523
7595
|
// Reset todo progress bar
|
|
7524
7596
|
var todoEl = document.getElementById("todo-progress");
|
|
7525
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");
|
|
7526
7606
|
var session = foundSession;
|
|
7527
7607
|
state.preferredCommand = getPreferredTool();
|
|
7528
7608
|
state.chatMode = getSafeModeForTool("claude", session && session.mode ? session.mode : state.chatMode);
|
|
@@ -13730,6 +13810,10 @@
|
|
|
13730
13810
|
state.lastRenderedEmpty = "empty";
|
|
13731
13811
|
state.lastRenderedMsgCount = 0;
|
|
13732
13812
|
}
|
|
13813
|
+
// 空会话进入空状态前,把上一会话残留的状态条 / todo 进度条清掉。
|
|
13814
|
+
// 这里是 selectSession 之外的兜底:WS init 等异步路径也会落到这条空分支。
|
|
13815
|
+
renderStructuredStatusBar(null, selectedSession);
|
|
13816
|
+
updateTodoProgress([]);
|
|
13733
13817
|
return;
|
|
13734
13818
|
}
|
|
13735
13819
|
|
|
@@ -14067,10 +14151,10 @@
|
|
|
14067
14151
|
if (prog && body) {
|
|
14068
14152
|
if (todoExpanded) {
|
|
14069
14153
|
prog.classList.add("expanded");
|
|
14070
|
-
body.classList.
|
|
14154
|
+
body.classList.add("expanded");
|
|
14071
14155
|
} else {
|
|
14072
14156
|
prog.classList.remove("expanded");
|
|
14073
|
-
body.classList.
|
|
14157
|
+
body.classList.remove("expanded");
|
|
14074
14158
|
}
|
|
14075
14159
|
}
|
|
14076
14160
|
});
|
|
@@ -14092,14 +14176,17 @@
|
|
|
14092
14176
|
}
|
|
14093
14177
|
|
|
14094
14178
|
var container = document.getElementById("todo-progress");
|
|
14179
|
+
var bodyEl = document.getElementById("todo-progress-body");
|
|
14095
14180
|
if (!container) return;
|
|
14096
14181
|
|
|
14097
14182
|
if (!todos || todos.length === 0) {
|
|
14098
14183
|
container.classList.add("hidden");
|
|
14184
|
+
if (bodyEl) bodyEl.classList.add("hidden");
|
|
14099
14185
|
return;
|
|
14100
14186
|
}
|
|
14101
14187
|
|
|
14102
14188
|
container.classList.remove("hidden");
|
|
14189
|
+
if (bodyEl) bodyEl.classList.remove("hidden");
|
|
14103
14190
|
|
|
14104
14191
|
var completed = 0;
|
|
14105
14192
|
var inProgress = 0;
|
|
@@ -14121,6 +14208,7 @@
|
|
|
14121
14208
|
if (allDone) {
|
|
14122
14209
|
// Hide todo when all tasks are completed
|
|
14123
14210
|
container.classList.add("hidden");
|
|
14211
|
+
if (bodyEl) bodyEl.classList.add("hidden");
|
|
14124
14212
|
return;
|
|
14125
14213
|
} else {
|
|
14126
14214
|
container.classList.remove("all-done");
|
|
@@ -14132,6 +14220,14 @@
|
|
|
14132
14220
|
var task = document.getElementById("todo-progress-task");
|
|
14133
14221
|
if (task) task.textContent = activeTask;
|
|
14134
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
|
+
|
|
14135
14231
|
// Render expanded list
|
|
14136
14232
|
var list = document.getElementById("todo-progress-list");
|
|
14137
14233
|
if (list) {
|
|
@@ -685,6 +685,40 @@
|
|
|
685
685
|
font-weight: 500;
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
+
/* When rendered as <input> (editable path) keep the same look but reveal
|
|
689
|
+
an editable affordance on hover/focus. */
|
|
690
|
+
input.file-explorer-path {
|
|
691
|
+
background: transparent;
|
|
692
|
+
border: 1px solid transparent;
|
|
693
|
+
border-radius: var(--radius-sm);
|
|
694
|
+
padding: 4px 8px;
|
|
695
|
+
height: 28px;
|
|
696
|
+
line-height: 1.2;
|
|
697
|
+
cursor: text;
|
|
698
|
+
transition: background-color var(--transition-fast),
|
|
699
|
+
border-color var(--transition-fast),
|
|
700
|
+
color var(--transition-fast);
|
|
701
|
+
outline: none;
|
|
702
|
+
text-overflow: ellipsis;
|
|
703
|
+
width: 100%;
|
|
704
|
+
box-sizing: border-box;
|
|
705
|
+
}
|
|
706
|
+
input.file-explorer-path::placeholder {
|
|
707
|
+
color: var(--text-muted);
|
|
708
|
+
opacity: 0.6;
|
|
709
|
+
}
|
|
710
|
+
input.file-explorer-path:hover {
|
|
711
|
+
background: rgba(255, 255, 255, 0.5);
|
|
712
|
+
border-color: var(--border);
|
|
713
|
+
color: var(--text-secondary);
|
|
714
|
+
}
|
|
715
|
+
input.file-explorer-path:focus {
|
|
716
|
+
background: var(--bg-primary);
|
|
717
|
+
border-color: var(--accent);
|
|
718
|
+
color: var(--text-primary);
|
|
719
|
+
text-overflow: clip;
|
|
720
|
+
}
|
|
721
|
+
|
|
688
722
|
.file-explorer-actions { display: flex; gap: 4px; margin-left: auto; flex-shrink: 0; }
|
|
689
723
|
|
|
690
724
|
.file-explorer-refresh {
|
|
@@ -5311,10 +5345,12 @@
|
|
|
5311
5345
|
.input-label { font-size: 0.6875rem; color: var(--text-muted); font-weight: 500; }
|
|
5312
5346
|
.input-textarea-wrap { position: relative; width: 100%; }
|
|
5313
5347
|
|
|
5314
|
-
/* 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. */
|
|
5315
5350
|
.composer-top-row {
|
|
5351
|
+
position: relative;
|
|
5316
5352
|
display: flex;
|
|
5317
|
-
align-items:
|
|
5353
|
+
align-items: center;
|
|
5318
5354
|
gap: 8px;
|
|
5319
5355
|
min-width: 0;
|
|
5320
5356
|
}
|
|
@@ -5357,31 +5393,52 @@
|
|
|
5357
5393
|
min-width: 0;
|
|
5358
5394
|
flex: 1;
|
|
5359
5395
|
}
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
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;
|
|
5368
5406
|
}
|
|
5369
|
-
.todo-progress
|
|
5370
|
-
|
|
5407
|
+
.todo-progress-ring svg {
|
|
5408
|
+
width: 100%;
|
|
5409
|
+
height: 100%;
|
|
5410
|
+
transform: rotate(-90deg);
|
|
5371
5411
|
}
|
|
5372
|
-
.todo-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
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); }
|
|
5379
5435
|
}
|
|
5380
5436
|
.todo-progress-counter {
|
|
5381
5437
|
font-size: 0.75rem;
|
|
5382
5438
|
font-weight: 600;
|
|
5383
5439
|
color: var(--text-primary);
|
|
5384
5440
|
white-space: nowrap;
|
|
5441
|
+
font-variant-numeric: tabular-nums;
|
|
5385
5442
|
}
|
|
5386
5443
|
.todo-progress-task {
|
|
5387
5444
|
font-size: 0.75rem;
|
|
@@ -5398,44 +5455,84 @@
|
|
|
5398
5455
|
.todo-progress.expanded .todo-progress-chevron {
|
|
5399
5456
|
transform: rotate(180deg);
|
|
5400
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. */
|
|
5401
5462
|
.todo-progress-body {
|
|
5402
|
-
|
|
5403
|
-
|
|
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;
|
|
5404
5481
|
}
|
|
5405
5482
|
.todo-progress-body.hidden { display: none; }
|
|
5483
|
+
.todo-progress-body.expanded {
|
|
5484
|
+
opacity: 1;
|
|
5485
|
+
transform: translateY(0);
|
|
5486
|
+
pointer-events: auto;
|
|
5487
|
+
}
|
|
5406
5488
|
.todo-progress-list {
|
|
5407
5489
|
list-style: none;
|
|
5408
5490
|
margin: 0;
|
|
5409
5491
|
padding: 0;
|
|
5410
5492
|
display: flex;
|
|
5411
5493
|
flex-direction: column;
|
|
5412
|
-
gap:
|
|
5494
|
+
gap: 1px;
|
|
5413
5495
|
}
|
|
5414
5496
|
.todo-progress-item {
|
|
5415
5497
|
display: flex;
|
|
5416
5498
|
align-items: center;
|
|
5417
|
-
gap:
|
|
5418
|
-
padding:
|
|
5419
|
-
border-radius:
|
|
5420
|
-
font-size: 0.
|
|
5499
|
+
gap: 10px;
|
|
5500
|
+
padding: 6px 8px;
|
|
5501
|
+
border-radius: 8px;
|
|
5502
|
+
font-size: 0.8125rem;
|
|
5421
5503
|
color: var(--text-secondary);
|
|
5504
|
+
line-height: 1.35;
|
|
5422
5505
|
transition: background var(--transition-fast);
|
|
5423
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; }
|
|
5424
5513
|
.todo-progress-item.active {
|
|
5425
5514
|
color: var(--text-primary);
|
|
5426
5515
|
font-weight: 500;
|
|
5427
|
-
background: rgba(197, 101, 61, 0.
|
|
5516
|
+
background: rgba(197, 101, 61, 0.07);
|
|
5428
5517
|
}
|
|
5429
5518
|
.todo-progress-item.done {
|
|
5430
5519
|
color: var(--text-muted);
|
|
5431
5520
|
text-decoration: line-through;
|
|
5432
5521
|
text-decoration-color: var(--text-muted);
|
|
5433
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
|
+
}
|
|
5434
5530
|
.todo-item-icon {
|
|
5435
5531
|
width: 16px;
|
|
5436
5532
|
min-width: 16px;
|
|
5437
5533
|
text-align: center;
|
|
5438
|
-
font-size: 0.
|
|
5534
|
+
font-size: 0.75rem;
|
|
5535
|
+
flex-shrink: 0;
|
|
5439
5536
|
}
|
|
5440
5537
|
.todo-item-icon.pending { color: var(--text-muted); }
|
|
5441
5538
|
.todo-item-icon.active { color: var(--accent); }
|
|
@@ -10473,7 +10570,10 @@
|
|
|
10473
10570
|
}
|
|
10474
10571
|
|
|
10475
10572
|
.file-preview-modal {
|
|
10476
|
-
|
|
10573
|
+
/* 桌面端:留出侧边栏空间,但保证最小宽度,避免在窄视口上塌成一条缝。
|
|
10574
|
+
之前是 min(90vw, calc(100vw - 380px)),当 100vw < 380px 时整个值为负,
|
|
10575
|
+
元素直接被收成 0 宽。这里用 max() 兜底一个最小宽度。 */
|
|
10576
|
+
width: min(90vw, max(640px, calc(100vw - 380px)));
|
|
10477
10577
|
max-width: 1000px;
|
|
10478
10578
|
height: 80vh;
|
|
10479
10579
|
max-height: 85vh;
|
|
@@ -10492,6 +10592,65 @@
|
|
|
10492
10592
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
10493
10593
|
}
|
|
10494
10594
|
|
|
10595
|
+
/* 移动端(含竖屏手机):铺满整个视口,去掉圆角和边框,
|
|
10596
|
+
使用 dvh 兼容浏览器地址栏占用。 */
|
|
10597
|
+
@media (max-width: 768px) {
|
|
10598
|
+
.file-preview-overlay {
|
|
10599
|
+
padding: 0;
|
|
10600
|
+
align-items: stretch;
|
|
10601
|
+
justify-content: stretch;
|
|
10602
|
+
}
|
|
10603
|
+
.file-preview-modal {
|
|
10604
|
+
width: 100vw;
|
|
10605
|
+
max-width: 100vw;
|
|
10606
|
+
height: 100vh;
|
|
10607
|
+
height: 100dvh;
|
|
10608
|
+
max-height: 100vh;
|
|
10609
|
+
max-height: 100dvh;
|
|
10610
|
+
border-radius: 0;
|
|
10611
|
+
border: none;
|
|
10612
|
+
box-shadow: none;
|
|
10613
|
+
animation: fade-in 0.18s ease;
|
|
10614
|
+
}
|
|
10615
|
+
.file-preview-header {
|
|
10616
|
+
padding: 10px 12px;
|
|
10617
|
+
gap: 8px;
|
|
10618
|
+
/* 给安全区让位,例如带刘海的设备 */
|
|
10619
|
+
padding-top: max(10px, env(safe-area-inset-top));
|
|
10620
|
+
padding-left: max(12px, env(safe-area-inset-left));
|
|
10621
|
+
padding-right: max(12px, env(safe-area-inset-right));
|
|
10622
|
+
}
|
|
10623
|
+
.file-preview-title {
|
|
10624
|
+
font-size: 0.875rem;
|
|
10625
|
+
gap: 6px;
|
|
10626
|
+
min-width: 0;
|
|
10627
|
+
flex: 1;
|
|
10628
|
+
}
|
|
10629
|
+
.file-preview-filename {
|
|
10630
|
+
max-width: 100%;
|
|
10631
|
+
}
|
|
10632
|
+
/* 路径在窄屏上极易抢空间,直接隐藏,hover/长按文件名仍可看到完整路径。 */
|
|
10633
|
+
.file-preview-path {
|
|
10634
|
+
display: none;
|
|
10635
|
+
}
|
|
10636
|
+
.file-preview-toolbar {
|
|
10637
|
+
gap: 4px;
|
|
10638
|
+
flex-shrink: 0;
|
|
10639
|
+
}
|
|
10640
|
+
.file-preview-toolbar-btn {
|
|
10641
|
+
padding: 4px 6px;
|
|
10642
|
+
font-size: 0.75rem;
|
|
10643
|
+
}
|
|
10644
|
+
.file-preview-close {
|
|
10645
|
+
font-size: 1.375rem;
|
|
10646
|
+
padding: 6px 10px;
|
|
10647
|
+
}
|
|
10648
|
+
/* 底部安全区(小白条 / 圆角) */
|
|
10649
|
+
.file-preview-body {
|
|
10650
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
10651
|
+
}
|
|
10652
|
+
}
|
|
10653
|
+
|
|
10495
10654
|
.file-preview-header {
|
|
10496
10655
|
display: flex;
|
|
10497
10656
|
align-items: center;
|
|
@@ -11919,7 +12078,9 @@
|
|
|
11919
12078
|
100% { background-position: 100% center; }
|
|
11920
12079
|
}
|
|
11921
12080
|
|
|
11922
|
-
/* ── 结构化会话状态条(与 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. */
|
|
11923
12084
|
.structured-status-bar {
|
|
11924
12085
|
display: flex;
|
|
11925
12086
|
align-items: center;
|
|
@@ -11932,7 +12093,7 @@
|
|
|
11932
12093
|
border: none;
|
|
11933
12094
|
font-size: 0.6875rem;
|
|
11934
12095
|
color: var(--text-muted);
|
|
11935
|
-
transition:
|
|
12096
|
+
transition: opacity 0.3s ease, color 0.3s ease;
|
|
11936
12097
|
overflow: hidden;
|
|
11937
12098
|
flex-shrink: 0;
|
|
11938
12099
|
}
|