mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
feat(cli): add support for numpad SS3 sequences (#19659)
This commit is contained in:
committed by
GitHub
parent
27b7fc04de
commit
d54702185b
@@ -758,6 +758,80 @@ describe('KeypressContext', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('Numpad support', () => {
|
||||
it.each([
|
||||
{
|
||||
sequence: '\x1bOj',
|
||||
expected: { name: '*', sequence: '*', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOk',
|
||||
expected: { name: '+', sequence: '+', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOm',
|
||||
expected: { name: '-', sequence: '-', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOo',
|
||||
expected: { name: '/', sequence: '/', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOp',
|
||||
expected: { name: '0', sequence: '0', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOq',
|
||||
expected: { name: '1', sequence: '1', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOr',
|
||||
expected: { name: '2', sequence: '2', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOs',
|
||||
expected: { name: '3', sequence: '3', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOt',
|
||||
expected: { name: '4', sequence: '4', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOu',
|
||||
expected: { name: '5', sequence: '5', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOv',
|
||||
expected: { name: '6', sequence: '6', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOw',
|
||||
expected: { name: '7', sequence: '7', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOx',
|
||||
expected: { name: '8', sequence: '8', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOy',
|
||||
expected: { name: '9', sequence: '9', insertable: true },
|
||||
},
|
||||
{
|
||||
sequence: '\x1bOn',
|
||||
expected: { name: '.', sequence: '.', insertable: true },
|
||||
},
|
||||
])(
|
||||
'should recognize numpad sequence "$sequence" as $expected.name',
|
||||
({ sequence, expected }) => {
|
||||
const { keyHandler } = setupKeypressTest();
|
||||
act(() => stdin.write(sequence));
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining(expected),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('Double-tap and batching', () => {
|
||||
it('should emit two delete events for double-tap CSI[3~', async () => {
|
||||
const { keyHandler } = setupKeypressTest();
|
||||
|
||||
@@ -122,6 +122,25 @@ const KEY_INFO_MAP: Record<
|
||||
'[8^': { name: 'end', ctrl: true },
|
||||
};
|
||||
|
||||
// Numpad keys in Application Keypad Mode (SS3 sequences)
|
||||
const NUMPAD_MAP: Record<string, string> = {
|
||||
Oj: '*',
|
||||
Ok: '+',
|
||||
Om: '-',
|
||||
Oo: '/',
|
||||
Op: '0',
|
||||
Oq: '1',
|
||||
Or: '2',
|
||||
Os: '3',
|
||||
Ot: '4',
|
||||
Ou: '5',
|
||||
Ov: '6',
|
||||
Ow: '7',
|
||||
Ox: '8',
|
||||
Oy: '9',
|
||||
On: '.',
|
||||
};
|
||||
|
||||
const kUTF16SurrogateThreshold = 0x10000; // 2 ** 16
|
||||
function charLengthAt(str: string, i: number): number {
|
||||
if (str.length <= i) {
|
||||
@@ -538,18 +557,27 @@ function* emitKeys(
|
||||
insertable = true;
|
||||
}
|
||||
} else {
|
||||
name = 'undefined';
|
||||
if (
|
||||
(ctrl || cmd || alt) &&
|
||||
(code.endsWith('u') || code.endsWith('~'))
|
||||
) {
|
||||
// CSI-u or tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
|
||||
const codeNumber = parseInt(code.slice(1, -1), 10);
|
||||
const numpadChar = NUMPAD_MAP[code];
|
||||
if (numpadChar) {
|
||||
name = numpadChar;
|
||||
if (!ctrl && !cmd && !alt) {
|
||||
sequence = numpadChar;
|
||||
insertable = true;
|
||||
}
|
||||
} else {
|
||||
name = 'undefined';
|
||||
if (
|
||||
codeNumber >= 'a'.charCodeAt(0) &&
|
||||
codeNumber <= 'z'.charCodeAt(0)
|
||||
(ctrl || cmd || alt) &&
|
||||
(code.endsWith('u') || code.endsWith('~'))
|
||||
) {
|
||||
name = String.fromCharCode(codeNumber);
|
||||
// CSI-u or tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
|
||||
const codeNumber = parseInt(code.slice(1, -1), 10);
|
||||
if (
|
||||
codeNumber >= 'a'.charCodeAt(0) &&
|
||||
codeNumber <= 'z'.charCodeAt(0)
|
||||
) {
|
||||
name = String.fromCharCode(codeNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user