fix(tui): preserved legacy terminal keyboard fallback

Kept Terminal.keyboardEnhancementSequence optional and fell back to kittyEnableSequence when custom Terminal implementations do not expose the new property.

Covered the legacy custom Terminal path in the fullscreen overlay regression tests.

Fixes #3705
This commit is contained in:
roboomp
2026-06-28 06:18:50 +00:00
parent 2edd55e494
commit 78c4a29cbe
3 changed files with 41 additions and 4 deletions
+3 -2
View File
@@ -337,8 +337,9 @@ export interface Terminal {
get kittyEnableSequence(): string | null;
// The active modified-key reporting sequence to reassert after terminal buffer
// switches, or null when no enhanced keyboard mode is active.
get keyboardEnhancementSequence(): string | null;
// switches, or null when no enhanced keyboard mode is active. Optional so
// custom Terminals built against older pi-tui versions keep working.
readonly keyboardEnhancementSequence?: string | null;
// Cursor positioning (relative to current position)
moveBy(lines: number): void; // Move cursor up (negative) or down (positive) by N lines
+6 -2
View File
@@ -2492,7 +2492,7 @@ export class TUI extends Container {
// modified-key reporting sequence on the freshly entered alternate
// screen, or Esc/modified keys revert to legacy encoding inside
// fullscreen overlays (Ghostty/kitty/iTerm2).
this.terminal.write(`\x1b[?1049h${this.terminal.keyboardEnhancementSequence ?? ""}${MOUSE_TRACKING_ON}`);
this.terminal.write(`\x1b[?1049h${this.#keyboardEnhancementSequence()}${MOUSE_TRACKING_ON}`);
setAltScreenActive(true);
this.terminal.hideCursor();
this.#forgetHardwareCursorState();
@@ -3340,13 +3340,17 @@ export class TUI extends Container {
}
/** Enter or leave the alternate screen borrowed for transient resize frames. */
#keyboardEnhancementSequence(): string {
return this.terminal.keyboardEnhancementSequence ?? this.terminal.kittyEnableSequence ?? "";
}
#enterResizeAltSequence(): string {
if (this.#resizeAltActive || this.#altActive) return "";
this.#resizeAltActive = true;
setAltScreenActive(true);
this.#forgetHardwareCursorState();
this.#recordHardwareCursorHidden();
return `${ALT_SCREEN_ENTER}${this.terminal.keyboardEnhancementSequence ?? ""}`;
return `${ALT_SCREEN_ENTER}${this.#keyboardEnhancementSequence()}`;
}
#leaveResizeAltSequence(): string {
@@ -117,6 +117,12 @@ class CountingViewportTerminal extends VirtualTerminal {
}
}
class LegacyKeyboardVirtualTerminal extends VirtualTerminal {
get keyboardEnhancementSequence(): string | null {
return undefined as unknown as string | null;
}
}
function rows(prefix: string, count: number): string[] {
return Array.from({ length: count }, (_v, i) => `${prefix}${i}`);
}
@@ -3281,6 +3287,32 @@ describe("TUI terminal-state regressions", () => {
}
});
it("falls back to kittyEnableSequence for legacy custom terminals", async () => {
const term = new LegacyKeyboardVirtualTerminal(40, 8, 200);
const writes = captureWrites(term);
const tui = new TUI(term);
tui.addChild(new MutableLinesComponent(rows("base-", 8)));
try {
tui.start();
await settle(term);
const showFrom = writes.length;
tui.showOverlay(new MutableLinesComponent(["MODAL-0"]), {
width: "100%",
maxHeight: "100%",
margin: 0,
fullscreen: true,
});
await settle(term);
const modalWrites = writes.slice(showFrom).join("");
expect(modalWrites).toContain("\x1b[?1049h\x1b[>1u");
} finally {
tui.stop();
}
});
it("leaves native scrollback untouched across the modal lifetime", async () => {
const term = new VirtualTerminal(40, 6, 200);
const tui = new TUI(term);