Skip to content
kerfjsGitHub

SpeechInput

Dictate a message: pick a microphone, speak, and insert what was heard.

SourcePackage @kerf-js/veneer

Preview

tsx
import {  Prompt,  SpeechInput,  Stack} from '@kerf-js/veneer'import type {  PromptControl,  SpeechInputProps} from '@kerf-js/veneer'export function Dictate() {  let prompt: PromptControl | undefined  return (    <Stack gap="group">      <SpeechInput        mics={MICS}        engine={fakeEngine}        onInsert={(text) => prompt?.insert(text)}      />      <Prompt        tools={[]}        control={(c) => (prompt = c)}        placeholder="Your dictation lands here"      />    </Stack>  )}const MICS = [  { value: 'mbp', label: 'MacBook Pro Microphone' },  { value: 'pods', label: 'AirPods Pro' },]const WORDS = [  'Roll back',  'Roll back mobile-bff',  'Roll back mobile-bff in ap-south',]const fakeEngine = (  on: Parameters<    NonNullable<SpeechInputProps['engine']>  >[0],) => {  let timer: ReturnType<typeof setInterval>  return {    start() {      let i = 0      on.onState('listening')      timer = setInterval(() => {        if (i < WORDS.length) {          on.onTranscript(            i ? WORDS[i - 1]! : '',            WORDS[i]!.slice(i ? WORDS[i - 1]!.length : 0),          )          on.onLevel(0.3 + (i % 2) * 0.4)          i++        } else {          on.onTranscript(WORDS[WORDS.length - 1]!, '')          clearInterval(timer)          on.onState('idle')        }      }, 800)    },    stop() {      clearInterval(timer)      on.onState('idle')    },  }}

Installation

npm install @kerf-js/veneer

Usage

tsx
import { SpeechInput } from '@kerf-js/veneer'

With words waiting

Grey words might still change.

tsx
import { SpeechInput } from '@kerf-js/veneer'import type { SpeechInputProps } from '@kerf-js/veneer'export function Transcript() {  return (  <SpeechInput    mics={MICS}    engine={fakeEngine}    initial={{      final: 'Show me the failing',      interim: 'check',    }}  />  )}const MICS = [  { value: 'mbp', label: 'MacBook Pro Microphone' },  { value: 'pods', label: 'AirPods Pro' },]const WORDS = [  'Roll back',  'Roll back mobile-bff',  'Roll back mobile-bff in ap-south',]const fakeEngine = (  on: Parameters<    NonNullable<SpeechInputProps['engine']>  >[0],) => {  let timer: ReturnType<typeof setInterval>  return {    start() {      let i = 0      on.onState('listening')      timer = setInterval(() => {        if (i < WORDS.length) {          on.onTranscript(            i ? WORDS[i - 1]! : '',            WORDS[i]!.slice(i ? WORDS[i - 1]!.length : 0),          )          on.onLevel(0.3 + (i % 2) * 0.4)          i++        } else {          on.onTranscript(WORDS[WORDS.length - 1]!, '')          clearInterval(timer)          on.onState('idle')        }      }, 800)    },    stop() {      clearInterval(timer)      on.onState('idle')    },  }}

Accessibility

  • Nothing is sent until Insert. A blocked microphone shows how to fix it instead of a dead button.

API Reference

PropTypeDefaultDescription
mics{ value: string; label: string; }[]—The microphones on offer.
engine((on: DictationEvents) => DictationEngine)—Replaces the browser's speech recognition.
initial{ state?: DictationState | undefined; final?: string | undefined; interi…—The state and words to start from.
EventTypeDefaultDescription
onInsert((text: string) => void)—Gets the transcript, usually for the Prompt's insert().

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="speech"] [data-part="mic"]

miclevelstripwavetimetranscriptinterim

RelatedPrompt