{
  "name": "keyboard",
  "type": "registry:ui",
  "dependencies": [
    "@tabler/icons-react",
    "motion/react"
  ],
  "files": [
    {
      "path": "components/ui/keyboard.tsx",
      "content": "\"use client\";\nimport React, {\n  createContext,\n  useContext,\n  useEffect,\n  useRef,\n  useState,\n  useCallback,\n} from \"react\";\nimport { motion, AnimatePresence } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  IconBrightnessDown,\n  IconBrightnessUp,\n  IconCaretRightFilled,\n  IconCaretUpFilled,\n  IconChevronUp,\n  IconMicrophone,\n  IconMoon,\n  IconPlayerSkipForward,\n  IconPlayerTrackNext,\n  IconPlayerTrackPrev,\n  IconTable,\n  IconVolume,\n  IconVolume2,\n  IconVolume3,\n  IconSearch,\n  IconWorld,\n  IconCommand,\n  IconCaretLeftFilled,\n  IconCaretDownFilled,\n} from \"@tabler/icons-react\";\n\n// Sound sprite definitions from config.json [startMs, durationMs]\n// Key down sounds - half duration for a snappy press sound\nconst SOUND_DEFINES_DOWN: Record<string, [number, number]> = {\n  Escape: [2894, 113],\n  F1: [3610, 98],\n  F2: [4210, 90],\n  F3: [4758, 90],\n  F4: [5250, 100],\n  F5: [5831, 105],\n  F6: [6396, 105],\n  F7: [6900, 105],\n  F8: [7443, 111],\n  F9: [7955, 91],\n  F10: [8504, 105],\n  F11: [9046, 94],\n  F12: [9582, 96],\n  Backquote: [12476, 100],\n  Digit1: [12946, 96],\n  Digit2: [13470, 95],\n  Digit3: [13963, 100],\n  Digit4: [14481, 102],\n  Digit5: [14994, 94],\n  Digit6: [15505, 109],\n  Digit7: [15990, 97],\n  Digit8: [16529, 92],\n  Digit9: [17012, 103],\n  Digit0: [17550, 87],\n  Minus: [18052, 93],\n  Equal: [18553, 89],\n  Backspace: [19065, 110],\n  Tab: [21734, 119],\n  KeyQ: [22245, 95],\n  KeyW: [22790, 89],\n  KeyE: [23317, 83],\n  KeyR: [23817, 92],\n  KeyT: [24297, 92],\n  KeyY: [24811, 93],\n  KeyU: [25313, 95],\n  KeyI: [25795, 91],\n  KeyO: [26309, 84],\n  KeyP: [26804, 83],\n  BracketLeft: [27330, 85],\n  BracketRight: [27883, 99],\n  Backslash: [28393, 100],\n  CapsLock: [31011, 126],\n  KeyA: [31542, 85],\n  KeyS: [32031, 88],\n  KeyD: [32492, 85],\n  KeyF: [32973, 87],\n  KeyG: [33453, 94],\n  KeyH: [33986, 93],\n  KeyJ: [34425, 88],\n  KeyK: [34932, 90],\n  KeyL: [35410, 95],\n  Semicolon: [35914, 95],\n  Quote: [36428, 87],\n  Enter: [36902, 117],\n  ShiftLeft: [38136, 133],\n  KeyZ: [38694, 80],\n  KeyX: [39148, 76],\n  KeyC: [39632, 95],\n  KeyV: [40136, 94],\n  KeyB: [40621, 107],\n  KeyN: [41103, 90],\n  KeyM: [41610, 93],\n  Comma: [42110, 92],\n  Period: [42594, 90],\n  Slash: [43105, 95],\n  ShiftRight: [43565, 137],\n  Fn: [44251, 110],\n  ControlLeft: [45327, 83],\n  AltLeft: [45750, 82],\n  MetaLeft: [46199, 100],\n  Space: [51541, 144],\n  MetaRight: [47929, 75],\n  AltRight: [49329, 82],\n  ArrowUp: [44251, 110],\n  ArrowLeft: [49837, 88],\n  ArrowDown: [50333, 90],\n  ArrowRight: [50783, 111],\n};\n\n// Key up sounds - shorter duration, offset for the release \"thock\"\n// Uses the tail end of each sound sample for a lighter release effect\nconst SOUND_DEFINES_UP: Record<string, [number, number]> = {\n  Escape: [2894 + 120, 100],\n  F1: [3610 + 100, 90],\n  F2: [4210 + 95, 80],\n  F3: [4758 + 95, 80],\n  F4: [5250 + 105, 90],\n  F5: [5831 + 110, 95],\n  F6: [6396 + 110, 95],\n  F7: [6900 + 110, 95],\n  F8: [7443 + 115, 100],\n  F9: [7955 + 95, 80],\n  F10: [8504 + 110, 95],\n  F11: [9046 + 100, 85],\n  F12: [9582 + 100, 85],\n  Backquote: [12476 + 105, 90],\n  Digit1: [12946 + 100, 85],\n  Digit2: [13470 + 100, 85],\n  Digit3: [13963 + 105, 90],\n  Digit4: [14481 + 110, 90],\n  Digit5: [14994 + 100, 85],\n  Digit6: [15505 + 115, 100],\n  Digit7: [15990 + 100, 90],\n  Digit8: [16529 + 95, 85],\n  Digit9: [17012 + 110, 90],\n  Digit0: [17550 + 90, 80],\n  Minus: [18052 + 100, 85],\n  Equal: [18553 + 90, 85],\n  Backspace: [19065 + 115, 100],\n  Tab: [21734 + 125, 110],\n  KeyQ: [22245 + 100, 85],\n  KeyW: [22790 + 90, 85],\n  KeyE: [23317 + 85, 80],\n  KeyR: [23817 + 95, 85],\n  KeyT: [24297 + 95, 85],\n  KeyY: [24811 + 100, 85],\n  KeyU: [25313 + 100, 85],\n  KeyI: [25795 + 95, 85],\n  KeyO: [26309 + 85, 80],\n  KeyP: [26804 + 85, 80],\n  BracketLeft: [27330 + 85, 80],\n  BracketRight: [27883 + 105, 90],\n  Backslash: [28393 + 105, 90],\n  CapsLock: [31011 + 135, 110],\n  KeyA: [31542 + 90, 80],\n  KeyS: [32031 + 90, 80],\n  KeyD: [32492 + 85, 80],\n  KeyF: [32973 + 90, 80],\n  KeyG: [33453 + 100, 85],\n  KeyH: [33986 + 95, 85],\n  KeyJ: [34425 + 90, 85],\n  KeyK: [34932 + 95, 85],\n  KeyL: [35410 + 100, 85],\n  Semicolon: [35914 + 100, 85],\n  Quote: [36428 + 90, 80],\n  Enter: [36902 + 125, 105],\n  ShiftLeft: [38136 + 140, 120],\n  KeyZ: [38694 + 85, 75],\n  KeyX: [39148 + 80, 70],\n  KeyC: [39632 + 100, 85],\n  KeyV: [40136 + 100, 85],\n  KeyB: [40621 + 115, 95],\n  KeyN: [41103 + 95, 85],\n  KeyM: [41610 + 100, 85],\n  Comma: [42110 + 95, 85],\n  Period: [42594 + 95, 85],\n  Slash: [43105 + 100, 85],\n  ShiftRight: [43565 + 145, 125],\n  Fn: [44251 + 115, 100],\n  ControlLeft: [45327 + 85, 80],\n  AltLeft: [45750 + 85, 80],\n  MetaLeft: [46199 + 105, 90],\n  Space: [51541 + 150, 130],\n  MetaRight: [47929 + 75, 70],\n  AltRight: [49329 + 85, 80],\n  ArrowUp: [44251 + 115, 100],\n  ArrowLeft: [49837 + 90, 85],\n  ArrowDown: [50333 + 95, 80],\n  ArrowRight: [50783 + 115, 100],\n};\n\n// Map key codes to display labels\nconst KEY_DISPLAY_LABELS: Record<string, string> = {\n  Escape: \"esc\",\n  Backspace: \"delete\",\n  Tab: \"tab\",\n  Enter: \"return\",\n  ShiftLeft: \"shift\",\n  ShiftRight: \"shift\",\n  ControlLeft: \"control\",\n  ControlRight: \"control\",\n  AltLeft: \"option\",\n  AltRight: \"option\",\n  MetaLeft: \"command\",\n  MetaRight: \"command\",\n  Space: \"space\",\n  CapsLock: \"caps\",\n  ArrowUp: \"↑\",\n  ArrowDown: \"↓\",\n  ArrowLeft: \"←\",\n  ArrowRight: \"→\",\n  Backquote: \"`\",\n  Minus: \"-\",\n  Equal: \"=\",\n  BracketLeft: \"[\",\n  BracketRight: \"]\",\n  Backslash: \"\\\\\",\n  Semicolon: \";\",\n  Quote: \"'\",\n  Comma: \",\",\n  Period: \".\",\n  Slash: \"/\",\n};\n\nconst getKeyDisplayLabel = (keyCode: string): string => {\n  if (KEY_DISPLAY_LABELS[keyCode]) return KEY_DISPLAY_LABELS[keyCode];\n  if (keyCode.startsWith(\"Key\")) return keyCode.slice(3);\n  if (keyCode.startsWith(\"Digit\")) return keyCode.slice(5);\n  if (keyCode.startsWith(\"F\") && keyCode.length <= 3) return keyCode;\n  return keyCode;\n};\n\ninterface KeyboardContextType {\n  playSoundDown: (keyCode: string) => void;\n  playSoundUp: (keyCode: string) => void;\n  pressedKeys: Set<string>;\n  setPressed: (keyCode: string) => void;\n  setReleased: (keyCode: string) => void;\n  lastPressedKey: string | null;\n}\n\nconst KeyboardContext = createContext<KeyboardContextType | null>(null);\n\nconst useKeyboardSound = () => {\n  const context = useContext(KeyboardContext);\n  if (!context) {\n    throw new Error(\"useKeyboardSound must be used within KeyboardProvider\");\n  }\n  return context;\n};\n\nconst KeyboardProvider = ({\n  children,\n  enableSound = false,\n  containerRef,\n}: {\n  children: React.ReactNode;\n  enableSound?: boolean;\n  containerRef: React.RefObject<HTMLDivElement | null>;\n}) => {\n  const audioContextRef = useRef<AudioContext | null>(null);\n  const audioBufferRef = useRef<AudioBuffer | null>(null);\n  const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set());\n  const [lastPressedKey, setLastPressedKey] = useState<string | null>(null);\n  const [soundLoaded, setSoundLoaded] = useState(false);\n  const [isVisible, setIsVisible] = useState(false);\n\n  useEffect(() => {\n    setSoundLoaded(false);\n    if (!enableSound) return;\n\n    const controller = new AbortController();\n    let context: AudioContext | null = null;\n    let started = false;\n\n    const removeListeners = () => {\n      window.removeEventListener(\"click\", initAudio);\n      window.removeEventListener(\"keydown\", initAudio);\n    };\n\n    // Keep automatic demos silent until the user clicks, taps, or presses a key.\n    async function initAudio() {\n      if (started) return;\n      started = true;\n      removeListeners();\n\n      try {\n        context = new AudioContext();\n        audioContextRef.current = context;\n        // Resume during the user gesture, before the download completes.\n        void context.resume().catch(() => {});\n        const response = await fetch(\"/sounds/sound.ogg\", {\n          signal: controller.signal,\n        });\n        if (!response.ok || controller.signal.aborted) return;\n        const bytes = await response.arrayBuffer();\n        if (controller.signal.aborted) return;\n        const buffer = await context.decodeAudioData(bytes);\n        if (controller.signal.aborted) return;\n        audioBufferRef.current = buffer;\n        setSoundLoaded(true);\n      } catch {\n        // A failed download or an unmounted demo must not stop the animation.\n      }\n    }\n\n    window.addEventListener(\"click\", initAudio, { once: true });\n    window.addEventListener(\"keydown\", initAudio, { once: true });\n\n    return () => {\n      removeListeners();\n      controller.abort();\n      setSoundLoaded(false);\n      audioBufferRef.current = null;\n      audioContextRef.current = null;\n      if (context) void context.close().catch(() => {});\n    };\n  }, [enableSound]);\n\n  const playSoundDown = useCallback(\n    (keyCode: string) => {\n      if (!enableSound || !soundLoaded) return;\n      if (!audioContextRef.current || !audioBufferRef.current) return;\n\n      const soundDef = SOUND_DEFINES_DOWN[keyCode];\n      if (!soundDef) return;\n\n      const [startMs, durationMs] = soundDef;\n      const startTime = startMs / 1000;\n      const duration = durationMs / 1000;\n\n      // Resume audio context if suspended (browser autoplay policy)\n      if (audioContextRef.current.state === \"suspended\") {\n        audioContextRef.current.resume();\n      }\n\n      const source = audioContextRef.current.createBufferSource();\n      source.buffer = audioBufferRef.current;\n      source.connect(audioContextRef.current.destination);\n      source.start(0, startTime, duration);\n    },\n    [enableSound, soundLoaded],\n  );\n\n  const playSoundUp = useCallback(\n    (keyCode: string) => {\n      if (!enableSound || !soundLoaded) return;\n      if (!audioContextRef.current || !audioBufferRef.current) return;\n\n      const soundDef = SOUND_DEFINES_UP[keyCode];\n      if (!soundDef) return;\n\n      const [startMs, durationMs] = soundDef;\n      const startTime = startMs / 1000;\n      const duration = durationMs / 1000;\n\n      // Resume audio context if suspended (browser autoplay policy)\n      if (audioContextRef.current.state === \"suspended\") {\n        audioContextRef.current.resume();\n      }\n\n      const source = audioContextRef.current.createBufferSource();\n      source.buffer = audioBufferRef.current;\n      source.connect(audioContextRef.current.destination);\n      source.start(0, startTime, duration);\n    },\n    [enableSound, soundLoaded],\n  );\n\n  const setPressed = useCallback((keyCode: string) => {\n    setPressedKeys((prev) => new Set(prev).add(keyCode));\n    setLastPressedKey(keyCode);\n  }, []);\n\n  const setReleased = useCallback((keyCode: string) => {\n    setPressedKeys((prev) => {\n      const next = new Set(prev);\n      next.delete(keyCode);\n      return next;\n    });\n  }, []);\n\n  // Track visibility with IntersectionObserver\n  useEffect(() => {\n    const element = containerRef.current;\n    if (!element) return;\n\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        setIsVisible(entry.isIntersecting);\n      },\n      { threshold: 0.1 },\n    );\n\n    observer.observe(element);\n\n    return () => {\n      observer.disconnect();\n    };\n  }, [containerRef]);\n\n  // Handle physical keyboard events (only when visible)\n  useEffect(() => {\n    if (!isVisible) return;\n\n    const handleKeyDown = (e: KeyboardEvent) => {\n      // Prevent repeat events\n      if (e.repeat) return;\n\n      const keyCode = e.code;\n      playSoundDown(keyCode);\n      setPressed(keyCode);\n    };\n\n    const handleKeyUp = (e: KeyboardEvent) => {\n      const keyCode = e.code;\n      playSoundUp(keyCode);\n      setReleased(keyCode);\n    };\n\n    document.addEventListener(\"keydown\", handleKeyDown);\n    document.addEventListener(\"keyup\", handleKeyUp);\n\n    return () => {\n      document.removeEventListener(\"keydown\", handleKeyDown);\n      document.removeEventListener(\"keyup\", handleKeyUp);\n    };\n  }, [isVisible, playSoundDown, playSoundUp, setPressed, setReleased]);\n\n  return (\n    <KeyboardContext.Provider\n      value={{\n        playSoundDown,\n        playSoundUp,\n        pressedKeys,\n        setPressed,\n        setReleased,\n        lastPressedKey,\n      }}\n    >\n      {children}\n    </KeyboardContext.Provider>\n  );\n};\n\nconst KeystrokePreview = () => {\n  const { lastPressedKey, pressedKeys } = useKeyboardSound();\n  const [displayKey, setDisplayKey] = useState<string | null>(null);\n  const [animationKey, setAnimationKey] = useState(0);\n\n  useEffect(() => {\n    if (lastPressedKey) {\n      // Clear display if space or shift is pressed\n      if (\n        lastPressedKey === \"Space\" ||\n        lastPressedKey === \"ShiftLeft\" ||\n        lastPressedKey === \"ShiftRight\"\n      ) {\n        setDisplayKey(null);\n        return;\n      }\n\n      setDisplayKey(getKeyDisplayLabel(lastPressedKey));\n      setAnimationKey((prev) => prev + 1);\n    }\n  }, [lastPressedKey]);\n\n  const isPressed = pressedKeys.size > 0;\n\n  return (\n    <div className=\"relative flex h-12 w-full items-center justify-center\">\n      <AnimatePresence mode=\"popLayout\">\n        {displayKey && (\n          <motion.div\n            key={animationKey}\n            layout\n            initial={{ opacity: 0, scale: 0.5, y: 5 }}\n            animate={{\n              opacity: 1,\n              scale: isPressed ? 0.95 : 1,\n              y: 0,\n            }}\n            exit={{ opacity: 0, scale: 0.8, y: -5 }}\n            transition={{\n              type: \"spring\",\n              stiffness: 500,\n              damping: 30,\n              mass: 0.5,\n            }}\n            className=\"absolute flex items-center justify-center rounded-lg px-4 py-2 font-mono text-2xl font-black text-neutral-700\"\n          >\n            <motion.span\n              initial={{ opacity: 0, scale: 1.2, filter: \"blur(10px)\" }}\n              animate={{ opacity: 0.6, scale: 1, filter: \"blur(0px)\" }}\n              transition={{ duration: 0.05 }}\n              className=\"text-2xl\"\n            >\n              {displayKey}\n            </motion.span>\n          </motion.div>\n        )}\n      </AnimatePresence>\n    </div>\n  );\n};\n\nexport const Keyboard = ({\n  className,\n  enableSound = false,\n  showPreview = false,\n}: {\n  className?: string;\n  enableSound?: boolean;\n  showPreview?: boolean;\n}) => {\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  return (\n    <KeyboardProvider enableSound={enableSound} containerRef={containerRef}>\n      <div\n        ref={containerRef}\n        className={cn(\n          \"mx-auto w-fit [zoom:0.8] sm:[zoom:1.25] md:[zoom:1.5] lg:[zoom:1.75] xl:[zoom:2]\",\n          className,\n        )}\n      >\n        {showPreview && <KeystrokePreview />}\n        <Keypad />\n      </div>\n    </KeyboardProvider>\n  );\n};\n\nexport const Keypad = () => {\n  return (\n    <div className=\"h-full w-fit rounded-xl bg-neutral-200 p-1 shadow-sm ring-1 shadow-black/5 ring-black/5\">\n      {/* Function Row */}\n      <Row>\n        <Key\n          keyCode=\"Escape\"\n          containerClassName=\"rounded-tl-xl\"\n          className=\"w-10 rounded-tl-lg\"\n          childrenClassName=\"items-start justify-end pb-[2px] pl-[4px]\"\n        >\n          <span>esc</span>\n        </Key>\n        <Key keyCode=\"F1\">\n          <IconBrightnessDown className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F1</span>\n        </Key>\n        <Key keyCode=\"F2\">\n          <IconBrightnessUp className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F2</span>\n        </Key>\n        <Key keyCode=\"F3\">\n          <IconTable className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F3</span>\n        </Key>\n        <Key keyCode=\"F4\">\n          <IconSearch className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F4</span>\n        </Key>\n        <Key keyCode=\"F5\">\n          <IconMicrophone className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F5</span>\n        </Key>\n        <Key keyCode=\"F6\">\n          <IconMoon className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F6</span>\n        </Key>\n        <Key keyCode=\"F7\">\n          <IconPlayerTrackPrev className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F7</span>\n        </Key>\n        <Key keyCode=\"F8\">\n          <IconPlayerSkipForward className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F8</span>\n        </Key>\n        <Key keyCode=\"F9\">\n          <IconPlayerTrackNext className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F9</span>\n        </Key>\n        <Key keyCode=\"F10\">\n          <IconVolume3 className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F10</span>\n        </Key>\n        <Key keyCode=\"F11\">\n          <IconVolume2 className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F11</span>\n        </Key>\n        <Key keyCode=\"F12\">\n          <IconVolume className=\"h-[6px] w-[6px]\" />\n          <span className=\"mt-1\">F12</span>\n        </Key>\n        <Key containerClassName=\"rounded-tr-xl\" className=\"rounded-tr-lg\">\n          <div className=\"h-4 w-4 rounded-full bg-gradient-to-b from-neutral-300 via-neutral-200 to-neutral-300 p-px\">\n            <div className=\"h-full w-full rounded-full bg-neutral-100\" />\n          </div>\n        </Key>\n      </Row>\n\n      {/* Number Row */}\n      <Row>\n        <Key keyCode=\"Backquote\">\n          <span>~</span>\n          <span>`</span>\n        </Key>\n        <Key keyCode=\"Digit1\">\n          <span>!</span>\n          <span>1</span>\n        </Key>\n        <Key keyCode=\"Digit2\">\n          <span>@</span>\n          <span>2</span>\n        </Key>\n        <Key keyCode=\"Digit3\">\n          <span>#</span>\n          <span>3</span>\n        </Key>\n        <Key keyCode=\"Digit4\">\n          <span>$</span>\n          <span>4</span>\n        </Key>\n        <Key keyCode=\"Digit5\">\n          <span>%</span>\n          <span>5</span>\n        </Key>\n        <Key keyCode=\"Digit6\">\n          <span>^</span>\n          <span>6</span>\n        </Key>\n        <Key keyCode=\"Digit7\">\n          <span>&</span>\n          <span>7</span>\n        </Key>\n        <Key keyCode=\"Digit8\">\n          <span>*</span>\n          <span>8</span>\n        </Key>\n        <Key keyCode=\"Digit9\">\n          <span>(</span>\n          <span>9</span>\n        </Key>\n        <Key keyCode=\"Digit0\">\n          <span>)</span>\n          <span>0</span>\n        </Key>\n        <Key keyCode=\"Minus\">\n          <span>—</span>\n          <span>_</span>\n        </Key>\n        <Key keyCode=\"Equal\">\n          <span>+</span>\n          <span>=</span>\n        </Key>\n        <Key\n          keyCode=\"Backspace\"\n          className=\"w-10\"\n          childrenClassName=\"items-end justify-end pr-[4px] pb-[2px]\"\n        >\n          <span>delete</span>\n        </Key>\n      </Row>\n\n      {/* QWERTY Row */}\n      <Row>\n        <Key\n          keyCode=\"Tab\"\n          className=\"w-10\"\n          childrenClassName=\"items-start justify-end pb-[2px] pl-[4px]\"\n        >\n          <span>tab</span>\n        </Key>\n        {[\"Q\", \"W\", \"E\", \"R\", \"T\", \"Y\", \"U\", \"I\", \"O\", \"P\"].map((letter) => (\n          <Key key={letter} keyCode={`Key${letter}`}>\n            {letter}\n          </Key>\n        ))}\n        <Key keyCode=\"BracketLeft\">\n          <span>{`{`}</span>\n          <span>{`[`}</span>\n        </Key>\n        <Key keyCode=\"BracketRight\">\n          <span>{`}`}</span>\n          <span>{`]`}</span>\n        </Key>\n        <Key keyCode=\"Backslash\">\n          <span>{`|`}</span>\n          <span>{`\\\\`}</span>\n        </Key>\n      </Row>\n\n      {/* Home Row */}\n      <Row>\n        <Key\n          keyCode=\"CapsLock\"\n          className=\"w-[2.8rem]\"\n          childrenClassName=\"items-start justify-end pb-[2px] pl-[4px]\"\n        >\n          <span>caps lock</span>\n        </Key>\n        {[\"A\", \"S\", \"D\", \"F\", \"G\", \"H\", \"J\", \"K\", \"L\"].map((letter) => (\n          <Key key={letter} keyCode={`Key${letter}`}>\n            {letter}\n          </Key>\n        ))}\n        <Key keyCode=\"Semicolon\">\n          <span>:</span>\n          <span>;</span>\n        </Key>\n        <Key keyCode=\"Quote\">\n          <span>{`\"`}</span>\n          <span>{`'`}</span>\n        </Key>\n        <Key\n          keyCode=\"Enter\"\n          className=\"w-[2.85rem]\"\n          childrenClassName=\"items-end justify-end pr-[4px] pb-[2px]\"\n        >\n          <span>return</span>\n        </Key>\n      </Row>\n\n      {/* Bottom Letter Row */}\n      <Row>\n        <Key\n          keyCode=\"ShiftLeft\"\n          className=\"w-[3.65rem]\"\n          childrenClassName=\"items-start justify-end pb-[2px] pl-[4px]\"\n        >\n          <span>shift</span>\n        </Key>\n        {[\"Z\", \"X\", \"C\", \"V\", \"B\", \"N\", \"M\"].map((letter) => (\n          <Key key={letter} keyCode={`Key${letter}`}>\n            {letter}\n          </Key>\n        ))}\n        <Key keyCode=\"Comma\">\n          <span>{`<`}</span>\n          <span>,</span>\n        </Key>\n        <Key keyCode=\"Period\">\n          <span>{`>`}</span>\n          <span>.</span>\n        </Key>\n        <Key keyCode=\"Slash\">\n          <span>?</span>\n          <span>/</span>\n        </Key>\n        <Key\n          keyCode=\"ShiftRight\"\n          className=\"w-[3.65rem]\"\n          childrenClassName=\"items-end justify-end pr-[4px] pb-[2px]\"\n        >\n          <span>shift</span>\n        </Key>\n      </Row>\n\n      {/* Modifier Row */}\n      <Row>\n        <ModifierKey\n          keyCode=\"Fn\"\n          containerClassName=\"rounded-bl-xl\"\n          className=\"rounded-bl-lg\"\n        >\n          <span>fn</span>\n          <IconWorld className=\"h-[6px] w-[6px]\" />\n        </ModifierKey>\n        <ModifierKey keyCode=\"ControlLeft\">\n          <IconChevronUp className=\"h-[6px] w-[6px]\" />\n          <span>control</span>\n        </ModifierKey>\n        <ModifierKey keyCode=\"AltLeft\">\n          <OptionKey className=\"h-[6px] w-[6px]\" />\n          <span>option</span>\n        </ModifierKey>\n        <ModifierKey keyCode=\"MetaLeft\" className=\"w-8\">\n          <IconCommand className=\"h-[6px] w-[6px]\" />\n          <span>command</span>\n        </ModifierKey>\n        <Key keyCode=\"Space\" className=\"w-[8.2rem]\" />\n        <ModifierKey keyCode=\"MetaRight\" className=\"w-8\">\n          <IconCommand className=\"h-[6px] w-[6px]\" />\n          <span>command</span>\n        </ModifierKey>\n        <ModifierKey keyCode=\"AltRight\">\n          <OptionKey className=\"h-[6px] w-[6px]\" />\n          <span>option</span>\n        </ModifierKey>\n        {/* Arrow Keys */}\n        <div className=\"flex h-6 w-[4.9rem] items-center justify-end rounded-[4px] p-[0.5px]\">\n          <Key keyCode=\"ArrowLeft\" className=\"h-6 w-6\">\n            <IconCaretLeftFilled className=\"h-[6px] w-[6px]\" />\n          </Key>\n          <div className=\"flex flex-col\">\n            <Key keyCode=\"ArrowUp\" className=\"h-3 w-6\">\n              <IconCaretUpFilled className=\"h-[6px] w-[6px]\" />\n            </Key>\n            <Key keyCode=\"ArrowDown\" className=\"h-3 w-6\">\n              <IconCaretDownFilled className=\"h-[6px] w-[6px]\" />\n            </Key>\n          </div>\n          <Key\n            keyCode=\"ArrowRight\"\n            containerClassName=\"rounded-br-xl\"\n            className=\"h-6 w-6 rounded-br-lg\"\n          >\n            <IconCaretRightFilled className=\"h-[6px] w-[6px]\" />\n          </Key>\n        </div>\n      </Row>\n    </div>\n  );\n};\n\nconst Row = ({ children }: { children: React.ReactNode }) => (\n  <div className=\"mb-[2px] flex w-full shrink-0 gap-[2px]\">{children}</div>\n);\n\nconst Key = ({\n  className,\n  childrenClassName,\n  containerClassName,\n  children,\n  keyCode,\n}: {\n  className?: string;\n  childrenClassName?: string;\n  containerClassName?: string;\n  children?: React.ReactNode;\n  keyCode?: string;\n}) => {\n  const { playSoundDown, playSoundUp, pressedKeys, setPressed, setReleased } =\n    useKeyboardSound();\n  const isPressed = keyCode ? pressedKeys.has(keyCode) : false;\n\n  const handleMouseDown = () => {\n    if (keyCode) {\n      playSoundDown(keyCode);\n      setPressed(keyCode);\n    }\n  };\n\n  const handleMouseUp = () => {\n    if (keyCode && isPressed) {\n      playSoundUp(keyCode);\n      setReleased(keyCode);\n    }\n  };\n\n  const handleMouseLeave = () => {\n    if (keyCode && isPressed) {\n      setReleased(keyCode);\n    }\n  };\n\n  return (\n    <div className={cn(\"rounded-[4px] p-[0.5px]\", containerClassName)}>\n      <button\n        type=\"button\"\n        onMouseDown={handleMouseDown}\n        onMouseUp={handleMouseUp}\n        onMouseLeave={handleMouseLeave}\n        className={cn(\n          \"flex h-6 w-6 cursor-pointer items-center justify-center rounded-[3.5px] bg-gray-100 shadow-[0px_0px_1px_0px_rgba(0,0,0,0.5),0px_1px_1px_0px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(255,255,255,1)_inset] transition-transform duration-75 active:scale-[0.98]\",\n          isPressed &&\n            \"scale-[0.98] bg-gray-100/80 shadow-[0px_0px_1px_0px_rgba(0,0,0,0.5),0px_1px_1px_0px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(255,255,255,0.5)]\",\n          className,\n        )}\n      >\n        <div\n          className={cn(\n            \"flex h-full w-full flex-col items-center justify-center text-[5px] text-neutral-700\",\n            childrenClassName,\n          )}\n        >\n          {children}\n        </div>\n      </button>\n    </div>\n  );\n};\n\nconst ModifierKey = ({\n  className,\n  containerClassName,\n  children,\n  keyCode,\n}: {\n  className?: string;\n  containerClassName?: string;\n  children?: React.ReactNode;\n  keyCode?: string;\n}) => {\n  const { playSoundDown, playSoundUp, pressedKeys, setPressed, setReleased } =\n    useKeyboardSound();\n  const isPressed = keyCode ? pressedKeys.has(keyCode) : false;\n\n  const handleMouseDown = () => {\n    if (keyCode) {\n      playSoundDown(keyCode);\n      setPressed(keyCode);\n    }\n  };\n\n  const handleMouseUp = () => {\n    if (keyCode && isPressed) {\n      playSoundUp(keyCode);\n      setReleased(keyCode);\n    }\n  };\n\n  const handleMouseLeave = () => {\n    if (keyCode && isPressed) {\n      setReleased(keyCode);\n    }\n  };\n\n  return (\n    <div className={cn(\"rounded-[4px] p-[0.5px]\", containerClassName)}>\n      <button\n        type=\"button\"\n        onMouseDown={handleMouseDown}\n        onMouseUp={handleMouseUp}\n        onMouseLeave={handleMouseLeave}\n        className={cn(\n          \"flex h-6 w-6 cursor-pointer items-center justify-center rounded-[3.5px] bg-gray-100 shadow-[0px_0px_1px_0px_rgba(0,0,0,0.5),0px_1px_1px_0px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(255,255,255,1)_inset] transition-transform duration-75 active:scale-[0.98]\",\n          isPressed &&\n            \"scale-[0.98] bg-gray-100/80 shadow-[0px_0px_1px_0px_rgba(0,0,0,0.5),0px_1px_1px_0px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(255,255,255,0.5)]\",\n          className,\n        )}\n      >\n        <div className=\"flex h-full w-full flex-col items-start justify-between p-1 text-[5px] text-neutral-700\">\n          {children}\n        </div>\n      </button>\n    </div>\n  );\n};\n\nconst OptionKey = ({ className }: { className?: string }) => {\n  return (\n    <svg\n      fill=\"none\"\n      version=\"1.1\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 32 32\"\n      className={className}\n    >\n      <rect\n        stroke=\"currentColor\"\n        strokeWidth={2}\n        x=\"18\"\n        y=\"5\"\n        width=\"10\"\n        height=\"2\"\n      />\n      <polygon\n        stroke=\"currentColor\"\n        strokeWidth={2}\n        points=\"10.6,5 4,5 4,7 9.4,7 18.4,27 28,27 28,25 19.6,25\"\n      />\n    </svg>\n  );\n};\n",
      "type": "registry:ui",
      "target": "components/ui/keyboard.tsx"
    }
  ],
  "author": "Manu Arora <hi@manuarora.in>",
  "title": "Keyboard"
}