{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "command-button",
  "type": "registry:ui",
  "title": "CommandButton",
  "description": "A button with a global keyboard shortcut, shown as keycaps that sink as you hold each key.",
  "files": [
    {
      "path": "components/evil-buttons/command-button.tsx",
      "type": "registry:ui",
      "target": "components/evil-buttons/command-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface CommandButtonProps extends Omit<\n  React.ButtonHTMLAttributes<HTMLButtonElement>,\n  \"onDrag\" | \"onDragStart\" | \"onDragEnd\" | \"onAnimationStart\"\n> {\n  /** Shortcut like `\"mod+s\"`. `mod` is ⌘ on Apple devices and Ctrl elsewhere. */\n  shortcut?: string;\n  /** Fired when the keyboard shortcut is pressed. */\n  onCommand?: () => void;\n  /** Stop the browser's own handling of the shortcut, like the save dialog. */\n  preventDefault?: boolean;\n  /** Show the shortcut as keycaps inside the button. */\n  showShortcut?: boolean;\n}\n\nconst MODIFIERS = [\"mod\", \"ctrl\", \"alt\", \"shift\"] as const;\n/** How long a fired shortcut keeps every cap pressed. */\nconst FIRE_MS = 160;\n\nfunction normalizeKey(value: string) {\n  const key = value.toLowerCase();\n  if (key === \"cmd\" || key === \"command\" || key === \"meta\") return \"mod\";\n  if (key === \"control\") return \"ctrl\";\n  if (key === \"option\") return \"alt\";\n  if (key === \"escape\") return \"esc\";\n  if (key === \"return\") return \"enter\";\n  if (key === \" \" || key === \"spacebar\") return \"space\";\n  return key;\n}\n\nfunction parseShortcut(shortcut: string) {\n  return shortcut\n    .split(\"+\")\n    .map((part) => normalizeKey(part.trim()))\n    .filter(Boolean);\n}\n\nfunction shortcutMatches(event: KeyboardEvent, parts: string[]) {\n  const wantsMod = parts.includes(\"mod\");\n  const wantsCtrl = parts.includes(\"ctrl\");\n  const finalKey = parts.find(\n    (part) => !(MODIFIERS as readonly string[]).includes(part),\n  );\n\n  if (wantsMod && !(event.metaKey || event.ctrlKey)) return false;\n  if (!wantsMod && wantsCtrl !== event.ctrlKey) return false;\n  if (parts.includes(\"alt\") !== event.altKey) return false;\n  if (parts.includes(\"shift\") !== event.shiftKey) return false;\n  return finalKey ? normalizeKey(event.key) === finalKey : false;\n}\n\n/** Whether a single shortcut part is physically held in this key event. */\nfunction partHeld(part: string, event: KeyboardEvent, isApple: boolean) {\n  if (part === \"mod\") return isApple ? event.metaKey : event.ctrlKey;\n  if (part === \"ctrl\") return event.ctrlKey;\n  if (part === \"alt\") return event.altKey;\n  if (part === \"shift\") return event.shiftKey;\n  return false;\n}\n\nfunction formatPart(part: string, isApple: boolean) {\n  if (part === \"mod\") return isApple ? \"⌘\" : \"Ctrl\";\n  if (part === \"ctrl\") return isApple ? \"⌃\" : \"Ctrl\";\n  if (part === \"alt\") return isApple ? \"⌥\" : \"Alt\";\n  if (part === \"shift\") return isApple ? \"⇧\" : \"Shift\";\n  if (part === \"enter\") return \"↵\";\n  if (part === \"esc\") return \"Esc\";\n  if (part === \"space\") return \"Space\";\n  if (part === \"arrowup\") return \"↑\";\n  if (part === \"arrowdown\") return \"↓\";\n  if (part === \"arrowleft\") return \"←\";\n  if (part === \"arrowright\") return \"→\";\n  return part.length === 1 ? part.toUpperCase() : part;\n}\n\nfunction detectApple() {\n  if (typeof navigator === \"undefined\") return false;\n  return /mac|iphone|ipad|ipod/i.test(\n    navigator.platform || navigator.userAgent,\n  );\n}\n\nexport const CommandButton = React.forwardRef<\n  HTMLButtonElement,\n  CommandButtonProps\n>(\n  (\n    {\n      shortcut = \"mod+s\",\n      onCommand,\n      preventDefault = true,\n      showShortcut = true,\n      className,\n      children = \"Save\",\n      disabled,\n      onClick,\n      type = \"button\",\n      ...props\n    },\n    ref,\n  ) => {\n    const parts = React.useMemo(() => parseShortcut(shortcut), [shortcut]);\n    const [isApple, setIsApple] = React.useState(false);\n    const [held, setHeld] = React.useState<ReadonlySet<string>>(new Set());\n    const [firing, setFiring] = React.useState(false);\n    const fireTimerRef = React.useRef<number | undefined>(undefined);\n    const onCommandRef = React.useRef(onCommand);\n\n    React.useEffect(() => {\n      onCommandRef.current = onCommand;\n    }, [onCommand]);\n\n    // Platform is only known on the client, so detect it after hydration.\n    React.useEffect(() => setIsApple(detectApple()), []);\n\n    const fire = React.useCallback(() => {\n      setFiring(true);\n      window.clearTimeout(fireTimerRef.current);\n      fireTimerRef.current = window.setTimeout(() => setFiring(false), FIRE_MS);\n    }, []);\n\n    React.useEffect(() => {\n      if (disabled) return;\n\n      // Mirror which modifier caps are held so each one sinks as it's pressed.\n      const syncModifiers = (event: KeyboardEvent) => {\n        const next = new Set(\n          parts.filter((part) => partHeld(part, event, isApple)),\n        );\n        setHeld((current) =>\n          current.size === next.size &&\n          [...next].every((part) => current.has(part))\n            ? current\n            : next,\n        );\n      };\n\n      const handleKeyDown = (event: KeyboardEvent) => {\n        syncModifiers(event);\n        if (event.repeat || !shortcutMatches(event, parts)) return;\n        if (preventDefault) event.preventDefault();\n        fire();\n        onCommandRef.current?.();\n      };\n      const clear = () => setHeld(new Set());\n\n      window.addEventListener(\"keydown\", handleKeyDown);\n      window.addEventListener(\"keyup\", syncModifiers);\n      window.addEventListener(\"blur\", clear);\n      return () => {\n        window.removeEventListener(\"keydown\", handleKeyDown);\n        window.removeEventListener(\"keyup\", syncModifiers);\n        window.removeEventListener(\"blur\", clear);\n        clear();\n      };\n    }, [disabled, fire, isApple, parts, preventDefault]);\n\n    React.useEffect(() => () => window.clearTimeout(fireTimerRef.current), []);\n\n    const label = parts.map((part) => formatPart(part, isApple)).join(\" \");\n\n    return (\n      <button\n        ref={ref}\n        type={type}\n        disabled={disabled}\n        data-shortcut={shortcut}\n        data-firing={firing || undefined}\n        aria-keyshortcuts={parts\n          .map((part) =>\n            part === \"mod\" ? (isApple ? \"Meta\" : \"Control\") : part,\n          )\n          .join(\"+\")}\n        onClick={onClick}\n        className={cn(\n          \"group/cmd relative inline-flex h-9 cursor-pointer items-center justify-center gap-2.5 rounded-[10px] px-4 text-sm font-medium outline-none select-none\",\n          // Graded dark surface: dark outer hairline, faint inner ring, top highlight, soft drop.\n          \"bg-linear-to-b from-[#353535] to-[#272727] text-neutral-50 shadow-[0_0_0_1px_rgb(0_0_0/0.9),inset_0_0_0_1px_rgb(255_255_255/0.06),inset_0_1px_0_rgb(255_255_255/0.14),0_1px_2px_rgb(0_0_0/0.25),0_4px_12px_-4px_rgb(0_0_0/0.4)]\",\n          \"transition-[scale,filter,box-shadow] duration-300 ease-[cubic-bezier(0.34,1.35,0.64,1)] hover:brightness-110\",\n          // Press eases in fast and settles the shadow; release springs back on the slower base curve.\n          \"active:scale-[0.97] active:brightness-95 active:duration-100 active:ease-out active:shadow-[0_0_0_1px_rgb(0_0_0/0.9),inset_0_0_0_1px_rgb(255_255_255/0.05),inset_0_1px_0_rgb(255_255_255/0.08),0_0_1px_rgb(0_0_0/0.2),0_1px_3px_-2px_rgb(0_0_0/0.3)] motion-reduce:active:scale-100\",\n          // The shortcut has no :active state, so data-firing plays the same press.\n          \"data-firing:scale-[0.97] data-firing:brightness-95 data-firing:duration-100 data-firing:ease-out data-firing:shadow-[0_0_0_1px_rgb(0_0_0/0.9),inset_0_0_0_1px_rgb(255_255_255/0.05),inset_0_1px_0_rgb(255_255_255/0.08),0_0_1px_rgb(0_0_0/0.2),0_1px_3px_-2px_rgb(0_0_0/0.3)] motion-reduce:data-firing:scale-100\",\n          showShortcut && \"pr-2\",\n          \"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50\",\n          className,\n        )}\n        {...props}\n      >\n        <span>{children}</span>\n        {showShortcut ? (\n          <kbd\n            aria-label={label}\n            className=\"inline-flex items-center gap-0.5 font-sans\"\n          >\n            {parts.map((part) => (\n              <span\n                key={part}\n                aria-hidden\n                data-pressed={held.has(part) || undefined}\n                className={cn(\n                  \"inline-flex h-5 min-w-5 items-center justify-center rounded-[4px] bg-white/8 px-1 text-[11px] leading-none font-medium text-neutral-50/55 inset-ring inset-ring-white/6 transition-[translate,background-color,color] duration-300 ease-[cubic-bezier(0.34,1.35,0.64,1)]\",\n                  // Same states and timing as the button's press, so caps and button move on the same frame.\n                  \"data-pressed:translate-y-px data-pressed:bg-white/20 data-pressed:text-neutral-50 data-pressed:duration-100 data-pressed:ease-out\",\n                  \"group-active/cmd:translate-y-px group-active/cmd:bg-white/20 group-active/cmd:text-neutral-50 group-active/cmd:duration-100 group-active/cmd:ease-out\",\n                  \"group-data-firing/cmd:translate-y-px group-data-firing/cmd:bg-white/20 group-data-firing/cmd:text-neutral-50 group-data-firing/cmd:duration-100 group-data-firing/cmd:ease-out\",\n                )}\n              >\n                {formatPart(part, isApple)}\n              </span>\n            ))}\n          </kbd>\n        ) : null}\n      </button>\n    );\n  },\n);\n\nCommandButton.displayName = \"CommandButton\";\n"
    }
  ],
  "dependencies": [
    "clsx",
    "tailwind-merge"
  ]
}
