{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "voice-aura",
  "title": "Voice Aura",
  "author": "mrap10",
  "description": "Mic toggle that activates a full-screen aura glow overlay.",
  "dependencies": ["motion"],
  "files": [
    {
      "path": "registry/default/button/voice-aura.tsx",
      "content": "\"use client\";\n\nimport { useState, type ReactNode } from \"react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\ntype VoiceAuraProps = {\n  iconSize?: string;\n  icon?: ReactNode;\n  buttonClassName?: string;\n  buttonWrapperClassName?: string;\n  overlayClassName?: string;\n  backdropClassName?: string;\n  defaultOpen?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n};\n\nexport default function VoiceAura({\n  iconSize = \"size-5\",\n  icon,\n  buttonClassName,\n  buttonWrapperClassName,\n  overlayClassName,\n  backdropClassName,\n  defaultOpen = false,\n  open,\n  onOpenChange,\n}: VoiceAuraProps) {\n  const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n  const isControlled = open !== undefined;\n  const isOpen = isControlled ? open : uncontrolledOpen;\n\n  const handleOpenChange = (open: boolean) => {\n    if (!isControlled) {\n      setUncontrolledOpen(open);\n    }\n\n    onOpenChange?.(open);\n  };\n\n  return (\n    <div\n      className={cn(\n        \"relative inline-flex items-center justify-center\",\n        buttonWrapperClassName\n      )}\n    >\n      <motion.button\n        type=\"button\"\n        onClick={() => handleOpenChange(!isOpen)}\n        aria-pressed={isOpen}\n        whileTap={{ scale: 0.95 }}\n        animate={\n          isOpen\n            ? {\n                boxShadow: [\n                  \"0 0 0 0px rgba(34,211,238,0)\",\n                  \"0 0 0 12px rgba(34,211,238,0.22)\",\n                  \"0 0 0 0px rgba(34,211,238,0)\",\n                ],\n              }\n            : { boxShadow: \"0 0 0 0px rgba(59,130,246,0)\" }\n        }\n        transition={\n          isOpen\n            ? { duration: 2, repeat: Infinity, ease: \"easeInOut\" }\n            : { duration: 0.3 }\n        }\n        className={cn(\n          \"relative z-10 rounded-full bg-neutral-800 p-2 text-white transition-colors duration-150 ease-out hover:bg-neutral-700 dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-neutral-200\",\n          buttonClassName\n        )}\n      >\n        {icon ?? <MicrophoneIcon className={iconSize} />}\n      </motion.button>\n\n      <AnimatePresence>\n        {isOpen && (\n          <AuraLayer\n            overlayClassName={overlayClassName}\n            backdropClassName={backdropClassName}\n          />\n        )}\n      </AnimatePresence>\n    </div>\n  );\n}\n\nfunction AuraLayer({\n  overlayClassName,\n  backdropClassName,\n}: {\n  overlayClassName?: string;\n  backdropClassName?: string;\n}) {\n  return (\n    <motion.div\n      initial={{ opacity: 0 }}\n      animate={{ opacity: 1 }}\n      exit={{ opacity: 0 }}\n      transition={{ duration: 0.4, ease: [0.22, 1, 0.36, 1] }}\n      className={cn(\n        \"pointer-events-none fixed inset-0 overflow-hidden\",\n        overlayClassName\n      )}\n    >\n      <div\n        className={cn(\n          \"absolute inset-0 bg-white/60 dark:bg-neutral-950/70\",\n          backdropClassName\n        )}\n      />\n      <GlowBlob\n        className=\"absolute inset-0 -left-32 w-64 bg-[radial-gradient(ellipse_at_left,rgba(56,189,248,0.84)_0%,rgba(59,130,246,0.5)_40%,transparent_76%)] dark:bg-[radial-gradient(ellipse_at_left,rgba(34,211,238,0.96)_0%,rgba(59,130,246,0.72)_34%,rgba(168,85,247,0.34)_60%,transparent_82%)] dark:mix-blend-screen\"\n        duration={4.5}\n        delay={0}\n      />\n\n      <GlowBlob\n        className=\"absolute inset-y-0 -right-32 w-64 bg-[radial-gradient(ellipse_at_right,rgba(168,85,247,0.84)_0%,rgba(244,114,182,0.5)_40%,transparent_76%)] dark:bg-[radial-gradient(ellipse_at_right,rgba(236,72,153,0.94)_0%,rgba(168,85,247,0.72)_34%,rgba(59,130,246,0.36)_62%,transparent_82%)] dark:mix-blend-screen\"\n        duration={5}\n        delay={0.6}\n      />\n\n      <GlowBlob\n        className=\"absolute inset-x-0 -bottom-32 h-64 bg-[radial-gradient(ellipse_at_bottom,rgba(34,211,238,0.9)_0%,rgba(59,130,246,0.58)_38%,transparent_78%)] dark:bg-[radial-gradient(ellipse_at_bottom,rgba(34,211,238,0.96)_0%,rgba(59,130,246,0.72)_34%,rgba(168,85,247,0.34)_60%,transparent_82%)] dark:mix-blend-screen\"\n        duration={4}\n        delay={0.3}\n      />\n      <motion.div\n        animate={{ opacity: [0.4, 0.9, 0.4] }}\n        transition={{ duration: 4, repeat: Infinity, ease: \"easeInOut\" }}\n        className=\"absolute inset-x-[8%] bottom-0 h-px bg-linear-to-r from-transparent via-cyan-300 to-transparent\"\n      />\n    </motion.div>\n  );\n}\n\nfunction GlowBlob({\n  className,\n  duration,\n  delay,\n}: {\n  className: string;\n  duration: number;\n  delay: number;\n}) {\n  return (\n    <motion.div\n      animate={{ opacity: [0.6, 1, 0.6], scale: [0.96, 1.04, 0.96] }}\n      transition={{ duration, repeat: Infinity, ease: \"easeInOut\", delay }}\n      className={cn(\"blur-3xl\", className)}\n    />\n  );\n}\n\nfunction MicrophoneIcon({ className }: { className?: string }) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" className={className}>\n      <path d=\"M19 9a1 1 0 0 1 1 1a8 8 0 0 1 -6.999 7.938l-.001 2.062h3a1 1 0 0 1 0 2h-8a1 1 0 0 1 0 -2h3v-2.062a8 8 0 0 1 -7 -7.938a1 1 0 1 1 2 0a6 6 0 0 0 12 0a1 1 0 0 1 1 -1m-7 -8a4 4 0 0 1 4 4v5a4 4 0 1 1 -8 0v-5a4 4 0 0 1 4 -4\" />\n    </svg>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "categories": ["buttons"],
  "type": "registry:component"
}
