@co0ontty/wand 1.17.4 → 1.17.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.
- package/dist/server.js +21 -3
- package/dist/web-ui/content/scripts.js +64 -21
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -60,13 +60,31 @@ async function checkNpmLatestVersion(forceRefresh = false) {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
function compareSemver(a, b) {
|
|
63
|
-
const
|
|
64
|
-
|
|
63
|
+
const parse = (v) => {
|
|
64
|
+
const [main, ...rest] = v.split("-");
|
|
65
|
+
const pre = rest.join("-");
|
|
66
|
+
const mainParts = main.split(".").map((n) => Number(n) || 0);
|
|
67
|
+
return { mainParts, pre };
|
|
68
|
+
};
|
|
69
|
+
const pa = parse(a);
|
|
70
|
+
const pb = parse(b);
|
|
65
71
|
for (let i = 0; i < 3; i++) {
|
|
66
|
-
const diff = (pa[i] || 0) - (pb[i] || 0);
|
|
72
|
+
const diff = (pa.mainParts[i] || 0) - (pb.mainParts[i] || 0);
|
|
67
73
|
if (diff !== 0)
|
|
68
74
|
return diff;
|
|
69
75
|
}
|
|
76
|
+
// Main version equal — apply semver prerelease rule: no prerelease > with prerelease.
|
|
77
|
+
if (!pa.pre && pb.pre)
|
|
78
|
+
return 1;
|
|
79
|
+
if (pa.pre && !pb.pre)
|
|
80
|
+
return -1;
|
|
81
|
+
if (!pa.pre && !pb.pre)
|
|
82
|
+
return 0;
|
|
83
|
+
// Both have prerelease: lexical compare handles debug.MMDDHHMM ordering.
|
|
84
|
+
if (pa.pre < pb.pre)
|
|
85
|
+
return -1;
|
|
86
|
+
if (pa.pre > pb.pre)
|
|
87
|
+
return 1;
|
|
70
88
|
return 0;
|
|
71
89
|
}
|
|
72
90
|
let cachedGitHubApk = null;
|
|
@@ -1257,7 +1257,7 @@
|
|
|
1257
1257
|
'<button id="attach-btn" class="btn-circle btn-circle-attach" type="button" title="附加文件">' +
|
|
1258
1258
|
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>' +
|
|
1259
1259
|
'</button>' +
|
|
1260
|
-
'<input type="file" id="file-upload-input" multiple style="
|
|
1260
|
+
'<input type="file" id="file-upload-input" multiple style="position:absolute;width:1px;height:1px;opacity:0;overflow:hidden;clip:rect(0,0,0,0);pointer-events:none">' +
|
|
1261
1261
|
'<select id="chat-mode-select" class="chat-mode-select" title="仅对新建会话生效">' +
|
|
1262
1262
|
renderModeOptions(preferredTool, composerMode) +
|
|
1263
1263
|
'</select>' +
|
|
@@ -4535,6 +4535,10 @@
|
|
|
4535
4535
|
state.terminal.write(state.terminalOutput);
|
|
4536
4536
|
state.lastTerminalResyncAt = Date.now();
|
|
4537
4537
|
maybeScrollTerminalToBottom("output");
|
|
4538
|
+
// Remeasure against real container: the refresh button used to only
|
|
4539
|
+
// reset+write, so a stale cols/rows (set at mount time with hidden
|
|
4540
|
+
// container) would survive the refresh and keep wrapping output wrong.
|
|
4541
|
+
ensureTerminalFit("refresh");
|
|
4538
4542
|
return true;
|
|
4539
4543
|
}
|
|
4540
4544
|
|
|
@@ -4708,6 +4712,10 @@
|
|
|
4708
4712
|
initTerminalResizeHandle();
|
|
4709
4713
|
observeTerminalResize();
|
|
4710
4714
|
startTerminalHealthCheck();
|
|
4715
|
+
// Container may have been hidden / zero-width at construction
|
|
4716
|
+
// time (hard-coded 120x36). Remeasure against the real container
|
|
4717
|
+
// so wterm reflows the just-written history to the correct cols.
|
|
4718
|
+
ensureTerminalFit("mount");
|
|
4711
4719
|
}).catch(function(err) {
|
|
4712
4720
|
state.terminalInitializing = false;
|
|
4713
4721
|
console.error("[wand] wterm init failed:", err);
|
|
@@ -5520,11 +5528,9 @@
|
|
|
5520
5528
|
|
|
5521
5529
|
if (state.terminal && id === state.selectedId && data.output !== undefined) {
|
|
5522
5530
|
syncTerminalBuffer(id, data.output, { mode: "append" });
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
});
|
|
5527
|
-
}
|
|
5531
|
+
// Session-switch / history replay: force a real fit so wterm
|
|
5532
|
+
// reflows the just-written output against the real container.
|
|
5533
|
+
ensureTerminalFit("session-switch");
|
|
5528
5534
|
}
|
|
5529
5535
|
|
|
5530
5536
|
var selectedSession = state.sessions.find(function(s) { return s.id === id; });
|
|
@@ -7884,6 +7890,10 @@
|
|
|
7884
7890
|
}
|
|
7885
7891
|
applyCurrentView();
|
|
7886
7892
|
focusInputBox(true);
|
|
7893
|
+
// Container just flipped from hidden -> visible (or geometry changed
|
|
7894
|
+
// because chat/terminal panels swapped). Refit now so the terminal
|
|
7895
|
+
// picks up the real cols/rows instead of keeping the stale ones.
|
|
7896
|
+
if (!structured) ensureTerminalFit("view-switch");
|
|
7887
7897
|
}
|
|
7888
7898
|
|
|
7889
7899
|
|
|
@@ -10089,6 +10099,15 @@
|
|
|
10089
10099
|
state.visualViewportHandler = function() { scheduleTerminalResize(true); };
|
|
10090
10100
|
window.visualViewport.addEventListener("resize", state.visualViewportHandler);
|
|
10091
10101
|
}
|
|
10102
|
+
// Page returning from background: container dimensions may have
|
|
10103
|
+
// drifted (PWA standalone, tab switch, iOS address-bar toggle).
|
|
10104
|
+
state.visibilityHandler = function() {
|
|
10105
|
+
if (!document.hidden) ensureTerminalFit("visibility");
|
|
10106
|
+
};
|
|
10107
|
+
document.addEventListener("visibilitychange", state.visibilityHandler);
|
|
10108
|
+
// Mobile device rotation — large geometry change.
|
|
10109
|
+
state.orientationHandler = function() { ensureTerminalFit("orientation"); };
|
|
10110
|
+
window.addEventListener("orientationchange", state.orientationHandler);
|
|
10092
10111
|
requestAnimationFrame(function() { scheduleTerminalResize(true); });
|
|
10093
10112
|
}
|
|
10094
10113
|
|
|
@@ -10098,17 +10117,14 @@
|
|
|
10098
10117
|
if (!state.terminal || state.currentView !== "terminal" || document.hidden) return;
|
|
10099
10118
|
var selectedSession = state.sessions.find(function(s) { return s.id === state.selectedId; });
|
|
10100
10119
|
if (!selectedSession || selectedSession.sessionKind === "structured") return;
|
|
10101
|
-
// Lightweight
|
|
10102
|
-
|
|
10103
|
-
// Full re-sync every 30s during output pauses
|
|
10120
|
+
// Lightweight fit every 5s: gated + double-rAF + remeasure.
|
|
10121
|
+
ensureTerminalFit("health");
|
|
10122
|
+
// Full re-sync every 30s during output pauses — also refits.
|
|
10104
10123
|
var now = Date.now();
|
|
10105
10124
|
var chunkPause = state.lastChunkAt > 0 && (now - state.lastChunkAt > 300);
|
|
10106
10125
|
var resyncDue = (now - state.lastTerminalResyncAt) > 30000;
|
|
10107
10126
|
if (resyncDue && (chunkPause || selectedSession.status !== "running") && state.terminalOutput) {
|
|
10108
|
-
|
|
10109
|
-
state.terminal.reset();
|
|
10110
|
-
state.terminal.write(state.terminalOutput);
|
|
10111
|
-
maybeScrollTerminalToBottom("output");
|
|
10127
|
+
softResyncTerminal();
|
|
10112
10128
|
}
|
|
10113
10129
|
}, 5000);
|
|
10114
10130
|
}
|
|
@@ -10138,6 +10154,14 @@
|
|
|
10138
10154
|
window.visualViewport.removeEventListener("resize", state.visualViewportHandler);
|
|
10139
10155
|
state.visualViewportHandler = null;
|
|
10140
10156
|
}
|
|
10157
|
+
if (state.visibilityHandler) {
|
|
10158
|
+
document.removeEventListener("visibilitychange", state.visibilityHandler);
|
|
10159
|
+
state.visibilityHandler = null;
|
|
10160
|
+
}
|
|
10161
|
+
if (state.orientationHandler) {
|
|
10162
|
+
window.removeEventListener("orientationchange", state.orientationHandler);
|
|
10163
|
+
state.orientationHandler = null;
|
|
10164
|
+
}
|
|
10141
10165
|
[["mousemove", "resizeMouseMove"], ["mouseup", "resizeMouseUp"],
|
|
10142
10166
|
["touchmove", "resizeTouchMove"], ["touchend", "resizeTouchEnd"]
|
|
10143
10167
|
].forEach(function(pair) {
|
|
@@ -10201,10 +10225,30 @@
|
|
|
10201
10225
|
}
|
|
10202
10226
|
}
|
|
10203
10227
|
|
|
10204
|
-
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10228
|
+
// Unified entry point for re-fitting the xterm grid to its container.
|
|
10229
|
+
// Why: wterm's `autoResize` ResizeObserver only fires on subsequent
|
|
10230
|
+
// container size changes. If the terminal is mounted or written to
|
|
10231
|
+
// while the container is hidden/zero-width, cols/rows stay wrong and
|
|
10232
|
+
// new output renders with broken wrapping (content visually piles at
|
|
10233
|
+
// the top). Call this after any layout change that might have altered
|
|
10234
|
+
// container geometry (mount, session switch, view switch, refresh).
|
|
10235
|
+
function ensureTerminalFit(reason) {
|
|
10236
|
+
if (!state.terminal) return false;
|
|
10237
|
+
var el = document.getElementById("output");
|
|
10238
|
+
if (!el || el.offsetWidth === 0 || el.offsetHeight === 0) return false;
|
|
10239
|
+
requestAnimationFrame(function() {
|
|
10240
|
+
requestAnimationFrame(function() {
|
|
10241
|
+
if (!state.terminal) return;
|
|
10242
|
+
if (typeof state.terminal.remeasure === "function") {
|
|
10243
|
+
state.terminal.remeasure();
|
|
10244
|
+
}
|
|
10245
|
+
sendTerminalResize(state.terminal.cols, state.terminal.rows);
|
|
10246
|
+
if (state.terminalAutoFollow || isTerminalNearBottom()) {
|
|
10247
|
+
maybeScrollTerminalToBottom("resize");
|
|
10248
|
+
}
|
|
10249
|
+
});
|
|
10250
|
+
});
|
|
10251
|
+
return true;
|
|
10208
10252
|
}
|
|
10209
10253
|
|
|
10210
10254
|
function scheduleTerminalResize(immediate) {
|
|
@@ -10269,10 +10313,9 @@
|
|
|
10269
10313
|
// Flush pending messages after reconnection
|
|
10270
10314
|
flushPendingMessages();
|
|
10271
10315
|
// Re-fit terminal on reconnect — the viewport may have changed
|
|
10272
|
-
// while disconnected,
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
}
|
|
10316
|
+
// while disconnected, so remeasure against real container size
|
|
10317
|
+
// rather than sending stale cols/rows from before the disconnect.
|
|
10318
|
+
ensureTerminalFit("ws-reconnect");
|
|
10276
10319
|
};
|
|
10277
10320
|
|
|
10278
10321
|
ws.onmessage = function(event) {
|