From 1a08279f78a80ade2c028651dd8be80f4e29f908 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 31 Dec 2025 11:38:17 -0600 Subject: [PATCH 1/2] Remember last save directory across file save operations Implement persistent last save directory feature that remembers the directory used in file save dialogs and defaults to it in future save operations. Changes: - Modified dialog.js showSaveDialog wrapper to store and retrieve last directory - Uses electron-store via electronAPI.storeGet/storeSet - Automatically extracts directory from selected file path - Sets as defaultPath for subsequent saves if no path specified - Cross-platform path handling (supports both / and \ separators) Benefits: - Reduces user friction when saving multiple files - Persists across app restarts - Works for all save operations (blackbox logs, diffs, configs, etc.) - No UI changes required - transparent to user - Backwards compatible - gracefully handles no saved directory Closes part of UX improvements for file management. --- js/dialog.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/js/dialog.js b/js/dialog.js index 4e60a09c9..267b8abe2 100644 --- a/js/dialog.js +++ b/js/dialog.js @@ -1,9 +1,35 @@ +const LAST_SAVE_DIRECTORY_KEY = 'lastSaveDirectory'; + const dialog = { showOpenDialog: async function (options) { return window.electronAPI.showOpenDialog(options); }, showSaveDialog: async function (options) { - return window.electronAPI.showSaveDialog(options); + const opts = options || {}; + + // Get the last save directory from storage + const lastDirectory = window.electronAPI.storeGet(LAST_SAVE_DIRECTORY_KEY, null); + + // If we have a last directory and no defaultPath is specified, use it + if (lastDirectory && !opts.defaultPath) { + opts.defaultPath = lastDirectory; + } + + // Show the save dialog + const result = await window.electronAPI.showSaveDialog(opts); + + // If user selected a file (didn't cancel), save the directory for next time + if (result && result.filePath) { + // Extract directory from the full file path + // Get parent directory by removing the filename + const lastSlash = Math.max(result.filePath.lastIndexOf('/'), result.filePath.lastIndexOf('\\')); + if (lastSlash !== -1) { + const directory = result.filePath.substring(0, lastSlash); + window.electronAPI.storeSet(LAST_SAVE_DIRECTORY_KEY, directory); + } + } + + return result; }, alert: function (message) { return window.electronAPI.alertDialog(message); From 4197435cc43834b5d52bac07a41f2fa2b91075cf Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 31 Dec 2025 12:13:31 -0600 Subject: [PATCH 2/2] Implement remember last save directory in main process Move save directory logic to main process IPC handler where it belongs. The implementation now: - Stores the last used directory in electron-store - Combines saved directory with provided filename using path.join() - Handles edge cases: filename-only, full paths, no saved directory - Preserves directory on cancel (only saves on successful file selection) This fixes the issue where the implementation in renderer process was never called. --- js/dialog.js | 28 +--------------------------- js/main/main.js | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/js/dialog.js b/js/dialog.js index 267b8abe2..4e60a09c9 100644 --- a/js/dialog.js +++ b/js/dialog.js @@ -1,35 +1,9 @@ -const LAST_SAVE_DIRECTORY_KEY = 'lastSaveDirectory'; - const dialog = { showOpenDialog: async function (options) { return window.electronAPI.showOpenDialog(options); }, showSaveDialog: async function (options) { - const opts = options || {}; - - // Get the last save directory from storage - const lastDirectory = window.electronAPI.storeGet(LAST_SAVE_DIRECTORY_KEY, null); - - // If we have a last directory and no defaultPath is specified, use it - if (lastDirectory && !opts.defaultPath) { - opts.defaultPath = lastDirectory; - } - - // Show the save dialog - const result = await window.electronAPI.showSaveDialog(opts); - - // If user selected a file (didn't cancel), save the directory for next time - if (result && result.filePath) { - // Extract directory from the full file path - // Get parent directory by removing the filename - const lastSlash = Math.max(result.filePath.lastIndexOf('/'), result.filePath.lastIndexOf('\\')); - if (lastSlash !== -1) { - const directory = result.filePath.substring(0, lastSlash); - window.electronAPI.storeSet(LAST_SAVE_DIRECTORY_KEY, directory); - } - } - - return result; + return window.electronAPI.showSaveDialog(options); }, alert: function (message) { return window.electronAPI.alertDialog(message); diff --git a/js/main/main.js b/js/main/main.js index 34faa50a8..fe3a099c5 100644 --- a/js/main/main.js +++ b/js/main/main.js @@ -280,8 +280,35 @@ app.whenReady().then(() => { return dialog.showOpenDialog(options); }), - ipcMain.handle('dialog.showSaveDialog', (_event, options) => { - return dialog.showSaveDialog(options); + ipcMain.handle('dialog.showSaveDialog', async (_event, options) => { + const opts = options || {}; + const LAST_SAVE_DIRECTORY_KEY = 'lastSaveDirectory'; + + // Get the last save directory from store + const lastDirectory = store.get(LAST_SAVE_DIRECTORY_KEY, null); + + // If we have a last directory, combine it with the filename if one was provided + if (lastDirectory && opts.defaultPath) { + // If defaultPath is just a filename (no directory), prepend the last directory + if (!path.dirname(opts.defaultPath) || path.dirname(opts.defaultPath) === '.') { + opts.defaultPath = path.join(lastDirectory, opts.defaultPath); + } + } else if (lastDirectory && !opts.defaultPath) { + // No filename provided, just use the directory + opts.defaultPath = lastDirectory; + } + + // Show the save dialog + const result = await dialog.showSaveDialog(opts); + + // If user selected a file (didn't cancel), save the directory for next time + if (result && result.filePath && !result.canceled) { + // Extract directory from the full file path (path already imported at top) + const directory = path.dirname(result.filePath); + store.set(LAST_SAVE_DIRECTORY_KEY, directory); + } + + return result; }), ipcMain.on('dialog.alert', (event, message) => {