Spaces:
Running
Running
Download apps/web/src/hooks/useKeyboardShortcuts.ts from Benaah/MicProtocol: direct link, hf CLI and curl.
- Browser
- Download file 3.56 kB
-
https://huggingface.co/spaces/Benaah/MicProtocol/resolve/main/apps/web/src/hooks/useKeyboardShortcuts.ts
- Command line
-
hf download hf://spaces/Benaah/MicProtocol/apps/web/src/hooks/useKeyboardShortcuts.ts
-
curl -L -o useKeyboardShortcuts.ts https://huggingface.co/spaces/Benaah/MicProtocol/resolve/main/apps/web/src/hooks/useKeyboardShortcuts.ts
3.56 kB
| "use client"; | |
| import { useEffect, useCallback, useRef, useMemo } from "react"; | |
| export interface KeyboardShortcut { | |
| key: string; | |
| description: string; | |
| action: () => void; | |
| category: string; | |
| meta?: boolean; // Cmd/Ctrl | |
| shift?: boolean; | |
| alt?: boolean; | |
| ctrl?: boolean; | |
| // When to disable this shortcut (e.g., when typing in input) | |
| disabledWhen?: () => boolean; | |
| } | |
| export interface ShortcutCategory { | |
| name: string; | |
| shortcuts: KeyboardShortcut[]; | |
| } | |
| interface UseKeyboardShortcutsOptions { | |
| shortcuts: KeyboardShortcut[]; | |
| enabled?: boolean; | |
| } | |
| export function useKeyboardShortcuts({ shortcuts, enabled = true }: UseKeyboardShortcutsOptions) { | |
| const shortcutsRef = useRef(shortcuts); | |
| useEffect(() => { | |
| shortcutsRef.current = shortcuts; | |
| }, [shortcuts]); | |
| const isInputFocused = useCallback(() => { | |
| const active = document.activeElement; | |
| if (!active) return false; | |
| const tag = active.tagName.toLowerCase(); | |
| return ( | |
| tag === "input" || | |
| tag === "textarea" || | |
| tag === "select" || | |
| (active as HTMLElement).isContentEditable || | |
| (active as HTMLElement).closest('[data-radix-popper-content-wrapper], [role="dialog"], [role="menu"]') !== null | |
| ); | |
| }, []); | |
| const handleKeyDown = useCallback( | |
| (event: KeyboardEvent) => { | |
| if (!enabled) return; | |
| // Allow shortcuts with meta/ctrl even in inputs for some cases | |
| const inInput = isInputFocused(); | |
| for (const shortcut of shortcutsRef.current) { | |
| const { key, meta, shift, alt, ctrl, disabledWhen } = shortcut; | |
| // Check if shortcut should be disabled in current context | |
| if (disabledWhen && disabledWhen()) continue; | |
| // Don't trigger shortcuts when typing in inputs (unless explicitly allowed) | |
| if (inInput && !(meta || ctrl)) continue; | |
| const keyMatches = event.key.toLowerCase() === key.toLowerCase(); | |
| const metaMatches = !!meta === (event.metaKey || event.ctrlKey); | |
| const shiftMatches = !!shift === event.shiftKey; | |
| const altMatches = !!alt === event.altKey; | |
| const ctrlMatches = !!ctrl === (event.ctrlKey || event.metaKey); | |
| if (keyMatches && metaMatches && shiftMatches && altMatches && ctrlMatches) { | |
| event.preventDefault(); | |
| event.stopPropagation(); | |
| shortcut.action(); | |
| break; | |
| } | |
| } | |
| }, | |
| [enabled, isInputFocused] | |
| ); | |
| useEffect(() => { | |
| if (!enabled) return; | |
| document.addEventListener("keydown", handleKeyDown); | |
| return () => document.removeEventListener("keydown", handleKeyDown); | |
| }, [enabled, handleKeyDown]); | |
| // Return categorized shortcuts for help modal | |
| const categorized = useMemo(() => { | |
| const categories: Record<string, KeyboardShortcut[]> = {}; | |
| for (const shortcut of shortcuts) { | |
| if (!categories[shortcut.category]) { | |
| categories[shortcut.category] = []; | |
| } | |
| categories[shortcut.category].push(shortcut); | |
| } | |
| return Object.entries(categories).map(([name, shortcuts]) => ({ name, shortcuts })); | |
| }, [shortcuts]); | |
| return { categorized }; | |
| } | |
| export function formatShortcut(shortcut: KeyboardShortcut): string { | |
| const parts: string[] = []; | |
| if (shortcut.meta) parts.push(navigator.platform.includes("Mac") ? "⌘" : "Ctrl"); | |
| if (shortcut.ctrl && !shortcut.meta) parts.push("Ctrl"); | |
| if (shortcut.shift) parts.push("Shift"); | |
| if (shortcut.alt) parts.push(navigator.platform.includes("Mac") ? "⌥" : "Alt"); | |
| parts.push(shortcut.key.toUpperCase()); | |
| return parts.join(" + "); | |
| } |