{
  "$schema": "https://www.cooki-ui.design/schema/registry-item.json",
  "name": "ai-voice-waveform",
  "type": "registry:ui",
  "title": "AI Voice Waveform",
  "description": "Interactive real-time audio waveform visualizer for conversational AI agents with pulsating aura, live frequency spectrum, and listening states.",
  "category": "ai",
  "dependencies": [
    "framer-motion",
    "@phosphor-icons/react"
  ],
  "files": [
    {
      "path": "registry/ai/ai-voice-waveform/ai-voice-waveform.tsx",
      "type": "registry:ui",
      "target": "components/ui/ai-voice-waveform.tsx",
      "content": "import React, { useState, useEffect } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport { \n  Microphone, \n  MicrophoneSlash, \n  SpeakerHigh, \n  Waveform, \n  Circle, \n  Play, \n  Pause,\n  ArrowsClockwise\n} from '@phosphor-icons/react';\n\nexport interface AiVoiceWaveformProps {\n  state?: 'listening' | 'speaking' | 'idle';\n  colorTheme?: 'cyan' | 'emerald' | 'violet' | 'amber';\n  barCount?: number;\n  active?: boolean;\n  className?: string;\n}\n\nexport const AiVoiceWaveform: React.FC<AiVoiceWaveformProps> = ({\n  state: controlledState,\n  colorTheme = 'cyan',\n  barCount = 24,\n  active: controlledActive,\n  className = '',\n}) => {\n  const [internalActive, setInternalActive] = useState(true);\n  const [internalState, setInternalState] = useState<'listening' | 'speaking' | 'idle'>('listening');\n  const [frequencies, setFrequencies] = useState<number[]>([]);\n\n  const isActive = controlledActive !== undefined ? controlledActive : internalActive;\n  const currentState = controlledState !== undefined ? controlledState : internalState;\n\n  // Generate responsive animated bar heights\n  useEffect(() => {\n    const bars = Math.max(12, Math.min(barCount, 40));\n    let frameId: number;\n    let tick = 0;\n\n    const updateSpectrum = () => {\n      tick += 0.08;\n      if (!isActive) {\n        setFrequencies(Array(bars).fill(4));\n        return;\n      }\n\n      const next = Array.from({ length: bars }, (_, i) => {\n        const centerOffset = 1 - Math.abs(i - bars / 2) / (bars / 2);\n        if (currentState === 'listening') {\n          // Dynamic noise with center bias\n          const wave = Math.sin(tick * 3 + i * 0.5) * 0.5 + 0.5;\n          const noise = Math.random() * 0.4 + 0.1;\n          return Math.max(6, Math.floor((wave * 0.6 + noise * 0.4) * 64 * centerOffset) + 6);\n        } else if (currentState === 'speaking') {\n          // Smooth sine undulating voice rhythm\n          const wave1 = Math.sin(tick * 2 + i * 0.35);\n          const wave2 = Math.cos(tick * 1.5 - i * 0.2);\n          const combined = (wave1 + wave2 + 2) / 4;\n          return Math.max(8, Math.floor(combined * 72 * centerOffset) + 8);\n        } else {\n          // Gentle idle breathing\n          const idleWave = (Math.sin(tick * 1.2 + i * 0.2) + 1) / 2;\n          return Math.max(4, Math.floor(idleWave * 18 * centerOffset) + 4);\n        }\n      });\n\n      setFrequencies(next);\n      frameId = requestAnimationFrame(updateSpectrum);\n    };\n\n    frameId = requestAnimationFrame(updateSpectrum);\n    return () => cancelAnimationFrame(frameId);\n  }, [isActive, currentState, barCount]);\n\n  const themeConfig = {\n    cyan: {\n      barGradient: 'from-cyan-400 via-sky-500 to-blue-600',\n      aura: 'bg-cyan-500/20 border-cyan-500/30 text-cyan-400',\n      glow: 'shadow-[0_0_30px_rgba(6,182,212,0.25)]',\n      pill: 'bg-cyan-500/10 text-cyan-400 border-cyan-500/20',\n    },\n    emerald: {\n      barGradient: 'from-emerald-400 via-teal-500 to-green-600',\n      aura: 'bg-emerald-500/20 border-emerald-500/30 text-emerald-400',\n      glow: 'shadow-[0_0_30px_rgba(16,185,129,0.25)]',\n      pill: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20',\n    },\n    violet: {\n      barGradient: 'from-violet-400 via-purple-500 to-indigo-600',\n      aura: 'bg-violet-500/20 border-violet-500/30 text-violet-400',\n      glow: 'shadow-[0_0_30px_rgba(139,92,246,0.25)]',\n      pill: 'bg-violet-500/10 text-violet-400 border-violet-500/20',\n    },\n    amber: {\n      barGradient: 'from-amber-400 via-orange-500 to-rose-600',\n      aura: 'bg-amber-500/20 border-amber-500/30 text-amber-400',\n      glow: 'shadow-[0_0_30px_rgba(245,158,11,0.25)]',\n      pill: 'bg-amber-500/10 text-amber-400 border-amber-500/20',\n    },\n  }[colorTheme];\n\n  const stateLabels = {\n    listening: 'Listening to prompt...',\n    speaking: 'Synthesizing voice response...',\n    idle: 'Standby mode (tap mic to speak)',\n  };\n\n  return (\n    <div className={`w-full max-w-xl mx-auto rounded-3xl border border-zinc-200 dark:border-white/10 bg-white dark:bg-zinc-950 p-6 shadow-xl transition-all ${className}`}>\n      {/* Top Header Status */}\n      <div className=\"flex items-center justify-between pb-4 border-b border-zinc-200/80 dark:border-white/5\">\n        <div className=\"flex items-center gap-2.5\">\n          <div className={`flex h-8 w-8 items-center justify-center rounded-xl border ${themeConfig.aura}`}>\n            {currentState === 'listening' ? (\n              <Microphone size={16} weight=\"bold\" />\n            ) : currentState === 'speaking' ? (\n              <SpeakerHigh size={16} weight=\"bold\" />\n            ) : (\n              <Waveform size={16} weight=\"bold\" />\n            )}\n          </div>\n          <div>\n            <div className=\"flex items-center gap-2\">\n              <h3 className=\"text-sm font-semibold text-zinc-900 dark:text-white\">Neural Voice Engine</h3>\n              <span className={`px-2 py-0.5 rounded-full text-[10px] font-mono uppercase font-semibold border ${themeConfig.pill}`}>\n                {currentState}\n              </span>\n            </div>\n            <p className=\"text-xs text-zinc-500 font-mono\">Sampling: 48kHz &bull; Latency &lt; 90ms</p>\n          </div>\n        </div>\n\n        {/* Mute/Active Toggle */}\n        <button\n          onClick={() => setInternalActive(!isActive)}\n          className={`flex h-9 w-9 items-center justify-center rounded-xl border transition-all cursor-pointer ${\n            isActive\n              ? 'bg-zinc-900 text-white dark:bg-white dark:text-zinc-900 border-transparent shadow-xs'\n              : 'bg-zinc-100 text-zinc-400 dark:bg-zinc-900 dark:text-zinc-500 border-zinc-200 dark:border-white/10'\n          }`}\n          title={isActive ? 'Mute microphone' : 'Unmute microphone'}\n        >\n          {isActive ? <Microphone size={16} weight=\"bold\" /> : <MicrophoneSlash size={16} weight=\"bold\" />}\n        </button>\n      </div>\n\n      {/* Main Waveform Visualization Stage */}\n      <div className=\"relative my-8 flex h-36 items-center justify-center overflow-hidden rounded-2xl bg-zinc-50 dark:bg-[#07070a] border border-zinc-200/60 dark:border-white/5 px-4\">\n        {/* Pulsating Center Aura */}\n        <div \n          className={`absolute h-28 w-28 rounded-full blur-2xl transition-all duration-700 pointer-events-none ${\n            isActive ? `${themeConfig.aura.split(' ')[0]} opacity-70 scale-125` : 'opacity-0 scale-75'\n          }`} \n        />\n\n        {/* Waveform Frequency Bars */}\n        <div className=\"relative z-10 flex items-center justify-center gap-1 sm:gap-1.5 h-full w-full max-w-md\">\n          {frequencies.map((height, i) => (\n            <div\n              key={i}\n              className=\"flex items-center justify-center h-full flex-1 max-w-[12px]\"\n            >\n              <div\n                style={{\n                  height: `${height}px`,\n                  transition: 'height 80ms ease-out',\n                }}\n                className={`w-full rounded-full bg-gradient-to-t ${themeConfig.barGradient} ${\n                  isActive ? themeConfig.glow : 'opacity-30'\n                }`}\n              />\n            </div>\n          ))}\n        </div>\n\n        {/* Live Audio Level Rings */}\n        {isActive && currentState !== 'idle' && (\n          <div className=\"absolute inset-0 pointer-events-none flex items-center justify-center\">\n            <div className=\"w-48 h-20 rounded-full border border-dashed border-zinc-300 dark:border-white/10 animate-pulse\" />\n          </div>\n        )}\n      </div>\n\n      {/* Live Text Caption & Activity */}\n      <div className=\"flex items-center justify-between text-xs font-mono\">\n        <div className=\"flex items-center gap-2 text-zinc-600 dark:text-zinc-300\">\n          <Circle \n            size={8} \n            weight=\"fill\" \n            className={isActive ? (currentState === 'listening' ? 'text-emerald-500 animate-pulse' : 'text-cyan-500 animate-ping') : 'text-zinc-500'} \n          />\n          <span className=\"truncate\">{stateLabels[currentState]}</span>\n        </div>\n\n        {/* State Switching Pills (Interactive in preview) */}\n        <div className=\"flex items-center gap-1 bg-zinc-100 dark:bg-zinc-900 p-1 rounded-xl border border-zinc-200 dark:border-white/5\">\n          {(['listening', 'speaking', 'idle'] as const).map((s) => (\n            <button\n              key={s}\n              onClick={() => setInternalState(s)}\n              className={`px-2.5 py-1 rounded-lg text-[10px] font-mono uppercase font-semibold transition-all cursor-pointer ${\n                currentState === s\n                  ? 'bg-white dark:bg-zinc-800 text-zinc-900 dark:text-white shadow-xs'\n                  : 'text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-300'\n              }`}\n            >\n              {s}\n            </button>\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n};\n"
    }
  ]
}