@co0ontty/wand 1.1.4 → 1.1.7
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/claude-pty-bridge.js +2 -3
- package/dist/process-manager.js +5 -8
- package/dist/server.js +10 -1
- package/dist/web-ui/content/scripts.js +118 -56
- package/dist/web-ui/content/styles.css +3 -12
- package/package.json +1 -1
|
@@ -435,12 +435,11 @@ export class ClaudePtyBridge extends EventEmitter {
|
|
|
435
435
|
/\bgrant\b.*\bpermission\b/i.test(normalized) ||
|
|
436
436
|
/\bhaven't granted\b/i.test(normalized) ||
|
|
437
437
|
/\benter to confirm\b/i.test(normalized) ||
|
|
438
|
-
/\bwould you like to proceed\b/i.test(normalized)
|
|
439
|
-
/❯/.test(normalized));
|
|
438
|
+
/\bwould you like to proceed\b/i.test(normalized));
|
|
440
439
|
}
|
|
441
440
|
extractPromptText(normalized) {
|
|
442
441
|
// Return a snippet around the permission prompt
|
|
443
|
-
const match = normalized.match(/.{0,100}(?:do you want to|permission|grant|enter to confirm|would you like to proceed
|
|
442
|
+
const match = normalized.match(/.{0,100}(?:do you want to|permission|grant|enter to confirm|would you like to proceed).{0,100}/i);
|
|
444
443
|
return match?.[0] ?? normalized.slice(-100);
|
|
445
444
|
}
|
|
446
445
|
extractPermissionTarget(normalized) {
|
package/dist/process-manager.js
CHANGED
|
@@ -40,8 +40,7 @@ const PROMPT_PATTERNS = [
|
|
|
40
40
|
/\bwould you like to\b/i,
|
|
41
41
|
/\bshall i\b/i,
|
|
42
42
|
/\bcan i\b/i,
|
|
43
|
-
/\bgrant\b.*\bpermission\b/i
|
|
44
|
-
/❯/
|
|
43
|
+
/\bgrant\b.*\bpermission\b/i
|
|
45
44
|
];
|
|
46
45
|
const REAL_CONVERSATION_MIN_LINES = 2;
|
|
47
46
|
const REAL_CONVERSATION_MIN_MESSAGES = 2;
|
|
@@ -1143,16 +1142,14 @@ export class ProcessManager extends EventEmitter {
|
|
|
1143
1142
|
continue;
|
|
1144
1143
|
const jsonlPath = path.join(getClaudeProjectDir(cwd), `${claudeSessionId}.jsonl`);
|
|
1145
1144
|
try {
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
deleted++;
|
|
1149
|
-
}
|
|
1145
|
+
unlinkSync(jsonlPath);
|
|
1146
|
+
deleted++;
|
|
1150
1147
|
}
|
|
1151
1148
|
catch {
|
|
1152
|
-
// Best-effort —
|
|
1149
|
+
// Best-effort — file may already be gone
|
|
1153
1150
|
}
|
|
1154
1151
|
}
|
|
1155
|
-
if (
|
|
1152
|
+
if (sessions.length > 0) {
|
|
1156
1153
|
this.claudeHistoryCache = null;
|
|
1157
1154
|
}
|
|
1158
1155
|
return deleted;
|
package/dist/server.js
CHANGED
|
@@ -481,9 +481,15 @@ export async function startServer(config, configPath) {
|
|
|
481
481
|
res.status(500).json({ error: getErrorMessage(error, "检查更新失败。") });
|
|
482
482
|
}
|
|
483
483
|
});
|
|
484
|
+
let updateInFlight = false;
|
|
484
485
|
app.post("/api/update", async (_req, res) => {
|
|
486
|
+
if (updateInFlight) {
|
|
487
|
+
res.status(409).json({ error: "更新正在进行中,请稍候。" });
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
updateInFlight = true;
|
|
485
491
|
try {
|
|
486
|
-
const { updateAvailable, latest } = await checkNpmLatestVersion();
|
|
492
|
+
const { updateAvailable, latest } = await checkNpmLatestVersion(true);
|
|
487
493
|
if (!updateAvailable) {
|
|
488
494
|
res.json({ ok: true, message: "已经是最新版本。" });
|
|
489
495
|
return;
|
|
@@ -494,6 +500,9 @@ export async function startServer(config, configPath) {
|
|
|
494
500
|
catch (error) {
|
|
495
501
|
res.status(500).json({ error: getErrorMessage(error, "更新失败。") });
|
|
496
502
|
}
|
|
503
|
+
finally {
|
|
504
|
+
updateInFlight = false;
|
|
505
|
+
}
|
|
497
506
|
});
|
|
498
507
|
app.get("/api/sessions", (_req, res) => {
|
|
499
508
|
res.json(processes.list());
|
|
@@ -165,6 +165,14 @@
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
function getConfigCwd() {
|
|
169
|
+
return (state.config && state.config.defaultCwd) || "/tmp";
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function getEffectiveCwd() {
|
|
173
|
+
return state.workingDir || getConfigCwd();
|
|
174
|
+
}
|
|
175
|
+
|
|
168
176
|
// PWA install prompt handling
|
|
169
177
|
window.addEventListener('beforeinstallprompt', function(e) {
|
|
170
178
|
e.preventDefault();
|
|
@@ -464,14 +472,14 @@
|
|
|
464
472
|
'</div>' +
|
|
465
473
|
'<div class="file-side-panel-body">' +
|
|
466
474
|
'<div class="file-explorer-header">' +
|
|
467
|
-
'<span class="file-explorer-path" id="file-explorer-cwd">' + escapeHtml(selectedSession && selectedSession.cwd ? selectedSession.cwd : (
|
|
475
|
+
'<span class="file-explorer-path" id="file-explorer-cwd">' + escapeHtml(selectedSession && selectedSession.cwd ? selectedSession.cwd : getConfigCwd()) + '</span>' +
|
|
468
476
|
'<button class="file-explorer-refresh" id="file-explorer-refresh" title="刷新" aria-label="刷新文件列表">↻</button>' +
|
|
469
477
|
'</div>' +
|
|
470
478
|
'<div class="file-search-box">' +
|
|
471
479
|
'<input type="text" id="file-search-input" class="file-search-input" placeholder="搜索文件..." autocomplete="off" />' +
|
|
472
480
|
'<button class="file-search-clear" id="file-search-clear" type="button" aria-label="清除搜索">×</button>' +
|
|
473
481
|
'</div>' +
|
|
474
|
-
'<div class="file-explorer" id="file-explorer">' + renderFileExplorer(selectedSession && selectedSession.cwd ? selectedSession.cwd : (
|
|
482
|
+
'<div class="file-explorer" id="file-explorer">' + renderFileExplorer(selectedSession && selectedSession.cwd ? selectedSession.cwd : getConfigCwd()) + '</div>' +
|
|
475
483
|
'</div>' +
|
|
476
484
|
'</div>' +
|
|
477
485
|
'<div id="output" class="terminal-container' + (state.selectedId ? "" : " hidden") + ' active">' +
|
|
@@ -498,7 +506,7 @@
|
|
|
498
506
|
'<div class="blank-chat-cwd-wrap">' +
|
|
499
507
|
'<div class="blank-chat-cwd" id="blank-chat-cwd" role="button" tabindex="0" title="点击切换工作目录">' +
|
|
500
508
|
'<span class="blank-chat-cwd-icon">📁</span>' +
|
|
501
|
-
'<span class="blank-chat-cwd-path" id="blank-chat-cwd-path">' + escapeHtml(
|
|
509
|
+
'<span class="blank-chat-cwd-path" id="blank-chat-cwd-path">' + escapeHtml(getEffectiveCwd()) + '</span>' +
|
|
502
510
|
'<span class="blank-chat-cwd-arrow" id="blank-chat-cwd-arrow">▼</span>' +
|
|
503
511
|
'</div>' +
|
|
504
512
|
'<div class="blank-chat-cwd-dropdown hidden" id="blank-chat-cwd-dropdown"></div>' +
|
|
@@ -1103,7 +1111,7 @@
|
|
|
1103
1111
|
function updateFilePanelCwd(session) {
|
|
1104
1112
|
var cwdEl = document.getElementById("file-explorer-cwd");
|
|
1105
1113
|
if (!cwdEl) return;
|
|
1106
|
-
var cwd = session && session.cwd ? session.cwd : (
|
|
1114
|
+
var cwd = session && session.cwd ? session.cwd : getConfigCwd();
|
|
1107
1115
|
cwdEl.textContent = cwd;
|
|
1108
1116
|
}
|
|
1109
1117
|
|
|
@@ -1142,7 +1150,7 @@
|
|
|
1142
1150
|
}
|
|
1143
1151
|
|
|
1144
1152
|
function renderFileExplorer(cwd) {
|
|
1145
|
-
var root = cwd || (
|
|
1153
|
+
var root = cwd || getConfigCwd();
|
|
1146
1154
|
if (!root) {
|
|
1147
1155
|
return '<div class="file-explorer empty">No working directory configured.</div>';
|
|
1148
1156
|
}
|
|
@@ -1161,8 +1169,8 @@
|
|
|
1161
1169
|
var session = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
1162
1170
|
if (session) cwd = session.cwd || "";
|
|
1163
1171
|
}
|
|
1164
|
-
if (!cwd
|
|
1165
|
-
cwd =
|
|
1172
|
+
if (!cwd) {
|
|
1173
|
+
cwd = getConfigCwd();
|
|
1166
1174
|
}
|
|
1167
1175
|
if (!cwd) {
|
|
1168
1176
|
explorer.innerHTML = '<div class="file-explorer empty">No working directory.</div>';
|
|
@@ -1504,7 +1512,7 @@
|
|
|
1504
1512
|
}
|
|
1505
1513
|
|
|
1506
1514
|
function renderFolderPicker(state) {
|
|
1507
|
-
var currentDir =
|
|
1515
|
+
var currentDir = getEffectiveCwd();
|
|
1508
1516
|
|
|
1509
1517
|
// 如果有选中的会话,不显示单独的工作目录标签(已嵌入输入框内部)
|
|
1510
1518
|
if (state.selectedId) {
|
|
@@ -1530,7 +1538,7 @@
|
|
|
1530
1538
|
|
|
1531
1539
|
// 渲染内嵌到输入框的工作目录指示器
|
|
1532
1540
|
function renderWorkingDirIndicator(state) {
|
|
1533
|
-
var currentDir =
|
|
1541
|
+
var currentDir = getEffectiveCwd();
|
|
1534
1542
|
var displayDir = currentDir;
|
|
1535
1543
|
|
|
1536
1544
|
// 如果有选中的会话,使用会话的工作目录
|
|
@@ -1653,7 +1661,7 @@
|
|
|
1653
1661
|
'<div class="field">' +
|
|
1654
1662
|
'<label class="field-label" for="cwd">工作目录</label>' +
|
|
1655
1663
|
'<div class="suggestions-wrap">' +
|
|
1656
|
-
'<input id="cwd" type="text" class="field-input" autocomplete="off" placeholder="' + escapeHtml(
|
|
1664
|
+
'<input id="cwd" type="text" class="field-input" autocomplete="off" placeholder="' + escapeHtml(getEffectiveCwd()) + '" />' +
|
|
1657
1665
|
'<div id="cwd-suggestions" class="suggestions hidden"></div>' +
|
|
1658
1666
|
'</div>' +
|
|
1659
1667
|
'<p class="field-hint">留空则使用上方目录,支持路径自动补全。</p>' +
|
|
@@ -2221,7 +2229,7 @@
|
|
|
2221
2229
|
|
|
2222
2230
|
if (folderPickerInput) {
|
|
2223
2231
|
// Load initial folders from saved or default path
|
|
2224
|
-
var initialPath =
|
|
2232
|
+
var initialPath = getEffectiveCwd();
|
|
2225
2233
|
loadFolderSuggestions(initialPath);
|
|
2226
2234
|
|
|
2227
2235
|
folderPickerInput.addEventListener("focus", function() {
|
|
@@ -2397,16 +2405,14 @@
|
|
|
2397
2405
|
folderPickerModal.classList.remove("hidden");
|
|
2398
2406
|
// Set initial path in input
|
|
2399
2407
|
if (folderPickerInput) {
|
|
2400
|
-
folderPickerInput.value =
|
|
2408
|
+
folderPickerInput.value = getEffectiveCwd();
|
|
2401
2409
|
}
|
|
2402
2410
|
// Load initial folders
|
|
2403
|
-
var initialPath =
|
|
2411
|
+
var initialPath = getEffectiveCwd();
|
|
2404
2412
|
loadFolderSuggestions(initialPath);
|
|
2405
2413
|
renderBreadcrumb(initialPath);
|
|
2406
2414
|
}
|
|
2407
2415
|
|
|
2408
|
-
// Welcome screen folder button (legacy, now handled by initBlankChatCwd)
|
|
2409
|
-
|
|
2410
2416
|
if (closeFolderPicker && folderPickerModal) {
|
|
2411
2417
|
closeFolderPicker.addEventListener("click", function() {
|
|
2412
2418
|
folderPickerModal.classList.add("hidden");
|
|
@@ -2426,6 +2432,16 @@
|
|
|
2426
2432
|
setupVisualViewportHandlers();
|
|
2427
2433
|
}
|
|
2428
2434
|
|
|
2435
|
+
function activateSessionItem(sessionId) {
|
|
2436
|
+
var session = state.sessions.find(function(s) { return s.id === sessionId; });
|
|
2437
|
+
if (session && session.status !== "running") {
|
|
2438
|
+
resumeSessionFromList(sessionId);
|
|
2439
|
+
} else {
|
|
2440
|
+
selectSession(sessionId);
|
|
2441
|
+
}
|
|
2442
|
+
closeSessionsDrawer();
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2429
2445
|
function handleSessionItemClick(event) {
|
|
2430
2446
|
var target = event.target;
|
|
2431
2447
|
if (!target || !(target instanceof Element)) return;
|
|
@@ -2502,13 +2518,22 @@
|
|
|
2502
2518
|
}
|
|
2503
2519
|
if (_swipeState) return;
|
|
2504
2520
|
if (item.dataset.sessionId) {
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2521
|
+
activateSessionItem(item.dataset.sessionId);
|
|
2522
|
+
} else if (item.dataset.claudeHistoryId) {
|
|
2523
|
+
var claudeSessionId = item.dataset.claudeHistoryId;
|
|
2524
|
+
var cwd = item.dataset.cwd;
|
|
2525
|
+
resumeClaudeHistorySession(claudeSessionId, cwd)
|
|
2526
|
+
.then(function(data) {
|
|
2527
|
+
if (data && data.id) {
|
|
2528
|
+
state.selectedId = data.id;
|
|
2529
|
+
persistSelectedId();
|
|
2530
|
+
state.drafts[data.id] = "";
|
|
2531
|
+
loadSessions().then(function() {
|
|
2532
|
+
selectSession(data.id);
|
|
2533
|
+
closeSessionsDrawer();
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
});
|
|
2512
2537
|
}
|
|
2513
2538
|
}
|
|
2514
2539
|
}
|
|
@@ -2527,13 +2552,22 @@
|
|
|
2527
2552
|
return;
|
|
2528
2553
|
}
|
|
2529
2554
|
if (item.dataset.sessionId) {
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2555
|
+
activateSessionItem(item.dataset.sessionId);
|
|
2556
|
+
} else if (item.dataset.claudeHistoryId) {
|
|
2557
|
+
var claudeSessionId = item.dataset.claudeHistoryId;
|
|
2558
|
+
var cwd = item.dataset.cwd;
|
|
2559
|
+
resumeClaudeHistorySession(claudeSessionId, cwd)
|
|
2560
|
+
.then(function(data) {
|
|
2561
|
+
if (data && data.id) {
|
|
2562
|
+
state.selectedId = data.id;
|
|
2563
|
+
persistSelectedId();
|
|
2564
|
+
state.drafts[data.id] = "";
|
|
2565
|
+
loadSessions().then(function() {
|
|
2566
|
+
selectSession(data.id);
|
|
2567
|
+
closeSessionsDrawer();
|
|
2568
|
+
});
|
|
2569
|
+
}
|
|
2570
|
+
});
|
|
2537
2571
|
}
|
|
2538
2572
|
}
|
|
2539
2573
|
|
|
@@ -2968,15 +3002,9 @@
|
|
|
2968
3002
|
state.terminalViewportSize = { width: 0, height: 0 };
|
|
2969
3003
|
state.terminalAutoFollow = true;
|
|
2970
3004
|
clearTerminalScrollIdleTimer();
|
|
2971
|
-
//
|
|
3005
|
+
// Retry-based fit: wait for browser to complete layout before measuring and fitting
|
|
2972
3006
|
if (state.fitAddon) {
|
|
2973
|
-
|
|
2974
|
-
requestAnimationFrame(function() {
|
|
2975
|
-
if (state.fitAddon && shouldResizeTerminalViewport()) {
|
|
2976
|
-
state.fitAddon.fit();
|
|
2977
|
-
}
|
|
2978
|
-
});
|
|
2979
|
-
});
|
|
3007
|
+
ensureTerminalFit();
|
|
2980
3008
|
}
|
|
2981
3009
|
|
|
2982
3010
|
var viewport = getTerminalViewport();
|
|
@@ -3403,7 +3431,7 @@
|
|
|
3403
3431
|
updateShellChrome();
|
|
3404
3432
|
|
|
3405
3433
|
var selectedSession = state.sessions.find(function(s) { return s.id === id; });
|
|
3406
|
-
state.currentMessages = [];
|
|
3434
|
+
state.currentMessages = data.messages || [];
|
|
3407
3435
|
|
|
3408
3436
|
if (state.terminal) {
|
|
3409
3437
|
syncTerminalBuffer(id, data.output || "", { mode: "replace" });
|
|
@@ -3849,7 +3877,8 @@
|
|
|
3849
3877
|
function performUpdate() {
|
|
3850
3878
|
var msgEl = document.getElementById("update-message");
|
|
3851
3879
|
var updateBtn = document.getElementById("do-update-button");
|
|
3852
|
-
if (updateBtn)
|
|
3880
|
+
if (!updateBtn) return;
|
|
3881
|
+
updateBtn.disabled = true;
|
|
3853
3882
|
if (msgEl) {
|
|
3854
3883
|
msgEl.textContent = "正在更新,请稍候...";
|
|
3855
3884
|
msgEl.style.color = "var(--text-secondary)";
|
|
@@ -3869,9 +3898,9 @@
|
|
|
3869
3898
|
msgEl.classList.remove("hidden");
|
|
3870
3899
|
}
|
|
3871
3900
|
if (data.error) {
|
|
3872
|
-
|
|
3901
|
+
updateBtn.disabled = false;
|
|
3873
3902
|
} else {
|
|
3874
|
-
|
|
3903
|
+
updateBtn.classList.add("hidden");
|
|
3875
3904
|
}
|
|
3876
3905
|
})
|
|
3877
3906
|
.catch(function() {
|
|
@@ -3880,13 +3909,13 @@
|
|
|
3880
3909
|
msgEl.style.color = "var(--error)";
|
|
3881
3910
|
msgEl.classList.remove("hidden");
|
|
3882
3911
|
}
|
|
3883
|
-
|
|
3912
|
+
updateBtn.disabled = false;
|
|
3884
3913
|
});
|
|
3885
3914
|
}
|
|
3886
3915
|
|
|
3887
3916
|
function quickStartSession() {
|
|
3888
3917
|
var command = getPreferredTool();
|
|
3889
|
-
var defaultCwd =
|
|
3918
|
+
var defaultCwd = getEffectiveCwd();
|
|
3890
3919
|
var defaultMode = (state.config && state.config.defaultMode) ? state.config.defaultMode : "default";
|
|
3891
3920
|
state.preferredCommand = command;
|
|
3892
3921
|
state.chatMode = getSafeModeForTool(command, state.chatMode);
|
|
@@ -3923,7 +3952,7 @@
|
|
|
3923
3952
|
|
|
3924
3953
|
hideError(errorEl);
|
|
3925
3954
|
|
|
3926
|
-
var defaultCwd =
|
|
3955
|
+
var defaultCwd = getEffectiveCwd();
|
|
3927
3956
|
var selectedMode = getSafeModeForTool(command, state.modeValue);
|
|
3928
3957
|
state.modeValue = selectedMode;
|
|
3929
3958
|
state.chatMode = selectedMode;
|
|
@@ -4001,7 +4030,7 @@
|
|
|
4001
4030
|
}
|
|
4002
4031
|
|
|
4003
4032
|
function loadBlankChatCwdDropdown(dropdown) {
|
|
4004
|
-
var defaultCwd =
|
|
4033
|
+
var defaultCwd = getConfigCwd();
|
|
4005
4034
|
dropdown.innerHTML = '<div class="blank-chat-cwd-loading">加载中...</div>';
|
|
4006
4035
|
fetch("/api/recent-paths", { credentials: "same-origin" })
|
|
4007
4036
|
.then(function(res) { return res.json(); })
|
|
@@ -4359,7 +4388,7 @@
|
|
|
4359
4388
|
welcomeInput.placeholder = "正在启动会话...";
|
|
4360
4389
|
welcomeInput.disabled = true;
|
|
4361
4390
|
var mode = state.chatMode || "full-access";
|
|
4362
|
-
var defaultCwd =
|
|
4391
|
+
var defaultCwd = getEffectiveCwd();
|
|
4363
4392
|
var preferredTool = getPreferredTool();
|
|
4364
4393
|
fetch("/api/commands", {
|
|
4365
4394
|
method: "POST",
|
|
@@ -4421,7 +4450,7 @@
|
|
|
4421
4450
|
|
|
4422
4451
|
// No selected session, create a new one
|
|
4423
4452
|
var mode = state.chatMode || "full-access";
|
|
4424
|
-
var defaultCwd =
|
|
4453
|
+
var defaultCwd = getEffectiveCwd();
|
|
4425
4454
|
var preferredTool = getPreferredTool();
|
|
4426
4455
|
fetch("/api/commands", {
|
|
4427
4456
|
method: "POST",
|
|
@@ -4488,9 +4517,8 @@
|
|
|
4488
4517
|
// Init terminal if not already done
|
|
4489
4518
|
if (!state.terminal) initTerminal();
|
|
4490
4519
|
applyCurrentView();
|
|
4491
|
-
if (state.
|
|
4492
|
-
|
|
4493
|
-
scheduleTerminalResize(true);
|
|
4520
|
+
if (state.terminal && state.fitAddon) {
|
|
4521
|
+
ensureTerminalFit();
|
|
4494
4522
|
}
|
|
4495
4523
|
// Don't call renderChat() here — loadOutput() always calls renderChat() after it resolves.
|
|
4496
4524
|
// Calling renderChat() prematurely would render with stale/empty messages.
|
|
@@ -5375,7 +5403,7 @@
|
|
|
5375
5403
|
welcomeInput.placeholder = "Claude 正在思考,请稍候...";
|
|
5376
5404
|
welcomeInput.disabled = true;
|
|
5377
5405
|
var mode = state.chatMode || "full-access";
|
|
5378
|
-
var defaultCwd =
|
|
5406
|
+
var defaultCwd = getEffectiveCwd();
|
|
5379
5407
|
var preferredTool = getPreferredTool();
|
|
5380
5408
|
fetch("/api/commands", {
|
|
5381
5409
|
method: "POST",
|
|
@@ -5411,7 +5439,7 @@
|
|
|
5411
5439
|
|
|
5412
5440
|
function createSessionFromInput(value, inputBox, welcomeInput) {
|
|
5413
5441
|
var mode = state.chatMode || "full-access";
|
|
5414
|
-
var defaultCwd =
|
|
5442
|
+
var defaultCwd = getEffectiveCwd();
|
|
5415
5443
|
var preferredTool = getPreferredTool();
|
|
5416
5444
|
fetch("/api/commands", {
|
|
5417
5445
|
method: "POST",
|
|
@@ -6458,6 +6486,34 @@
|
|
|
6458
6486
|
updateTerminalJumpToBottomButton();
|
|
6459
6487
|
}
|
|
6460
6488
|
|
|
6489
|
+
function ensureTerminalFit() {
|
|
6490
|
+
var maxAttempts = 10;
|
|
6491
|
+
var attempt = 0;
|
|
6492
|
+
function tryFit() {
|
|
6493
|
+
attempt++;
|
|
6494
|
+
state.terminalViewportSize = { width: 0, height: 0 };
|
|
6495
|
+
if (shouldResizeTerminalViewport() && state.fitAddon) {
|
|
6496
|
+
state.fitAddon.fit();
|
|
6497
|
+
maybeScrollTerminalToBottom("resize");
|
|
6498
|
+
if (state.selectedId && state.terminal) {
|
|
6499
|
+
var nextSize = { cols: state.terminal.cols, rows: state.terminal.rows };
|
|
6500
|
+
if (state.lastResize.cols !== nextSize.cols || state.lastResize.rows !== nextSize.rows) {
|
|
6501
|
+
state.lastResize = nextSize;
|
|
6502
|
+
fetch("/api/sessions/" + state.selectedId + "/resize", {
|
|
6503
|
+
method: "POST",
|
|
6504
|
+
headers: { "Content-Type": "application/json" },
|
|
6505
|
+
credentials: "same-origin",
|
|
6506
|
+
body: JSON.stringify(nextSize)
|
|
6507
|
+
}).catch(function() {});
|
|
6508
|
+
}
|
|
6509
|
+
}
|
|
6510
|
+
} else if (attempt < maxAttempts) {
|
|
6511
|
+
requestAnimationFrame(tryFit);
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6514
|
+
requestAnimationFrame(tryFit);
|
|
6515
|
+
}
|
|
6516
|
+
|
|
6461
6517
|
function scheduleTerminalResize(immediate) {
|
|
6462
6518
|
if (state.resizeTimer) {
|
|
6463
6519
|
clearTimeout(state.resizeTimer);
|
|
@@ -6583,7 +6639,11 @@
|
|
|
6583
6639
|
}
|
|
6584
6640
|
updateSessionSnapshot(snapshot);
|
|
6585
6641
|
if (msg.sessionId === state.selectedId) {
|
|
6642
|
+
if (msg.data.messages) {
|
|
6643
|
+
state.currentMessages = msg.data.messages;
|
|
6644
|
+
}
|
|
6586
6645
|
updateTaskDisplay();
|
|
6646
|
+
scheduleChatRender();
|
|
6587
6647
|
}
|
|
6588
6648
|
|
|
6589
6649
|
}
|
|
@@ -6652,6 +6712,8 @@
|
|
|
6652
6712
|
if (msg.sessionId === state.selectedId && msg.data) {
|
|
6653
6713
|
if (chatRenderTimer) { clearTimeout(chatRenderTimer); chatRenderTimer = null; }
|
|
6654
6714
|
updateTerminalOutput(msg.data.output || "", msg.sessionId, "replace");
|
|
6715
|
+
// Ensure terminal is properly fitted after receiving initial data
|
|
6716
|
+
scheduleTerminalResize(true);
|
|
6655
6717
|
}
|
|
6656
6718
|
break;
|
|
6657
6719
|
case 'usage':
|
|
@@ -6710,10 +6772,10 @@
|
|
|
6710
6772
|
}
|
|
6711
6773
|
}
|
|
6712
6774
|
if (permissionActionsEl) permissionActionsEl.classList.remove("hidden");
|
|
6713
|
-
//
|
|
6714
|
-
taskEl.textContent =
|
|
6715
|
-
taskEl.classList.
|
|
6716
|
-
taskEl.classList.
|
|
6775
|
+
// Hide top task bar — permission info is already shown in the composer
|
|
6776
|
+
taskEl.textContent = "";
|
|
6777
|
+
taskEl.classList.add("hidden");
|
|
6778
|
+
taskEl.classList.remove("permission-blocked");
|
|
6717
6779
|
return;
|
|
6718
6780
|
}
|
|
6719
6781
|
|
|
@@ -1529,15 +1529,6 @@
|
|
|
1529
1529
|
flex-shrink: 0;
|
|
1530
1530
|
animation: task-pulse 1.2s ease-in-out infinite;
|
|
1531
1531
|
}
|
|
1532
|
-
.current-task.permission-blocked {
|
|
1533
|
-
color: #d18b00;
|
|
1534
|
-
background: rgba(240, 165, 0, 0.14);
|
|
1535
|
-
border-color: rgba(240, 165, 0, 0.3);
|
|
1536
|
-
}
|
|
1537
|
-
.current-task.permission-blocked::before {
|
|
1538
|
-
background: #f0a500;
|
|
1539
|
-
animation: none;
|
|
1540
|
-
}
|
|
1541
1532
|
@keyframes task-pulse {
|
|
1542
1533
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
1543
1534
|
50% { opacity: 0.5; transform: scale(0.8); }
|
|
@@ -1559,8 +1550,8 @@
|
|
|
1559
1550
|
padding: 10px;
|
|
1560
1551
|
overflow: hidden;
|
|
1561
1552
|
min-height: 0;
|
|
1562
|
-
margin: 0
|
|
1563
|
-
border-radius: var(--radius-
|
|
1553
|
+
margin: 0 6px 6px;
|
|
1554
|
+
border-radius: var(--radius-md);
|
|
1564
1555
|
border: 1px solid rgba(140, 110, 85, 0.35);
|
|
1565
1556
|
box-shadow:
|
|
1566
1557
|
inset 0 1px 0 rgba(255, 255, 255, 0.06),
|
|
@@ -4861,7 +4852,7 @@
|
|
|
4861
4852
|
width: min(300px, calc(100vw - 20px));
|
|
4862
4853
|
top: 0;
|
|
4863
4854
|
}
|
|
4864
|
-
.terminal-container { margin: 0
|
|
4855
|
+
.terminal-container { margin: 0 6px 6px; min-height: 0; }
|
|
4865
4856
|
.btn { min-height: 40px; }
|
|
4866
4857
|
.btn-sm { min-height: 36px; padding: 0 10px; font-size: 0.75rem; height: 36px; }
|
|
4867
4858
|
.btn-icon { width: 36px; height: 36px; min-height: 36px; }
|