theNorms's picture
Upload project files
0baa396 verified
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { Mic, MicOff } from "lucide-react";
interface VoiceButtonProps {
isRecording: boolean;
onToggle: () => void;
}
export function VoiceButton({ isRecording, onToggle }: VoiceButtonProps) {
return (
<div className="relative">
{/* Pulsing rings when recording */}
<AnimatePresence>
{isRecording && (
<>
<motion.div
className="absolute inset-0 rounded-full"
style={{ border: "2px solid #a1a1aa" }}
initial={{ scale: 1, opacity: 0.6 }}
animate={{ scale: 1.8, opacity: 0 }}
transition={{ duration: 1.5, repeat: Infinity, ease: "easeOut" }}
/>
<motion.div
className="absolute inset-0 rounded-full"
style={{ border: "2px solid #a1a1aa" }}
initial={{ scale: 1, opacity: 0.4 }}
animate={{ scale: 2.2, opacity: 0 }}
transition={{
duration: 1.5,
repeat: Infinity,
ease: "easeOut",
delay: 0.3,
}}
/>
<motion.div
className="absolute inset-0 rounded-full"
style={{ border: "1px solid #a1a1aa" }}
initial={{ scale: 1, opacity: 0.3 }}
animate={{ scale: 2.6, opacity: 0 }}
transition={{
duration: 1.5,
repeat: Infinity,
ease: "easeOut",
delay: 0.6,
}}
/>
</>
)}
</AnimatePresence>
{/* Glow background */}
<AnimatePresence>
{isRecording && (
<motion.div
className="absolute inset-0 rounded-full"
style={{ backgroundColor: "#a1a1aa30" }}
initial={{ scale: 1 }}
animate={{ scale: 1.2 }}
exit={{ scale: 1 }}
transition={{ duration: 0.3 }}
/>
)}
</AnimatePresence>
{/* Main button */}
<motion.button
onClick={onToggle}
className={`relative z-10 w-9 h-9 rounded-full flex items-center justify-center transition-colors ${
isRecording
? "bg-foreground text-background shadow-[0_0_20px_#a1a1aa40]"
: "bg-foreground/10 text-foreground hover:bg-foreground/15 border border-border"
}`}
whileTap={{ scale: 0.9 }}
whileHover={{ scale: 1.05 }}
aria-label={isRecording ? "Stop recording" : "Start voice recording"}
>
{isRecording ? (
<motion.div
animate={{ scale: [1, 1.2, 1] }}
transition={{ duration: 0.8, repeat: Infinity }}
>
<MicOff className="w-3.5 h-3.5" />
</motion.div>
) : (
<Mic className="w-3.5 h-3.5" />
)}
</motion.button>
</div>
);
}
/** Simple waveform visualization component */
export function WaveformVisualizer({ isActive }: { isActive: boolean }) {
const bars = 24;
return (
<div className="flex items-center justify-center gap-[2px] h-8">
{Array.from({ length: bars }).map((_, i) => (
<motion.div
key={i}
className="w-[3px] rounded-full"
style={{ backgroundColor: isActive ? "#a1a1aa" : "#3f3f4640" }}
animate={
isActive
? {
height: [4, Math.random() * 28 + 4, 4],
}
: { height: 4 }
}
transition={
isActive
? {
duration: 0.4 + Math.random() * 0.3,
repeat: Infinity,
repeatType: "reverse",
delay: i * 0.03,
}
: { duration: 0.3 }
}
/>
))}
</div>
);
}