Compare commits
2 commits
e794f241ba
...
c064e3aab1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c064e3aab1 | ||
|
|
283bb7c772 |
4 changed files with 37 additions and 15 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "folio",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.8",
|
||||
"description": "A simple, local, open-source desktop writing app.",
|
||||
"productName": "Folio",
|
||||
"author": "Folio Contributors",
|
||||
|
|
|
|||
|
|
@ -246,13 +246,30 @@ async function openBookAt(p: string): Promise<BookResult | ErrResult> {
|
|||
}
|
||||
}
|
||||
|
||||
// Create a new book as a dedicated folder <name>/ 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 <name>/ 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<string>(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);
|
||||
|
|
|
|||
|
|
@ -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: <book>/Folio-Backups";
|
||||
resolvedEl.textContent = `Saving to: ${shortPath(s.resolvedRoot || "—")}`;
|
||||
locationInput.placeholder = "Default: Documents (books) · <book>/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.";
|
||||
|
|
|
|||
|
|
@ -343,9 +343,9 @@
|
|||
<label>Keep last <input type="number" id="settingKeep" class="modal-input setting-num" min="0" max="200" value="20" /></label>
|
||||
</div>
|
||||
<div class="backup-control">
|
||||
<span class="backup-label">Folder</span>
|
||||
<input type="text" id="settingLocation" class="backup-input" readonly placeholder="Default: <book>/Folio-Backups" title="Snapshot folder (empty = default)" />
|
||||
<button type="button" class="button button-ghost backup-browse" id="settingBrowse" title="Choose snapshot folder">…</button>
|
||||
<span class="backup-label">Storage folder</span>
|
||||
<input type="text" id="settingLocation" class="backup-input" readonly placeholder="Default: Documents" title="Where new books and snapshots are saved (empty = Documents / <book>/Folio-Backups)" />
|
||||
<button type="button" class="button button-ghost backup-browse" id="settingBrowse" title="Choose storage folder">…</button>
|
||||
<button type="button" class="button button-ghost" id="settingResetLoc" title="Back to the default folder">Reset</button>
|
||||
</div>
|
||||
<p class="setting-meta" id="settingResolved"></p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue