diff --git a/src/components/Library.tsx b/src/components/Library.tsx
index ccdee2a..038c0dc 100644
--- a/src/components/Library.tsx
+++ b/src/components/Library.tsx
@@ -79,7 +79,11 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
)}
{books.map((b) => (
-
loadBook(b.id).then(onOpen)}>
+
loadBook(b.id).then(onOpen).catch((e) => setNotice(`Could not open book: ${e}`))}
+ >
{b.title || "Untitled"}
{b.author && {b.author}}
setPendingDelete(b)} />
diff --git a/src/library.ts b/src/library.ts
index e329473..7f39c92 100644
--- a/src/library.ts
+++ b/src/library.ts
@@ -15,7 +15,11 @@ export async function listBooks(): Promise {
export async function loadBook(id: string): Promise {
const contents = await invoke("load_book", { id });
- return JSON.parse(contents) as Book;
+ try {
+ return JSON.parse(contents) as Book;
+ } catch {
+ throw new Error("the book file is corrupt or unreadable");
+ }
}
export async function saveBook(book: Book): Promise {
diff --git a/src/model/book.ts b/src/model/book.ts
index 5db51dc..dc8017e 100644
--- a/src/model/book.ts
+++ b/src/model/book.ts
@@ -140,11 +140,19 @@ export function createCover(): Cover {
return { kind: "default", image: "", bg: COVER_PALETTES[0].bg, ink: COVER_PALETTES[0].ink };
}
+const DEFAULT_METADATA: BookMetadata = { title: "Untitled", subtitle: "", author: "", isbn: "", language: "en" };
+const DEFAULT_SETTINGS: BookSettings = { trim: "6x9", bleed: true };
+
export function normalizeBook(book: Book): Book {
+ const chapters = (Array.isArray(book.chapters) ? book.chapters : []).filter(Boolean);
return {
...book,
+ metadata: { ...DEFAULT_METADATA, ...book.metadata },
+ settings: { ...DEFAULT_SETTINGS, ...book.settings },
cover: book.cover ? { ...createCover(), ...book.cover } : createCover(),
- chapters: book.chapters.map((c) => (c.updatedAt ? c : { ...c, updatedAt: Date.now() })),
+ chapters: (chapters.length ? chapters : [createChapter()]).map((c) =>
+ c.updatedAt ? c : { ...c, updatedAt: Date.now() },
+ ),
};
}
@@ -152,9 +160,9 @@ export function createBook(): Book {
return {
schema: "margin/1",
id: crypto.randomUUID(),
- metadata: { title: "Untitled", subtitle: "", author: "", isbn: "", language: "en" },
+ metadata: { ...DEFAULT_METADATA },
theme: "quiet-press",
- settings: { trim: "6x9", bleed: true },
+ settings: { ...DEFAULT_SETTINGS },
cover: createCover(),
chapters: [createChapter("Chapter One")],
};