{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shortcut-recorder",
  "type": "registry:ui",
  "title": "Shortcut Recorder",
  "description": "Records a key combo live, sinks held keys and asks before stealing one.",
  "categories": [
    "settings"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://www.stealth.pm/r/tokens.json",
    "https://www.stealth.pm/r/cn.json",
    "https://www.stealth.pm/r/icons.json",
    "https://www.stealth.pm/r/motion.json",
    "https://www.stealth.pm/r/use-controllable-state.json"
  ],
  "files": [
    {
      "path": "components/ui/shortcut-recorder.tsx",
      "content": "\"use client\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useEffect, useId, useRef, useState, useSyncExternalStore } from \"react\";\nimport { cn } from \"@/lib/cn\";\nimport { Undo, Warning, X } from \"@/lib/icons\";\nimport { ease, spring } from \"@/lib/motion\";\nimport { useControllableState } from \"@/lib/use-controllable-state\";\n\n/* ------------------------------------------------------------------ */\n/* Keys: parsing, formatting, reading events                           */\n/* ------------------------------------------------------------------ */\n\nexport type ShortcutPlatform = \"mac\" | \"other\";\n\nconst noop = () => () => {};\nconst detect = (): ShortcutPlatform => {\n  const nav = navigator as Navigator & { userAgentData?: { platform?: string } };\n  return /mac|iphone|ipad|ipod/i.test(nav.userAgentData?.platform || nav.platform || nav.userAgent) ? \"mac\" : \"other\";\n};\n/** Mac glyphs on the server, corrected on the client right after hydration without a mismatch. */\nfunction usePlatform(override?: ShortcutPlatform) {\n  const detected = useSyncExternalStore(noop, detect, () => \"mac\" as ShortcutPlatform);\n  return override ?? detected;\n}\n\n// \"mod\" is ⌘ on a Mac and Ctrl elsewhere, so one stored value works on both.\nconst MODS = [\"mod\", \"ctrl\", \"alt\", \"shift\", \"meta\"] as const;\ntype Mod = (typeof MODS)[number];\nconst isMod = (k: string): k is Mod => (MODS as readonly string[]).includes(k);\n\n/** \"shift+mod+K\" → \"mod+shift+k\": one spelling per combo, so two combos compare as strings. */\nexport function normalizeShortcut(keys: string) {\n  const parts = keys.toLowerCase().split(\"+\").filter(Boolean);\n  if (keys.endsWith(\"++\")) parts.push(\"+\");\n  const mods = MODS.filter((m) => parts.includes(m));\n  const key = parts.filter((p) => !isMod(p)).pop();\n  return [...mods, ...(key ? [key] : [])].join(\"+\");\n}\n\nconst codeKeys: Record<string, string> = {\n  Space: \"space\", Enter: \"enter\", NumpadEnter: \"enter\", Tab: \"tab\", Backspace: \"backspace\", Delete: \"delete\", Escape: \"escape\",\n  ArrowUp: \"up\", ArrowDown: \"down\", ArrowLeft: \"left\", ArrowRight: \"right\", Home: \"home\", End: \"end\", PageUp: \"pageup\", PageDown: \"pagedown\",\n  Slash: \"/\", Backslash: \"\\\\\", Comma: \",\", Period: \".\", Semicolon: \";\", Quote: \"'\", BracketLeft: \"[\", BracketRight: \"]\",\n  Minus: \"-\", Equal: \"=\", Backquote: \"`\",\n};\nconst modCodes: Record<string, Mod | \"os\"> = {\n  MetaLeft: \"os\", MetaRight: \"os\", OSLeft: \"os\", OSRight: \"os\", ControlLeft: \"ctrl\", ControlRight: \"ctrl\",\n  AltLeft: \"alt\", AltRight: \"alt\", ShiftLeft: \"shift\", ShiftRight: \"shift\",\n};\n\n/** The physical key of an event, so ⌥K on a Mac is still \"k\" and Shift+/ is still \"/\". */\nfunction eventKey(e: KeyboardEvent): string | null {\n  if (/^Key[A-Z]$/.test(e.code)) return e.code.slice(3).toLowerCase();\n  if (/^Digit\\d$/.test(e.code)) return e.code.slice(5);\n  if (/^F\\d{1,2}$/.test(e.code)) return e.code.toLowerCase();\n  if (codeKeys[e.code]) return codeKeys[e.code];\n  if (e.key.length === 1) return e.key.toLowerCase();\n  return null;\n}\n\n/** Which modifier a held modifier key means on this platform. */\nfunction modName(code: string, platform: ShortcutPlatform): Mod | null {\n  const m = modCodes[code];\n  if (!m) return null;\n  if (m === \"os\") return platform === \"mac\" ? \"mod\" : \"meta\";\n  if (m === \"ctrl\") return platform === \"mac\" ? \"ctrl\" : \"mod\";\n  return m;\n}\n\nconst macGlyph: Record<string, string> = {\n  mod: \"⌘\", ctrl: \"⌃\", alt: \"⌥\", shift: \"⇧\", enter: \"↵\", backspace: \"⌫\", delete: \"⌦\", tab: \"⇥\", escape: \"esc\",\n  up: \"↑\", down: \"↓\", left: \"←\", right: \"→\", space: \"Space\", pageup: \"PgUp\", pagedown: \"PgDn\", home: \"Home\", end: \"End\",\n};\nconst otherGlyph: Record<string, string> = {\n  mod: \"Ctrl\", meta: \"Win\", alt: \"Alt\", shift: \"Shift\", enter: \"Enter\", backspace: \"Backspace\", delete: \"Del\", tab: \"Tab\", escape: \"Esc\",\n  up: \"↑\", down: \"↓\", left: \"←\", right: \"→\", space: \"Space\", pageup: \"PgUp\", pagedown: \"PgDn\", home: \"Home\", end: \"End\",\n};\nconst spokenNames: Record<string, [string, string]> = {\n  mod: [\"Command\", \"Control\"], ctrl: [\"Control\", \"Control\"], alt: [\"Option\", \"Alt\"], shift: [\"Shift\", \"Shift\"], meta: [\"Command\", \"Windows\"],\n  enter: [\"Return\", \"Enter\"], backspace: [\"Delete\", \"Backspace\"], delete: [\"Forward delete\", \"Delete\"], tab: [\"Tab\", \"Tab\"],\n  up: [\"Up arrow\", \"Up arrow\"], down: [\"Down arrow\", \"Down arrow\"], left: [\"Left arrow\", \"Left arrow\"], right: [\"Right arrow\", \"Right arrow\"],\n  space: [\"Space\", \"Space\"], \"/\": [\"Slash\", \"Slash\"], \"\\\\\": [\"Backslash\", \"Backslash\"], \",\": [\"Comma\", \"Comma\"], \".\": [\"Period\", \"Period\"],\n};\n\n/** Keys in the order each platform prints them: ⌃⌥⇧⌘K on a Mac, Ctrl+Alt+Shift+K elsewhere. */\nfunction orderKeys(keys: string[], platform: ShortcutPlatform) {\n  const order: string[] = platform === \"mac\" ? [\"ctrl\", \"alt\", \"shift\", \"mod\", \"meta\"] : [\"mod\", \"meta\", \"alt\", \"shift\", \"ctrl\"];\n  return [...order.filter((m) => keys.includes(m)), ...keys.filter((k) => !isMod(k))];\n}\n\nexport function keyGlyph(key: string, platform: ShortcutPlatform) {\n  const g = (platform === \"mac\" ? macGlyph : otherGlyph)[key];\n  if (g) return g;\n  if (/^f\\d+$/.test(key)) return key.toUpperCase();\n  return key.length === 1 ? key.toUpperCase() : key[0].toUpperCase() + key.slice(1);\n}\n\n/** \"mod+shift+k\" → \"⌘⇧K\" on a Mac, \"Ctrl+Shift+K\" elsewhere, or spoken: \"Command Shift K\". */\nexport function formatShortcut(keys: string, platform: ShortcutPlatform, { spoken = false } = {}) {\n  const list = orderKeys(normalizeShortcut(keys).split(\"+\").filter(Boolean), platform);\n  if (spoken) return list.map((k) => spokenNames[k]?.[platform === \"mac\" ? 0 : 1] ?? keyGlyph(k, platform)).join(\" \");\n  return list.map((k) => keyGlyph(k, platform)).join(platform === \"mac\" ? \"\" : \"+\");\n}\n\n/** Combos the browser keeps for itself: pages never receive them, so they can't be assigned. */\nexport const browserReservedShortcuts = [\"mod+w\", \"mod+t\", \"mod+n\", \"mod+q\", \"mod+shift+w\", \"mod+shift+t\", \"mod+shift+n\", \"mod+tab\", \"mod+shift+tab\"];\n\n/* ------------------------------------------------------------------ */\n/* ShortcutRecorder                                                    */\n/* ------------------------------------------------------------------ */\n\nexport type TakenShortcut = { keys: string; label: string };\n\ntype Phase =\n  | { kind: \"idle\" }\n  | { kind: \"recording\"; held: string[]; hint?: string }\n  | { kind: \"conflict\"; keys: string; with: TakenShortcut };\n\nexport type ShortcutRecorderProps = Omit<React.ComponentProps<\"div\">, \"defaultValue\" | \"onChange\"> & {\n  /** What the shortcut does. Names the control for screen readers: “Search: Command K”. */\n  label: string;\n  /** \"mod+shift+k\". mod is ⌘ on a Mac and Ctrl elsewhere. null means not set. */\n  value?: string | null;\n  defaultValue?: string | null;\n  onValueChange?: (value: string | null) => void;\n  /** The shortcut it ships with. When the value differs, a reset button brings it back. */\n  resetValue?: string | null;\n  /** Shortcuts other actions already use. Recording one asks before taking it. */\n  taken?: TakenShortcut[];\n  /** Called when the person chooses to take a shortcut from another action; unassign it there. */\n  onReplace?: (conflict: TakenShortcut) => void;\n  /** Combos that can't be assigned. Defaults to the ones browsers keep for themselves. */\n  reserved?: string[];\n  /** Allow a plain key with no modifier, like C or /. F-keys are always allowed. */\n  allowSingleKey?: boolean;\n  disabled?: boolean;\n  /** Write the keys for this platform instead of the one detected. */\n  platform?: ShortcutPlatform;\n  size?: \"sm\" | \"md\";\n  placeholder?: string;\n};\n\nexport function ShortcutRecorder({\n  label,\n  value: valueProp,\n  defaultValue = null,\n  onValueChange,\n  resetValue,\n  taken = [],\n  onReplace,\n  reserved = browserReservedShortcuts,\n  allowSingleKey = false,\n  disabled = false,\n  platform: platformProp,\n  size = \"md\",\n  placeholder = \"Not set\",\n  className,\n  ...rest\n}: ShortcutRecorderProps) {\n  const platform = usePlatform(platformProp);\n  const reduce = !!useReducedMotion();\n  const uid = useId();\n  const [value, setValue] = useControllableState<string | null>({ value: valueProp, defaultValue, onChange: onValueChange });\n  const [phase, setPhase] = useState<Phase>({ kind: \"idle\" });\n  const [saved, setSaved] = useState(0);\n  const [announcement, setAnnouncement] = useState(\"\");\n  const recorderRef = useRef<HTMLButtonElement>(null);\n  const replaceRef = useRef<HTMLButtonElement>(null);\n\n  const recording = phase.kind === \"recording\";\n  const current = value ? normalizeShortcut(value) : null;\n  const canReset = resetValue !== undefined && (resetValue ? normalizeShortcut(resetValue) : null) !== current;\n  const say = (keys: string) => formatShortcut(keys, platform, { spoken: true });\n\n  // The tick after a save stays for a beat, then the field is plain again.\n  useEffect(() => {\n    if (!saved) return;\n    const t = window.setTimeout(() => setSaved(0), 1400);\n    return () => window.clearTimeout(t);\n  }, [saved]);\n\n  const commit = (keys: string | null) => {\n    setPhase({ kind: \"idle\" });\n    if (keys === current) return;\n    setValue(keys);\n    if (keys) setSaved((n) => n + 1);\n    setAnnouncement(keys ? `${label} set to ${say(keys)}` : `${label} shortcut cleared`);\n  };\n\n  // The key listener lives for the whole recording; it reads the latest props through this ref.\n  const latest = useRef({ platform, allowSingleKey, reserved, taken, current, commit, say });\n  useEffect(() => {\n    latest.current = { platform, allowSingleKey, reserved, taken, current, commit, say };\n  });\n\n  // While recording, every key goes to the recorder first: ⌘K must not open the page's palette.\n  useEffect(() => {\n    if (!recording) return;\n    const held = new Map<string, string>();\n    const show = (hint?: string) => setPhase({ kind: \"recording\", held: [...new Set(held.values())], hint });\n\n    const down = (e: KeyboardEvent) => {\n      const { platform, allowSingleKey, reserved, taken, current, commit, say } = latest.current;\n      const noMods = !e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey;\n      // Tab still moves focus (and blur ends recording); Escape cancels; Backspace alone clears.\n      if (e.key === \"Tab\" && noMods) return;\n      e.preventDefault();\n      e.stopPropagation();\n      if (e.repeat) return;\n      if (e.key === \"Escape\" && noMods) {\n        setPhase({ kind: \"idle\" });\n        setAnnouncement(\"Recording cancelled\");\n        return;\n      }\n      if ((e.key === \"Backspace\" || e.key === \"Delete\") && noMods) {\n        commit(null);\n        return;\n      }\n      const mod = modName(e.code, platform);\n      if (mod) {\n        held.set(e.code, mod);\n        show();\n        return;\n      }\n      const key = eventKey(e);\n      if (!key) return;\n      const mods: string[] = [];\n      if (platform === \"mac\") {\n        if (e.metaKey) mods.push(\"mod\");\n        if (e.ctrlKey) mods.push(\"ctrl\");\n      } else {\n        if (e.ctrlKey) mods.push(\"mod\");\n        if (e.metaKey) mods.push(\"meta\");\n      }\n      if (e.altKey) mods.push(\"alt\");\n      if (e.shiftKey) mods.push(\"shift\");\n      const keys = normalizeShortcut([...mods, key].join(\"+\"));\n      // Shift alone only types a capital, so it doesn't count as a modifier.\n      const strong = mods.some((m) => m !== \"shift\");\n      if (!strong && !allowSingleKey && !/^f\\d+$/.test(key)) {\n        const hint = platform === \"mac\" ? \"Add ⌘, ⌥ or ⌃ to the key\" : \"Add Ctrl or Alt to the key\";\n        held.clear();\n        show(hint);\n        setAnnouncement(hint);\n        return;\n      }\n      if (reserved.some((r) => normalizeShortcut(r) === keys)) {\n        const hint = `${formatShortcut(keys, platform)} belongs to the browser. Try another.`;\n        held.clear();\n        show(hint);\n        setAnnouncement(hint);\n        return;\n      }\n      const clash = taken.find((t) => normalizeShortcut(t.keys) === keys);\n      if (clash && keys !== current) {\n        setPhase({ kind: \"conflict\", keys, with: clash });\n        setAnnouncement(`${say(keys)} is already used for ${clash.label}. Replace it, or cancel.`);\n        return;\n      }\n      commit(keys);\n    };\n    const up = (e: KeyboardEvent) => {\n      if (held.delete(e.code)) show();\n      // macOS sends no keyup for keys released while ⌘ is held; ⌘ letting go releases everything.\n      if (e.key === \"Meta\" && held.size) {\n        held.clear();\n        show();\n      }\n    };\n    const blur = () => setPhase({ kind: \"idle\" });\n    window.addEventListener(\"keydown\", down, true);\n    window.addEventListener(\"keyup\", up, true);\n    window.addEventListener(\"blur\", blur);\n    return () => {\n      window.removeEventListener(\"keydown\", down, true);\n      window.removeEventListener(\"keyup\", up, true);\n      window.removeEventListener(\"blur\", blur);\n    };\n  }, [recording]);\n\n  // A conflict hands focus to Replace, so Enter takes the shortcut and Escape backs out.\n  useEffect(() => {\n    if (phase.kind === \"conflict\") replaceRef.current?.focus();\n  }, [phase.kind]);\n\n  const cancelConflict = () => {\n    setPhase({ kind: \"idle\" });\n    recorderRef.current?.focus();\n  };\n\n  const shown = phase.kind === \"recording\" ? phase.held : phase.kind === \"conflict\" ? phase.keys.split(\"+\") : current ? current.split(\"+\") : [];\n  const keys = orderKeys(shown, platform);\n  const message = phase.kind === \"recording\" ? phase.hint : undefined;\n  const sm = size === \"sm\";\n\n  return (\n    <div className={cn(\"flex w-full min-w-0 flex-col\", className)} {...rest}>\n      <div\n        data-state={phase.kind}\n        data-disabled={disabled || undefined}\n        className={cn(\n          \"group/field relative flex min-w-0 items-center rounded-lg border bg-raised shadow-[var(--shadow)]\",\n          \"transition-[border-color,box-shadow] duration-150\",\n          sm ? \"h-7 rounded-md\" : \"h-8\",\n          phase.kind === \"recording\"\n            ? \"border-fg-4 ring-3 ring-fg/10\"\n            : phase.kind === \"conflict\"\n              ? \"border-warning/60 ring-3 ring-warning/15\"\n              : \"border-line-2 has-[[data-recorder]:hover]:border-fg-4 has-[[data-recorder]:focus-visible]:border-fg-4 has-[[data-recorder]:focus-visible]:ring-3 has-[[data-recorder]:focus-visible]:ring-fg/10\",\n          disabled && \"opacity-50\",\n        )}\n      >\n        <button\n          ref={recorderRef}\n          type=\"button\"\n          data-recorder=\"\"\n          disabled={disabled}\n          aria-label={\n            recording ? `Recording a shortcut for ${label}. Press the keys, or Escape to cancel.` : `${label}: ${current ? say(current) : \"not set\"}. Press to change.`\n          }\n          aria-describedby={message ? `${uid}-msg` : undefined}\n          onClick={() => {\n            if (phase.kind === \"recording\") {\n              setPhase({ kind: \"idle\" });\n              return;\n            }\n            setPhase({ kind: \"recording\", held: [] });\n            setAnnouncement(\"Recording. Press the new shortcut.\");\n          }}\n          onBlur={(e) => {\n            // Leaving the field ends recording; moving to a conflict's buttons doesn't.\n            if (phase.kind === \"recording\" && !e.currentTarget.parentElement?.parentElement?.contains(e.relatedTarget)) setPhase({ kind: \"idle\" });\n          }}\n          className={cn(\n            \"relative flex h-full min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-[inherit] text-left outline-none select-none\",\n            sm ? \"pl-1.5 pr-1\" : \"pl-2 pr-1\",\n            \"disabled:cursor-not-allowed\",\n          )}\n        >\n          {recording && (\n            <span aria-hidden className=\"relative grid size-2 shrink-0 place-items-center\">\n              <span className=\"absolute inset-0 animate-ping-soft rounded-full bg-danger/60 motion-reduce:hidden\" />\n              <span className=\"size-1.5 rounded-full bg-danger\" />\n            </span>\n          )}\n          <span className=\"relative flex min-w-0 flex-1 items-center gap-1 overflow-hidden\">\n            <AnimatePresence initial={false} mode=\"popLayout\">\n              {keys.map((k) => (\n                <motion.span\n                  key={k}\n                  layout={reduce ? false : \"position\"}\n                  initial={reduce ? { opacity: 0 } : { opacity: 0, scale: 0.6, y: 2 }}\n                  animate={{ opacity: 1, scale: 1, y: 0 }}\n                  exit={reduce ? { opacity: 0, transition: { duration: 0.08 } } : { opacity: 0, scale: 0.6, transition: { duration: 0.1 } }}\n                  transition={reduce ? { duration: 0.12 } : spring.pop}\n                  className=\"flex\"\n                >\n                  <Cap k={k} platform={platform} pressed={recording} tone={phase.kind === \"conflict\" ? \"warning\" : \"default\"} sm={sm} />\n                </motion.span>\n              ))}\n            </AnimatePresence>\n            {keys.length === 0 && (\n              <span className={cn(\"truncate\", sm ? \"text-[12px]\" : \"text-[12.5px]\", recording ? \"text-fg-2\" : \"text-fg-4\")}>\n                {recording ? \"Press a shortcut\" : placeholder}\n              </span>\n            )}\n          </span>\n        </button>\n\n        <span className=\"flex shrink-0 items-center pr-1\">\n          <AnimatePresence initial={false} mode=\"popLayout\">\n            {saved > 0 && phase.kind === \"idle\" ? (\n              <motion.span\n                key=\"saved\"\n                initial={{ opacity: 0 }}\n                animate={{ opacity: 1 }}\n                exit={{ opacity: 0, transition: { duration: 0.15 } }}\n                className=\"grid size-6 place-items-center text-success\"\n              >\n                <svg width={14} height={14} viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden>\n                  <motion.path\n                    d=\"M3.5 8.5 6.5 11.5 12.5 4.5\"\n                    stroke=\"currentColor\"\n                    strokeWidth={1.7}\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                    initial={reduce ? false : { pathLength: 0 }}\n                    animate={{ pathLength: 1 }}\n                    transition={{ duration: 0.3, ease: ease.out, delay: 0.05 }}\n                  />\n                </svg>\n              </motion.span>\n            ) : (\n              phase.kind === \"idle\" &&\n              !disabled && (\n                <motion.span\n                  key=\"actions\"\n                  initial={{ opacity: 0 }}\n                  animate={{ opacity: 1 }}\n                  exit={{ opacity: 0, transition: { duration: 0.08 } }}\n                  className=\"flex items-center\"\n                >\n                  {canReset && (\n                    <IconButton label={resetValue ? `Reset ${label} to ${say(resetValue)}` : `Reset ${label}`} onClick={() => commit(resetValue ? normalizeShortcut(resetValue) : null)} sm={sm}>\n                      <Undo size={sm ? 12 : 14} />\n                    </IconButton>\n                  )}\n                  {current && (\n                    <IconButton label={`Clear the ${label} shortcut`} onClick={() => commit(null)} sm={sm}>\n                      <X size={sm ? 12 : 14} />\n                    </IconButton>\n                  )}\n                </motion.span>\n              )\n            )}\n          </AnimatePresence>\n        </span>\n      </div>\n\n      <AnimatePresence initial={false}>\n        {(message || phase.kind === \"conflict\") && (\n          <motion.div\n            key=\"message\"\n            initial={reduce ? { opacity: 0 } : { opacity: 0, height: 0 }}\n            animate={{ opacity: 1, height: \"auto\" }}\n            exit={reduce ? { opacity: 0 } : { opacity: 0, height: 0, transition: { duration: 0.14 } }}\n            transition={{ duration: 0.2, ease: ease.out }}\n            className=\"overflow-hidden\"\n          >\n            <div id={`${uid}-msg`} className=\"flex items-start gap-1.5 pb-1 pt-1.5 text-[12px] leading-4 text-warning\">\n              <Warning size={13} className=\"mt-px shrink-0\" />\n              {phase.kind === \"conflict\" ? (\n                <div\n                  className=\"flex min-w-0 flex-1 flex-wrap items-center gap-x-2 gap-y-1\"\n                  onKeyDown={(e) => {\n                    if (e.key === \"Escape\") {\n                      e.preventDefault();\n                      cancelConflict();\n                    }\n                  }}\n                >\n                  <span className=\"min-w-0 text-pretty\">\n                    Already used for <span className=\"font-medium\">{phase.with.label}</span>\n                  </span>\n                  <span className=\"flex items-center gap-1\">\n                    <MessageButton\n                      ref={replaceRef}\n                      onClick={() => {\n                        onReplace?.(phase.with);\n                        commit(phase.keys);\n                        recorderRef.current?.focus();\n                      }}\n                    >\n                      {onReplace ? \"Replace\" : \"Use anyway\"}\n                    </MessageButton>\n                    <MessageButton onClick={cancelConflict}>Cancel</MessageButton>\n                  </span>\n                </div>\n              ) : (\n                <span className=\"min-w-0 text-pretty\">{message}</span>\n              )}\n            </div>\n          </motion.div>\n        )}\n      </AnimatePresence>\n\n      <span role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n        {announcement}\n      </span>\n    </div>\n  );\n}\n\n/* ------------------------------------------------------------------ */\n/* Parts                                                               */\n/* ------------------------------------------------------------------ */\n\nfunction Cap({ k, platform, pressed, tone, sm }: { k: string; platform: ShortcutPlatform; pressed: boolean; tone: \"default\" | \"warning\"; sm: boolean }) {\n  const glyph = keyGlyph(k, platform);\n  const symbol = /^[⌘⇧⌥⌃↵⌫⌦⇥↑↓←→]$/.test(glyph);\n  return (\n    <kbd\n      data-pressed={pressed || undefined}\n      className={cn(\n        \"inline-grid shrink-0 place-items-center rounded-[5px] border font-sans font-medium leading-none\",\n        sm ? \"h-[18px] min-w-[18px] px-1 text-[10.5px]\" : \"h-5 min-w-5 px-1.5 text-[11px]\",\n        symbol && (sm ? \"text-[11.5px]\" : \"text-[12.5px]\"),\n        // A keycap has a lip; held keys sink onto it.\n        \"transition-[translate,box-shadow,background-color,color,border-color] duration-100\",\n        tone === \"warning\"\n          ? \"border-warning/40 bg-warning-soft text-warning shadow-[inset_0_-1px_0_var(--warning-soft)]\"\n          : \"border-line-2 bg-frame text-fg-2 shadow-[inset_0_-1px_0_var(--line-2)]\",\n        \"data-pressed:translate-y-px data-pressed:text-fg data-pressed:shadow-none\",\n      )}\n    >\n      {glyph}\n    </kbd>\n  );\n}\n\nfunction IconButton({ label, onClick, sm, children }: { label: string; onClick: () => void; sm: boolean; children: React.ReactNode }) {\n  return (\n    <button\n      type=\"button\"\n      aria-label={label}\n      title={label}\n      onClick={onClick}\n      className={cn(\n        \"relative grid shrink-0 place-items-center rounded-md text-fg-3 outline-none\",\n        \"transition-[background-color,color,scale] duration-150 hover:bg-hover hover:text-fg active:scale-[0.9] active:duration-75\",\n        \"focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-fg-3\",\n        \"before:absolute before:-inset-2 before:content-[''] pointer-fine:before:hidden\",\n        sm ? \"size-5\" : \"size-6\",\n      )}\n    >\n      {children}\n    </button>\n  );\n}\n\nfunction MessageButton({ className, ...props }: React.ComponentProps<\"button\">) {\n  return (\n    <button\n      type=\"button\"\n      className={cn(\n        \"relative h-6 rounded-md px-1.5 text-[12px] font-medium text-fg outline-none\",\n        \"transition-[background-color,scale] duration-150 hover:bg-hover active:scale-[0.96] active:duration-75\",\n        \"focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-fg-3\",\n        \"before:absolute before:-inset-y-2.5 before:inset-x-0 before:content-[''] pointer-fine:before:hidden\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "docs": "https://www.stealth.pm/ui/shortcut-recorder"
}
