{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hotkey",
  "title": "Hotkey",
  "description": "Parse, label, announce and match keyboard shortcuts, with ⌘ on Apple platforms and Ctrl everywhere else.",
  "files": [
    {
      "path": "lib/hotkey.ts",
      "content": "import * as React from \"react\"\n\nfunction subscribePlatform() {\n  return () => {}\n}\n\nfunction isApplePlatform() {\n  if (typeof navigator === \"undefined\") {\n    return true\n  }\n  return /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent)\n}\n\nfunction useIsApple() {\n  return React.useSyncExternalStore(\n    subscribePlatform,\n    isApplePlatform,\n    () => true\n  )\n}\n\nconst modifierOrder = [\"ctrl\", \"alt\", \"shift\", \"mod\", \"meta\"]\n\nconst appleKeys: Record<string, string> = {\n  mod: \"⌘\",\n  meta: \"⌘\",\n  ctrl: \"⌃\",\n  alt: \"⌥\",\n  shift: \"⇧\",\n  enter: \"↵\",\n  backspace: \"⌫\",\n  escape: \"esc\",\n  arrowup: \"↑\",\n  arrowdown: \"↓\",\n  arrowleft: \"←\",\n  arrowright: \"→\",\n}\n\nconst otherKeys: Record<string, string> = {\n  mod: \"Ctrl\",\n  meta: \"Win\",\n  ctrl: \"Ctrl\",\n  alt: \"Alt\",\n  shift: \"Shift\",\n  enter: \"Enter\",\n  backspace: \"Backspace\",\n  escape: \"Esc\",\n  arrowup: \"↑\",\n  arrowdown: \"↓\",\n  arrowleft: \"←\",\n  arrowright: \"→\",\n}\n\nconst sharedKeys: Record<string, string> = {\n  space: \"Space\",\n  tab: \"Tab\",\n  delete: \"Del\",\n  home: \"Home\",\n  end: \"End\",\n  pageup: \"PgUp\",\n  pagedown: \"PgDn\",\n}\n\nconst spokenKeys: Record<string, string> = {\n  mod: \"Command\",\n  meta: \"Command\",\n  ctrl: \"Control\",\n  alt: \"Option\",\n  shift: \"Shift\",\n  enter: \"Enter\",\n  backspace: \"Delete\",\n  escape: \"Escape\",\n  arrowup: \"Up arrow\",\n  arrowdown: \"Down arrow\",\n  arrowleft: \"Left arrow\",\n  arrowright: \"Right arrow\",\n}\n\nconst otherSpokenKeys: Record<string, string> = {\n  mod: \"Control\",\n  meta: \"Windows\",\n  alt: \"Alt\",\n  backspace: \"Backspace\",\n}\n\nfunction normalizeKey(token: string) {\n  const key = token.trim().toLowerCase()\n  const aliases: Record<string, string> = {\n    cmd: \"meta\",\n    command: \"meta\",\n    control: \"ctrl\",\n    option: \"alt\",\n    opt: \"alt\",\n    return: \"enter\",\n    esc: \"escape\",\n    up: \"arrowup\",\n    down: \"arrowdown\",\n    left: \"arrowleft\",\n    right: \"arrowright\",\n    \" \": \"space\",\n  }\n  return aliases[key] ?? key\n}\n\nfunction sortModifiers(parts: string[]) {\n  const key = parts.at(-1) ?? \"\"\n  const modifiers = parts\n    .slice(0, -1)\n    .sort((a, b) => modifierOrder.indexOf(a) - modifierOrder.indexOf(b))\n  return [...modifiers, key]\n}\n\nfunction parseHotkey(hotkey: string) {\n  return hotkey\n    .trim()\n    .split(/\\s+/)\n    .filter(Boolean)\n    .map((chord) =>\n      sortModifiers(\n        chord\n          .split(/\\+(?!$)/)\n          .map(normalizeKey)\n          .filter(Boolean)\n      )\n    )\n}\n\nfunction keyLabel(key: string, apple: boolean) {\n  const names = apple ? appleKeys : otherKeys\n  return (\n    names[key] ??\n    sharedKeys[key] ??\n    (key.length === 1 ? key.toUpperCase() : key[0].toUpperCase() + key.slice(1))\n  )\n}\n\nfunction spokenKey(key: string, apple: boolean) {\n  return (\n    (!apple ? otherSpokenKeys[key] : undefined) ??\n    spokenKeys[key] ??\n    sharedKeys[key] ??\n    keyLabel(key, apple)\n  )\n}\n\nfunction parseChord(hotkey: string) {\n  return parseHotkey(hotkey.replace(/\\s+/g, \"\"))[0] ?? []\n}\n\nfunction formatHotkey(hotkey: string, apple: boolean) {\n  const labels = parseChord(hotkey).map((part) => keyLabel(part, apple))\n  return apple ? labels.join(\"\") : labels.join(\"+\")\n}\n\nfunction eventKeys(event: KeyboardEvent) {\n  const keys = new Set([normalizeKey(event.key === \" \" ? \"space\" : event.key)])\n  const physical = /^(?:Key([A-Z])|Digit([0-9]))$/.exec(event.code ?? \"\")\n  if (physical) {\n    keys.add((physical[1] ?? physical[2]).toLowerCase())\n  }\n  return keys\n}\n\nfunction matchesHotkey(event: KeyboardEvent, hotkey: string) {\n  const parts = parseChord(hotkey)\n  const key = parts.pop()\n  const wants = new Set(parts)\n  const apple = isApplePlatform()\n  const meta = wants.has(\"meta\") || (wants.has(\"mod\") && apple)\n  const ctrl = wants.has(\"ctrl\") || (wants.has(\"mod\") && !apple)\n\n  return (\n    key !== undefined &&\n    eventKeys(event).has(key) &&\n    event.metaKey === meta &&\n    event.ctrlKey === ctrl &&\n    event.altKey === wants.has(\"alt\") &&\n    event.shiftKey === wants.has(\"shift\")\n  )\n}\n\nexport {\n  formatHotkey,\n  isApplePlatform,\n  keyLabel,\n  matchesHotkey,\n  parseHotkey,\n  spokenKey,\n  useIsApple,\n}\n",
      "type": "registry:lib"
    }
  ],
  "type": "registry:lib"
}