Spaces:
Sleeping
Sleeping
| import { cn } from "@/lib/utils"; | |
| import { useEffect, useState } from "react"; | |
| interface SoundwaveIndicatorProps { | |
| speaker: "ai" | "customer"; | |
| isActive?: boolean; | |
| className?: string; | |
| } | |
| export function SoundwaveIndicator({ speaker, isActive = true, className }: SoundwaveIndicatorProps) { | |
| const [bars, setBars] = useState<number[]>([0.3, 0.5, 0.7, 0.4, 0.6, 0.8, 0.5, 0.3]); | |
| useEffect(() => { | |
| if (!isActive) return; | |
| const interval = setInterval(() => { | |
| setBars(prev => prev.map(() => 0.2 + Math.random() * 0.8)); | |
| }, 100); | |
| return () => clearInterval(interval); | |
| }, [isActive]); | |
| const speakerConfig = { | |
| ai: { | |
| color: "bg-primary", | |
| label: "AI Speaking", | |
| gradientFrom: "from-primary/20", | |
| gradientTo: "to-primary/5", | |
| }, | |
| customer: { | |
| color: "bg-emerald-500", | |
| label: "Customer Speaking", | |
| gradientFrom: "from-emerald-500/20", | |
| gradientTo: "to-emerald-500/5", | |
| }, | |
| }; | |
| const config = speakerConfig[speaker]; | |
| return ( | |
| <div | |
| className={cn( | |
| "flex flex-col items-center gap-2 p-4 rounded-lg", | |
| `bg-gradient-to-b ${config.gradientFrom} ${config.gradientTo}`, | |
| className | |
| )} | |
| role="status" | |
| aria-label={isActive ? config.label : "No one speaking"} | |
| data-testid={`soundwave-indicator-${speaker}`} | |
| > | |
| <div className="flex items-end justify-center gap-1 h-12"> | |
| {bars.map((height, index) => ( | |
| <div | |
| key={index} | |
| className={cn( | |
| "w-1.5 rounded-full transition-all duration-100", | |
| isActive ? config.color : "bg-muted" | |
| )} | |
| style={{ | |
| height: isActive ? `${height * 48}px` : "8px", | |
| opacity: isActive ? 0.7 + height * 0.3 : 0.3, | |
| }} | |
| aria-hidden="true" | |
| /> | |
| ))} | |
| </div> | |
| <span className={cn( | |
| "text-xs font-medium", | |
| speaker === "ai" ? "text-primary" : "text-emerald-600 dark:text-emerald-400" | |
| )}> | |
| {isActive ? config.label : "Waiting..."} | |
| </span> | |
| </div> | |
| ); | |
| } | |