From 38da218e33384624062787394f55ea450f274a3e Mon Sep 17 00:00:00 2001 From: avi Date: Sat, 5 Sep 2026 13:37:24 -0500 Subject: [PATCH] feat(menu): add Save As to File menu with Ctrl+Shift+S - Add 'Save As...' item to native File menu, right under Save - New IPC event folio:save-as-requested routes through preload to renderer - Wired in index.ts to call saveChapterAs with current chapter ID --- src/main/menu.ts | 18 ++++++++++++------ src/main/preload.ts | 2 ++ src/renderer/book/index.ts | 9 +++++++++ src/renderer/book/types.ts | 1 + 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/main/menu.ts b/src/main/menu.ts index 3478928..f5189fa 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -39,12 +39,18 @@ export function buildMenu( { label: "New", accelerator: "CmdOrCtrl+N", click: onRequestNew }, { label: "Open", accelerator: "CmdOrCtrl+O", click: onRequestOpen }, { type: "separator" }, - { - label: "Save", - accelerator: "CmdOrCtrl+S", - enabled: !!getState().bookPath, - click: () => getWindow()?.webContents.send("folio:save-requested"), - }, + { + label: "Save", + accelerator: "CmdOrCtrl+S", + enabled: !!getState().bookPath, + click: () => getWindow()?.webContents.send("folio:save-requested"), + }, + { + label: "Save As…", + accelerator: "CmdOrCtrl+Shift+S", + enabled: !!getState().bookPath, + click: () => getWindow()?.webContents.send("folio:save-as-requested"), + }, { type: "separator" }, { label: "Open Recent", submenu: recentItems }, { type: "separator" }, diff --git a/src/main/preload.ts b/src/main/preload.ts index d1f1748..7431d15 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -102,6 +102,8 @@ contextBridge.exposeInMainWorld("folio", { ipcRenderer.on("folio:selectAll-requested", () => cb()), onSaveRequested: (cb: () => void) => ipcRenderer.on("folio:save-requested", () => cb()), + onSaveAsRequested: (cb: () => void) => + ipcRenderer.on("folio:save-as-requested", () => cb()), onZoomInRequested: (cb: () => void) => ipcRenderer.on("folio:zoom-in-requested", () => cb()), onZoomOutRequested: (cb: () => void) => diff --git a/src/renderer/book/index.ts b/src/renderer/book/index.ts index 6925034..083de0c 100644 --- a/src/renderer/book/index.ts +++ b/src/renderer/book/index.ts @@ -170,6 +170,15 @@ export function initBook(api: FolioAPI) { } }); api.onSaveRequested(() => void hooks.saveAndNotify()); + api.onSaveAsRequested(async () => { + if (!state.selectedId) return; + const res = await api.saveChapterAs(state.selectedId); + if (res && "error" in res) { + showToast("Save As failed"); + } else if (res && "filePath" in res) { + showToast(`Saved to ${basename(res.filePath as string)}`); + } + }); // ---- comments / annotations wiring ---- // Already wired through the comments feature's internal listeners; the diff --git a/src/renderer/book/types.ts b/src/renderer/book/types.ts index 1cdc15c..109f226 100644 --- a/src/renderer/book/types.ts +++ b/src/renderer/book/types.ts @@ -87,6 +87,7 @@ export type FolioAPI = { onRedoRequested: (cb: () => void) => void; onSelectAllRequested: (cb: () => void) => void; onSaveRequested: (cb: () => void) => void; + onSaveAsRequested: (cb: () => void) => void; onZoomInRequested: (cb: () => void) => void; onZoomOutRequested: (cb: () => void) => void; onZoomResetRequested: (cb: () => void) => void;