({
children
}: Props)
| 117 | return w.severity === "error"; |
| 118 | } |
| 119 | export function KeybindingSetup({ |
| 120 | children |
| 121 | }: Props): React.ReactNode { |
| 122 | // Load bindings synchronously for initial render |
| 123 | const [{ |
| 124 | bindings, |
| 125 | warnings |
| 126 | }, setLoadResult] = useState<KeybindingsLoadResult>(() => { |
| 127 | const result = loadKeybindingsSyncWithWarnings(); |
| 128 | logForDebugging(`[keybindings] KeybindingSetup initialized with ${result.bindings.length} bindings, ${result.warnings.length} warnings`); |
| 129 | return result; |
| 130 | }); |
| 131 | |
| 132 | // Track if this is a reload (not initial load) |
| 133 | const [isReload, setIsReload] = useState(false); |
| 134 | |
| 135 | // Display warnings via notifications |
| 136 | useKeybindingWarnings(warnings, isReload); |
| 137 | |
| 138 | // Chord state management - use ref for immediate access, state for re-renders |
| 139 | // The ref is used by resolve() to get the current value without waiting for re-render |
| 140 | // The state is used to trigger re-renders when needed (e.g., for UI updates) |
| 141 | const pendingChordRef = useRef<ParsedKeystroke[] | null>(null); |
| 142 | const [pendingChord, setPendingChordState] = useState<ParsedKeystroke[] | null>(null); |
| 143 | const chordTimeoutRef = useRef<NodeJS.Timeout | null>(null); |
| 144 | |
| 145 | // Handler registry for action callbacks (used by ChordInterceptor to invoke handlers) |
| 146 | const handlerRegistryRef = useRef(new Map<string, Set<{ |
| 147 | action: string; |
| 148 | context: KeybindingContextName; |
| 149 | handler: () => void; |
| 150 | }>>()); |
| 151 | |
| 152 | // Active context tracking for keybinding priority resolution |
| 153 | // Using a ref instead of state for synchronous updates - input handlers need |
| 154 | // to see the current value immediately, not after a React render cycle. |
| 155 | const activeContextsRef = useRef<Set<KeybindingContextName>>(new Set()); |
| 156 | const registerActiveContext = useCallback((context: KeybindingContextName) => { |
| 157 | activeContextsRef.current.add(context); |
| 158 | }, []); |
| 159 | const unregisterActiveContext = useCallback((context_0: KeybindingContextName) => { |
| 160 | activeContextsRef.current.delete(context_0); |
| 161 | }, []); |
| 162 | |
| 163 | // Clear chord timeout when component unmounts or chord changes |
| 164 | const clearChordTimeout = useCallback(() => { |
| 165 | if (chordTimeoutRef.current) { |
| 166 | clearTimeout(chordTimeoutRef.current); |
| 167 | chordTimeoutRef.current = null; |
| 168 | } |
| 169 | }, []); |
| 170 | |
| 171 | // Wrapper for setPendingChord that manages timeout and syncs ref+state |
| 172 | const setPendingChord = useCallback((pending: ParsedKeystroke[] | null) => { |
| 173 | clearChordTimeout(); |
| 174 | if (pending !== null) { |
| 175 | // Set timeout to cancel chord if not completed |
| 176 | chordTimeoutRef.current = setTimeout((pendingChordRef_0, setPendingChordState_0) => { |
nothing calls this directly
no test coverage detected