From bcf439ca5e94a5598b34bd0f032905a5ab505155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Wed, 19 Nov 2025 10:10:32 +0100 Subject: [PATCH] feat: add manual save with Ctrl+S and suppress auto-save toasts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Modified handleSaveProject to accept showToast parameter (default: false) - Auto-saves now run silently without toast notifications - Added Ctrl+S / Cmd+S keyboard shortcut for manual save with toast - Added "Save Project" and "Open Projects" to command palette - Error toasts still shown for all save failures This provides the best of both worlds: - Automatic background saves don't interrupt the user - Manual saves (Ctrl+S or command palette) provide confirmation - Users can work without being constantly notified 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- components/editor/AudioEditor.tsx | 45 ++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/components/editor/AudioEditor.tsx b/components/editor/AudioEditor.tsx index da076db..dc56755 100644 --- a/components/editor/AudioEditor.tsx +++ b/components/editor/AudioEditor.tsx @@ -887,7 +887,7 @@ export function AudioEditor() { currentTimeRef.current = currentTime; }, [currentTime]); - const handleSaveProject = React.useCallback(async () => { + const handleSaveProject = React.useCallback(async (showToast = false) => { if (tracks.length === 0) return; try { @@ -920,14 +920,18 @@ export function AudioEditor() { // Save last project ID to localStorage for auto-load on next visit localStorage.setItem('audio-ui-last-project', projectId); - addToast({ - title: 'Project Saved', - description: `"${currentProjectName}" saved successfully`, - variant: 'success', - duration: 2000, - }); + // Only show toast for manual saves + if (showToast) { + addToast({ + title: 'Project Saved', + description: `"${currentProjectName}" saved successfully`, + variant: 'success', + duration: 2000, + }); + } } catch (error) { console.error('Failed to save project:', error); + // Always show error toasts addToast({ title: 'Save Failed', description: 'Could not save project', @@ -1173,6 +1177,22 @@ export function AudioEditor() { category: 'playback', action: stop, }, + // Project + { + id: 'save-project', + label: 'Save Project', + description: 'Save current project', + shortcut: 'Ctrl+S', + category: 'file', + action: () => handleSaveProject(true), + }, + { + id: 'open-projects', + label: 'Open Projects', + description: 'Open projects dialog', + category: 'file', + action: handleOpenProjectsDialog, + }, // View { id: 'zoom-in', @@ -1219,7 +1239,7 @@ export function AudioEditor() { }, ]; return actions; - }, [play, pause, stop, handleZoomIn, handleZoomOut, handleFitToView, handleImportTracks, handleClearTracks, addTrack]); + }, [play, pause, stop, handleSaveProject, handleOpenProjectsDialog, handleZoomIn, handleZoomOut, handleFitToView, handleImportTracks, handleClearTracks, addTrack]); // Keyboard shortcuts React.useEffect(() => { @@ -1275,6 +1295,13 @@ export function AudioEditor() { return; } + // Ctrl/Cmd+S: Save project (manual save with toast) + if ((e.ctrlKey || e.metaKey) && e.key === 's') { + e.preventDefault(); + handleSaveProject(true); // Show toast for manual saves + return; + } + // Ctrl/Cmd+D: Duplicate if ((e.ctrlKey || e.metaKey) && e.key === 'd') { e.preventDefault(); @@ -1298,7 +1325,7 @@ export function AudioEditor() { window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); - }, [togglePlayPause, canUndo, canRedo, undo, redo, handleCut, handleCopy, handlePaste, handleDelete, handleDuplicate]); + }, [togglePlayPause, canUndo, canRedo, undo, redo, handleCut, handleCopy, handlePaste, handleDelete, handleDuplicate, handleSaveProject]); return ( <>