Revert "fix: enable Shift+Tab shortcut in Windows PowerShell"

This reverts commit 9bee51dc17.
This commit is contained in:
LaZzyMan 2026-02-02 17:24:01 +08:00
parent f43bd7f8ab
commit 7935482c3a
2 changed files with 86 additions and 110 deletions

View file

@ -1256,13 +1256,13 @@ describe('KeypressContext - Kitty Protocol', () => {
});
expect(consoleLogSpy).toHaveBeenCalledWith(
'[DEBUG] CSI buffer accumulating:',
'[DEBUG] Kitty buffer accumulating:',
expect.stringContaining('\x1b[27u'),
);
const parsedCall = consoleLogSpy.mock.calls.find(
(args) =>
typeof args[0] === 'string' &&
args[0].includes('[DEBUG] CSI sequence parsed successfully'),
args[0].includes('[DEBUG] Kitty sequence parsed successfully'),
);
expect(parsedCall).toBeTruthy();
expect(parsedCall?.[1]).toEqual(expect.stringContaining('\x1b[27u'));
@ -1293,7 +1293,7 @@ describe('KeypressContext - Kitty Protocol', () => {
});
expect(consoleLogSpy).toHaveBeenCalledWith(
'[DEBUG] CSI buffer overflow, clearing:',
'[DEBUG] Kitty buffer overflow, clearing:',
expect.any(String),
);
});
@ -1384,13 +1384,13 @@ describe('KeypressContext - Kitty Protocol', () => {
// Verify debug logging for accumulation
expect(consoleLogSpy).toHaveBeenCalledWith(
'[DEBUG] CSI buffer accumulating:',
'[DEBUG] Kitty buffer accumulating:',
sequence,
);
// Verify warning for char codes
expect(consoleWarnSpy).toHaveBeenCalledWith(
'CSI sequence buffer has char codes:',
'Kitty sequence buffer has char codes:',
[27, 91, 49, 50],
);
});
@ -1468,29 +1468,6 @@ describe('KeypressContext - Kitty Protocol', () => {
);
},
);
it('should recognize Shift+Tab in non-Kitty protocol mode (Windows PowerShell)', () => {
const keyHandler = vi.fn();
// Create a wrapper with Kitty protocol disabled to simulate Windows PowerShell
const nonKittyWrapper = ({ children }: { children: React.ReactNode }) => (
<KeypressProvider kittyProtocolEnabled={false}>
{children}
</KeypressProvider>
);
const { result } = renderHook(() => useKeypressContext(), {
wrapper: nonKittyWrapper,
});
act(() => result.current.subscribe(keyHandler));
// Send legacy reverse Tab sequence (ESC [ Z)
act(() => stdin.sendKittySequence(`\x1b[Z`));
expect(keyHandler).toHaveBeenCalledWith(
expect.objectContaining({ name: 'tab', shift: true }),
);
});
});
describe('Double-tap and batching', () => {

View file

@ -508,96 +508,95 @@ export function KeypressProvider({
return;
}
// Parse CSI sequences for both Kitty protocol and legacy terminals
// This ensures Shift+Tab and other special keys work correctly even when
// Kitty protocol is not available (e.g., Windows PowerShell)
if (
kittySequenceBuffer ||
(key.sequence.startsWith(`${ESC}[`) &&
!key.sequence.startsWith(PASTE_MODE_PREFIX) &&
!key.sequence.startsWith(PASTE_MODE_SUFFIX) &&
!key.sequence.startsWith(FOCUS_IN) &&
!key.sequence.startsWith(FOCUS_OUT))
) {
kittySequenceBuffer += key.sequence;
if (debugKeystrokeLogging) {
console.log('[DEBUG] CSI buffer accumulating:', kittySequenceBuffer);
}
// Try to peel off as many complete sequences as are available at the
// start of the buffer. This handles batched inputs cleanly. If the
// prefix is incomplete or invalid, skip to the next CSI introducer
// (ESC[) so that a following valid sequence can still be parsed.
let parsedAny = false;
while (kittySequenceBuffer) {
const parsed = parseKittyPrefix(kittySequenceBuffer);
if (!parsed) {
// Look for the next potential CSI start beyond index 0
const nextStart = kittySequenceBuffer.indexOf(`${ESC}[`, 1);
if (nextStart > 0) {
if (debugKeystrokeLogging) {
console.log(
'[DEBUG] Skipping incomplete/invalid CSI prefix:',
kittySequenceBuffer.slice(0, nextStart),
);
}
kittySequenceBuffer = kittySequenceBuffer.slice(nextStart);
continue;
}
break;
}
if (debugKeystrokeLogging) {
const parsedSequence = kittySequenceBuffer.slice(0, parsed.length);
if (kittySequenceBuffer.length > parsed.length) {
console.log(
'[DEBUG] CSI sequence parsed successfully (prefix):',
parsedSequence,
);
} else {
console.log(
'[DEBUG] CSI sequence parsed successfully:',
parsedSequence,
);
}
}
// Consume the parsed prefix and broadcast it.
kittySequenceBuffer = kittySequenceBuffer.slice(parsed.length);
broadcast(parsed.key);
parsedAny = true;
}
if (parsedAny) return;
if (config?.getDebugMode() || debugKeystrokeLogging) {
const codes = Array.from(kittySequenceBuffer).map((ch) =>
ch.charCodeAt(0),
);
console.warn('CSI sequence buffer has char codes:', codes);
}
if (kittyProtocolEnabled) {
if (
kittyProtocolEnabled &&
kittySequenceBuffer.length > MAX_KITTY_SEQUENCE_LENGTH
kittySequenceBuffer ||
(key.sequence.startsWith(`${ESC}[`) &&
!key.sequence.startsWith(PASTE_MODE_PREFIX) &&
!key.sequence.startsWith(PASTE_MODE_SUFFIX) &&
!key.sequence.startsWith(FOCUS_IN) &&
!key.sequence.startsWith(FOCUS_OUT))
) {
kittySequenceBuffer += key.sequence;
if (debugKeystrokeLogging) {
console.log(
'[DEBUG] CSI buffer overflow, clearing:',
'[DEBUG] Kitty buffer accumulating:',
kittySequenceBuffer,
);
}
if (config) {
const event = new KittySequenceOverflowEvent(
kittySequenceBuffer.length,
kittySequenceBuffer,
);
logKittySequenceOverflow(config, event);
// Try to peel off as many complete sequences as are available at the
// start of the buffer. This handles batched inputs cleanly. If the
// prefix is incomplete or invalid, skip to the next CSI introducer
// (ESC[) so that a following valid sequence can still be parsed.
let parsedAny = false;
while (kittySequenceBuffer) {
const parsed = parseKittyPrefix(kittySequenceBuffer);
if (!parsed) {
// Look for the next potential CSI start beyond index 0
const nextStart = kittySequenceBuffer.indexOf(`${ESC}[`, 1);
if (nextStart > 0) {
if (debugKeystrokeLogging) {
console.log(
'[DEBUG] Skipping incomplete/invalid CSI prefix:',
kittySequenceBuffer.slice(0, nextStart),
);
}
kittySequenceBuffer = kittySequenceBuffer.slice(nextStart);
continue;
}
break;
}
if (debugKeystrokeLogging) {
const parsedSequence = kittySequenceBuffer.slice(
0,
parsed.length,
);
if (kittySequenceBuffer.length > parsed.length) {
console.log(
'[DEBUG] Kitty sequence parsed successfully (prefix):',
parsedSequence,
);
} else {
console.log(
'[DEBUG] Kitty sequence parsed successfully:',
parsedSequence,
);
}
}
// Consume the parsed prefix and broadcast it.
kittySequenceBuffer = kittySequenceBuffer.slice(parsed.length);
broadcast(parsed.key);
parsedAny = true;
}
if (parsedAny) return;
if (config?.getDebugMode() || debugKeystrokeLogging) {
const codes = Array.from(kittySequenceBuffer).map((ch) =>
ch.charCodeAt(0),
);
console.warn('Kitty sequence buffer has char codes:', codes);
}
if (kittySequenceBuffer.length > MAX_KITTY_SEQUENCE_LENGTH) {
if (debugKeystrokeLogging) {
console.log(
'[DEBUG] Kitty buffer overflow, clearing:',
kittySequenceBuffer,
);
}
if (config) {
const event = new KittySequenceOverflowEvent(
kittySequenceBuffer.length,
kittySequenceBuffer,
);
logKittySequenceOverflow(config, event);
}
kittySequenceBuffer = '';
} else {
return;
}
kittySequenceBuffer = '';
} else if (!kittyProtocolEnabled) {
// For non-Kitty terminals, clear the buffer to avoid accumulation
kittySequenceBuffer = '';
} else {
return;
}
}