@co0ontty/wand 1.29.1 → 1.29.6
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/server.js
CHANGED
|
@@ -976,6 +976,10 @@ export async function startServer(config, configPath) {
|
|
|
976
976
|
res.setHeader("Content-Disposition", `attachment; filename="${encodeURIComponent(macosDmg.fileName)}"`);
|
|
977
977
|
createReadStream(macosDmg.filePath).pipe(res);
|
|
978
978
|
});
|
|
979
|
+
// Public probe so the unauthenticated browser does not log a 401 on /api/config
|
|
980
|
+
app.get("/api/session-check", (req, res) => {
|
|
981
|
+
res.json({ authed: validateSession(readSessionCookie(req)) });
|
|
982
|
+
});
|
|
979
983
|
app.use("/api", requireAuth);
|
|
980
984
|
// ── Config & Session info ──
|
|
981
985
|
app.get("/api/config", async (_req, res) => {
|
|
@@ -1148,14 +1148,24 @@
|
|
|
1148
1148
|
}
|
|
1149
1149
|
|
|
1150
1150
|
function restoreLoginSession() {
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1151
|
+
// Probe an unauthenticated endpoint first so an anonymous visit
|
|
1152
|
+
// does not leave a noisy 401 on /api/config in DevTools.
|
|
1153
|
+
fetch("/api/session-check", { credentials: "same-origin" })
|
|
1154
|
+
.then(function(res) { return res.ok ? res.json() : { authed: false }; })
|
|
1155
|
+
.then(function(info) {
|
|
1156
|
+
if (!info || !info.authed) {
|
|
1154
1157
|
state.loginChecked = true;
|
|
1155
1158
|
render();
|
|
1156
1159
|
return null;
|
|
1157
1160
|
}
|
|
1158
|
-
return
|
|
1161
|
+
return fetch("/api/config", { credentials: "same-origin" }).then(function(res) {
|
|
1162
|
+
if (!res.ok) {
|
|
1163
|
+
state.loginChecked = true;
|
|
1164
|
+
render();
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
return res.json();
|
|
1168
|
+
});
|
|
1159
1169
|
})
|
|
1160
1170
|
.then(function(config) {
|
|
1161
1171
|
if (!config) return;
|
|
@@ -1286,6 +1296,7 @@
|
|
|
1286
1296
|
'<span class="shortcut-sep">·</span>' +
|
|
1287
1297
|
'<button class="shortcut-key" data-key="enter" type="button">↵</button>' +
|
|
1288
1298
|
'<button class="shortcut-key" data-key="ctrl_enter" type="button">C-↵</button>' +
|
|
1299
|
+
'<button class="shortcut-key" data-key="shift_tab" type="button" title="Shift+Tab(切换 plan / 自动接受 模式)">⇧⇥</button>' +
|
|
1289
1300
|
'<button class="shortcut-key" data-key="escape" type="button">Esc</button>';
|
|
1290
1301
|
}
|
|
1291
1302
|
|
|
@@ -1365,7 +1376,8 @@
|
|
|
1365
1376
|
'</div>' +
|
|
1366
1377
|
'<div class="login-subtitle">在浏览器中运行本机终端</div>' +
|
|
1367
1378
|
'</div>' +
|
|
1368
|
-
'<
|
|
1379
|
+
'<form id="login-form" class="login-body" autocomplete="on">' +
|
|
1380
|
+
'<input type="text" name="username" autocomplete="username" value="wand" tabindex="-1" aria-hidden="true" style="position:absolute;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none" readonly />' +
|
|
1369
1381
|
'<p class="login-hint">输入 Wand 访问密码以进入控制台。</p>' +
|
|
1370
1382
|
'<div class="field">' +
|
|
1371
1383
|
'<label class="field-label" for="password">密码</label>' +
|
|
@@ -1376,7 +1388,7 @@
|
|
|
1376
1388
|
'<p id="password-hint" class="hint">使用你在 Wand 中设置的访问密码。</p>' +
|
|
1377
1389
|
'<p id="login-error" class="error-message hidden" role="alert"></p>' +
|
|
1378
1390
|
'</div>' +
|
|
1379
|
-
'<button id="login-button" class="btn btn-primary btn-block">进入控制台</button>' +
|
|
1391
|
+
'<button id="login-button" type="submit" class="btn btn-primary btn-block">进入控制台</button>' +
|
|
1380
1392
|
(hasNativeSwitchServer() ?
|
|
1381
1393
|
'<button id="login-switch-server-button" class="btn btn-ghost btn-block login-switch-server" type="button">' +
|
|
1382
1394
|
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="8" rx="2"/><rect x="2" y="13" width="20" height="8" rx="2"/><line x1="6" y1="7" x2="6.01" y2="7"/><line x1="6" y1="17" x2="6.01" y2="17"/></svg>' +
|
|
@@ -1384,7 +1396,7 @@
|
|
|
1384
1396
|
'</button>'
|
|
1385
1397
|
: ''
|
|
1386
1398
|
) +
|
|
1387
|
-
'</
|
|
1399
|
+
'</form>' +
|
|
1388
1400
|
'</div>' +
|
|
1389
1401
|
'</div>';
|
|
1390
1402
|
}
|
|
@@ -1399,10 +1411,14 @@
|
|
|
1399
1411
|
var preferredTool = getComposerTool();
|
|
1400
1412
|
var composerMode = getSafeModeForTool(preferredTool, state.chatMode);
|
|
1401
1413
|
|
|
1414
|
+
var isDesktopPinned = state.sidebarPinned && !isMobileLayout();
|
|
1415
|
+
var isCollapsed = isDesktopPinned && state.sidebarCollapsed;
|
|
1416
|
+
var collapsedCls = isCollapsed ? ' sidebar-collapsed' : '';
|
|
1417
|
+
var sidebarCollapsedCls = isCollapsed ? ' collapsed' : '';
|
|
1402
1418
|
return '<div class="app-container">' +
|
|
1403
1419
|
'<div id="sessions-drawer-backdrop" class="drawer-backdrop' + drawerClass + '"></div>' +
|
|
1404
|
-
'<div class="main-layout' + (state.sessionsDrawerOpen ? ' sidebar-open' : '') + (
|
|
1405
|
-
'<aside id="sessions-drawer" class="sidebar' + drawerClass + (
|
|
1420
|
+
'<div class="main-layout' + (state.sessionsDrawerOpen ? ' sidebar-open' : '') + (isDesktopPinned ? ' sidebar-pinned' : '') + collapsedCls + '">' +
|
|
1421
|
+
'<aside id="sessions-drawer" class="sidebar' + drawerClass + (isDesktopPinned ? ' pinned' : '') + sidebarCollapsedCls + '">' +
|
|
1406
1422
|
'<div class="sidebar-header">' +
|
|
1407
1423
|
'<div class="sidebar-header-main">' +
|
|
1408
1424
|
'<div class="topbar-logo-icon">W</div>' +
|
|
@@ -1428,15 +1444,17 @@
|
|
|
1428
1444
|
'<button id="sidebar-pin-btn" class="btn btn-ghost btn-sm sidebar-pin-toggle' + (state.sidebarPinned ? ' pinned' : '') + '" type="button" title="' + (state.sidebarPinned ? '取消固定侧栏' : '固定侧栏') + '">' +
|
|
1429
1445
|
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="17" x2="12" y2="22"/><path d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24z"/></svg>' +
|
|
1430
1446
|
'</button>' +
|
|
1431
|
-
'<button id="sidebar-collapse-btn" class="btn btn-ghost btn-sm sidebar-collapse-toggle" type="button" title="收起为窄条" aria-label="收起为窄条">' +
|
|
1432
|
-
|
|
1447
|
+
'<button id="sidebar-collapse-btn" class="btn btn-ghost btn-sm sidebar-collapse-toggle' + (isCollapsed ? ' collapsed' : '') + '" type="button" title="' + (isCollapsed ? '展开侧栏' : '收起为窄条') + '" aria-label="' + (isCollapsed ? '展开侧栏' : '收起为窄条') + '">' +
|
|
1448
|
+
(isCollapsed
|
|
1449
|
+
? '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="10 6 16 12 10 18"/><line x1="20" y1="5" x2="20" y2="19"/></svg>'
|
|
1450
|
+
: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="14 6 8 12 14 18"/><line x1="4" y1="5" x2="4" y2="19"/></svg>') +
|
|
1433
1451
|
'</button>' +
|
|
1434
1452
|
'<button id="close-drawer-button" class="btn btn-ghost btn-icon sidebar-close drawer-close-btn" type="button" aria-label="关闭菜单"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg></button>' +
|
|
1435
1453
|
'</div>' +
|
|
1436
1454
|
'</div>' +
|
|
1437
1455
|
'<div class="sidebar-body">' +
|
|
1438
1456
|
'<div id="sessions-panel">' +
|
|
1439
|
-
'<div class="sessions-list" id="sessions-list">' +
|
|
1457
|
+
'<div class="sessions-list" id="sessions-list">' + renderSessionsListContent() + '</div>' +
|
|
1440
1458
|
'</div>' +
|
|
1441
1459
|
'</div>' +
|
|
1442
1460
|
'<div class="sidebar-footer">' +
|
|
@@ -2937,19 +2955,22 @@
|
|
|
2937
2955
|
'<p class="settings-card-desc">至少 6 个字符;保存后下次登录生效。</p>' +
|
|
2938
2956
|
'</div>' +
|
|
2939
2957
|
'</div>' +
|
|
2940
|
-
'<
|
|
2941
|
-
'<
|
|
2942
|
-
'<
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
'
|
|
2946
|
-
'<
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
'
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2958
|
+
'<form id="change-password-form" autocomplete="on" onsubmit="return false;">' +
|
|
2959
|
+
'<input type="text" name="username" autocomplete="username" value="wand" tabindex="-1" aria-hidden="true" style="position:absolute;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none" readonly />' +
|
|
2960
|
+
'<div class="field">' +
|
|
2961
|
+
'<label class="field-label" for="new-password">新密码</label>' +
|
|
2962
|
+
'<input id="new-password" type="password" class="field-input" placeholder="输入新密码(至少 6 个字符)" autocomplete="new-password" />' +
|
|
2963
|
+
'</div>' +
|
|
2964
|
+
'<div class="field">' +
|
|
2965
|
+
'<label class="field-label" for="confirm-password">确认密码</label>' +
|
|
2966
|
+
'<input id="confirm-password" type="password" class="field-input" placeholder="再次输入新密码" autocomplete="new-password" />' +
|
|
2967
|
+
'</div>' +
|
|
2968
|
+
'<div class="settings-card-actions">' +
|
|
2969
|
+
'<button id="save-password-button" class="btn btn-primary" type="submit">保存密码</button>' +
|
|
2970
|
+
'</div>' +
|
|
2971
|
+
'<p id="settings-error" class="error-message hidden"></p>' +
|
|
2972
|
+
'<p id="settings-success" class="hint settings-success-message hidden"></p>' +
|
|
2973
|
+
'</form>' +
|
|
2953
2974
|
'</div>' +
|
|
2954
2975
|
'<div class="settings-card">' +
|
|
2955
2976
|
'<div class="settings-card-head">' +
|
|
@@ -3085,6 +3106,35 @@
|
|
|
3085
3106
|
return groups.join("");
|
|
3086
3107
|
}
|
|
3087
3108
|
|
|
3109
|
+
function isSidebarNarrow() {
|
|
3110
|
+
return !!state.sidebarPinned && !isMobileLayout() && !!state.sidebarCollapsed;
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
function renderCollapsedSessionTiles() {
|
|
3114
|
+
var running = state.sessions.filter(function(s) {
|
|
3115
|
+
return !s.archived && s.status === "running";
|
|
3116
|
+
});
|
|
3117
|
+
running.sort(function(a, b) {
|
|
3118
|
+
var ta = a.startedAt ? new Date(a.startedAt).getTime() : 0;
|
|
3119
|
+
var tb = b.startedAt ? new Date(b.startedAt).getTime() : 0;
|
|
3120
|
+
return ta - tb;
|
|
3121
|
+
});
|
|
3122
|
+
if (running.length === 0) {
|
|
3123
|
+
return '<div class="sidebar-collapsed-empty" title="无运行中的会话">—</div>';
|
|
3124
|
+
}
|
|
3125
|
+
return '<div class="sidebar-collapsed-tiles">' +
|
|
3126
|
+
running.map(function(s, i) {
|
|
3127
|
+
var activeCls = s.id === state.selectedId ? " active" : "";
|
|
3128
|
+
var title = s.summary || s.command || ("会话 " + (i + 1));
|
|
3129
|
+
return '<button class="sidebar-collapsed-tile' + activeCls + '" type="button" data-collapsed-session-id="' + escapeHtml(s.id) + '" title="' + escapeHtml(title) + '">' + (i + 1) + '</button>';
|
|
3130
|
+
}).join("") +
|
|
3131
|
+
'</div>';
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
function renderSessionsListContent() {
|
|
3135
|
+
return isSidebarNarrow() ? renderCollapsedSessionTiles() : renderSessions();
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3088
3138
|
function renderSessionManageBar() {
|
|
3089
3139
|
if (!state.sessionsManageMode) {
|
|
3090
3140
|
return '<div class="session-manage-bar">' +
|
|
@@ -5372,6 +5422,11 @@
|
|
|
5372
5422
|
var loginButton = document.getElementById("login-button");
|
|
5373
5423
|
if (loginButton) {
|
|
5374
5424
|
loginButton.addEventListener("click", login);
|
|
5425
|
+
var loginForm = document.getElementById("login-form");
|
|
5426
|
+
if (loginForm) loginForm.addEventListener("submit", function(e) {
|
|
5427
|
+
e.preventDefault();
|
|
5428
|
+
login();
|
|
5429
|
+
});
|
|
5375
5430
|
var loginSwitchServerBtn = document.getElementById("login-switch-server-button");
|
|
5376
5431
|
if (loginSwitchServerBtn) loginSwitchServerBtn.addEventListener("click", switchServer);
|
|
5377
5432
|
var passwordEl = document.getElementById("password");
|
|
@@ -5511,6 +5566,8 @@
|
|
|
5511
5566
|
if (closeDrawerBtn) closeDrawerBtn.addEventListener("click", closeSessionsDrawer);
|
|
5512
5567
|
var pinBtn = document.getElementById("sidebar-pin-btn");
|
|
5513
5568
|
if (pinBtn) pinBtn.addEventListener("click", toggleSidebarPin);
|
|
5569
|
+
var collapseBtn = document.getElementById("sidebar-collapse-btn");
|
|
5570
|
+
if (collapseBtn) collapseBtn.addEventListener("click", toggleSidebarCollapsed);
|
|
5514
5571
|
var sidebarMoreBtn = document.getElementById("sidebar-more-btn");
|
|
5515
5572
|
var sidebarOverflow = document.getElementById("sidebar-overflow-menu");
|
|
5516
5573
|
if (sidebarMoreBtn && sidebarOverflow) {
|
|
@@ -6532,6 +6589,14 @@
|
|
|
6532
6589
|
var target = event.target;
|
|
6533
6590
|
if (!target || !(target instanceof Element)) return;
|
|
6534
6591
|
|
|
6592
|
+
var collapsedTile = target.closest(".sidebar-collapsed-tile");
|
|
6593
|
+
if (collapsedTile && collapsedTile instanceof HTMLElement && collapsedTile.dataset.collapsedSessionId) {
|
|
6594
|
+
event.preventDefault();
|
|
6595
|
+
event.stopPropagation();
|
|
6596
|
+
activateSessionItem(collapsedTile.dataset.collapsedSessionId);
|
|
6597
|
+
return;
|
|
6598
|
+
}
|
|
6599
|
+
|
|
6535
6600
|
var historyToggle = target.closest("#claude-history-toggle");
|
|
6536
6601
|
if (historyToggle) {
|
|
6537
6602
|
event.preventDefault();
|
|
@@ -8301,7 +8366,7 @@
|
|
|
8301
8366
|
updateSessionsList();
|
|
8302
8367
|
} else {
|
|
8303
8368
|
var listEl = document.getElementById("sessions-list");
|
|
8304
|
-
var rendered =
|
|
8369
|
+
var rendered = renderSessionsListContent();
|
|
8305
8370
|
if (listEl && listEl.innerHTML === rendered) {
|
|
8306
8371
|
var countEl = document.getElementById("session-count");
|
|
8307
8372
|
if (countEl) countEl.textContent = String(state.sessions.length);
|
|
@@ -8360,7 +8425,7 @@
|
|
|
8360
8425
|
function updateSessionsList() {
|
|
8361
8426
|
var listEl = document.getElementById("sessions-list");
|
|
8362
8427
|
var countEl = document.getElementById("session-count");
|
|
8363
|
-
if (listEl) listEl.innerHTML =
|
|
8428
|
+
if (listEl) listEl.innerHTML = renderSessionsListContent();
|
|
8364
8429
|
if (countEl) countEl.textContent = String(state.sessions.length);
|
|
8365
8430
|
updateShellChrome();
|
|
8366
8431
|
// Re-render cross-session queue (container may have been destroyed by DOM rebuild)
|
|
@@ -8571,11 +8636,15 @@
|
|
|
8571
8636
|
var drawer = document.getElementById("sessions-drawer");
|
|
8572
8637
|
var mainLayout = document.querySelector(".main-layout");
|
|
8573
8638
|
var pinBtn = document.getElementById("sidebar-pin-btn");
|
|
8639
|
+
var isDesktopPinned = state.sidebarPinned && !isMobileLayout();
|
|
8640
|
+
var isCollapsed = isDesktopPinned && state.sidebarCollapsed;
|
|
8574
8641
|
if (drawer) {
|
|
8575
|
-
drawer.classList.toggle("pinned",
|
|
8642
|
+
drawer.classList.toggle("pinned", isDesktopPinned);
|
|
8643
|
+
drawer.classList.toggle("collapsed", isCollapsed);
|
|
8576
8644
|
}
|
|
8577
8645
|
if (mainLayout) {
|
|
8578
|
-
mainLayout.classList.toggle("sidebar-pinned",
|
|
8646
|
+
mainLayout.classList.toggle("sidebar-pinned", isDesktopPinned);
|
|
8647
|
+
mainLayout.classList.toggle("sidebar-collapsed", isCollapsed);
|
|
8579
8648
|
}
|
|
8580
8649
|
if (pinBtn) {
|
|
8581
8650
|
pinBtn.classList.toggle("pinned", state.sidebarPinned);
|
|
@@ -8623,6 +8692,35 @@
|
|
|
8623
8692
|
updateLayoutState();
|
|
8624
8693
|
}
|
|
8625
8694
|
|
|
8695
|
+
function toggleSidebarCollapsed() {
|
|
8696
|
+
if (isMobileLayout()) return;
|
|
8697
|
+
// 在 drawer 模式(未 pin)下点 collapse 视为「先固定、再收起为窄条」——
|
|
8698
|
+
// 用户直觉是「点了就该看到窄条」,过去这里 early return 让按钮看上去没反应。
|
|
8699
|
+
if (!state.sidebarPinned) {
|
|
8700
|
+
state.sidebarPinned = true;
|
|
8701
|
+
state.sessionsDrawerOpen = true;
|
|
8702
|
+
try {
|
|
8703
|
+
localStorage.setItem("wand-sidebar-pinned", "true");
|
|
8704
|
+
} catch (e) {}
|
|
8705
|
+
}
|
|
8706
|
+
state.sidebarCollapsed = !state.sidebarCollapsed;
|
|
8707
|
+
try {
|
|
8708
|
+
localStorage.setItem("wand-sidebar-collapsed", String(state.sidebarCollapsed));
|
|
8709
|
+
} catch (e) {}
|
|
8710
|
+
render();
|
|
8711
|
+
var mainLayout = document.querySelector(".main-layout");
|
|
8712
|
+
if (mainLayout) {
|
|
8713
|
+
var onEnd = function(e) {
|
|
8714
|
+
if (e.propertyName === "padding-left") {
|
|
8715
|
+
mainLayout.removeEventListener("transitionend", onEnd);
|
|
8716
|
+
scheduleTerminalResize(true);
|
|
8717
|
+
}
|
|
8718
|
+
};
|
|
8719
|
+
mainLayout.addEventListener("transitionend", onEnd);
|
|
8720
|
+
}
|
|
8721
|
+
setTimeout(function() { scheduleTerminalResize(true); }, 350);
|
|
8722
|
+
}
|
|
8723
|
+
|
|
8626
8724
|
function toggleSidebarPin() {
|
|
8627
8725
|
if (isMobileLayout()) return;
|
|
8628
8726
|
state.sidebarPinned = !state.sidebarPinned;
|
|
@@ -11936,6 +12034,7 @@
|
|
|
11936
12034
|
var ptySpecialKeyMap = {
|
|
11937
12035
|
space: " ",
|
|
11938
12036
|
tab: String.fromCharCode(9),
|
|
12037
|
+
shift_tab: String.fromCharCode(27) + "[Z",
|
|
11939
12038
|
backspace: String.fromCharCode(127),
|
|
11940
12039
|
home: String.fromCharCode(27) + "[H",
|
|
11941
12040
|
end: String.fromCharCode(27) + "[F",
|
|
@@ -11993,7 +12092,9 @@
|
|
|
11993
12092
|
return {
|
|
11994
12093
|
ctrl: event.ctrlKey,
|
|
11995
12094
|
alt: event.altKey,
|
|
11996
|
-
|
|
12095
|
+
// 仅对单字符键保留 shift(控制 toUpperCase 路径),
|
|
12096
|
+
// 但 Tab 特例:物理 Shift+Tab 要走 buildPtySequence 的 back-tab 分支。
|
|
12097
|
+
shift: event.shiftKey && (key.length === 1 || key === "tab"),
|
|
11997
12098
|
meta: event.metaKey
|
|
11998
12099
|
};
|
|
11999
12100
|
}
|
|
@@ -12216,6 +12317,8 @@
|
|
|
12216
12317
|
function buildPtySequence(key, modifiers) {
|
|
12217
12318
|
var mods = modifiers || { ctrl: false, alt: false, shift: false };
|
|
12218
12319
|
if (isModifierKey(key)) return "";
|
|
12320
|
+
// Shift+Tab → CSI Z (back-tab)。Claude Code 用它在 plan / 自动接受 模式间切换。
|
|
12321
|
+
if (key === "tab" && mods.shift) return String.fromCharCode(27) + "[Z";
|
|
12219
12322
|
var specialSequence = getPtySpecialSequence(key);
|
|
12220
12323
|
if (specialSequence) return specialSequence;
|
|
12221
12324
|
if (key.indexOf("ctrl_") === 0) {
|
|
@@ -13903,6 +14006,9 @@
|
|
|
13903
14006
|
function sendTerminalResize(cols, rows) {
|
|
13904
14007
|
if (!state.selectedId) return;
|
|
13905
14008
|
var selectedSess = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
14009
|
+
// 会话已被清除(如服务重启后 localStorage 还残留旧 id),后端 resize 会
|
|
14010
|
+
// 直接 400/404,console 留一条红色错误;这里提前剪掉,避免噪音。
|
|
14011
|
+
if (!selectedSess || selectedSess.status !== "running") return;
|
|
13906
14012
|
if (isStructuredSession(selectedSess)) return;
|
|
13907
14013
|
var nextSize = { cols: cols, rows: rows };
|
|
13908
14014
|
if (state.lastResize.cols !== nextSize.cols || state.lastResize.rows !== nextSize.rows) {
|