diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts index 573aee8..b2a3720 100644 --- a/electron/electron-env.d.ts +++ b/electron/electron-env.d.ts @@ -26,6 +26,8 @@ interface Window { electronAPI: { getSources: (opts: Electron.SourcesOptions) => Promise; switchToEditor: () => Promise; + switchToHud: () => Promise; + startNewRecording: () => Promise<{ success: boolean; error?: string }>; openSourceSelector: () => Promise; selectSource: (source: ProcessedDesktopSource) => Promise; getSelectedSource: () => Promise; diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts index e43f53c..4cb4875 100644 --- a/electron/ipc/handlers.ts +++ b/electron/ipc/handlers.ts @@ -355,7 +355,24 @@ export function registerIpcHandlers( getMainWindow: () => BrowserWindow | null, getSourceSelectorWindow: () => BrowserWindow | null, onRecordingStateChange?: (recording: boolean, sourceName: string) => void, + switchToHud?: () => void, ) { + ipcMain.handle("switch-to-hud", () => { + if (switchToHud) switchToHud(); + }); + ipcMain.handle("start-new-recording", async () => { + try { + setCurrentRecordingSessionState(null); + if (switchToHud) { + switchToHud(); + } + return { success: true }; + } catch (error) { + console.error("Failed to start new recording:", error); + return { success: false, error: String(error) }; + } + }); + ipcMain.handle("get-sources", async (_, opts) => { const sources = await desktopCapturer.getSources(opts); return sources.map((source) => ({ diff --git a/electron/main.ts b/electron/main.ts index 7e19d46..0f06f9e 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -371,6 +371,16 @@ app.whenReady().then(async () => { // Ensure recordings directory exists await ensureRecordingsDir(); + function switchToHudWrapper() { + if (mainWindow) { + isForceClosing = true; + mainWindow.close(); + isForceClosing = false; + mainWindow = null; + } + showMainWindow(); + } + registerIpcHandlers( createEditorWindowWrapper, createSourceSelectorWindowWrapper, @@ -384,6 +394,7 @@ app.whenReady().then(async () => { showMainWindow(); } }, + switchToHudWrapper, ); createWindow(); }); diff --git a/electron/preload.ts b/electron/preload.ts index 8f1836b..eeca25c 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -18,6 +18,12 @@ contextBridge.exposeInMainWorld("electronAPI", { switchToEditor: () => { return ipcRenderer.invoke("switch-to-editor"); }, + switchToHud: () => { + return ipcRenderer.invoke("switch-to-hud"); + }, + startNewRecording: () => { + return ipcRenderer.invoke("start-new-recording"); + }, openSourceSelector: () => { return ipcRenderer.invoke("open-source-selector"); }, diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index a27fbb9..58dd360 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -1,8 +1,16 @@ import type { Span } from "dnd-timeline"; -import { FolderOpen, Languages, Save } from "lucide-react"; +import { FolderOpen, Languages, Save, Video } from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; import { toast } from "sonner"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import { useI18n, useScopedT } from "@/contexts/I18nContext"; import { useShortcuts } from "@/contexts/ShortcutsContext"; import { INITIAL_EDITOR_STATE, useEditorHistory } from "@/hooks/useEditorHistory"; @@ -117,6 +125,7 @@ export default function VideoEditor() { const [exportProgress, setExportProgress] = useState(null); const [exportError, setExportError] = useState(null); const [showExportDialog, setShowExportDialog] = useState(false); + const [showNewRecordingDialog, setShowNewRecordingDialog] = useState(false); const [exportQuality, setExportQuality] = useState("good"); const [exportFormat, setExportFormat] = useState("mp4"); const [gifFrameRate, setGifFrameRate] = useState(15); @@ -501,6 +510,16 @@ export default function VideoEditor() { await saveProject(true); }, [saveProject]); + const handleNewRecordingConfirm = useCallback(async () => { + const result = await window.electronAPI.startNewRecording(); + if (result.success) { + setShowNewRecordingDialog(false); + } else { + console.error("Failed to start new recording:", result.error); + setError("Failed to start new recording: " + (result.error || "Unknown error")); + } + }, []); + const handleLoadProject = useCallback(async () => { const result = await window.electronAPI.loadProjectFile(); @@ -1482,6 +1501,34 @@ export default function VideoEditor() { return (
+ + + + {t("newRecording.title")} + {t("newRecording.description")} + + + + + + + +
+