diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts index bda33cdf..252ab510 100644 --- a/electron/electron-env.d.ts +++ b/electron/electron-env.d.ts @@ -400,16 +400,28 @@ interface Window { }>; setCurrentVideoPath: ( path: string, - options?: { preserveProjectPath?: boolean }, + options?: { + preserveProjectPath?: boolean; + hideOverlayCursorByDefault?: boolean; + }, ) => Promise<{ success: boolean; webcamPath: string | null }>; - setCurrentRecordingSession: (session: { - videoPath: string; - webcamPath?: string | null; - timeOffsetMs?: number; - }, options?: { preserveProjectPath?: boolean }) => Promise<{ success: boolean }>; + setCurrentRecordingSession: ( + session: { + videoPath: string; + webcamPath?: string | null; + timeOffsetMs?: number; + hideOverlayCursorByDefault?: boolean; + }, + options?: { preserveProjectPath?: boolean }, + ) => Promise<{ success: boolean }>; getCurrentRecordingSession: () => Promise<{ success: boolean; - session?: { videoPath: string; webcamPath?: string | null; timeOffsetMs?: number }; + session?: { + videoPath: string; + webcamPath?: string | null; + timeOffsetMs?: number; + hideOverlayCursorByDefault?: boolean; + }; }>; getCurrentVideoPath: () => Promise<{ success: boolean; path?: string }>; clearCurrentVideoPath: () => Promise<{ success: boolean }>; diff --git a/electron/ipc/register/project.ts b/electron/ipc/register/project.ts index 570d7b58..a18d263d 100644 --- a/electron/ipc/register/project.ts +++ b/electron/ipc/register/project.ts @@ -45,6 +45,10 @@ function normalizeRecordingTimeOffsetMs(value: unknown): number { return typeof value === "number" && Number.isFinite(value) ? Math.round(value) : 0; } +function normalizeBoolean(value: unknown, fallback = false): boolean { + return typeof value === "boolean" ? value : fallback; +} + /** * Produces a filesystem-safe project base name without the project extension. */ @@ -527,7 +531,7 @@ export function registerProjectHandlers() { return { success: false, error: String(error), message: 'Failed to open projects folder.' } } }) - ipcMain.handle('set-current-video-path', async (_, path: string, options?: { preserveProjectPath?: boolean }) => { + ipcMain.handle('set-current-video-path', async (_, path: string, options?: { preserveProjectPath?: boolean; hideOverlayCursorByDefault?: boolean }) => { setCurrentVideoPath(normalizeVideoSourcePath(path) ?? path) approveUserPath(currentVideoPath) const resolvedSession = await resolveRecordingSession(currentVideoPath) @@ -537,7 +541,12 @@ export function registerProjectHandlers() { timeOffsetMs: 0, } - setCurrentRecordingSession(resolvedSession) + setCurrentRecordingSession({ + ...resolvedSession, + hideOverlayCursorByDefault: + normalizeBoolean(options?.hideOverlayCursorByDefault) || + normalizeBoolean(resolvedSession.hideOverlayCursorByDefault), + }) await replaceApprovedSessionLocalReadPaths([ resolvedSession.videoPath, resolvedSession.webcamPath, @@ -553,13 +562,14 @@ export function registerProjectHandlers() { return { success: true, webcamPath: resolvedSession.webcamPath ?? null } }) - ipcMain.handle('set-current-recording-session', async (_, session: { videoPath: string; webcamPath?: string | null; timeOffsetMs?: number }, options?: { preserveProjectPath?: boolean }) => { + ipcMain.handle('set-current-recording-session', async (_, session: { videoPath: string; webcamPath?: string | null; timeOffsetMs?: number; hideOverlayCursorByDefault?: boolean }, options?: { preserveProjectPath?: boolean }) => { const normalizedVideoPath = normalizeVideoSourcePath(session.videoPath) ?? session.videoPath setCurrentVideoPath(normalizedVideoPath) setCurrentRecordingSession({ videoPath: normalizedVideoPath, webcamPath: normalizeVideoSourcePath(session.webcamPath ?? null), timeOffsetMs: normalizeRecordingTimeOffsetMs(session.timeOffsetMs), + hideOverlayCursorByDefault: normalizeBoolean(session.hideOverlayCursorByDefault), }); await replaceApprovedSessionLocalReadPaths([ currentRecordingSession!.videoPath, diff --git a/electron/ipc/types.ts b/electron/ipc/types.ts index 7c7de9c1..58f5425b 100644 --- a/electron/ipc/types.ts +++ b/electron/ipc/types.ts @@ -47,6 +47,7 @@ export type RecordingSessionData = { videoPath: string; webcamPath?: string | null; timeOffsetMs?: number; + hideOverlayCursorByDefault?: boolean; }; export type PauseSegment = { diff --git a/electron/preload.ts b/electron/preload.ts index e41acc26..d2e38d27 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -437,6 +437,7 @@ contextBridge.exposeInMainWorld("electronAPI", { videoPath: string; webcamPath?: string | null; timeOffsetMs?: number; + hideOverlayCursorByDefault?: boolean; }, options?: { preserveProjectPath?: boolean }) => { return ipcRenderer.invoke("set-current-recording-session", session, options); }, diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 2749de16..357ad2a5 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -2005,6 +2005,9 @@ export default function VideoEditor() { pendingFreshRecordingAutoZoomPathRef.current = autoApplyFreshRecordingAutoZooms ? sourceVideoUrl : null; + if (sessionResult.session.hideOverlayCursorByDefault) { + setShowCursor(false); + } setWebcam((prev) => ({ ...prev, enabled: Boolean(sessionResult.session?.webcamPath), diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts index 3709fa84..6b6f24a1 100644 --- a/src/hooks/useScreenRecorder.ts +++ b/src/hooks/useScreenRecorder.ts @@ -154,6 +154,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { const micFallbackRecorder = useRef(null); const micFallbackChunks = useRef([]); const micFallbackStartDelayMs = useRef(null); + const hideEditorOverlayCursorByDefault = useRef(false); const notifyRecordingFinalizationFailure = useCallback(async (message: string) => { setFinalizing(false); @@ -397,21 +398,27 @@ export function useScreenRecorder(): UseScreenRecorderReturn { const finalizeRecordingSession = useCallback( async (videoPath: string, webcamPath: string | null) => { + const shouldHideOverlayCursor = hideEditorOverlayCursorByDefault.current; try { if (webcamPath) { await window.electronAPI.setCurrentRecordingSession({ videoPath, webcamPath, timeOffsetMs: webcamTimeOffsetMs.current, + hideOverlayCursorByDefault: shouldHideOverlayCursor, }); } else { - await window.electronAPI.setCurrentVideoPath(videoPath); + await window.electronAPI.setCurrentVideoPath(videoPath, { + hideOverlayCursorByDefault: shouldHideOverlayCursor, + }); } } catch (error) { console.error("Failed to persist recording session metadata:", error); try { - await window.electronAPI.setCurrentVideoPath(videoPath); + await window.electronAPI.setCurrentVideoPath(videoPath, { + hideOverlayCursorByDefault: shouldHideOverlayCursor, + }); } catch (fallbackError) { console.error("Failed to persist fallback video path:", fallbackError); } @@ -908,6 +915,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { try { const platform = await window.electronAPI.getPlatform(); + hideEditorOverlayCursorByDefault.current = false; const existingSource = await window.electronAPI.getSelectedSource(); const selectedSource = existingSource ?? (platform === "linux" ? LINUX_PORTAL_SOURCE : null); @@ -1096,6 +1104,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { const wantsAudioCapture = microphoneEnabled || systemAudioEnabled; const browserCaptureSource = await resolveBrowserCaptureSource(selectedSource); + hideEditorOverlayCursorByDefault.current = true; if ( browserCaptureSource?.id?.startsWith("screen:fallback:") ||