fix(tui): treated WSL scrollback replay as unsafe
This commit is contained in:
+17
-2
@@ -167,6 +167,15 @@ function isMultiplexerSession(): boolean {
|
||||
return Boolean(Bun.env.TMUX || Bun.env.STY || Bun.env.ZELLIJ);
|
||||
}
|
||||
|
||||
function requiresNativeViewportProofForReplay(): boolean {
|
||||
return (
|
||||
process.platform === "win32" ||
|
||||
(process.platform === "linux" &&
|
||||
Boolean(Bun.env.WT_SESSION) &&
|
||||
Boolean(Bun.env.WSL_DISTRO_NAME || Bun.env.WSL_INTEROP))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for overlay positioning and sizing.
|
||||
* Values can be absolute numbers or percentage strings (e.g., "50%").
|
||||
@@ -1429,7 +1438,10 @@ export class TUI extends Container {
|
||||
}
|
||||
|
||||
#nativeViewportIsScrolled(nativeViewportAtBottom: boolean | undefined): boolean {
|
||||
return nativeViewportAtBottom === false || (nativeViewportAtBottom === undefined && process.platform === "win32");
|
||||
return (
|
||||
nativeViewportAtBottom === false ||
|
||||
(nativeViewportAtBottom === undefined && requiresNativeViewportProofForReplay())
|
||||
);
|
||||
}
|
||||
|
||||
#nativeViewportIsAtBottom(nativeViewportAtBottom: boolean | undefined): boolean {
|
||||
@@ -1437,7 +1449,10 @@ export class TUI extends Container {
|
||||
}
|
||||
|
||||
#canReplayNativeScrollbackAtCheckpoint(nativeViewportAtBottom: boolean | undefined): boolean {
|
||||
return nativeViewportAtBottom === true || (nativeViewportAtBottom === undefined && process.platform !== "win32");
|
||||
return (
|
||||
nativeViewportAtBottom === true ||
|
||||
(nativeViewportAtBottom === undefined && !requiresNativeViewportProofForReplay())
|
||||
);
|
||||
}
|
||||
|
||||
#padDeferredShrinkLines(lines: string[], paddedLength: number): string[] {
|
||||
|
||||
@@ -995,6 +995,49 @@ describe("TUI terminal-state regressions", () => {
|
||||
tui.stop();
|
||||
}
|
||||
});
|
||||
|
||||
it("treats unknown WSL Windows Terminal viewport state as scrolled", async () => {
|
||||
const originalPlatform = process.platform;
|
||||
const originalWtSession = Bun.env.WT_SESSION;
|
||||
const originalWslDistroName = Bun.env.WSL_DISTRO_NAME;
|
||||
const originalWslInterop = Bun.env.WSL_INTEROP;
|
||||
Object.defineProperty(process, "platform", { configurable: true, value: "linux" });
|
||||
Bun.env.WT_SESSION = "wt-test";
|
||||
Bun.env.WSL_DISTRO_NAME = "Ubuntu";
|
||||
delete Bun.env.WSL_INTEROP;
|
||||
|
||||
const term = new UnknownViewportTerminal(32, 5);
|
||||
const tui = new TUI(term);
|
||||
const component = new MutableLinesComponent(rows("line-", 12));
|
||||
tui.addChild(component);
|
||||
|
||||
try {
|
||||
tui.start();
|
||||
await settle(term);
|
||||
term.scrollLines(-2);
|
||||
const before = term.getBufferPosition();
|
||||
expect(before.viewportY).toBeGreaterThan(0);
|
||||
|
||||
component.setLines(rows("line-", 8));
|
||||
tui.requestRender();
|
||||
await settle(term);
|
||||
|
||||
const after = term.getBufferPosition();
|
||||
expect(after.viewportY).toBe(before.viewportY);
|
||||
expect(visible(term).map(line => line.trim())).toEqual(["line-5", "line-6", "line-7", "", ""]);
|
||||
expect(tui.refreshNativeScrollbackIfDirty()).toBe(false);
|
||||
expect(term.getBufferPosition().viewportY).toBe(before.viewportY);
|
||||
} finally {
|
||||
Object.defineProperty(process, "platform", { configurable: true, value: originalPlatform });
|
||||
if (originalWtSession === undefined) delete Bun.env.WT_SESSION;
|
||||
else Bun.env.WT_SESSION = originalWtSession;
|
||||
if (originalWslDistroName === undefined) delete Bun.env.WSL_DISTRO_NAME;
|
||||
else Bun.env.WSL_DISTRO_NAME = originalWslDistroName;
|
||||
if (originalWslInterop === undefined) delete Bun.env.WSL_INTEROP;
|
||||
else Bun.env.WSL_INTEROP = originalWslInterop;
|
||||
tui.stop();
|
||||
}
|
||||
});
|
||||
it("refreshes deferred native scrollback when the native viewport reaches bottom", async () => {
|
||||
const term = new VirtualTerminal(32, 5);
|
||||
const tui = new TUI(term);
|
||||
|
||||
Reference in New Issue
Block a user