fix(tui): balanced keyboard frames on emergency exit
- Popped the alternate-screen keyboard frame before restoring the main screen. - Reset the main-screen keyboard state in live and blind emergency cleanup. - Added regression coverage for both restore paths. Fixes #6810
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed emergency exits from fullscreen overlays leaving omp's main-screen Kitty keyboard frame active, which could corrupt Arrow Up input in the terminal pane after omp exited ([#6810](https://github.com/can1357/oh-my-pi/issues/6810)).
|
||||
|
||||
## [17.1.4] - 2026-07-26
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -275,17 +275,15 @@ export function emergencyTerminalRestore(): void {
|
||||
restoreTerminalStderr();
|
||||
const terminal = activeTerminal;
|
||||
if (terminal) {
|
||||
terminal.stop();
|
||||
// stop() never touches the alternate screen — the TUI owns that
|
||||
// state and exits it on the normal shutdown path. Only crash paths
|
||||
// with a fullscreen overlay still hold the alt buffer here. The
|
||||
// leave sequence is gated on the tracked state because it is NOT a
|
||||
// universally safe no-op: Windows' VT dispatcher homes the cursor
|
||||
// on DECRST 1049 even when the alt buffer is inactive.
|
||||
// Keyboard enhancement state is screen-local: pop the alt-screen
|
||||
// frame before leaving it, then let stop() pop omp's main-screen frame.
|
||||
if (altScreenActive) {
|
||||
terminal.write("\x1b[?1049l");
|
||||
const keyboardExit =
|
||||
terminal.keyboardEnhancementExitSequence ?? (terminal.kittyEnableSequence ? "\x1b[<u" : "");
|
||||
terminal.write(`${keyboardExit}\x1b[?1049l`);
|
||||
altScreenActive = false;
|
||||
}
|
||||
terminal.stop();
|
||||
terminal.showCursor();
|
||||
} else if (terminalEverStarted && !isTerminalHeadless()) {
|
||||
// Blind restore only if we know a terminal was started but lost track of it
|
||||
@@ -305,7 +303,7 @@ export function emergencyTerminalRestore(): void {
|
||||
// actually holds it — on Windows, DECRST 1049 on the main
|
||||
// buffer homes the cursor (unconditional CursorRestoreState
|
||||
// with no prior save), corrupting the shell handoff on exit.
|
||||
(altScreenActive ? "\x1b[?1049l" : "") +
|
||||
(altScreenActive ? "\x1b[?1049l\x1b[?1l\x1b>\x1b[<u" : "") + // Leave alt; reset main keyboard
|
||||
"\x1b[?25h", // Show cursor
|
||||
);
|
||||
altScreenActive = false;
|
||||
|
||||
@@ -92,6 +92,8 @@ describe("emergencyTerminalRestore alt-screen gating", () => {
|
||||
emergencyTerminalRestore();
|
||||
const firstRestore = writes.join("");
|
||||
expect(firstRestore).toContain("\x1b[?1049l");
|
||||
const altExit = firstRestore.indexOf("\x1b[?1049l");
|
||||
expect(firstRestore.indexOf("\x1b[<u", altExit + 1)).toBeGreaterThan(altExit);
|
||||
expect(firstRestore).toContain("\x1b[?1006l");
|
||||
expect(firstRestore).toContain("\x1b[?1003l");
|
||||
expect(firstRestore).toContain("\x1b[?1000l");
|
||||
@@ -122,4 +124,23 @@ describe("emergencyTerminalRestore alt-screen gating", () => {
|
||||
expect(activeRestore).toContain("\x1b[?1003l");
|
||||
expect(activeRestore).toContain("\x1b[?1000l");
|
||||
});
|
||||
it("pops keyboard enhancement frames on both screens when crashing from a fullscreen overlay", () => {
|
||||
const { terminal, writes } = startCapturedTerminal();
|
||||
process.stdin.emit("data", "\x1b[?0u");
|
||||
expect(terminal.kittyEnableSequence).toBe("\x1b[>1u");
|
||||
|
||||
terminal.write(`\x1b[?1049h${terminal.kittyEnableSequence}`);
|
||||
setAltScreenActive(true);
|
||||
writes.length = 0;
|
||||
|
||||
emergencyTerminalRestore();
|
||||
|
||||
const restored = writes.join("");
|
||||
const altPop = restored.indexOf("\x1b[<u");
|
||||
const altExit = restored.indexOf("\x1b[?1049l");
|
||||
const mainPop = restored.indexOf("\x1b[<u", altExit + 1);
|
||||
expect(altPop).toBeGreaterThanOrEqual(0);
|
||||
expect(altPop).toBeLessThan(altExit);
|
||||
expect(mainPop).toBeGreaterThan(altExit);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user