Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions electron/electron-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,10 @@ interface Window {
microphoneEnabled: boolean;
microphoneDeviceId?: string;
systemAudioEnabled: boolean;
webcamEnabled?: boolean;
webcamDeviceId?: string;
selectedSourceId?: string;
selectedSourceName?: string;
}>;
getRecordingAudioLabConfig: () => Promise<{
browserMicrophoneProfile: string;
Expand All @@ -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 }>;
Expand Down
17 changes: 15 additions & 2 deletions electron/ipc/register/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,30 @@ 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,
}
Comment on lines +150 to 154
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return full selected-source metadata from get-recording-preferences.

This handler currently restores only selectedSourceId/selectedSourceName, but the renderer restore flow also consumes selectedSourceDisplayId, selectedSourceThumbnail, selectedSourceAppIcon, and selectedSourceType. Missing these forces fallback values during startup restore.

Proposed fix
         return {
           success: true,
           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,
+          selectedSourceDisplayId:
+            typeof parsed.selectedSourceDisplayId === 'string' ? parsed.selectedSourceDisplayId : undefined,
+          selectedSourceThumbnail:
+            typeof parsed.selectedSourceThumbnail === 'string' || parsed.selectedSourceThumbnail === null
+              ? parsed.selectedSourceThumbnail
+              : undefined,
+          selectedSourceAppIcon:
+            typeof parsed.selectedSourceAppIcon === 'string' || parsed.selectedSourceAppIcon === null
+              ? parsed.selectedSourceAppIcon
+              : undefined,
+          selectedSourceType:
+            parsed.selectedSourceType === 'screen' || parsed.selectedSourceType === 'window'
+              ? parsed.selectedSourceType
+              : undefined,
         }
       } catch {
         return {
           success: true,
           microphoneEnabled: false,
           microphoneDeviceId: undefined,
           systemAudioEnabled: false,
           webcamEnabled: false,
           webcamDeviceId: undefined,
           selectedSourceId: undefined,
           selectedSourceName: undefined,
+          selectedSourceDisplayId: undefined,
+          selectedSourceThumbnail: undefined,
+          selectedSourceAppIcon: undefined,
+          selectedSourceType: undefined,
         }
       }

Also applies to: 161-165

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/ipc/register/settings.ts` around lines 150 - 154, The
get-recording-preferences IPC handler currently only returns
selectedSourceId/selectedSourceName; update its return object (in the block that
builds the parsed preferences) to also include selectedSourceDisplayId,
selectedSourceThumbnail, selectedSourceAppIcon, and selectedSourceType so the
renderer receives full selected-source metadata; perform the same typeof/string
checks as used for selectedSourceId/selectedSourceName (e.g., typeof
parsed.selectedSourceDisplayId === 'string' ? parsed.selectedSourceDisplayId :
undefined) and apply the identical changes to the second similar block around
lines 161-165 so both handlers return the complete set of fields.

} 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,
}
}
})

ipcMain.handle('get-recording-audio-lab-config', () => {
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 }) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Align set-recording-preferences handler typing with the expanded contract.

The prefs type omits selectedSourceDisplayId, selectedSourceThumbnail, selectedSourceAppIcon, and selectedSourceType, which are now part of the renderer API contract.

Proposed fix
-    ipcMain.handle('set-recording-preferences', async (_, prefs: { microphoneEnabled?: boolean; microphoneDeviceId?: string; systemAudioEnabled?: boolean; webcamEnabled?: boolean; webcamDeviceId?: string; selectedSourceId?: string; selectedSourceName?: string }) => {
+    ipcMain.handle('set-recording-preferences', async (_, prefs: {
+      microphoneEnabled?: boolean;
+      microphoneDeviceId?: string;
+      systemAudioEnabled?: boolean;
+      webcamEnabled?: boolean;
+      webcamDeviceId?: string;
+      selectedSourceId?: string;
+      selectedSourceName?: string;
+      selectedSourceDisplayId?: string;
+      selectedSourceThumbnail?: string | null;
+      selectedSourceAppIcon?: string | null;
+      selectedSourceType?: "screen" | "window";
+    }) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ipcMain.handle('set-recording-preferences', async (_, prefs: { microphoneEnabled?: boolean; microphoneDeviceId?: string; systemAudioEnabled?: boolean; webcamEnabled?: boolean; webcamDeviceId?: string; selectedSourceId?: string; selectedSourceName?: string }) => {
ipcMain.handle('set-recording-preferences', async (_, prefs: {
microphoneEnabled?: boolean;
microphoneDeviceId?: string;
systemAudioEnabled?: boolean;
webcamEnabled?: boolean;
webcamDeviceId?: string;
selectedSourceId?: string;
selectedSourceName?: string;
selectedSourceDisplayId?: string;
selectedSourceThumbnail?: string | null;
selectedSourceAppIcon?: string | null;
selectedSourceType?: "screen" | "window";
}) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/ipc/register/settings.ts` at line 173, The IPC handler
"set-recording-preferences" currently types the prefs parameter without the new
fields; update the prefs type in the ipcMain.handle callback to include
selectedSourceDisplayId, selectedSourceThumbnail, selectedSourceAppIcon, and
selectedSourceType (in addition to the existing microphoneEnabled,
microphoneDeviceId, systemAudioEnabled, webcamEnabled, webcamDeviceId,
selectedSourceId, selectedSourceName) so the handler signature matches the
expanded renderer API contract; locate the handler function named
"set-recording-preferences" and extend its prefs parameter type accordingly and
adjust any downstream usage within that handler to consume the new fields.

