Download src/hooks/useExitOnCtrlCD.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 3.23 kB
-
https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/hooks/useExitOnCtrlCD.ts
- Command line
-
hf download hf://chenbhao/codev@afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/hooks/useExitOnCtrlCD.ts
-
curl -L -o useExitOnCtrlCD.ts https://huggingface.co/chenbhao/codev/resolve/afbc7edaae4e6bb0102ba024b49ce8be89d2264c/src/hooks/useExitOnCtrlCD.ts
3.23 kB
| import { useCallback, useMemo, useState } from 'react' | |
| import useApp from '../ink/hooks/use-app.js' | |
| import type { KeybindingContextName } from '../keybindings/types.js' | |
| import { useDoublePress } from './useDoublePress.js' | |
| export type ExitState = { | |
| pending: boolean | |
| keyName: 'Ctrl-C' | 'Ctrl-D' | null | |
| } | |
| type KeybindingOptions = { | |
| context?: KeybindingContextName | |
| isActive?: boolean | |
| } | |
| type UseKeybindingsHook = ( | |
| handlers: Record<string, () => void>, | |
| options?: KeybindingOptions, | |
| ) => void | |
| /** | |
| * Handle ctrl+c and ctrl+d for exiting the application. | |
| * | |
| * Uses a time-based double-press mechanism: | |
| * - First press: Shows "Press X again to exit" message | |
| * - Second press within timeout: Exits the application | |
| * | |
| * Note: We use time-based double-press rather than the chord system because | |
| * we want the first ctrl+c to also trigger interrupt (handled elsewhere). | |
| * The chord system would prevent the first press from firing any action. | |
| * | |
| * These keys are hardcoded and cannot be rebound via keybindings.json. | |
| * | |
| * @param useKeybindingsHook - The useKeybindings hook to use for registering handlers | |
| * (dependency injection to avoid import cycles) | |
| * @param onInterrupt - Optional callback for features to handle interrupt (ctrl+c). | |
| * Return true if handled, false to fall through to double-press exit. | |
| * @param onExit - Optional custom exit handler | |
| * @param isActive - Whether the keybinding is active (default true). Set false | |
| * while an embedded TextInput is focused — TextInput's own | |
| * ctrl+c/d handlers will manage cancel/exit, and Dialog's | |
| * handler would otherwise double-fire (child useInput runs | |
| * before parent useKeybindings, so both see every keypress). | |
| */ | |
| export function useExitOnCtrlCD( | |
| useKeybindingsHook: UseKeybindingsHook, | |
| onInterrupt?: () => boolean, | |
| onExit?: () => void, | |
| isActive = true, | |
| ): ExitState { | |
| const { exit } = useApp() | |
| const [exitState, setExitState] = useState<ExitState>({ | |
| pending: false, | |
| keyName: null, | |
| }) | |
| const exitFn = useMemo(() => onExit ?? exit, [onExit, exit]) | |
| // Double-press handler for ctrl+c | |
| const handleCtrlCDoublePress = useDoublePress( | |
| pending => setExitState({ pending, keyName: 'Ctrl-C' }), | |
| exitFn, | |
| ) | |
| // Double-press handler for ctrl+d | |
| const handleCtrlDDoublePress = useDoublePress( | |
| pending => setExitState({ pending, keyName: 'Ctrl-D' }), | |
| exitFn, | |
| ) | |
| // Handler for app:interrupt (ctrl+c by default) | |
| // Let features handle interrupt first via callback | |
| const handleInterrupt = useCallback(() => { | |
| if (onInterrupt?.()) return // Feature handled it | |
| handleCtrlCDoublePress() | |
| }, [handleCtrlCDoublePress, onInterrupt]) | |
| // Handler for app:exit (ctrl+d by default) | |
| // This also uses double-press to confirm exit | |
| const handleExit = useCallback(() => { | |
| handleCtrlDDoublePress() | |
| }, [handleCtrlDDoublePress]) | |
| const handlers = useMemo( | |
| () => ({ | |
| 'app:interrupt': handleInterrupt, | |
| 'app:exit': handleExit, | |
| }), | |
| [handleInterrupt, handleExit], | |
| ) | |
| useKeybindingsHook(handlers, { context: 'Global', isActive }) | |
| return exitState | |
| } | |