diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts index f4c391e06..ad7ee11d0 100644 --- a/electron/electron-env.d.ts +++ b/electron/electron-env.d.ts @@ -826,6 +826,10 @@ interface Window { microphoneEnabled: boolean; microphoneDeviceId?: string; systemAudioEnabled: boolean; + webcamEnabled?: boolean; + webcamDeviceId?: string; + selectedSourceId?: string; + selectedSourceName?: string; }>; getRecordingAudioLabConfig: () => Promise<{ browserMicrophoneProfile: string; @@ -835,6 +839,10 @@ interface Window { microphoneEnabled?: boolean; microphoneDeviceId?: string; systemAudioEnabled?: boolean; + webcamEnabled?: boolean; + webcamDeviceId?: string; + selectedSourceId?: string; + selectedSourceName?: string; }) => Promise<{ success: boolean; error?: string }>; /** Countdown timer before recording */ getCountdownDelay: () => Promise<{ success: boolean; delay: number }>; diff --git a/electron/ipc/register/settings.ts b/electron/ipc/register/settings.ts index cecabb5f5..6e16c7041 100644 --- a/electron/ipc/register/settings.ts +++ b/electron/ipc/register/settings.ts @@ -147,9 +147,22 @@ export function registerSettingsHandlers() { microphoneEnabled: parsed.microphoneEnabled === true, microphoneDeviceId: typeof parsed.microphoneDeviceId === 'string' ? parsed.microphoneDeviceId : undefined, systemAudioEnabled: parsed.systemAudioEnabled === true, + webcamEnabled: parsed.webcamEnabled === true, + webcamDeviceId: typeof parsed.webcamDeviceId === 'string' ? parsed.webcamDeviceId : undefined, + selectedSourceId: typeof parsed.selectedSourceId === 'string' ? parsed.selectedSourceId : undefined, + selectedSourceName: typeof parsed.selectedSourceName === 'string' ? parsed.selectedSourceName : undefined, } } catch { - return { success: true, microphoneEnabled: false, microphoneDeviceId: undefined, systemAudioEnabled: false } + return { + success: true, + microphoneEnabled: false, + microphoneDeviceId: undefined, + systemAudioEnabled: false, + webcamEnabled: false, + webcamDeviceId: undefined, + selectedSourceId: undefined, + selectedSourceName: undefined, + } } }) @@ -157,7 +170,7 @@ export function registerSettingsHandlers() { return getBrowserMicrophoneProfileFromEnv() }) - ipcMain.handle('set-recording-preferences', async (_, prefs: { microphoneEnabled?: boolean; microphoneDeviceId?: string; systemAudioEnabled?: boolean }) => { + ipcMain.handle('set-recording-preferences', async (_, prefs: { microphoneEnabled?: boolean; microphoneDeviceId?: string; systemAudioEnabled?: boolean; webcamEnabled?: boolean; webcamDeviceId?: string; selectedSourceId?: string; selectedSourceName?: string }) => { try { let existing: Record = {} try { diff --git a/electron/preload.ts b/electron/preload.ts index 8ef19765e..f5486f500 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -907,6 +907,10 @@ contextBridge.exposeInMainWorld("electronAPI", { microphoneEnabled?: boolean; microphoneDeviceId?: string; systemAudioEnabled?: boolean; + webcamEnabled?: boolean; + webcamDeviceId?: string; + selectedSourceId?: string; + selectedSourceName?: string; }) => ipcRenderer.invoke("set-recording-preferences", prefs), getCountdownDelay: () => ipcRenderer.invoke("get-countdown-delay"), setCountdownDelay: (delay: number) => ipcRenderer.invoke("set-countdown-delay", delay), diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx index 49547069d..e67a81644 100644 --- a/src/components/launch/LaunchWindow.tsx +++ b/src/components/launch/LaunchWindow.tsx @@ -119,11 +119,11 @@ function LaunchWindowContent() { const supportsHudCaptureProtection = platform !== "linux"; useEffect(() => { - if (!selectedDeviceId) { + if (!selectedDeviceId || selectedDeviceId === "default") { return; } - setMicrophoneDeviceId(selectedDeviceId === "default" ? undefined : selectedDeviceId); + setMicrophoneDeviceId(selectedDeviceId); }, [selectedDeviceId, setMicrophoneDeviceId]); useEffect(() => { diff --git a/src/components/launch/hooks/useLaunchWindowActions.ts b/src/components/launch/hooks/useLaunchWindowActions.ts index 90acfe577..cdfb8e57e 100644 --- a/src/components/launch/hooks/useLaunchWindowActions.ts +++ b/src/components/launch/hooks/useLaunchWindowActions.ts @@ -9,6 +9,10 @@ export function useLaunchWindowActions() { const handleSourceSelect = useCallback(async (source: DesktopSource) => { await window.electronAPI.selectSource(source); + void window.electronAPI.setRecordingPreferences({ + selectedSourceId: source.id, + selectedSourceName: source.name, + }); setSelectedSource(source.name); setHasSelectedSource(true); window.electronAPI.showSourceHighlight?.({ diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts index 6f021761a..c718cef2a 100644 --- a/src/hooks/useScreenRecorder.ts +++ b/src/hooks/useScreenRecorder.ts @@ -1198,6 +1198,31 @@ export function useScreenRecorder(): UseScreenRecorderReturn { setMicrophoneDeviceId(result.microphoneDeviceId); } setSystemAudioEnabled(result.systemAudioEnabled); + if (result.webcamEnabled) { + setWebcamEnabled(true); + } + if (result.webcamDeviceId) { + setWebcamDeviceId(result.webcamDeviceId); + } + if (result.selectedSourceId && result.selectedSourceName) { + // Electron desktopCapturer ids are not stable across + // reboots / display changes. Only restore the source if it + // still exists in the current capture list, otherwise the + // app would silently capture a non-existent target. + try { + const available = await window.electronAPI.getSources({ + types: ["screen", "window"], + }); + const match = available.find( + (source) => source.id === result.selectedSourceId, + ); + if (match) { + void window.electronAPI.selectSource(match); + } + } catch { + // Source enumeration failed — skip restore silently. + } + } } })(); }, []); @@ -1217,6 +1242,16 @@ export function useScreenRecorder(): UseScreenRecorderReturn { void window.electronAPI.setRecordingPreferences({ systemAudioEnabled: enabled }); }, []); + const persistWebcamEnabled = useCallback((enabled: boolean) => { + setWebcamEnabled(enabled); + void window.electronAPI.setRecordingPreferences({ webcamEnabled: enabled }); + }, []); + + const persistWebcamDeviceId = useCallback((deviceId: string | undefined) => { + setWebcamDeviceId(deviceId); + void window.electronAPI.setRecordingPreferences({ webcamDeviceId: deviceId }); + }, []); + useEffect(() => { let cleanup: (() => void) | undefined; @@ -2009,9 +2044,9 @@ export function useScreenRecorder(): UseScreenRecorderReturn { systemAudioEnabled, setSystemAudioEnabled: persistSystemAudioEnabled, webcamEnabled, - setWebcamEnabled, + setWebcamEnabled: persistWebcamEnabled, webcamDeviceId, - setWebcamDeviceId, + setWebcamDeviceId: persistWebcamDeviceId, countdownDelay, setCountdownDelay, };