feat(renderer): add Save As button to chapter header
- Add 'Save As' button next to Save in the chapter writing view - Exports current chapter as Markdown to a user-chosen file location - New IPC handler folio:saveChapterAs shows OS save dialog
This commit is contained in:
parent
1a1d69936c
commit
04c6540e4c
5 changed files with 34 additions and 0 deletions
|
|
@ -550,6 +550,22 @@ handleIpc("folio:saveChapter", (_e, id: string, content: string) => {
|
|||
return { ok: true };
|
||||
});
|
||||
|
||||
handleIpc("folio:saveChapterAs", async (_e, id: string) => {
|
||||
const bp = requireBook();
|
||||
const meta = loadBook(bp);
|
||||
const entry = meta.chapters[id];
|
||||
if (!entry) return { error: "Chapter not found" };
|
||||
const { canceled, filePath } = await showSaveDialog({
|
||||
title: "Save Chapter As",
|
||||
defaultPath: `${entry.title}.md`,
|
||||
filters: [{ name: "Markdown", extensions: ["md"] }],
|
||||
});
|
||||
if (canceled || !filePath) return { canceled: true } as const;
|
||||
const content = getChapterContent(bp, id);
|
||||
fs.writeFileSync(filePath, content, "utf-8");
|
||||
return { ok: true, filePath };
|
||||
});
|
||||
|
||||
// Explicit, opt-in migration of a single chapter (or the whole book) to the
|
||||
// native Folio document format. Never automatic — the UI triggers this. The
|
||||
// migration pipeline makes a recovery backup and keeps the original .md files.
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ contextBridge.exposeInMainWorld("folio", {
|
|||
ipcRenderer.invoke("folio:loadChapterDoc", id),
|
||||
saveChapterDoc: (id: string, doc: unknown) =>
|
||||
ipcRenderer.invoke("folio:saveChapterDoc", id, doc),
|
||||
saveChapterAs: (id: string) =>
|
||||
ipcRenderer.invoke("folio:saveChapterAs", id),
|
||||
importImageAsset: () => ipcRenderer.invoke("folio:importImageAsset"),
|
||||
exportMarkdown: () => ipcRenderer.invoke("folio:exportMarkdown"),
|
||||
exportZip: () => ipcRenderer.invoke("folio:exportZip"),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import type { BookCtx } from "./types.js";
|
|||
import { alertMessage } from "../dialog.js";
|
||||
import {
|
||||
$,
|
||||
basename,
|
||||
closeEl,
|
||||
countWords,
|
||||
formatWordCount,
|
||||
|
|
@ -230,6 +231,19 @@ export function createEditorFeature(ctx: BookCtx): EditorFeature {
|
|||
const saveBtn = $("saveBtn");
|
||||
saveBtn?.addEventListener("click", () => void saveAndNotify());
|
||||
|
||||
// Save As button — exports the current chapter as a Markdown file to a
|
||||
// user-chosen location via the OS save dialog.
|
||||
const saveAsBtn = $("saveAsBtn");
|
||||
saveAsBtn?.addEventListener("click", 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)}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Flush any pending auto-save when the window is hidden or loses focus, so the
|
||||
// last stretch of typing is persisted even if the app closes within the
|
||||
// debounce window.
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ export type FolioAPI = {
|
|||
migrateBook: () => Promise<any>;
|
||||
loadChapterDoc: (id: string) => Promise<any>;
|
||||
saveChapterDoc: (id: string, doc: unknown) => Promise<any>;
|
||||
saveChapterAs: (id: string) => Promise<any>;
|
||||
importImageAsset: () => Promise<any>;
|
||||
exportMarkdown: () => Promise<any>;
|
||||
exportZip: () => Promise<any>;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
<div class="editor-actions">
|
||||
<span class="save-state" id="saveState">Saved</span>
|
||||
<button class="button button-ghost" id="saveBtn" title="Save chapter (Ctrl+S)">Save</button>
|
||||
<button class="button button-ghost" id="saveAsBtn" title="Save chapter as Markdown file">Save As</button>
|
||||
<span class="word-count" id="wordCount">words: 0</span>
|
||||
<button class="icon-button" id="statsBtn" title="Writing statistics">📊</button>
|
||||
<button class="icon-button" id="commentsBtn" title="Comments">💬</button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue