"use client" ;
import { useState, useEffect } from "react" ;
import { Mic, Check } from "lucide-react" ;
import { Button } from "@/components/ui/button" ;
import {
Dialog,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog" ;
import { cn } from "@/lib/utils" ;
const commands = [
{ trigger: "play music" , action: "Playing your playlist" },
{ trigger: "turn off lights" , action: "Turning off lights" },
{ trigger: "set timer" , action: "Timer set for 5 minutes" },
];
export const title = "React Dialog Block Voice Command" ;
export default function DialogVoiceCommand () {
const [ open , setOpen ] = useState ( false );
const [ listening , setListening ] = useState ( false );
const [ transcript , setTranscript ] = useState ( "" );
const [ recognized , setRecognized ] = useState < string | null >( null );
useEffect (() => {
if ( ! listening) return ;
const phrases = [ "" , "play" , "play music" ];
let i = 0 ;
const interval = setInterval (() => {
if (i < phrases. length ) {
setTranscript (phrases[i]);
i ++ ;
} else {
setListening ( false );
setRecognized ( "Playing your playlist" );
clearInterval (interval);
}
}, 600 );
return () => clearInterval (interval);
}, [listening]);
const handleStart = () => {
setListening ( true );
setTranscript ( "" );
setRecognized ( null );
};
const handleClose = () => {
setOpen ( false );
setTimeout (() => {
setListening ( false );
setTranscript ( "" );
setRecognized ( null );
}, 200 );
};
return (
< Dialog open = {open} onOpenChange = {setOpen}>
< div className = "flex min-h-[350px] items-center justify-center" >
< DialogTrigger asChild >
< Button variant = "outline" >
< Mic className = "mr-2 h-4 w-4" />
Voice Command
</ Button >
</ DialogTrigger >
</ div >
< DialogContent style = {{ maxWidth: "320px" }} className = "text-center" >
< DialogTitle className = "sr-only" >Voice Command</ DialogTitle >
< div className = "py-6 space-y-6" >
{recognized ? (
<>
< div className = "flex flex-col items-center" >
< div className = "h-16 w-16 rounded-full bg-primary/10 flex items-center justify-center" >
< Check className = "h-8 w-8 text-primary" />
</ div >
</ div >
< div >
< p className = "font-medium" >{recognized}</ p >
< p className = "text-sm text-muted-foreground mt-1" >
Command recognized
</ p >
</ div >
< Button onClick = {handleClose} className = "w-full" >
Done
</ Button >
</>
) : (
<>
{ /* Mic button with pulse */ }
< div className = "flex flex-col items-center" >
< button
onClick = {handleStart}
disabled = {listening}
className = { cn (
"relative h-20 w-20 rounded-full flex items-center justify-center transition-colors" ,
listening
? "bg-primary text-primary-foreground"
: "bg-muted hover:bg-muted/80"
)}
>
{listening && (
<>
< span className = "absolute inset-0 rounded-full bg-primary animate-ping opacity-25" />
< span className = "absolute inset-[-8px] rounded-full border-2 border-primary/30 animate-pulse" />
</>
)}
< Mic className = "h-8 w-8 relative z-10" />
</ button >
</ div >
{ /* Status */ }
< div className = "min-h-[48px]" >
{listening ? (
<>
< p className = "text-sm text-muted-foreground" >Listening...</ p >
{transcript && (
< p className = "font-medium mt-1" >"{transcript}"</ p >
)}
</>
) : (
< p className = "text-sm text-muted-foreground" >
Tap the microphone and speak
</ p >
)}
</ div >
{ /* Example commands */ }
< div className = "space-y-1" >
< p className = "text-xs text-muted-foreground" >Try saying:</ p >
< div className = "flex flex-wrap justify-center gap-1.5" >
{commands. map (( cmd ) => (
< span
key = {cmd.trigger}
className = "text-xs bg-muted px-2 py-1 rounded-full"
>
"{cmd.trigger}"
</ span >
))}
</ div >
</ div >
</>
)}
</ div >
</ DialogContent >
</ Dialog >
);
}