Initial commit: OpenCode mobile app (extracted from opencode monorepo)
This commit is contained in:
80
src/lib/speech.ts
Normal file
80
src/lib/speech.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { useState, useCallback, useRef } from "react"
|
||||
import { ExpoSpeechRecognitionModule, useSpeechRecognitionEvent } from "expo-speech-recognition"
|
||||
|
||||
interface SpeechState {
|
||||
listening: boolean
|
||||
transcript: string
|
||||
error: string | null
|
||||
}
|
||||
|
||||
interface SpeechActions {
|
||||
start: () => Promise<void>
|
||||
stop: () => void
|
||||
cancel: () => void
|
||||
}
|
||||
|
||||
export function useSpeech(onResult: (text: string) => void): SpeechState & SpeechActions {
|
||||
const [listening, setListening] = useState(false)
|
||||
const [transcript, setTranscript] = useState("")
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const pending = useRef("")
|
||||
|
||||
useSpeechRecognitionEvent("start", () => {
|
||||
setListening(true)
|
||||
setError(null)
|
||||
setTranscript("")
|
||||
pending.current = ""
|
||||
})
|
||||
|
||||
useSpeechRecognitionEvent("end", () => {
|
||||
setListening(false)
|
||||
// Deliver final transcript
|
||||
if (pending.current.trim()) {
|
||||
onResult(pending.current.trim())
|
||||
}
|
||||
setTranscript("")
|
||||
pending.current = ""
|
||||
})
|
||||
|
||||
useSpeechRecognitionEvent("result", (event) => {
|
||||
const text = event.results[0]?.transcript || ""
|
||||
pending.current = text
|
||||
setTranscript(text)
|
||||
})
|
||||
|
||||
useSpeechRecognitionEvent("error", (event) => {
|
||||
// "no-speech" is not really an error — user just didn't say anything
|
||||
if (event.error === "no-speech") {
|
||||
setListening(false)
|
||||
return
|
||||
}
|
||||
setError(event.message || event.error)
|
||||
setListening(false)
|
||||
})
|
||||
|
||||
const start = useCallback(async () => {
|
||||
const result = await ExpoSpeechRecognitionModule.requestPermissionsAsync()
|
||||
if (!result.granted) {
|
||||
setError("Microphone permission denied")
|
||||
return
|
||||
}
|
||||
ExpoSpeechRecognitionModule.start({
|
||||
lang: "en-US",
|
||||
interimResults: true,
|
||||
continuous: true,
|
||||
})
|
||||
}, [])
|
||||
|
||||
const stop = useCallback(() => {
|
||||
ExpoSpeechRecognitionModule.stop()
|
||||
}, [])
|
||||
|
||||
const cancel = useCallback(() => {
|
||||
pending.current = ""
|
||||
ExpoSpeechRecognitionModule.abort()
|
||||
setListening(false)
|
||||
setTranscript("")
|
||||
}, [])
|
||||
|
||||
return { listening, transcript, error, start, stop, cancel }
|
||||
}
|
||||
Reference in New Issue
Block a user