"use client"
import React from "react"
import { Mic } from "lucide-react"
import { AnimatePresence, motion } from "motion/react"
import { cn } from "@/lib/utils"
export interface VoiceInputProps {
/**
* Callback function called when voice recording starts.
*/
onStart?: () => void
/**
* Callback function called when voice recording stops.
*/
onStop?: () => void
}
export function VoiceInput({
className,
onStart,
onStop,
...props
}: React.ComponentProps<"div"> & VoiceInputProps) {
const [_listening, _setListening] = React.useState<boolean>(false)
const [_time, _setTime] = React.useState<number>(0)
React.useEffect(() => {
let intervalId: NodeJS.Timeout
if (_listening) {
onStart?.()
intervalId = setInterval(() => {
_setTime((t) => t + 1)
}, 1000)
} else {
onStop?.()
_setTime(0)
}
return () => clearInterval(intervalId)
}, [_listening, onStart, onStop])
const formatTime = (seconds: number) => {
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`
}
const onClickHandler = () => {
_setListening(!_listening)
}
return (
<div
className={cn("flex flex-col items-center justify-center", className)}
{...props}
>
<motion.div
className="flex cursor-pointer items-center justify-center rounded-full border p-2"
layout
transition={{
layout: {
duration: 0.4,
},
}}
onClick={onClickHandler}
>
<div className="flex h-6 w-6 items-center justify-center">
{_listening ? (
<motion.div
className="bg-primary h-4 w-4 rounded-sm"
animate={{
rotate: [0, 180, 360],
}}
transition={{
duration: 2,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut",
}}
/>
) : (
<Mic />
)}
</div>
<AnimatePresence mode="wait">
{_listening && (
<motion.div
initial={{ opacity: 0, width: 0, marginLeft: 0 }}
animate={{ opacity: 1, width: "auto", marginLeft: 8 }}
exit={{ opacity: 0, width: 0, marginLeft: 0 }}
transition={{
duration: 0.4,
}}
className="flex items-center justify-center gap-2 overflow-hidden"
>
{/* Frequency Animation */}
<div className="flex items-center justify-center gap-0.5">
{[...Array(12)].map((_, i) => (
<motion.div
key={i}
className="bg-primary w-0.5 rounded-full"
initial={{ height: 2 }}
animate={{
height: _listening
? [2, 3 + Math.random() * 10, 3 + Math.random() * 5, 2]
: 2,
}}
transition={{
duration: _listening ? 1 : 0.3,
repeat: _listening ? Infinity : 0,
delay: _listening ? i * 0.05 : 0,
ease: "easeInOut",
}}
/>
))}
</div>
{/* Timer */}
<div className="text-muted-foreground w-10 text-center text-xs">
{formatTime(_time)}
</div>
</motion.div>
)}
</AnimatePresence>
</motion.div>
</div>
)
}