import { contextBridge, ipcRenderer } from 'electron'; import { IPC, type AppInfo, type HermesPushEvent, type HotkeyEvent, type HttpProxyRequest, type HttpProxyResponse, type HttpStreamEvent, type HttpStreamStart, type LogEntry, type SystemMetrics, type WebhookStatus, type WindowMode } from '../shared/ipc'; import type { EveFlowBridge, Unsubscribe } from '../shared/bridge'; import { VOICE_IPC, type SynthesizeRequest, type SynthesizeResult, type TranscribeRequest, type TranscribeResult, type VoiceDownloadProgress, type VoiceEngineStatus, type VoiceModelStatus } from '../shared/voice'; function subscribe(channel: string, callback: (payload: T) => void): Unsubscribe { const listener = (_event: Electron.IpcRendererEvent, payload: T) => callback(payload); ipcRenderer.on(channel, listener); return () => ipcRenderer.removeListener(channel, listener); } const api: EveFlowBridge = { window: { control: (action: 'minimize' | 'maximize' | 'close' | 'hide') => ipcRenderer.send(IPC.windowControl, action), setMode: (mode: WindowMode) => ipcRenderer.send(IPC.windowSetMode, mode), onModeChanged: (cb: (mode: WindowMode) => void) => subscribe(IPC.windowModeChanged, cb), onVisibility: (cb: (visible: boolean) => void) => subscribe(IPC.windowVisibility, cb) }, log: (entry: LogEntry) => ipcRenderer.send(IPC.log, entry), store: { get: (key: string) => ipcRenderer.invoke(IPC.storeGet, key) as Promise, set: (key: string, value: unknown) => ipcRenderer.invoke(IPC.storeSet, key, value) as Promise, delete: (key: string) => ipcRenderer.invoke(IPC.storeDelete, key) as Promise }, http: { fetch: (req: HttpProxyRequest) => ipcRenderer.invoke(IPC.httpFetch, req) as Promise, streamStart: (id: string, req: HttpProxyRequest) => ipcRenderer.invoke(IPC.httpStreamStart, id, req) as Promise, streamAbort: (id: string) => ipcRenderer.send(IPC.httpStreamAbort, id), onStreamEvent: (cb: (event: HttpStreamEvent) => void) => subscribe(IPC.httpStreamEvent, cb) }, system: { metrics: () => ipcRenderer.invoke(IPC.metrics) as Promise, appInfo: () => ipcRenderer.invoke(IPC.appInfo) as Promise }, files: { readLocal: (filePath: string) => ipcRenderer.invoke(IPC.readLocalFile, filePath) as Promise, writeShared: (filename: string, content: string, isBase64 = false) => ipcRenderer.invoke(IPC.writeSharedFile, filename, content, isBase64) as Promise<{ path: string; url: string }>, openPath: (filePath: string) => ipcRenderer.invoke(IPC.openPath, filePath) as Promise, showInFolder: (filePath: string) => ipcRenderer.invoke(IPC.showInFolder, filePath) as Promise }, hermes: { onPush: (cb: (event: HermesPushEvent) => void) => subscribe(IPC.hermesPush, cb), webhookStatus: () => ipcRenderer.invoke(IPC.webhookStatus) as Promise, webhookRestart: () => ipcRenderer.invoke(IPC.webhookRestart) as Promise }, hotkeys: { on: (cb: (event: HotkeyEvent) => void) => subscribe(IPC.hotkey, cb) }, voice: { status: () => ipcRenderer.invoke(VOICE_IPC.status) as Promise, listModels: () => ipcRenderer.invoke(VOICE_IPC.modelsList) as Promise, downloadModel: (id: string) => ipcRenderer.invoke(VOICE_IPC.modelsDownload, id) as Promise, cancelDownload: (id: string) => ipcRenderer.invoke(VOICE_IPC.modelsCancel, id) as Promise, removeModel: (id: string) => ipcRenderer.invoke(VOICE_IPC.modelsRemove, id) as Promise, onProgress: (cb: (progress: VoiceDownloadProgress) => void) => subscribe(VOICE_IPC.modelsProgress, cb), transcribe: (req: TranscribeRequest) => ipcRenderer.invoke(VOICE_IPC.transcribe, req) as Promise, synthesize: (req: SynthesizeRequest) => ipcRenderer.invoke(VOICE_IPC.synthesize, req) as Promise, unload: (id?: string) => ipcRenderer.invoke(VOICE_IPC.unload, id) as Promise } }; contextBridge.exposeInMainWorld('eveflow', api);