source dump of claude code
0
fork

Configure Feed

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

at main 82 lines 2.7 kB view raw
1import { useEffect, useRef } from 'react' 2import { normalizeFullWidthDigits } from '../../utils/stringUtils.js' 3 4// Delay before accepting a digit as a response, to prevent accidental 5// submissions when users start messages with numbers (e.g., numbered lists). 6// Short enough to feel instant for intentional presses, long enough to 7// cancel when the user types more characters. 8const DEFAULT_DEBOUNCE_MS = 400 9 10/** 11 * Detects when the user types a single valid digit into the prompt input, 12 * debounces to avoid accidental submissions (e.g., "1. First item"), 13 * trims the digit from the input, and fires a callback. 14 * 15 * Used by survey components that accept numeric responses typed directly 16 * into the main prompt input. 17 */ 18export function useDebouncedDigitInput<T extends string = string>({ 19 inputValue, 20 setInputValue, 21 isValidDigit, 22 onDigit, 23 enabled = true, 24 once = false, 25 debounceMs = DEFAULT_DEBOUNCE_MS, 26}: { 27 inputValue: string 28 setInputValue: (value: string) => void 29 isValidDigit: (char: string) => char is T 30 onDigit: (digit: T) => void 31 enabled?: boolean 32 once?: boolean 33 debounceMs?: number 34}): void { 35 const initialInputValue = useRef(inputValue) 36 const hasTriggeredRef = useRef(false) 37 const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null) 38 39 // Latest-ref pattern so callers can pass inline callbacks without causing 40 // the effect to re-run (which would reset the debounce timer every render). 41 const callbacksRef = useRef({ setInputValue, isValidDigit, onDigit }) 42 callbacksRef.current = { setInputValue, isValidDigit, onDigit } 43 44 useEffect(() => { 45 if (!enabled || (once && hasTriggeredRef.current)) { 46 return 47 } 48 49 if (debounceRef.current !== null) { 50 clearTimeout(debounceRef.current) 51 debounceRef.current = null 52 } 53 54 if (inputValue !== initialInputValue.current) { 55 const lastChar = normalizeFullWidthDigits(inputValue.slice(-1)) 56 if (callbacksRef.current.isValidDigit(lastChar)) { 57 const trimmed = inputValue.slice(0, -1) 58 debounceRef.current = setTimeout( 59 (debounceRef, hasTriggeredRef, callbacksRef, trimmed, lastChar) => { 60 debounceRef.current = null 61 hasTriggeredRef.current = true 62 callbacksRef.current.setInputValue(trimmed) 63 callbacksRef.current.onDigit(lastChar) 64 }, 65 debounceMs, 66 debounceRef, 67 hasTriggeredRef, 68 callbacksRef, 69 trimmed, 70 lastChar, 71 ) 72 } 73 } 74 75 return () => { 76 if (debounceRef.current !== null) { 77 clearTimeout(debounceRef.current) 78 debounceRef.current = null 79 } 80 } 81 }, [inputValue, enabled, once, debounceMs]) 82}