mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-11 22:51:00 -07:00
fix(cli): remove problematic modifyOtherKeys terminal query
The '\x1b[>4;?m' query causes some terminals (like macOS Terminal.app) to echo a stray 'm' character on startup. This mode is redundant as we already support the modern Kitty keyboard protocol.
This commit is contained in:
@@ -7,10 +7,6 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { TerminalCapabilityManager } from './terminalCapabilityManager.js';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import {
|
||||
enableKittyKeyboardProtocol,
|
||||
enableModifyOtherKeys,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
// Mock fs
|
||||
vi.mock('node:fs', () => ({
|
||||
@@ -25,8 +21,6 @@ vi.mock('@google/gemini-cli-core', () => ({
|
||||
},
|
||||
enableKittyKeyboardProtocol: vi.fn(),
|
||||
disableKittyKeyboardProtocol: vi.fn(),
|
||||
enableModifyOtherKeys: vi.fn(),
|
||||
disableModifyOtherKeys: vi.fn(),
|
||||
enableBracketedPasteMode: vi.fn(),
|
||||
disableBracketedPasteMode: vi.fn(),
|
||||
}));
|
||||
@@ -182,112 +176,4 @@ describe('TerminalCapabilityManager', () => {
|
||||
await promise;
|
||||
expect(manager.isKittyProtocolEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
describe('modifyOtherKeys detection', () => {
|
||||
it('should detect modifyOtherKeys support (level 2)', async () => {
|
||||
const manager = TerminalCapabilityManager.getInstance();
|
||||
const promise = manager.detectCapabilities();
|
||||
|
||||
// Simulate modifyOtherKeys level 2 response: \x1b[>4;2m
|
||||
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
|
||||
// Complete detection with DA1
|
||||
stdin.emit('data', Buffer.from('\x1b[?62c'));
|
||||
|
||||
await promise;
|
||||
|
||||
expect(enableModifyOtherKeys).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not enable modifyOtherKeys for level 0', async () => {
|
||||
const manager = TerminalCapabilityManager.getInstance();
|
||||
const promise = manager.detectCapabilities();
|
||||
|
||||
// Simulate modifyOtherKeys level 0 response: \x1b[>4;0m
|
||||
stdin.emit('data', Buffer.from('\x1b[>4;0m'));
|
||||
// Complete detection with DA1
|
||||
stdin.emit('data', Buffer.from('\x1b[?62c'));
|
||||
|
||||
await promise;
|
||||
|
||||
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should prefer Kitty over modifyOtherKeys', async () => {
|
||||
const manager = TerminalCapabilityManager.getInstance();
|
||||
const promise = manager.detectCapabilities();
|
||||
|
||||
// Simulate both Kitty and modifyOtherKeys responses
|
||||
stdin.emit('data', Buffer.from('\x1b[?1u'));
|
||||
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
|
||||
// Complete detection with DA1
|
||||
stdin.emit('data', Buffer.from('\x1b[?62c'));
|
||||
|
||||
await promise;
|
||||
expect(manager.isKittyProtocolEnabled()).toBe(true);
|
||||
|
||||
expect(enableKittyKeyboardProtocol).toHaveBeenCalled();
|
||||
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should enable modifyOtherKeys when Kitty not supported', async () => {
|
||||
const manager = TerminalCapabilityManager.getInstance();
|
||||
const promise = manager.detectCapabilities();
|
||||
|
||||
// Simulate only modifyOtherKeys response (no Kitty)
|
||||
stdin.emit('data', Buffer.from('\x1b[>4;2m'));
|
||||
// Complete detection with DA1
|
||||
stdin.emit('data', Buffer.from('\x1b[?62c'));
|
||||
|
||||
await promise;
|
||||
|
||||
expect(manager.isKittyProtocolEnabled()).toBe(false);
|
||||
expect(enableModifyOtherKeys).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle split modifyOtherKeys response chunks', async () => {
|
||||
const manager = TerminalCapabilityManager.getInstance();
|
||||
const promise = manager.detectCapabilities();
|
||||
|
||||
// Split response: \x1b[>4;2m
|
||||
stdin.emit('data', Buffer.from('\x1b[>4;'));
|
||||
stdin.emit('data', Buffer.from('2m'));
|
||||
// Complete detection with DA1
|
||||
stdin.emit('data', Buffer.from('\x1b[?62c'));
|
||||
|
||||
await promise;
|
||||
|
||||
expect(enableModifyOtherKeys).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should detect modifyOtherKeys with other capabilities', async () => {
|
||||
const manager = TerminalCapabilityManager.getInstance();
|
||||
const promise = manager.detectCapabilities();
|
||||
|
||||
stdin.emit('data', Buffer.from('\x1b]11;rgb:1a1a/1a1a/1a1a\x1b\\')); // background color
|
||||
stdin.emit('data', Buffer.from('\x1bP>|tmux\x1b\\')); // Terminal name
|
||||
stdin.emit('data', Buffer.from('\x1b[>4;2m')); // modifyOtherKeys
|
||||
// Complete detection with DA1
|
||||
stdin.emit('data', Buffer.from('\x1b[?62c'));
|
||||
|
||||
await promise;
|
||||
|
||||
expect(manager.getTerminalBackgroundColor()).toBe('#1a1a1a');
|
||||
expect(manager.getTerminalName()).toBe('tmux');
|
||||
|
||||
expect(enableModifyOtherKeys).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not enable modifyOtherKeys without explicit response', async () => {
|
||||
const manager = TerminalCapabilityManager.getInstance();
|
||||
const promise = manager.detectCapabilities();
|
||||
|
||||
// Simulate only DA1 response (no specific MOK or Kitty response)
|
||||
stdin.emit('data', Buffer.from('\x1b[?62c'));
|
||||
|
||||
await promise;
|
||||
|
||||
expect(manager.isKittyProtocolEnabled()).toBe(false);
|
||||
expect(enableModifyOtherKeys).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,8 +9,6 @@ import {
|
||||
debugLogger,
|
||||
enableKittyKeyboardProtocol,
|
||||
disableKittyKeyboardProtocol,
|
||||
enableModifyOtherKeys,
|
||||
disableModifyOtherKeys,
|
||||
enableBracketedPasteMode,
|
||||
disableBracketedPasteMode,
|
||||
} from '@google/gemini-cli-core';
|
||||
@@ -25,7 +23,6 @@ export class TerminalCapabilityManager {
|
||||
private static readonly OSC_11_QUERY = '\x1b]11;?\x1b\\';
|
||||
private static readonly TERMINAL_NAME_QUERY = '\x1b[>q';
|
||||
private static readonly DEVICE_ATTRIBUTES_QUERY = '\x1b[c';
|
||||
private static readonly MODIFY_OTHER_KEYS_QUERY = '\x1b[>4;?m';
|
||||
|
||||
// Kitty keyboard flags: CSI ? flags u
|
||||
// eslint-disable-next-line no-control-regex
|
||||
@@ -40,15 +37,11 @@ export class TerminalCapabilityManager {
|
||||
static readonly OSC_11_REGEX =
|
||||
// eslint-disable-next-line no-control-regex
|
||||
/\x1b\]11;rgb:([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})(\x1b\\|\x07)?/;
|
||||
// modifyOtherKeys response: CSI > 4 ; level m
|
||||
// eslint-disable-next-line no-control-regex
|
||||
private static readonly MODIFY_OTHER_KEYS_REGEX = /\x1b\[>4;(\d+)m/;
|
||||
|
||||
private detectionComplete = false;
|
||||
private terminalBackgroundColor: TerminalBackgroundColor;
|
||||
private kittySupported = false;
|
||||
private kittyEnabled = false;
|
||||
private modifyOtherKeysSupported = false;
|
||||
private terminalName: string | undefined;
|
||||
|
||||
private constructor() {}
|
||||
@@ -81,7 +74,6 @@ export class TerminalCapabilityManager {
|
||||
// don't bother catching errors since if one write
|
||||
// fails, the other probably will too
|
||||
disableKittyKeyboardProtocol();
|
||||
disableModifyOtherKeys();
|
||||
disableBracketedPasteMode();
|
||||
};
|
||||
process.on('exit', cleanupOnExit);
|
||||
@@ -99,7 +91,6 @@ export class TerminalCapabilityManager {
|
||||
let terminalNameReceived = false;
|
||||
let deviceAttributesReceived = false;
|
||||
let bgReceived = false;
|
||||
let modifyOtherKeysReceived = false;
|
||||
// eslint-disable-next-line prefer-const
|
||||
let timeoutId: NodeJS.Timeout;
|
||||
|
||||
@@ -149,21 +140,6 @@ export class TerminalCapabilityManager {
|
||||
this.kittySupported = true;
|
||||
}
|
||||
|
||||
// check for modifyOtherKeys support
|
||||
if (!modifyOtherKeysReceived) {
|
||||
const match = buffer.match(
|
||||
TerminalCapabilityManager.MODIFY_OTHER_KEYS_REGEX,
|
||||
);
|
||||
if (match) {
|
||||
modifyOtherKeysReceived = true;
|
||||
const level = parseInt(match[1], 10);
|
||||
this.modifyOtherKeysSupported = level >= 2;
|
||||
debugLogger.log(
|
||||
`Detected modifyOtherKeys support: ${this.modifyOtherKeysSupported} (level ${level})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Terminal Name/Version response.
|
||||
if (!terminalNameReceived) {
|
||||
const match = buffer.match(
|
||||
@@ -199,7 +175,6 @@ export class TerminalCapabilityManager {
|
||||
TerminalCapabilityManager.KITTY_QUERY +
|
||||
TerminalCapabilityManager.OSC_11_QUERY +
|
||||
TerminalCapabilityManager.TERMINAL_NAME_QUERY +
|
||||
TerminalCapabilityManager.MODIFY_OTHER_KEYS_QUERY +
|
||||
TerminalCapabilityManager.DEVICE_ATTRIBUTES_QUERY,
|
||||
);
|
||||
} catch (e) {
|
||||
@@ -214,8 +189,6 @@ export class TerminalCapabilityManager {
|
||||
if (this.kittySupported) {
|
||||
enableKittyKeyboardProtocol();
|
||||
this.kittyEnabled = true;
|
||||
} else if (this.modifyOtherKeysSupported) {
|
||||
enableModifyOtherKeys();
|
||||
}
|
||||
// Always enable bracketed paste since it'll be ignored if unsupported.
|
||||
enableBracketedPasteMode();
|
||||
|
||||
Reference in New Issue
Block a user