index.ts5.3 KBView on GitHub import { contextBridge, ipcRenderer } from 'electron'
import { IPC } from '@/main/ipc/channels'
const recordingBridge = {
meetingRecorder: {
// renderer → main (invoke)
startRecording: (windowId: string, uploadToken=[redacted], sdkUploadId?: string): Promise<void> =>
ipcRenderer.invoke(IPC.capture.start, windowId, uploadToken, sdkUploadId),
stopRecording: (): Promise<void> =>
ipcRenderer.invoke(IPC.capture.stop),
getStatus: (): Promise<{
isRecording: boolean
permissionStatus: 'granted' | 'denied' | 'not-requested'
detectedMeeting: { windowId: string; platform: string; title: string | null; url: string | null } | null
permissions: { microphone: string; screen: string; accessibility: boolean }
}> => ipcRenderer.invoke(IPC.capture.getStatus),
requestPermissions: (): Promise<void> =>
ipcRenderer.invoke(IPC.capture.requestPermissions),
getCalendarDebug: (): Promise<Record<string, unknown>> =>
ipcRenderer.invoke(IPC.capture.calendarDebug),
// main → renderer (listen)
onMeetingDetected: (cb: (data: { windowId: string; platform: string; title: string | null; url: string | null }) => void): void => {
ipcRenderer.on(IPC.capture.meetingDetected, (_event, data) => cb(data))
},
onMeetingEnded: (cb: () => void): void => {
ipcRenderer.on(IPC.capture.meetingEnded, () => cb())
},
onRecordingStatus: (cb: (data: { isRecording: boolean }) => void): void => {
ipcRenderer.on(IPC.capture.recordingStatus, (_event, data) => cb(data))
},
// main → renderer: main process asks renderer to get an upload token via tRPC
onPermissionsGranted: (cb: () => void): void => {
ipcRenderer.on(IPC.capture.permissionsGranted, () => cb())
},
onUploadTokenRequest: (cb: () => void): void => {
ipcRenderer.on(IPC.capture.requestUploadToken, () => cb())
},
// renderer → main: respond with the upload token and sdkUploadId
respondUploadToken=[redacted] { uploadToken=[redacted]; sdkUploadId: string } | { error: string }): void => {
ipcRenderer.invoke(IPC.capture.requestUploadToken, result)
},
// renderer → main: push upcoming calendar events with meeting info
pushUpcomingEvents: (events: Array<{
eventId: string; title: string; startTime: string; endTime: string;
meetingUrl: string | null; platform: string | null; calendarEmail: string | null
}>): void => {
ipcRenderer.send(IPC.calendar.upcomingEvents, events)
},
// main → renderer: listen for calendar poll requests
onPollRequest: (cb: () => void): void => {
ipcRenderer.on(IPC.calendar.requestPoll, () => cb())
},
startManualSession: (): Promise<{ sessionId: string }> =>
ipcRenderer.invoke(IPC.callUI.startManual),
},
callUI: {
// main → renderer: open/close the call UI blocking modal
onOpen: (cb: (data: { uploadId: string | null; title: string | null; eventId: string | null; meetingUrl: string | null }) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: Parameters<typeof cb>[0]) => cb(data)
ipcRenderer.on(IPC.callUI.openModal, listener)
return () => ipcRenderer.removeListener(IPC.callUI.openModal, listener)
},
onClose: (cb: () => void): (() => void) => {
const listener = () => cb()
ipcRenderer.on(IPC.callUI.closeModal, listener)
return () => ipcRenderer.removeListener(IPC.callUI.closeModal, listener)
},
// main → renderer: realtime data
onRealtimeEvent: (cb: (data: unknown) => void): void => {
ipcRenderer.on(IPC.callUI.realtimeEvent, (_event, data) => cb(data))
},
onFlagsUpdate: (cb: (flags: unknown) => void): void => {
ipcRenderer.on(IPC.callUI.flagsUpdate, (_event, flags) => cb(flags))
},
onRecordingState: (cb: (state: { isPaused: boolean; isRecording: boolean }) => void): void => {
ipcRenderer.on(IPC.callUI.recordingState, (_event, state) => cb(state))
},
onGlobalFlag: (cb: (type: string) => void): void => {
ipcRenderer.removeAllListeners(IPC.callUI.globalFlag)
ipcRenderer.on(IPC.callUI.globalFlag, (_event, type) => cb(type))
},
// renderer → main (invoke): recording controls
pause: (): Promise<void> => ipcRenderer.invoke(IPC.callUI.pause),
resume: (): Promise<void> => ipcRenderer.invoke(IPC.callUI.resume),
flagMoment: (type: string): Promise<void> => ipcRenderer.invoke(IPC.callUI.flagMoment, type),
openKB: (): Promise<void> => ipcRenderer.invoke(IPC.callUI.openKB),
stopRecording: (): Promise<void> => ipcRenderer.invoke(IPC.callUI.stopRecording),
},
}
contextBridge.exposeInMainWorld('electronAPI', {
platform: process.platform,
openExternal: (url: string): Promise<void> =>
ipcRenderer.invoke(IPC.shell.openExternal, url),
doubleClickTitleBar: (): void => {
ipcRenderer.invoke(IPC.window.doubleClickTitle)
},
notify: (title: string, body: string, tag?: string): void => {
ipcRenderer.invoke(IPC.app.notify, title, body, tag)
},
onDeepLink: (cb: (url: string) => void): void => {
ipcRenderer.on(IPC.protocol.deepLink, (_event, url: string) => cb(url))
},
onNavigate: (cb: (path: string) => void): void => {
ipcRenderer.on(IPC.window.navigate, (_event, path: string) => cb(path))
},
...(__RECORDING_ENABLED__ ? recordingBridge : {}),
})