try {
let existing: Record<string, unknown> = {}
try {
Expand Down
4 changes: 4 additions & 0 deletions electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions src/components/launch/LaunchWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down
4 changes: 4 additions & 0 deletions src/components/launch/hooks/useLaunchWindowActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Comment on lines +12 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle rejected preference writes in source selection flow.

The persistence call is intentionally not awaited, but it should still be .catch(...)-handled to avoid unhandled rejections and silent loss of diagnostics.

Proposed fix
-		void window.electronAPI.setRecordingPreferences({
+		void window.electronAPI
+			.setRecordingPreferences({
 			selectedSourceId: source.id,
 			selectedSourceName: source.name,
 			selectedSourceDisplayId: source.display_id,
 			selectedSourceThumbnail: source.thumbnail,
 			selectedSourceAppIcon: source.appIcon,
 			selectedSourceType: source.sourceType,
-		});
+			})
+			.catch((error) => {
+				console.warn("Failed to persist selected source preferences:", error);
+			});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void window.electronAPI.setRecordingPreferences({
selectedSourceId: source.id,
selectedSourceName: source.name,
selectedSourceDisplayId: source.display_id,
selectedSourceThumbnail: source.thumbnail,
selectedSourceAppIcon: source.appIcon,
selectedSourceType: source.sourceType,
});
void window.electronAPI
.setRecordingPreferences({
selectedSourceId: source.id,
selectedSourceName: source.name,
selectedSourceDisplayId: source.display_id,
selectedSourceThumbnail: source.thumbnail,
selectedSourceAppIcon: source.appIcon,
selectedSourceType: source.sourceType,
})
.catch((error) => {
console.warn("Failed to persist selected source preferences:", error);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/launch/hooks/useLaunchWindowActions.ts` around lines 12 - 19,
The call to window.electronAPI.setRecordingPreferences in useLaunchWindowActions
is fire-and-forget and currently can produce unhandled promise rejections;
update the source selection flow to append a .catch(...) on the returned promise
from setRecordingPreferences (inside the function handling the source selection)
to log the error and any useful diagnostics (e.g., source.id/name) using the
existing logger or console.error so failures are observed but the UI remains
non-blocking.

setSelectedSource(source.name);
setHasSelectedSource(true);
window.electronAPI.showSourceHighlight?.({
Expand Down
39 changes: 37 additions & 2 deletions src/hooks/useScreenRecorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add rejection handling for new fire-and-forget IPC calls.

These new non-awaited calls can reject without diagnostics. Please attach .catch(...) handlers.

Proposed fix
-							void window.electronAPI.selectSource(match);
+							void window.electronAPI.selectSource(match).catch((error) => {
+								console.warn("Failed to restore persisted capture source:", {
+									error,
+									sourceId: match.id,
+								});
+							});

 const persistWebcamEnabled = useCallback((enabled: boolean) => {
 	setWebcamEnabled(enabled);
-	void window.electronAPI.setRecordingPreferences({ webcamEnabled: enabled });
+	void window.electronAPI
+		.setRecordingPreferences({ webcamEnabled: enabled })
+		.catch((error) => {
+			console.warn("Failed to persist webcamEnabled preference:", error);
+		});
 }, []);

 const persistWebcamDeviceId = useCallback((deviceId: string | undefined) => {
 	setWebcamDeviceId(deviceId);
-	void window.electronAPI.setRecordingPreferences({ webcamDeviceId: deviceId });
+	void window.electronAPI
+		.setRecordingPreferences({ webcamDeviceId: deviceId })
+		.catch((error) => {
+			console.warn("Failed to persist webcamDeviceId preference:", error);
+		});
 }, []);

Also applies to: 1245-1253

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useScreenRecorder.ts` at line 1220, The fire-and-forget IPC calls
using window.electronAPI.selectSource (and the other non-awaited electronAPI
calls around lines 1245-1253) lack rejection handling; update each invocation to
append a .catch(...) that logs the error (e.g., via console.error or the
existing logger) and includes contextual info (function name/selected source) so
rejections surface. Locate usages of window.electronAPI.selectSource and the
similar electronAPI calls and add .catch(err => /* log with context */) to each
call.

}
} catch {
// Source enumeration failed — skip restore silently.
}
}
}
})();
}, []);
Expand All @@ -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;

Expand Down Expand Up @@ -2009,9 +2044,9 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
systemAudioEnabled,
setSystemAudioEnabled: persistSystemAudioEnabled,
webcamEnabled,
setWebcamEnabled,
setWebcamEnabled: persistWebcamEnabled,
webcamDeviceId,
setWebcamDeviceId,
setWebcamDeviceId: persistWebcamDeviceId,
countdownDelay,
setCountdownDelay,
};
Expand Down