story-generator/src/features/voice-commands/components/VoiceCommandButton.tsx
2025-01-25 11:58:30 -03:00

79 lines
2.2 KiB
TypeScript

import { useSpeechRecognition } from '../hooks/useSpeechRecognition';
import { Mic, Waves } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useEffect } from 'react';
interface VoiceCommandButtonProps {
className?: string;
onTranscriptUpdate: (transcript: string) => void;
disabled?: boolean;
}
export function VoiceCommandButton({
className,
onTranscriptUpdate,
disabled = false
}: VoiceCommandButtonProps) {
const {
transcript,
start,
stop,
status,
error,
isSupported
} = useSpeechRecognition();
useEffect(() => {
if (transcript) {
onTranscriptUpdate(transcript);
}
}, [transcript, onTranscriptUpdate]);
useEffect(() => {
if (status === 'recording') {
onTranscriptUpdate(''); // Limpar contexto ao iniciar nova gravação
}
}, [status, onTranscriptUpdate]);
if (!isSupported) {
return (
<div className="p-3 bg-yellow-50 text-yellow-700 rounded-lg text-sm">
Seu navegador não suporta gravação por voz
</div>
);
}
return (
<div className="relative group">
<button
onClick={status === 'recording' ? stop : start}
className={cn(
'flex items-center gap-3 px-4 py-2 rounded-lg transition-all',
status === 'recording'
? 'bg-red-100 text-red-600 hover:bg-red-200 shadow-sm'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200',
disabled && 'opacity-50 cursor-not-allowed',
className
)}
aria-label={status === 'recording' ? "Parar gravação" : "Iniciar gravação"}
disabled={disabled}
>
<div className="relative">
<Mic className="h-5 w-5" />
{status === 'recording' && (
<Waves className="absolute -top-2 -right-2 h-4 w-4 animate-pulse text-red-500" />
)}
</div>
<span className="text-sm font-medium">
{status === 'recording' ? 'Gravando...' : 'Gravar por Voz'}
</span>
</button>
{error && (
<div className="absolute top-full mt-2 p-2 bg-red-50 text-red-600 text-sm rounded-lg shadow-lg border border-red-100">
{error}
</div>
)}
</div>
);
}