Add Save Project As menu action and force prompt behavior

This commit is contained in:
Yusuf Mohsinally
2026-02-18 11:08:01 -08:00
parent 491db0ab2e
commit bd50b193a1
5 changed files with 30 additions and 4 deletions
+1
View File
@@ -43,6 +43,7 @@ interface Window {
loadProjectFile: () => Promise<{ success: boolean; path?: string; project?: unknown; message?: string; cancelled?: boolean; error?: string }>
onMenuLoadProject: (callback: () => void) => () => void
onMenuSaveProject: (callback: () => void) => () => void
onMenuSaveProjectAs: (callback: () => void) => () => void
getPlatform: () => Promise<string>
hudOverlayHide: () => void;
hudOverlayClose: () => void;
+6 -1
View File
@@ -57,7 +57,7 @@ function isEditorWindow(window: BrowserWindow) {
return window.webContents.getURL().includes('windowType=editor')
}
function sendEditorMenuAction(channel: 'menu-load-project' | 'menu-save-project') {
function sendEditorMenuAction(channel: 'menu-load-project' | 'menu-save-project' | 'menu-save-project-as') {
let targetWindow = BrowserWindow.getFocusedWindow() ?? mainWindow
if (!targetWindow || targetWindow.isDestroyed() || !isEditorWindow(targetWindow)) {
@@ -102,6 +102,11 @@ function setupApplicationMenu() {
accelerator: 'CmdOrCtrl+S',
click: () => sendEditorMenuAction('menu-save-project'),
},
{
label: 'Save Project As…',
accelerator: 'CmdOrCtrl+Shift+S',
click: () => sendEditorMenuAction('menu-save-project-as'),
},
],
},
{
+5
View File
@@ -76,6 +76,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
ipcRenderer.on('menu-save-project', listener)
return () => ipcRenderer.removeListener('menu-save-project', listener)
},
onMenuSaveProjectAs: (callback: () => void) => {
const listener = () => callback()
ipcRenderer.on('menu-save-project-as', listener)
return () => ipcRenderer.removeListener('menu-save-project-as', listener)
},
getPlatform: () => {
return ipcRenderer.invoke('get-platform')
},
+17 -3
View File
@@ -303,7 +303,7 @@ export default function VideoEditor() {
loadVideo();
}, []);
const handleSaveProject = useCallback(async () => {
const saveProject = useCallback(async (forceSaveAs: boolean) => {
if (!videoPath) {
toast.error('No video loaded');
return;
@@ -339,7 +339,11 @@ export default function VideoEditor() {
};
const fileNameBase = sourcePath.split(/[\\/]/).pop()?.replace(/\.[^.]+$/, '') || `project-${Date.now()}`;
const result = await window.electronAPI.saveProjectFile(projectData, fileNameBase, currentProjectPath ?? undefined);
const result = await window.electronAPI.saveProjectFile(
projectData,
fileNameBase,
forceSaveAs ? undefined : currentProjectPath ?? undefined,
);
if (result.cancelled) {
toast.info('Project save cancelled');
@@ -378,6 +382,14 @@ export default function VideoEditor() {
gifSizePreset,
]);
const handleSaveProject = useCallback(async () => {
await saveProject(false);
}, [saveProject]);
const handleSaveProjectAs = useCallback(async () => {
await saveProject(true);
}, [saveProject]);
const handleLoadProject = useCallback(async () => {
const result = await window.electronAPI.loadProjectFile();
@@ -455,12 +467,14 @@ export default function VideoEditor() {
useEffect(() => {
const removeLoadListener = window.electronAPI.onMenuLoadProject(handleLoadProject);
const removeSaveListener = window.electronAPI.onMenuSaveProject(handleSaveProject);
const removeSaveAsListener = window.electronAPI.onMenuSaveProjectAs(handleSaveProjectAs);
return () => {
removeLoadListener?.();
removeSaveListener?.();
removeSaveAsListener?.();
};
}, [handleLoadProject, handleSaveProject]);
}, [handleLoadProject, handleSaveProject, handleSaveProjectAs]);
// Initialize default wallpaper with resolved asset path
useEffect(() => {
+1
View File
@@ -59,5 +59,6 @@ interface Window {
}>
onMenuLoadProject: (callback: () => void) => () => void
onMenuSaveProject: (callback: () => void) => () => void
onMenuSaveProjectAs: (callback: () => void) => () => void
}
}