{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-held-keys",
  "title": "useHeldKeys",
  "description": "The keys someone is holding down right now, shared by every subscriber through a single set of window listeners.",
  "files": [
    {
      "path": "hooks/use-held-keys.ts",
      "content": "import * as React from \"react\"\n\nconst modifierNames: Record<string, string> = {\n  Meta: \"meta\",\n  OS: \"meta\",\n  Control: \"ctrl\",\n  Alt: \"alt\",\n  AltGraph: \"alt\",\n  Shift: \"shift\",\n}\n\nconst emptyHeld: ReadonlySet<string> = new Set()\n\nlet held: ReadonlySet<string> = emptyHeld\nconst subscribers = new Set<() => void>()\n\nfunction physicalKey(event: KeyboardEvent) {\n  const modifier = modifierNames[event.key]\n  if (modifier) {\n    return modifier\n  }\n  const letter = /^Key([A-Z])$/.exec(event.code)\n  if (letter) {\n    return letter[1].toLowerCase()\n  }\n  const digit = /^(?:Digit|Numpad)([0-9])$/.exec(event.code)\n  if (digit) {\n    return digit[1]\n  }\n  if (event.key === \" \") {\n    return \"space\"\n  }\n  return event.key.toLowerCase()\n}\n\nfunction publish(next: ReadonlySet<string>) {\n  held = next\n  for (const notify of subscribers) {\n    notify()\n  }\n}\n\nfunction onKeyDown(event: KeyboardEvent) {\n  if (event.repeat) {\n    return\n  }\n  const key = physicalKey(event)\n  if (!held.has(key)) {\n    publish(new Set([...held, key]))\n  }\n}\n\nfunction onKeyUp(event: KeyboardEvent) {\n  const key = physicalKey(event)\n  if (key === \"meta\") {\n    publish(\n      new Set(\n        [...held].filter(\n          (name) =>\n            name !== \"meta\" && Object.values(modifierNames).includes(name)\n        )\n      )\n    )\n    return\n  }\n  if (held.has(key)) {\n    const next = new Set(held)\n    next.delete(key)\n    publish(next)\n  }\n}\n\nfunction releaseAll() {\n  if (held.size > 0) {\n    publish(emptyHeld)\n  }\n}\n\nfunction onVisibilityChange() {\n  if (document.visibilityState === \"hidden\") {\n    releaseAll()\n  }\n}\n\nfunction subscribeHeld(notify: () => void) {\n  subscribers.add(notify)\n  if (subscribers.size === 1) {\n    window.addEventListener(\"keydown\", onKeyDown, { passive: true })\n    window.addEventListener(\"keyup\", onKeyUp, { passive: true })\n    window.addEventListener(\"blur\", releaseAll)\n    document.addEventListener(\"visibilitychange\", onVisibilityChange)\n  }\n  return () => {\n    subscribers.delete(notify)\n    if (subscribers.size === 0) {\n      window.removeEventListener(\"keydown\", onKeyDown)\n      window.removeEventListener(\"keyup\", onKeyUp)\n      window.removeEventListener(\"blur\", releaseAll)\n      document.removeEventListener(\"visibilitychange\", onVisibilityChange)\n      held = emptyHeld\n    }\n  }\n}\n\nfunction subscribeNothing() {\n  return () => {}\n}\n\nfunction getHeld() {\n  return held\n}\n\nfunction getServerHeld() {\n  return emptyHeld\n}\n\nfunction useHeldKeys(enabled: boolean) {\n  return React.useSyncExternalStore(\n    enabled ? subscribeHeld : subscribeNothing,\n    enabled ? getHeld : getServerHeld,\n    getServerHeld\n  )\n}\n\nexport { useHeldKeys }\n",
      "type": "registry:hook"
    }
  ],
  "type": "registry:hook"
}