/** * VoiceTranscript - Real-time voice transcript display. * * Renders as a floating card above the VoiceButton showing: * - Recording indicator with live dot * - Final and interim transcript text * - Command recognition feedback (success / unrecognized) * - Animated voice wave bars while listening */ import { motion } from "framer-motion"; import { CheckCircleIcon, XCircleIcon, SpeakerWaveIcon, } from "@heroicons/react/24/outline"; import PropTypes from "prop-types"; import { INTENTS } from "@services/voiceService"; import { LANGUAGES } from "@utils/constants"; // --------------------------------------------------------------------------- // Localized Labels // --------------------------------------------------------------------------- const LABELS = { listening: { [LANGUAGES.EN]: "Listening...", [LANGUAGES.HI]: "सुन रहे हैं...", }, processing: { [LANGUAGES.EN]: "Processing...", [LANGUAGES.HI]: "प्रसंस्करण...", }, noSpeech: { [LANGUAGES.EN]: "Say a command...", [LANGUAGES.HI]: "कमांड बोलें...", }, }; // --------------------------------------------------------------------------- // Wave Bar Configuration // --------------------------------------------------------------------------- const WAVE_BAR_COUNT = 5; const WAVE_HEIGHTS = [4, 16, 8, 20, 4]; const WAVE_DURATION = 1.2; // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- function VoiceTranscript({ transcript, interimTranscript, isListening, commandResult, language, onClose, }) { const hasContent = Boolean(transcript || interimTranscript); const isCommandRecognized = commandResult && commandResult.intent !== INTENTS.UNKNOWN; const lang = language || LANGUAGES.EN; return ( {/* Header */}
{isListening && ( )} {isListening ? LABELS.listening[lang] || LABELS.listening[LANGUAGES.EN] : commandResult ? LABELS.processing[lang] || LABELS.processing[LANGUAGES.EN] : ""}
{/* Transcript content */}
{!hasContent && isListening && (

{LABELS.noSpeech[lang] || LABELS.noSpeech[LANGUAGES.EN]}

)} {transcript && (

{transcript}

)} {interimTranscript && (

{interimTranscript}

)}
{/* Command result feedback */} {commandResult && (
{isCommandRecognized ? ( ) : ( )} {commandResult.response} {isCommandRecognized && ( )}
)} {/* Voice wave visualization */} {isListening && (
{Array.from({ length: WAVE_BAR_COUNT }).map((_, i) => ( ))}
)}
); } VoiceTranscript.propTypes = { transcript: PropTypes.string, interimTranscript: PropTypes.string, isListening: PropTypes.bool.isRequired, commandResult: PropTypes.shape({ intent: PropTypes.string.isRequired, confidence: PropTypes.number.isRequired, route: PropTypes.string, response: PropTypes.string.isRequired, }), language: PropTypes.string.isRequired, onClose: PropTypes.func.isRequired, }; export default VoiceTranscript;