{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bar-visualizer",
  "type": "registry:ui",
  "description": "Real-time audio frequency bar visualizer (canvas)",
  "files": [
    {
      "path": "components/ui/BarVisualizer.tsx",
      "content": "import { useRef, useEffect, useCallback } from \"react\";\nimport { useAgentSession, useAgentMode } from \"@deepgram/react\";\n\nexport interface BarVisualizerProps {\n  /** \"input\" for microphone, \"output\" for agent audio. Default: \"output\" */\n  source?: \"input\" | \"output\";\n  /** Number of bars. Default: 16 */\n  barCount?: number;\n  /** Bar color when idle. Uses CSS variable. */\n  className?: string;\n}\n\n/**\n * Real-time audio frequency bar visualizer.\n * Renders a canvas with vertical bars that react to audio input or output.\n *\n * Requires an active session — bars flatten when disconnected.\n *\n * @example\n * ```tsx\n * <AgentProvider config={config}>\n *   <BarVisualizer source=\"output\" barCount={24} />\n * </AgentProvider>\n * ```\n */\nexport function BarVisualizer({\n  source = \"output\",\n  barCount = 16,\n  className,\n}: BarVisualizerProps) {\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const session = useAgentSession();\n  const { mode } = useAgentMode();\n  const rafRef = useRef<number>(0);\n\n  const draw = useCallback(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const ctx = canvas.getContext(\"2d\");\n    if (!ctx) return;\n\n    const dpr = window.devicePixelRatio || 1;\n    const rect = canvas.getBoundingClientRect();\n    canvas.width = rect.width * dpr;\n    canvas.height = rect.height * dpr;\n    ctx.scale(dpr, dpr);\n\n    ctx.clearRect(0, 0, rect.width, rect.height);\n\n    // Get frequency data from the session's mic or player\n    let data: Uint8Array | undefined;\n    try {\n      // Access the underlying mic/player through the session\n      // The session doesn't directly expose these — we use a workaround\n      // by reading frequency data at the current instant\n      if (source === \"input\") {\n        data = (session as unknown as { micRef?: { current?: { getInputByteFrequencyData?: () => Uint8Array } } })\n          ?.micRef?.current?.getInputByteFrequencyData?.();\n      }\n    } catch {}\n\n    const barWidth = rect.width / barCount;\n    const gap = Math.max(1, barWidth * 0.15);\n    const effectiveBarWidth = barWidth - gap;\n\n    const style = getComputedStyle(canvas);\n    const color = style.getPropertyValue(\"--dg-va-primary\").trim() || \"#13EF93\";\n    const mutedColor = style.getPropertyValue(\"--dg-va-border\").trim() || \"rgba(255,255,255,0.08)\";\n\n    for (let i = 0; i < barCount; i++) {\n      let value = 0;\n      if (data && data.length > 0) {\n        const index = Math.floor((i / barCount) * data.length);\n        value = data[index] / 255;\n      }\n\n      const minH = rect.height * 0.05;\n      const barH = Math.max(minH, value * rect.height * 0.9);\n      const x = i * barWidth + gap / 2;\n      const y = (rect.height - barH) / 2;\n\n      ctx.fillStyle = value > 0.05 ? color : mutedColor;\n      ctx.beginPath();\n      ctx.roundRect(x, y, effectiveBarWidth, barH, 2);\n      ctx.fill();\n    }\n\n    rafRef.current = requestAnimationFrame(draw);\n  }, [barCount, source, session]);\n\n  useEffect(() => {\n    if (mode !== \"idle\") {\n      rafRef.current = requestAnimationFrame(draw);\n    }\n    return () => cancelAnimationFrame(rafRef.current);\n  }, [mode, draw]);\n\n  return (\n    <canvas\n      ref={canvasRef}\n      className={className}\n      data-agent-bar-visualizer\n      style={{ width: \"100%\", height: \"100%\", display: \"block\" }}\n      aria-hidden=\"true\"\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "dependencies": [
    "@deepgram/react"
  ]
}
