From 283bb7c77294a00a35c9b13a1d5541b0304b02d2 Mon Sep 17 00:00:00 2001 From: avi Date: Sun, 4 Oct 2026 11:34:10 -0500 Subject: [PATCH] =?UTF-8?q?feat(settings):=20the=20storage=20folder=20now?= =?UTF-8?q?=20decides=20where=20books=20are=20saved=20=E2=80=94=20new=20bo?= =?UTF-8?q?oks,=20.md=20imports,=20and=20the=20export=20dialog=20default?= =?UTF-8?q?=20all=20land=20there=20(was:=20always=20Documents);=20label=20?= =?UTF-8?q?clarifies=20it=20covers=20books=20+=20snapshots?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/index.ts | 36 ++++++++++++++++++++++++++--------- src/renderer/book/settings.ts | 8 ++++++-- src/renderer/index.html | 6 +++--- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 4bf7926..24cd5fa 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -246,13 +246,30 @@ async function openBookAt(p: string): Promise { } } -// Create a new book as a dedicated folder / inside the user's Documents -// directory, then open it. The folder name is the sanitized book title; if a -// folder with that name already exists we append a numeric suffix. +// Create a new book as a dedicated folder / inside the user's chosen +// storage folder (Settings → Folder), falling back to Documents when none is +// set or the stored one no longer exists, then open it. The folder name is +// the sanitized book title; if a folder with that name already exists we +// append a numeric suffix. +function getBooksDir(): string { + // One storage folder drives everything (snapshots, recovery, new books). + // snapshotLocation is the one the Settings modal writes; backupLocation is + // the older key — honor both so an existing choice keeps working. + for (const key of ["snapshotLocation", "backupLocation"]) { + const v = String(getSetting(key, "") || "").trim(); + try { + if (v && fs.statSync(v).isDirectory()) return v; + } catch { + // stale or unreadable path — fall through to the next candidate + } + } + return app.getPath("documents"); +} + handleIpc("folio:newBookNamed", async (_e, name: string) => { const title = (name || "").trim(); if (!title) return { error: "Please provide a book name." }; - const base = app.getPath("documents"); + const base = getBooksDir(); const folder = sanitizeBookFolderName(title) || "Untitled"; let target = path.join(base, folder); let n = 2; @@ -591,7 +608,7 @@ function exportDefaultPath(filename: string): string { const dir = last && fs.existsSync(last) && fs.statSync(last).isDirectory() ? last - : app.getPath("documents"); + : getBooksDir(); return path.join(dir, filename); } @@ -972,6 +989,7 @@ function snapshotSettingsSnapshot() { keep: clampInt(getSetting("snapshotKeep", SNAPSHOT_DEFAULTS.keep), SNAPSHOT_DEFAULTS.keep, 0, 200), location, resolvedRoot: resolved, + booksDir: getBooksDir(), snapshotCount: snaps.length, lastSnapshot: snaps.length ? snaps[snaps.length - 1] : null, }; @@ -1008,7 +1026,7 @@ handleIpc("folio:setSnapshotSettings", (_e, patch: unknown) => { handleIpc("folio:pickSnapshotFolder", async () => { const { canceled, filePaths } = await showOpenDialog({ properties: ["openDirectory"], - title: "Choose snapshot folder", + title: "Choose storage folder (new books + snapshots)", }); return { canceled, filePaths }; }); @@ -1089,14 +1107,14 @@ handleIpc("folio:importMarkdown", async () => { filters: [{ name: "Markdown and text", extensions: ["md", "markdown", "txt"] }], }); if (canceled || !filePaths.length) return { canceled: true } as const; - return importMarkdownFiles(filePaths, app.getPath("documents")); + return importMarkdownFiles(filePaths, getBooksDir()); }); // Open a path that may be either a Folio book directory or a Markdown file // (the desktop launcher can be handed either via file associations). handleIpc("folio:openPathOrImport", async (_e, p: string) => { if (p && isImportableMarkdown(p)) { - const results = importMarkdownFiles([p], app.getPath("documents")); + const results = importMarkdownFiles([p], getBooksDir()); const r = results[0]; if (!r.ok) return { error: r.error }; return openBookAt(r.bookPath as string); @@ -1300,7 +1318,7 @@ function fileArgFrom(argv: string[]): string | null { function openFromArg(p: string): void { if (isImportableMarkdown(p)) { - const results = importMarkdownFiles([p], app.getPath("documents")); + const results = importMarkdownFiles([p], getBooksDir()); const r = results[0]; if (r.ok) { openBookAt(r.bookPath as string); diff --git a/src/renderer/book/settings.ts b/src/renderer/book/settings.ts index c9adad1..9cb3949 100644 --- a/src/renderer/book/settings.ts +++ b/src/renderer/book/settings.ts @@ -23,6 +23,8 @@ interface SnapshotSettings { keep: number; location: string; resolvedRoot: string; + /** Where new books are saved (Settings → Storage folder). */ + booksDir?: string; snapshotCount: number; lastSnapshot: string | null; } @@ -59,8 +61,10 @@ export function createSettingsFeature(ctx: BookCtx): SettingsFeature { intervalInput.value = String(s.intervalMin ?? 5); keepInput.value = String(s.keep ?? 20); locationInput.value = s.location || ""; - locationInput.placeholder = "Default: /Folio-Backups"; - resolvedEl.textContent = `Saving to: ${shortPath(s.resolvedRoot || "—")}`; + locationInput.placeholder = "Default: Documents (books) · /Folio-Backups (snapshots)"; + resolvedEl.textContent = s.booksDir + ? `New books save to: ${shortPath(s.booksDir)}` + : `Saving to: ${shortPath(s.resolvedRoot || "—")}`; lastSnapEl.textContent = s.lastSnapshot ? `${s.snapshotCount} snapshot${s.snapshotCount === 1 ? "" : "s"} · latest ${s.lastSnapshot.split("/").pop()}` : "No snapshots yet."; diff --git a/src/renderer/index.html b/src/renderer/index.html index d41d81b..8bb16c8 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -343,9 +343,9 @@
- Folder - - + Storage folder + +