diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json
index 4ec5d4a..b8e3643 100644
--- a/src-tauri/capabilities/default.json
+++ b/src-tauri/capabilities/default.json
@@ -5,6 +5,7 @@
"windows": ["main"],
"permissions": [
"core:default",
+ "core:window:allow-destroy",
"opener:default",
"dialog:default",
"updater:default",
diff --git a/src/App.tsx b/src/App.tsx
index 4e39ac2..acb50f7 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,10 +1,11 @@
import { useEffect } from "react";
import { listen } from "@tauri-apps/api/event";
+import { getCurrentWindow } from "@tauri-apps/api/window";
import { Library } from "./components/Library";
import { EditorView } from "./components/EditorView";
import { useBook } from "./store/useBook";
import { isDesktop } from "./ipc";
-import { newBook } from "./library";
+import { newBook, saveBook } from "./library";
import { runExport } from "./export/run";
import { checkForUpdates } from "./updater";
@@ -27,6 +28,21 @@ function App() {
};
}, []);
+ useEffect(() => {
+ if (!isDesktop) return;
+ const win = getCurrentWindow();
+ const unlisten = win.onCloseRequested(async (event) => {
+ const { book, dirty } = useBook.getState();
+ if (!book || !dirty) return;
+ event.preventDefault();
+ await saveBook(book).catch(() => {});
+ win.destroy();
+ });
+ return () => {
+ unlisten.then((stop) => stop());
+ };
+ }, []);
+
return book ? : ;
}
diff --git a/src/store/useBook.ts b/src/store/useBook.ts
index 8b7f2b0..79414f5 100644
--- a/src/store/useBook.ts
+++ b/src/store/useBook.ts
@@ -13,6 +13,7 @@ import {
normalizeBook,
} from "../model/book";
import { loadActiveChapter, saveActiveChapter } from "../editor/positions";
+import { saveBook } from "../library";
export const COVER_ID = "__cover__";
@@ -41,7 +42,11 @@ interface BookState {
markSaved: () => void;
}
-export const useBook = create((set) => ({
+function flushOutgoing(book: Book | null, dirty: boolean): void {
+ if (book && dirty) saveBook(book).catch(() => {});
+}
+
+export const useBook = create((set, get) => ({
book: null,
activeChapterId: "",
dirty: false,
@@ -50,13 +55,17 @@ export const useBook = create((set) => ({
setExporting: (label) => set({ exporting: label }),
setNotice: (message) => set({ notice: message }),
openBook: (book) => {
+ flushOutgoing(get().book, get().dirty);
const normalized = normalizeBook(book);
const saved = loadActiveChapter(normalized.id);
const valid = saved === COVER_ID || normalized.chapters.some((c) => c.id === saved);
const activeChapterId = valid ? (saved as string) : normalized.chapters[0]?.id ?? "";
set({ book: normalized, activeChapterId, dirty: false });
},
- closeBook: () => set({ book: null, activeChapterId: "", dirty: false }),
+ closeBook: () => {
+ flushOutgoing(get().book, get().dirty);
+ set({ book: null, activeChapterId: "", dirty: false });
+ },
setActiveChapter: (id) => set({ activeChapterId: id }),
setChapterContent: (id, content) =>
set((s) =>