mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
fix(keyboard): Implement Tab and Backspace handling for Kitty Protocol (#7006)
This commit is contained in:
@@ -17,6 +17,8 @@ import { EventEmitter } from 'events';
|
|||||||
import {
|
import {
|
||||||
KITTY_KEYCODE_ENTER,
|
KITTY_KEYCODE_ENTER,
|
||||||
KITTY_KEYCODE_NUMPAD_ENTER,
|
KITTY_KEYCODE_NUMPAD_ENTER,
|
||||||
|
KITTY_KEYCODE_TAB,
|
||||||
|
KITTY_KEYCODE_BACKSPACE,
|
||||||
CHAR_CODE_ESC,
|
CHAR_CODE_ESC,
|
||||||
CHAR_CODE_LEFT_BRACKET,
|
CHAR_CODE_LEFT_BRACKET,
|
||||||
CHAR_CODE_1,
|
CHAR_CODE_1,
|
||||||
@@ -282,10 +284,86 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Tab and Backspace handling', () => {
|
||||||
|
it('should recognize Tab key in kitty protocol', async () => {
|
||||||
|
const keyHandler = vi.fn();
|
||||||
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||||
|
act(() => result.current.subscribe(keyHandler));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
stdin.sendKittySequence(`\x1b[${KITTY_KEYCODE_TAB}u`);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(keyHandler).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'tab',
|
||||||
|
kittyProtocol: true,
|
||||||
|
shift: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should recognize Shift+Tab in kitty protocol', async () => {
|
||||||
|
const keyHandler = vi.fn();
|
||||||
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||||
|
act(() => result.current.subscribe(keyHandler));
|
||||||
|
|
||||||
|
// Modifier 2 is Shift
|
||||||
|
act(() => {
|
||||||
|
stdin.sendKittySequence(`\x1b[${KITTY_KEYCODE_TAB};2u`);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(keyHandler).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'tab',
|
||||||
|
kittyProtocol: true,
|
||||||
|
shift: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should recognize Backspace key in kitty protocol', async () => {
|
||||||
|
const keyHandler = vi.fn();
|
||||||
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||||
|
act(() => result.current.subscribe(keyHandler));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
stdin.sendKittySequence(`\x1b[${KITTY_KEYCODE_BACKSPACE}u`);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(keyHandler).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'backspace',
|
||||||
|
kittyProtocol: true,
|
||||||
|
meta: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should recognize Option+Backspace in kitty protocol', async () => {
|
||||||
|
const keyHandler = vi.fn();
|
||||||
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||||
|
act(() => result.current.subscribe(keyHandler));
|
||||||
|
|
||||||
|
// Modifier 3 is Alt/Option
|
||||||
|
act(() => {
|
||||||
|
stdin.sendKittySequence(`\x1b[${KITTY_KEYCODE_BACKSPACE};3u`);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(keyHandler).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'backspace',
|
||||||
|
kittyProtocol: true,
|
||||||
|
meta: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('paste mode', () => {
|
describe('paste mode', () => {
|
||||||
it('should handle multiline paste as a single event', async () => {
|
it('should handle multiline paste as a single event', async () => {
|
||||||
const keyHandler = vi.fn();
|
const keyHandler = vi.fn();
|
||||||
const pastedText = 'This \nis \na \nmultiline \npaste.';
|
const pastedText = 'This \n is \n a \n multiline \n paste.';
|
||||||
|
|
||||||
const { result } = renderHook(() => useKeypressContext(), {
|
const { result } = renderHook(() => useKeypressContext(), {
|
||||||
wrapper,
|
wrapper,
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ import { PassThrough } from 'stream';
|
|||||||
import {
|
import {
|
||||||
BACKSLASH_ENTER_DETECTION_WINDOW_MS,
|
BACKSLASH_ENTER_DETECTION_WINDOW_MS,
|
||||||
KITTY_CTRL_C,
|
KITTY_CTRL_C,
|
||||||
|
KITTY_KEYCODE_BACKSPACE,
|
||||||
KITTY_KEYCODE_ENTER,
|
KITTY_KEYCODE_ENTER,
|
||||||
KITTY_KEYCODE_NUMPAD_ENTER,
|
KITTY_KEYCODE_NUMPAD_ENTER,
|
||||||
|
KITTY_KEYCODE_TAB,
|
||||||
MAX_KITTY_SEQUENCE_LENGTH,
|
MAX_KITTY_SEQUENCE_LENGTH,
|
||||||
} from '../utils/platformConstants.js';
|
} from '../utils/platformConstants.js';
|
||||||
|
|
||||||
@@ -136,6 +138,30 @@ export function KeypressProvider({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keyCode === KITTY_KEYCODE_TAB) {
|
||||||
|
return {
|
||||||
|
name: 'tab',
|
||||||
|
ctrl,
|
||||||
|
meta: alt,
|
||||||
|
shift,
|
||||||
|
paste: false,
|
||||||
|
sequence,
|
||||||
|
kittyProtocol: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyCode === KITTY_KEYCODE_BACKSPACE) {
|
||||||
|
return {
|
||||||
|
name: 'backspace',
|
||||||
|
ctrl,
|
||||||
|
meta: alt,
|
||||||
|
shift,
|
||||||
|
paste: false,
|
||||||
|
sequence,
|
||||||
|
kittyProtocol: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
keyCode === KITTY_KEYCODE_ENTER ||
|
keyCode === KITTY_KEYCODE_ENTER ||
|
||||||
keyCode === KITTY_KEYCODE_NUMPAD_ENTER
|
keyCode === KITTY_KEYCODE_NUMPAD_ENTER
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export const KITTY_CTRL_C = '[99;5u';
|
|||||||
*/
|
*/
|
||||||
export const KITTY_KEYCODE_ENTER = 13;
|
export const KITTY_KEYCODE_ENTER = 13;
|
||||||
export const KITTY_KEYCODE_NUMPAD_ENTER = 57414;
|
export const KITTY_KEYCODE_NUMPAD_ENTER = 57414;
|
||||||
|
export const KITTY_KEYCODE_TAB = 9;
|
||||||
|
export const KITTY_KEYCODE_BACKSPACE = 127;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timing constants for terminal interactions
|
* Timing constants for terminal interactions
|
||||||
|
|||||||
Reference in New Issue
Block a user