pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
at main 90 lines 2.7 kB view raw
1import { useCallback, useEffect, useRef } from "react"; 2 3import { useOverlayStack } from "@/stores/interface/overlayStack"; 4 5/** 6 * Global keyboard event handler that works across the entire application. 7 * Handles Escape key to close modals and other global shortcuts. 8 */ 9export function useGlobalKeyboardEvents() { 10 const { getTopModal, hideModal, showModal } = useOverlayStack(); 11 const holdTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(); 12 const isKeyHeldRef = useRef<boolean>(false); 13 14 const showKeyboardCommands = useCallback(() => { 15 showModal("keyboard-commands"); 16 }, [showModal]); 17 18 const hideKeyboardCommands = useCallback(() => { 19 hideModal("keyboard-commands"); 20 }, [hideModal]); 21 22 useEffect(() => { 23 const handleKeyDown = (event: KeyboardEvent) => { 24 // Don't handle keyboard events if user is typing in an input 25 if ( 26 event.target && 27 (event.target as HTMLInputElement).nodeName === "INPUT" 28 ) { 29 return; 30 } 31 32 // Cancel if command or alt is pressed 33 if (event.metaKey || event.altKey) return; 34 35 // Handle backtick (`) key hold for keyboard commands 36 if (event.key === "`") { 37 // Prevent default browser behavior (console opening in some browsers) 38 event.preventDefault(); 39 40 if (!isKeyHeldRef.current) { 41 isKeyHeldRef.current = true; 42 43 // Show modal after 500ms hold 44 holdTimeoutRef.current = setTimeout(() => { 45 showKeyboardCommands(); 46 }, 150); 47 } 48 } 49 50 // Handle Escape key to close modals 51 if (event.key === "Escape") { 52 const topModal = getTopModal(); 53 if (topModal) { 54 hideModal(topModal); 55 } 56 } 57 }; 58 59 const handleKeyUp = (event: KeyboardEvent) => { 60 if (event.key === "`") { 61 // Clear the hold timeout if key is released before modal shows 62 if (holdTimeoutRef.current) { 63 clearTimeout(holdTimeoutRef.current); 64 holdTimeoutRef.current = undefined; 65 } 66 67 // Hide modal if it was shown 68 if (isKeyHeldRef.current) { 69 hideKeyboardCommands(); 70 } 71 72 isKeyHeldRef.current = false; 73 } 74 }; 75 76 // Add event listeners to document for global coverage 77 document.addEventListener("keydown", handleKeyDown); 78 document.addEventListener("keyup", handleKeyUp); 79 80 return () => { 81 document.removeEventListener("keydown", handleKeyDown); 82 document.removeEventListener("keyup", handleKeyUp); 83 84 // Clean up any pending timeouts 85 if (holdTimeoutRef.current) { 86 clearTimeout(holdTimeoutRef.current); 87 } 88 }; 89 }, [getTopModal, hideModal, showKeyboardCommands, hideKeyboardCommands]); 90}