Files
llink/js/src/preload.ts
T
talksik f91068a4c1 fix: freezes during longer screen recordings
Closes #131

Fundamentally, we cannot afford to composite webcam and screen on the
client. We should create two streams and then combine them with ffmpeg
processing in the backend / orion.
2026-04-09 08:31:23 -07:00

72 lines
3.3 KiB
TypeScript

// See the Electron documentation for details on how to use preload scripts:
// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('electronWindow', {
minimize: () => ipcRenderer.send('window:minimize'),
maximize: () => ipcRenderer.send('window:maximize'),
fullscreen: () => ipcRenderer.send('window:fullscreen'),
close: () => ipcRenderer.send('window:close'),
openHuddle: (data: { token: string; serverUrl: string }) => ipcRenderer.send('window:open-huddle', data),
closeHuddle: () => ipcRenderer.send('window:close-huddle'),
});
contextBridge.exposeInMainWorld('electronHuddle', {
onConnect: (callback: (data: { token: string; serverUrl: string }) => void) => {
const handler = (_event: Electron.IpcRendererEvent, data: { token: string; serverUrl: string }) => callback(data);
ipcRenderer.on('huddle:connect', handler);
return () => { ipcRenderer.removeListener('huddle:connect', handler); };
},
getScreenSources: () => ipcRenderer.invoke('screen:get-sources'),
});
contextBridge.exposeInMainWorld('electronAutoplay', {
play: (payload: unknown) => ipcRenderer.send('autoplay:play', payload),
dismiss: () => ipcRenderer.send('autoplay:dismiss'),
navigate: (data: { networkId: string; streamId: string }) => ipcRenderer.send('autoplay:navigate', data),
onPlay: (callback: (payload: unknown) => void) => {
const handler = (_event: Electron.IpcRendererEvent, payload: unknown) => callback(payload);
ipcRenderer.on('autoplay:play', handler);
return () => { ipcRenderer.removeListener('autoplay:play', handler); };
},
onStop: (callback: () => void) => {
const handler = () => callback();
ipcRenderer.on('autoplay:stop', handler);
return () => { ipcRenderer.removeListener('autoplay:stop', handler); };
},
onNavigate: (callback: (data: { networkId: string; streamId: string }) => void) => {
const handler = (_event: Electron.IpcRendererEvent, data: { networkId: string; streamId: string }) => callback(data);
ipcRenderer.on('autoplay:navigate', handler);
return () => { ipcRenderer.removeListener('autoplay:navigate', handler); };
},
});
contextBridge.exposeInMainWorld('electronScreen', {
getScreenSources: () => ipcRenderer.invoke('screen:get-sources'),
startRecordingWindow: () => ipcRenderer.send('screen-record:start'),
stopRecordingWindow: () => ipcRenderer.send('screen-record:cancel'),
onStopRequested: (callback: () => void) => {
const handler = () => callback();
ipcRenderer.on('screen-record:stopped', handler);
return () => { ipcRenderer.removeListener('screen-record:stopped', handler); };
},
});
contextBridge.exposeInMainWorld('electronScreenRecord', {
stop: () => ipcRenderer.send('screen-record:stop'),
onInit: (callback: () => void) => {
const handler = () => callback();
ipcRenderer.on('screen-record:init', handler);
return () => { ipcRenderer.removeListener('screen-record:init', handler); };
},
});
contextBridge.exposeInMainWorld('electronLink', {
fetchMetadata: (url: string) => ipcRenderer.invoke('link:fetch-metadata', url),
openExternal: (url: string) => ipcRenderer.invoke('link:open-external', url),
});
contextBridge.exposeInMainWorld('electronApp', {
setDockBadge: (count: number) => ipcRenderer.send('app:set-dock-badge', count),
});