diff --git a/packages/cli/src/ui/utils/terminalCapabilityManager.ts b/packages/cli/src/ui/utils/terminalCapabilityManager.ts index 7867f48e6f..6aeda005dc 100644 --- a/packages/cli/src/ui/utils/terminalCapabilityManager.ts +++ b/packages/cli/src/ui/utils/terminalCapabilityManager.ts @@ -13,12 +13,14 @@ import { disableModifyOtherKeys, enableBracketedPasteMode, disableBracketedPasteMode, + disableMouseEvents, } from '@google/gemini-cli-core'; import { parseColor } from '../themes/color-utils.js'; export type TerminalBackgroundColor = string | undefined; -const TERMINAL_CLEANUP_SEQUENCE = '\x1b[4;0m\x1b[?2004l'; +const TERMINAL_CLEANUP_SEQUENCE = + '\x1b[4;0m\x1b[?2004l\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l'; export function cleanupTerminalOnExit() { try { @@ -33,6 +35,7 @@ export function cleanupTerminalOnExit() { disableKittyKeyboardProtocol(); disableModifyOtherKeys(); disableBracketedPasteMode(); + disableMouseEvents(); } export class TerminalCapabilityManager { diff --git a/packages/cli/src/utils/cleanup.test.ts b/packages/cli/src/utils/cleanup.test.ts index a722e1a737..0e2454cb82 100644 --- a/packages/cli/src/utils/cleanup.test.ts +++ b/packages/cli/src/utils/cleanup.test.ts @@ -72,6 +72,46 @@ describe('cleanup', () => { expect(asyncFn).toHaveBeenCalledTimes(1); }); + it('should run cleanupFunctions BEFORE draining stdin and BEFORE runSyncCleanup', async () => { + const callOrder: string[] = []; + + // Cleanup function + registerCleanup(() => { + callOrder.push('cleanup'); + }); + + // Sync cleanup function (e.g. setRawMode(false)) + registerSyncCleanup(() => { + callOrder.push('sync'); + }); + + // Mock stdin.resume to track drainStdin + const originalResume = process.stdin.resume; + process.stdin.resume = vi.fn().mockImplementation(() => { + callOrder.push('drain'); + return process.stdin; + }); + + // Mock stdin properties for drainStdin + const originalIsTTY = process.stdin.isTTY; + Object.defineProperty(process.stdin, 'isTTY', { + value: true, + configurable: true, + }); + + try { + await runExitCleanup(); + } finally { + process.stdin.resume = originalResume; + Object.defineProperty(process.stdin, 'isTTY', { + value: originalIsTTY, + configurable: true, + }); + } + + expect(callOrder).toEqual(['drain', 'drain', 'sync', 'cleanup']); + }); + it('should continue running cleanup functions even if one throws an error', async () => { const errorFn = vi.fn().mockImplementation(() => { throw new Error('test error'); diff --git a/packages/cli/src/utils/cleanup.ts b/packages/cli/src/utils/cleanup.ts index 6185b34fe5..19aa795640 100644 --- a/packages/cli/src/utils/cleanup.ts +++ b/packages/cli/src/utils/cleanup.ts @@ -59,7 +59,7 @@ export function registerTelemetryConfig(config: Config) { export async function runExitCleanup() { // drain stdin to prevent printing garbage on exit - // https://github.com/google-gemini/gemini-cli/issues/1680 + // https://github.com/google-gemini/gemini-cli/issues/16801 await drainStdin(); runSyncCleanup();