Add streaming ASR partial transcripts and reset hosts to env default

- Stream partial transcripts to the client during dictation for live feedback
- Use OpenAI semantic VAD instead of manual commit intervals for better word boundaries
- Append silence tail on finish to flush final utterance through VAD
- Skip committing silence-only audio tails
- Reset hosts to env-defined daemon when env var is present, overriding stored state
This commit is contained in:
Mohamed Boudra
2026-01-24 19:01:14 +07:00
parent 9b82c02fd2
commit b498c9ea81
8 changed files with 290 additions and 79 deletions
@@ -8,6 +8,7 @@ import type { DictationStatus } from "@/hooks/use-dictation";
interface DictationControlsProps {
volume: number;
duration: number;
transcript?: string;
isRecording: boolean;
isProcessing: boolean;
status: DictationStatus;
@@ -131,6 +132,7 @@ export function DictationControls({
export function DictationOverlay({
volume,
duration,
transcript,
isRecording,
isProcessing,
status,
@@ -169,21 +171,34 @@ export function DictationOverlay({
</Pressable>
<View style={overlayStyles.centerContainer}>
<VolumeMeter
volume={volume}
isMuted={false}
isDetecting
isSpeaking={false}
orientation="horizontal"
/>
<Text
style={[
overlayStyles.timerText,
{ color: theme.colors.palette.white },
]}
>
{formatDuration(duration)}
</Text>
<View style={overlayStyles.meterRow}>
<VolumeMeter
volume={volume}
isMuted={false}
isDetecting
isSpeaking={false}
orientation="horizontal"
/>
<Text
style={[
overlayStyles.timerText,
{ color: theme.colors.palette.white },
]}
>
{formatDuration(duration)}
</Text>
</View>
{!!transcript && (
<Text
numberOfLines={2}
style={[
overlayStyles.transcriptText,
{ color: theme.colors.palette.white },
]}
>
{transcript}
</Text>
)}
</View>
<View style={overlayStyles.actionButtonsContainer}>
@@ -334,6 +349,12 @@ const overlayStyles = StyleSheet.create((theme) => ({
},
centerContainer: {
flex: 1,
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: theme.spacing[2],
},
meterRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
@@ -344,6 +365,13 @@ const overlayStyles = StyleSheet.create((theme) => ({
fontWeight: theme.fontWeight.semibold,
fontVariant: ["tabular-nums"],
},
transcriptText: {
fontSize: theme.fontSize.sm,
fontWeight: theme.fontWeight.normal,
textAlign: "center",
paddingHorizontal: theme.spacing[2],
opacity: 0.9,
},
actionButtonsContainer: {
flexDirection: "row",
alignItems: "center",
@@ -191,6 +191,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
const {
isRecording: isDictating,
isProcessing: isDictationProcessing,
partialTranscript: dictationPartialTranscript,
volume: dictationVolume,
duration: dictationDuration,
status: dictationStatus,
@@ -591,6 +592,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
<DictationOverlay
volume={dictationVolume}
duration={dictationDuration}
transcript={dictationPartialTranscript}
isRecording={isDictating}
isProcessing={isDictationProcessing}
status={dictationStatus}