Spaces:
Sleeping
Sleeping
| "use client"; | |
| import { ReactNode, useState } from "react"; | |
| type RecognitionInstance = { | |
| continuous: boolean; | |
| interimResults: boolean; | |
| lang: string; | |
| onresult: ((event: any) => void) | null; | |
| onerror: ((event: any) => void) | null; | |
| onend: (() => void) | null; | |
| start: () => void; | |
| stop: () => void; | |
| }; | |
| type RecognitionConstructor = new () => RecognitionInstance; | |
| declare global { | |
| interface Window { | |
| SpeechRecognition?: RecognitionConstructor; | |
| webkitSpeechRecognition?: RecognitionConstructor; | |
| } | |
| } | |
| type VoiceInputProps = { | |
| onTranscript: (text: string) => void; | |
| disabled?: boolean; | |
| className?: string; | |
| idleLabel?: string; | |
| listeningLabel?: string; | |
| title?: string; | |
| children?: ReactNode; | |
| listeningContent?: ReactNode; | |
| }; | |
| export function VoiceInput({ | |
| onTranscript, | |
| disabled = false, | |
| className, | |
| idleLabel = "Voice", | |
| listeningLabel = "Listening...", | |
| title, | |
| children, | |
| listeningContent | |
| }: VoiceInputProps) { | |
| const [isListening, setIsListening] = useState(false); | |
| const toggleListen = () => { | |
| const Constructor = window.SpeechRecognition ?? window.webkitSpeechRecognition; | |
| if (!Constructor) { | |
| alert("Speech recognition is not supported in this browser."); | |
| return; | |
| } | |
| const recognition = new Constructor(); | |
| recognition.lang = "en-US"; | |
| recognition.continuous = false; | |
| recognition.interimResults = false; | |
| recognition.onresult = (event) => { | |
| const transcript = event?.results?.[0]?.[0]?.transcript; | |
| if (typeof transcript === "string" && transcript.trim()) { | |
| onTranscript(transcript.trim()); | |
| } | |
| }; | |
| recognition.onerror = () => { | |
| setIsListening(false); | |
| }; | |
| recognition.onend = () => { | |
| setIsListening(false); | |
| }; | |
| setIsListening(true); | |
| recognition.start(); | |
| }; | |
| return ( | |
| <button | |
| type="button" | |
| onClick={toggleListen} | |
| disabled={disabled || isListening} | |
| className={className} | |
| title={title} | |
| > | |
| {isListening | |
| ? (listeningContent ?? listeningLabel) | |
| : (children ?? idleLabel)} | |
| </button> | |
| ); | |
| } | |