feat(dictation): pulse three dots in the pill while spoken words are still on their way (#40443)

* feat(dictation): show pending dots while the engine holds speech it hasn't committed

The VAD pause segmenter already knows when a phrase is open. The recognizer
now posts speech-pending when a phrase opens and clears it once that phrase's
text is posted, and the desktop pill shows three dots after the text while it
is on. Only pause-cut on-device streams send it, so cloud, the paired web
client and phones keep today's pill.

* fix(speech): keep a lone click from turning the pending dots on
This commit is contained in:
Kelvin Amoaba
2026-11-09 20:14:48 +00:00
committed by GitHub
parent 4783b08ae5
commit 98bc1acf16
15 changed files with 138 additions and 22 deletions
+2
View File
@@ -7,6 +7,7 @@ import type {
SpeechLifecycleEvent,
SpeechModelManifest,
SpeechModelState,
SpeechPendingEvent,
SpeechTranscriptEvent
} from '../../shared/speech-types'
@@ -39,6 +40,7 @@ export type SpeechApi = {
cancelDictation: (sessionId: string) => Promise<void>
onPartialTranscript: (callback: (data: SpeechTranscriptEvent) => void) => () => void
onFinalTranscript: (callback: (data: SpeechTranscriptEvent) => void) => () => void
onSpeechPending: (callback: (data: SpeechPendingEvent) => void) => () => void
onDownloadProgress: (
callback: (data: { modelId: string; progress: number }) => void
) => () => void
+7
View File
@@ -9,6 +9,7 @@ import type {
SpeechLifecycleEvent,
SpeechModelManifest,
SpeechModelState,
SpeechPendingEvent,
SpeechTranscriptEvent
} from '../../shared/speech-types'
import type { PreloadApi } from '../api-types'
@@ -111,6 +112,12 @@ export const speechApi = {
ipcRenderer.on('speech:final', listener)
return () => ipcRenderer.removeListener('speech:final', listener)
},
onSpeechPending: (callback: (data: SpeechPendingEvent) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: SpeechPendingEvent): void =>
callback(data)
ipcRenderer.on('speech:pending', listener)
return () => ipcRenderer.removeListener('speech:pending', listener)
},
onDownloadProgress: (
callback: (data: { modelId: string; progress: number }) => void
): (() => void) => {