Voice input
● Stable · v0.1Speak instead of type. A mic button that records, shows a live waveform while it listens, then a transcript once it's understood — built for an owner who'd rather say "twelve thousand kyat from Daw Hla" than tap it out.
In context
Try it
Tap the mic, watch the waveform and timer, then stop — it runs the record → transcribe → result cycle.
Every state
The flow has real moments — listening, thinking, done — and each one is shown plainly so the owner is never unsure whether it's still recording.
Purpose
Voice input lowers the cost of capture for a low-literacy owner. Typing an amount and a name is friction; saying it is not. It's the most inclusive way to get a sale into DayTwo — so it gets a big, obvious mic and unambiguous states.
Anatomy
- Mic button — a large (72dp) circular target. The single most important control on the field.
- Waveform + timer — while recording, jittering bars and a running
m:ssprove it's listening; a stop button ends it. - Processing — a spinner with "Transcribing…" so the wait is accounted for.
- Transcript — the recognised text in a quiet panel, with Record again to redo it.
- Recovery — an error state with retry, and a distinct permission-denied state that explains how to turn the mic on.
Behaviour
Controlled by status: your app drives idle → recording → processing → done
(or error / denied) and supplies duration, transcript, and error. The
component fires onStart, onStop, onReset, and onRetry. Capturing audio
and transcribing it is the app's job (e.g. expo-av plus a speech-to-text
service) — this component is the interface around it. All on-screen words can be
overridden for Burmese/Thai.
Code
import { VoiceInput } from '@daytwo/components'const [status, setStatus] = useState('idle')const [duration, setDuration] = useState(0)<VoiceInputlabel="Record the sale"helperText="Say the amount and the customer's name"status={status}duration={duration}maxDuration={60}transcript={text}onStart={beginRecording} // start expo-av + the timeronStop={stopAndTranscribe} // -> 'processing' -> 'done'onReset={() => setStatus('idle')}onRetry={beginRecording}/>
status is 'idle' | 'recording' | 'processing' | 'done' | 'error' | 'denied'.
When to use
- To capture a short spoken value — an amount, a customer name, a quick note.
- When the user may struggle to type, or has their hands full at the stall.
- As a faster alternative beside a text Input, not its only replacement.
When NOT to use
- For long-form or precise text where the owner must see and edit every character — pair it with an Input.
- For exact numbers that must be confirmed — always show the transcript so it can be corrected before saving.
- Where there's no transcription backend — without it, this is just an audio recorder.
Anti-patterns
Voice is only trustworthy if the owner can see what was heard. Show the transcript and let them re-record — never save a spoken amount blind.