diff --git a/electron/ipc/register/project.ts b/electron/ipc/register/project.ts index af6150ff..a65fd907 100644 --- a/electron/ipc/register/project.ts +++ b/electron/ipc/register/project.ts @@ -20,6 +20,7 @@ import { persistRecordingsDirectorySetting, rememberRecentProject, replaceApprovedSessionLocalReadPaths, + rememberApprovedLocalReadPath, resolveApprovedLocalMediaPath, saveProjectThumbnail, saveRecentProjectPaths, @@ -581,10 +582,8 @@ export function registerProjectHandlers() { timeOffsetMs: normalizeRecordingTimeOffsetMs(session.timeOffsetMs), hideOverlayCursorByDefault: normalizeBoolean(session.hideOverlayCursorByDefault), }); - await replaceApprovedSessionLocalReadPaths([ - currentRecordingSession!.videoPath, - currentRecordingSession!.webcamPath, - ]) + await rememberApprovedLocalReadPath(currentRecordingSession!.videoPath) + await rememberApprovedLocalReadPath(currentRecordingSession!.webcamPath) if (!options?.preserveProjectPath) { setCurrentProjectPath(null) } diff --git a/electron/main.ts b/electron/main.ts index a4836f74..20b12c81 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -283,9 +283,14 @@ function focusOrCreateMainWindow() { return; } - if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); - return; + if (!mainWindow || mainWindow.isDestroyed()) { + const existingHud = getHudOverlayWindow(); + if (existingHud && !existingHud.isDestroyed()) { + mainWindow = existingHud; + } else { + createWindow(); + return; + } } if (mainWindow && !mainWindow.isDestroyed()) { @@ -793,7 +798,15 @@ function createEditorWindowWrapper() { const previousWindow = mainWindow; if (previousWindow && !previousWindow.isDestroyed()) { const closingEditorWindow = isEditorWindow(previousWindow); - closeEditorWindowBypassingUnsavedPrompt(previousWindow); + + if (closingEditorWindow) { + closeEditorWindowBypassingUnsavedPrompt(previousWindow); + } else { + // It's the HUD or another window. Hide it instead of closing so background + // tasks (like webcam finalizing) can finish in its renderer process. + previousWindow.hide(); + } + if (!closingEditorWindow) { isForceClosing = false; } @@ -923,7 +936,21 @@ app.whenReady().then(async () => { } ipcMain.on("hud-overlay-close", () => { - app.quit(); + const hud = getHudOverlayWindow(); + if (hud) { + console.log("[main] Closing HUD window via hud-overlay-close"); + hud.close(); + } + + // If this was the last window (or we are in a state where we should quit), do it. + // We use a small delay to allow window.close() to propagate. + setTimeout(() => { + const windows = BrowserWindow.getAllWindows().filter((w) => !w.isDestroyed()); + if (windows.length === 0) { + console.log("[main] No windows left, quitting app"); + app.quit(); + } + }, 100); }); syncDockIcon(); createTray(); diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index dbec5865..fd3a3141 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -2505,6 +2505,13 @@ export default function VideoEditor() { } return window.electronAPI.onRecordingSessionChanged((session) => { + console.log("[VideoEditor] onRecordingSessionChanged received!", { + sessionVideoPath: session?.videoPath, + videoSourcePath: videoSourcePath, + match: session?.videoPath === videoSourcePath, + webcamPath: session?.webcamPath + }); + if (!session || session.videoPath !== videoSourcePath) { return; } diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts index 2fda1611..6f021761 100644 --- a/src/hooks/useScreenRecorder.ts +++ b/src/hooks/useScreenRecorder.ts @@ -867,6 +867,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn { const webcamPath = await stopWebcamRecorder(); await storeMicrophoneSidecar(resolvedMicFallbackBlobPromise, result.path, startDelayMs); await finalizeRecordingSession(result.path, webcamPath); + + if (typeof window.electronAPI?.hudOverlayClose === "function") { + window.electronAPI.hudOverlayClose(); + } + return result.path; }, [ @@ -1073,6 +1078,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { try { // Await the webcam path in the background const webcamPath = await webcamPathPromise; + console.log("[useScreenRecorder] Background native processing: webcamPath is", webcamPath); // Store sidecars await storeMicrophoneSidecar( @@ -1087,6 +1093,8 @@ export function useScreenRecorder(): UseScreenRecorderReturn { await window.electronAPI.muxNativeWindowsRecording(expectedDurationMs); } + console.log("[useScreenRecorder] Emitting setCurrentRecordingSession with:", { finalPath, webcamPath }); + // Update the session state to notify the editor that all background assets (webcam, mic, etc.) are now ready. // This broadcasts a 'recording-session-changed' event that the open editor listens to for re-scanning assets. await window.electronAPI.setCurrentRecordingSession({ @@ -1101,6 +1109,13 @@ export function useScreenRecorder(): UseScreenRecorderReturn { ); } catch (bgError) { console.error("Error in background finalization:", bgError); + } finally { + // After all background tasks are done (webcam, mic sidecars, muxing), + // we can safely close the HUD window to release hardware and resources. + if (typeof window.electronAPI?.hudOverlayClose === "function") { + console.log("[useScreenRecorder] All background tasks finished, closing HUD"); + window.electronAPI.hudOverlayClose(); + } } })(); })(); @@ -1749,13 +1764,22 @@ export function useScreenRecorder(): UseScreenRecorderReturn { ? await pendingWebcamPathPromise.current : resolvedWebcamPath.current; - if (webcamPath) { - await window.electronAPI.setCurrentRecordingSession({ - videoPath: finalVideoPath, - webcamPath, - timeOffsetMs: webcamTimeOffsetMs.current, - hideOverlayCursorByDefault: hideEditorOverlayCursorByDefault.current, - }); + try { + if (webcamPath) { + await window.electronAPI.setCurrentRecordingSession({ + videoPath: finalVideoPath, + webcamPath, + timeOffsetMs: webcamTimeOffsetMs.current, + hideOverlayCursorByDefault: hideEditorOverlayCursorByDefault.current, + }); + } + } finally { + // After all background tasks are done (webcam), + // we can safely close the HUD window to release hardware and resources. + if (typeof window.electronAPI?.hudOverlayClose === "function") { + console.log("[useScreenRecorder:browser] All background tasks finished, closing HUD"); + window.electronAPI.hudOverlayClose(); + } } })(); } else {