source dump of claude code
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 92 lines 3.1 kB view raw
1import { useEffect, useLayoutEffect } from 'react' 2import { useEventCallback } from 'usehooks-ts' 3import type { InputEvent, Key } from '../events/input-event.js' 4import useStdin from './use-stdin.js' 5 6type Handler = (input: string, key: Key, event: InputEvent) => void 7 8type Options = { 9 /** 10 * Enable or disable capturing of user input. 11 * Useful when there are multiple useInput hooks used at once to avoid handling the same input several times. 12 * 13 * @default true 14 */ 15 isActive?: boolean 16} 17 18/** 19 * This hook is used for handling user input. 20 * It's a more convenient alternative to using `StdinContext` and listening to `data` events. 21 * The callback you pass to `useInput` is called for each character when user enters any input. 22 * However, if user pastes text and it's more than one character, the callback will be called only once and the whole string will be passed as `input`. 23 * 24 * ``` 25 * import {useInput} from 'ink'; 26 * 27 * const UserInput = () => { 28 * useInput((input, key) => { 29 * if (input === 'q') { 30 * // Exit program 31 * } 32 * 33 * if (key.leftArrow) { 34 * // Left arrow key pressed 35 * } 36 * }); 37 * 38 * return … 39 * }; 40 * ``` 41 */ 42const useInput = (inputHandler: Handler, options: Options = {}) => { 43 const { setRawMode, internal_exitOnCtrlC, internal_eventEmitter } = useStdin() 44 45 // useLayoutEffect (not useEffect) so that raw mode is enabled synchronously 46 // during React's commit phase, before render() returns. With useEffect, raw 47 // mode setup is deferred to the next event loop tick via React's scheduler, 48 // leaving the terminal in cooked mode — keystrokes echo and the cursor is 49 // visible until the effect fires. 50 useLayoutEffect(() => { 51 if (options.isActive === false) { 52 return 53 } 54 55 setRawMode(true) 56 57 return () => { 58 setRawMode(false) 59 } 60 }, [options.isActive, setRawMode]) 61 62 // Register the listener once on mount so its slot in the EventEmitter's 63 // listener array is stable. If isActive were in the effect's deps, the 64 // listener would re-append on false→true, moving it behind listeners 65 // that registered while it was inactive — breaking 66 // stopImmediatePropagation() ordering. useEventCallback keeps the 67 // reference stable while reading latest isActive/inputHandler from 68 // closure (it syncs via useLayoutEffect, so it's compiler-safe). 69 const handleData = useEventCallback((event: InputEvent) => { 70 if (options.isActive === false) { 71 return 72 } 73 const { input, key } = event 74 75 // If app is not supposed to exit on Ctrl+C, then let input listener handle it 76 // Note: discreteUpdates is called at the App level when emitting events, 77 // so all listeners are already within a high-priority update context. 78 if (!(input === 'c' && key.ctrl) || !internal_exitOnCtrlC) { 79 inputHandler(input, key, event) 80 } 81 }) 82 83 useEffect(() => { 84 internal_eventEmitter?.on('input', handleData) 85 86 return () => { 87 internal_eventEmitter?.removeListener('input', handleData) 88 } 89 }, [internal_eventEmitter, handleData]) 90} 91 92export default useInput