mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 00:02:19 +00:00
The split silently dropped jira.searchUsers and runtimeEnvironments.retryControlConnection. Neither failed typecheck: the bridge modules carried no satisfies annotation and the composed api object was unannotated, so a missing key was only a runtime TypeError in the renderer. Annotates each module against PreloadApi, the type window.api is already declared as, so the contract supplies the shape rather than a parallel copy. Deleting jira.searchUsers now fails with TS2741 naming the key. Turning this on surfaced 106 places where a bridge locally annotated Promise<unknown> or unknown[] over a contract that declares concrete types -- the bridge was erasing types the renderer relied on. Those annotations are gone. Also exposes app.awaitBeforeUnloadCheckpoint, which was declared and called but never actually on the bridge, so the lazy-chunk recovery reload optional-chained to a no-op and navigated without joining the checkpoint. The missing key was caught by the new annotation rather than by hand.
83 lines
3.8 KiB
TypeScript
83 lines
3.8 KiB
TypeScript
import { ipcRenderer } from 'electron'
|
|
import type {
|
|
SpeechErrorEvent,
|
|
SpeechLifecycleEvent,
|
|
SpeechModelManifest,
|
|
SpeechModelState,
|
|
SpeechTranscriptEvent
|
|
} from '../../shared/speech-types'
|
|
import type { PreloadApi } from '../api-types'
|
|
|
|
export const speechApi = {
|
|
getCatalog: (): Promise<SpeechModelManifest[]> => ipcRenderer.invoke('speech:getCatalog'),
|
|
getModelStates: (): Promise<SpeechModelState[]> => ipcRenderer.invoke('speech:getModelStates'),
|
|
getOpenAiApiKeyStatus: (): Promise<{ configured: boolean }> =>
|
|
ipcRenderer.invoke('speech:getOpenAiApiKeyStatus'),
|
|
saveOpenAiApiKey: (apiKey: string): Promise<{ configured: boolean }> =>
|
|
ipcRenderer.invoke('speech:saveOpenAiApiKey', apiKey),
|
|
clearOpenAiApiKey: (): Promise<{ configured: boolean }> =>
|
|
ipcRenderer.invoke('speech:clearOpenAiApiKey'),
|
|
downloadModel: (modelId: string): Promise<void> =>
|
|
ipcRenderer.invoke('speech:downloadModel', modelId),
|
|
cancelDownload: (modelId: string): Promise<void> =>
|
|
ipcRenderer.invoke('speech:cancelDownload', modelId),
|
|
deleteModel: (modelId: string): Promise<void> =>
|
|
ipcRenderer.invoke('speech:deleteModel', modelId),
|
|
startDictation: (
|
|
modelId: string,
|
|
hotwords: string[] | undefined,
|
|
sessionId: string
|
|
): Promise<void> => ipcRenderer.invoke('speech:startDictation', modelId, hotwords, sessionId),
|
|
feedAudio: (samples: Float32Array, sampleRate: number, sessionId = 'desktop'): Promise<void> =>
|
|
// Why: Float32Array is zeroed crossing the contextBridge/IPC boundary; wrap in a Buffer to preserve bytes.
|
|
ipcRenderer.invoke(
|
|
'speech:feedAudio',
|
|
Buffer.from(samples.buffer, samples.byteOffset, samples.byteLength),
|
|
sampleRate,
|
|
sessionId
|
|
),
|
|
stopDictation: (sessionId = 'desktop'): Promise<void> =>
|
|
ipcRenderer.invoke('speech:stopDictation', sessionId),
|
|
|
|
onPartialTranscript: (callback: (data: SpeechTranscriptEvent) => void): (() => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: SpeechTranscriptEvent): void =>
|
|
callback(data)
|
|
ipcRenderer.on('speech:partial', listener)
|
|
return () => ipcRenderer.removeListener('speech:partial', listener)
|
|
},
|
|
onFinalTranscript: (callback: (data: SpeechTranscriptEvent) => void): (() => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: SpeechTranscriptEvent): void =>
|
|
callback(data)
|
|
ipcRenderer.on('speech:final', listener)
|
|
return () => ipcRenderer.removeListener('speech:final', listener)
|
|
},
|
|
onDownloadProgress: (
|
|
callback: (data: { modelId: string; progress: number }) => void
|
|
): (() => void) => {
|
|
const listener = (
|
|
_event: Electron.IpcRendererEvent,
|
|
data: { modelId: string; progress: number }
|
|
): void => callback(data)
|
|
ipcRenderer.on('speech:downloadProgress', listener)
|
|
return () => ipcRenderer.removeListener('speech:downloadProgress', listener)
|
|
},
|
|
onReady: (callback: (data: SpeechLifecycleEvent) => void): (() => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: SpeechLifecycleEvent): void =>
|
|
callback(data)
|
|
ipcRenderer.on('speech:ready', listener)
|
|
return () => ipcRenderer.removeListener('speech:ready', listener)
|
|
},
|
|
onStopped: (callback: (data: SpeechLifecycleEvent) => void): (() => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: SpeechLifecycleEvent): void =>
|
|
callback(data)
|
|
ipcRenderer.on('speech:stopped', listener)
|
|
return () => ipcRenderer.removeListener('speech:stopped', listener)
|
|
},
|
|
onError: (callback: (data: SpeechErrorEvent) => void): (() => void) => {
|
|
const listener = (_event: Electron.IpcRendererEvent, data: SpeechErrorEvent): void =>
|
|
callback(data)
|
|
ipcRenderer.on('speech:error', listener)
|
|
return () => ipcRenderer.removeListener('speech:error', listener)
|
|
}
|
|
} satisfies PreloadApi['speech']
|