fix(tui): preserved direct herdr clears

Scoped direct Herdr multiplexer behavior to resize repaint selection so explicit transcript replacements continue to clear and replay scrollback.

Added coverage for resize stability and resetDisplay ED3 behavior.
This commit is contained in:
roboomp
2026-08-13 14:45:03 +00:00
parent bb0314a5a1
commit 9b389d6006
2 changed files with 42 additions and 6 deletions
+22 -6
View File
@@ -413,9 +413,25 @@ function parseSizeValue(value: SizeValue | undefined, referenceSize: number): nu
return undefined;
}
/** Detect terminal multiplexers where scrollback clearing and height-change redraws are hostile. */
/**
* Detect sessions where ED3 cannot safely rebuild scrollback. Direct HerdR
* panes support explicit clears; nested multiplexers remain unsafe because the
* inner tmux/screen/Zellij layer owns their history.
*/
function isMultiplexerSession(): boolean {
return isInsideTerminalMultiplexer();
if (!isInsideTerminalMultiplexer()) return false;
if (Bun.env.HERDR_ENV !== "1") return true;
const term = Bun.env.TERM?.toLowerCase() ?? "";
return Boolean(
Bun.env.TMUX ||
Bun.env.STY ||
Bun.env.ZELLIJ ||
Bun.env.CMUX_WORKSPACE_ID ||
Bun.env.CMUX_SURFACE_ID ||
Bun.env.CMUX_REMOTE_TRANSPORT ||
term.startsWith("tmux") ||
term.startsWith("screen"),
);
}
/**
@@ -439,12 +455,12 @@ function reportsSizeOnAltScreenToggle(): boolean {
/**
* Resize should repaint the visible window in place — no alternate-screen
* borrow, no ED3 scrollback rewrap — for multiplexer panes and for terminals
* that loop on alt-screen toggles. The tradeoff is identical to a multiplexer:
* scrollback above the window keeps its old wrap instead of being re-flowed.
* borrow, no ED3 scrollback rewrap — for multiplexer and direct HerdR panes,
* plus terminals that loop on alt-screen toggles. Direct HerdR remains a
* direct terminal for explicit transcript replacement and display reset.
*/
function resizeRepaintsInPlace(): boolean {
return isMultiplexerSession() || reportsSizeOnAltScreenToggle();
return isMultiplexerSession() || Bun.env.HERDR_ENV === "1" || reportsSizeOnAltScreenToggle();
}
/**
@@ -2564,6 +2564,26 @@ describe("multiplexer detection gates ED3 on resize", () => {
});
});
it("preserves explicit scrollback clears in direct HerdR", async () => {
await withEnvPatch({ ...NO_MULTIPLEXER_ENV, TERM: "dumb", HERDR_ENV: "1" }, async () => {
const term = new VirtualTerminal(40, 10, 1000);
const tui = new TUI(term);
tui.addChild(new MutableLinesComponent(Array.from({ length: 20 }, (_value, index) => `line-${index}`)));
try {
tui.start();
await settle(term);
const writes = captureWrites(term);
tui.resetDisplay();
await settle(term);
expect(writes.join("")).toContain(ED3);
} finally {
tui.stop();
}
});
});
it("keeps nested tmux inside HerdR on the ED3-unsafe path", async () => {
await withEnvPatch({ ...TMUX_ENV, TERM: "tmux-256color", HERDR_ENV: "1" }, async () => {
const term = new VirtualTerminal(40, 10, 1000);