import { useEffect, useRef, useState } from 'react'; import { isTypingTarget } from '@/lib/keyboard'; interface UseTapGestureOptions { /** Detected against KeyboardEvent.key. */ targetKey: string; /** Optional KeyboardEvent.code fallback (e.g. 'Backquote'). */ targetCode?: string; /** Taps required before `satisfied` flips true. Defaults to 1. */ taps?: number; /** Fires on each counted tap, with the new running count. */ onTap?: (count: number) => void; } /** * Counts discrete taps of a single key, ignoring auto-repeat. Used both for the * one-tap audio/video toggle and the two-tap start/stop demonstration. */ export function useTapGesture({ targetKey, targetCode, taps = 1, onTap, }: UseTapGestureOptions) { const [count, setCount] = useState(0); const onTapRef = useRef(onTap); useEffect(() => { onTapRef.current = onTap; }); useEffect(() => { const matches = (e: KeyboardEvent) => e.key === targetKey || (targetCode != null && e.code === targetCode); const handleKeyDown = (e: KeyboardEvent) => { if (e.repeat || isTypingTarget(e) || !matches(e)) return; e.preventDefault(); setCount((prev) => { if (prev >= taps) return prev; const next = prev + 1; onTapRef.current?.(next); return next; }); }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [targetKey, targetCode, taps]); return { taps: count, satisfied: count >= taps }; }