mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix: terminal state restoration and abort handling regression
Fixes regression introduced in 1a23bfa430.
- Add restoreTerminalState helper to disable kitty keyboard protocol and
CSI u key release reporting when switching modes or cleaning up
- Improve claudeLocal abort handling with SIGINT → SIGTERM → SIGKILL
escalation timeouts for graceful process termination
- Fix useSwitchControls to properly handle CSI u space sequences (0x20)
and key.name/key.sequence detection for space key presses
- Fix typo: exutFuture → exitFuture in claudeLocalLauncher
This commit is contained in:
@@ -6,6 +6,8 @@ import { useSwitchControls, type ConfirmationMode, type ActionInProgress } from
|
||||
|
||||
type Key = {
|
||||
ctrl?: boolean;
|
||||
name?: string;
|
||||
sequence?: string;
|
||||
};
|
||||
|
||||
type SwitchState = {
|
||||
@@ -194,6 +196,53 @@ describe('useSwitchControls', () => {
|
||||
expect(latestState?.actionInProgress).toBe(null);
|
||||
});
|
||||
|
||||
it('accepts CSI u space sequences', async () => {
|
||||
const onSwitch = vi.fn();
|
||||
await mount({ onSwitch });
|
||||
|
||||
await triggerInput('\u001b[32u', {});
|
||||
expect(latestState?.confirmationMode).toBe('switch');
|
||||
});
|
||||
|
||||
it('accepts CSI u space sequences with modifiers', async () => {
|
||||
const onSwitch = vi.fn();
|
||||
await mount({ onSwitch });
|
||||
|
||||
await triggerInput('\u001b[32;2u', {});
|
||||
expect(latestState?.confirmationMode).toBe('switch');
|
||||
});
|
||||
|
||||
it('ignores CSI u key-release space sequences', async () => {
|
||||
const onSwitch = vi.fn();
|
||||
await mount({ onSwitch });
|
||||
|
||||
await triggerInput(' ', {});
|
||||
expect(latestState?.confirmationMode).toBe('switch');
|
||||
|
||||
await triggerInput('\u001b[32;2:3u', {});
|
||||
expect(latestState?.confirmationMode).toBe('switch');
|
||||
expect(onSwitch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('accepts space via key name when input is empty', async () => {
|
||||
const onSwitch = vi.fn();
|
||||
await mount({ onSwitch });
|
||||
|
||||
await triggerInput('', { name: 'space' });
|
||||
expect(latestState?.confirmationMode).toBe('switch');
|
||||
});
|
||||
|
||||
it('ignores key-release sequences from key.sequence', async () => {
|
||||
const onSwitch = vi.fn();
|
||||
await mount({ onSwitch });
|
||||
|
||||
await triggerInput(' ', {});
|
||||
expect(latestState?.confirmationMode).toBe('switch');
|
||||
|
||||
await triggerInput('', { sequence: '\u001b[1:3u' });
|
||||
expect(latestState?.confirmationMode).toBe('switch');
|
||||
});
|
||||
|
||||
it('does not switch on key-release space sequences', async () => {
|
||||
const onSwitch = vi.fn();
|
||||
await mount({ onSwitch });
|
||||
|
||||
@@ -72,8 +72,14 @@ export function useSwitchControls(opts: {
|
||||
return;
|
||||
}
|
||||
|
||||
const isKeyRelease = /^\u001b\[[0-9;]*:3u$/.test(input);
|
||||
const isSpace = Boolean(onSwitch) && !isKeyRelease && input === ' ';
|
||||
const sequence = typeof key.sequence === 'string' ? key.sequence : input;
|
||||
const isKeyRelease = typeof sequence === 'string' && /^\u001b\[[0-9;]*:3u$/.test(sequence);
|
||||
const csiUMatch = typeof sequence === 'string'
|
||||
? sequence.match(/^\u001b\[(\d+)(?:;(\d+))?u$/)
|
||||
: null;
|
||||
const csiUCodepoint = csiUMatch ? Number(csiUMatch[1]) : null;
|
||||
const isCsiUSpace = csiUCodepoint === 32;
|
||||
const isSpace = Boolean(onSwitch) && !isKeyRelease && (input === ' ' || key.name === 'space' || isCsiUSpace);
|
||||
const hasPrintableInput = typeof input === 'string' && input.length > 0;
|
||||
|
||||
if (isSpace) {
|
||||
|
||||
Reference in New Issue
Block a user