{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-keypress",
  "title": "UseKeypress",
  "description": "Tracks keyboard combinations and key presses.",
  "files": [
    {
      "path": "registry/hooks/use-keypress.tsx",
      "content": "'use client';\n\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\n\ninterface UseKeyboardOptions {\n  combo: string | string[];\n  callback: (event: KeyboardEvent) => void;\n  preventDefault?: boolean;\n  target?: HTMLElement | null;\n}\n\nfunction normalizeCombo(combo: string): string {\n  const parts = combo\n    .toLowerCase()\n    .split('+')\n    .map((p) => p.trim());\n\n  const modifiers = ['ctrl', 'alt', 'shift', 'meta'];\n  const keys: string[] = [];\n  const mods: string[] = [];\n\n  for (const part of parts) {\n    if (modifiers.includes(part)) {\n      mods.push(part);\n    } else {\n      keys.push(part);\n    }\n  }\n\n  // Sort modifiers for consistent comparison\n  mods.sort((a, b) => modifiers.indexOf(a) - modifiers.indexOf(b));\n  return [...mods, ...keys].join('+');\n}\n\nfunction eventMatchesCombo(\n  event: KeyboardEvent,\n  normalizedCombo: string,\n): boolean {\n  const modifiers = ['ctrl', 'alt', 'shift', 'meta'];\n  const eventMods = [\n    event.ctrlKey ? 'ctrl' : null,\n    event.altKey ? 'alt' : null,\n    event.shiftKey ? 'shift' : null,\n    event.metaKey ? 'meta' : null,\n  ].filter(Boolean) as string[];\n\n  const key = event.key.toLowerCase();\n  const comboParts = normalizedCombo.split('+');\n  const comboMods = comboParts.filter((p) => modifiers.includes(p));\n  const comboKey = comboParts.find((p) => !modifiers.includes(p));\n\n  // All modifiers in combo must be pressed\n  if (comboMods.length !== eventMods.length) return false;\n\n  for (const mod of comboMods) {\n    if (!eventMods.includes(mod)) return false;\n  }\n\n  // Key must match\n  return key === comboKey;\n}\n\nexport function useKeypress({\n  combo,\n  callback,\n  preventDefault = false,\n  target,\n}: UseKeyboardOptions) {\n  const callbackRef = useRef(callback);\n\n  useEffect(() => {\n    callbackRef.current = callback;\n  }, [callback]);\n\n  // Memoize normalized combos for performance\n  const normalizedCombos = useMemo(() => {\n    const combos = Array.isArray(combo) ? combo : [combo];\n    return combos.map(normalizeCombo);\n  }, [combo]);\n\n  // Memoize handler to avoid unnecessary re-attachments\n  const handler = useCallback(\n    (event: Event) => {\n      const keyboardEvent = event as KeyboardEvent;\n      for (const normCombo of normalizedCombos) {\n        if (eventMatchesCombo(keyboardEvent, normCombo)) {\n          if (preventDefault) keyboardEvent.preventDefault();\n          callbackRef.current(keyboardEvent);\n          break;\n        }\n      }\n    },\n    [normalizedCombos, preventDefault],\n  );\n\n  useEffect(() => {\n    const el = target ?? window;\n    el.addEventListener('keydown', handler);\n    return () => {\n      el.removeEventListener('keydown', handler);\n    };\n  }, [handler, target]);\n}\n",
      "type": "registry:hook",
      "target": "hooks/guarahooks/use-keypress.tsx"
    }
  ],
  "categories": [
    "ui-and-dom"
  ],
  "type": "registry:hook"
}