From 0bc3e36ee6ead57ae71499b87376f71bad0e0a79 Mon Sep 17 00:00:00 2001 From: PJ Date: Mon, 22 Jun 2026 13:15:01 -0400 Subject: [PATCH] feat(editor): persist active chapter per book Remember which chapter was open per book and restore it on reopen, falling back to the first chapter when the saved id is no longer valid. Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk --- src/editor/positions.ts | 20 ++++++++++++++++++++ src/store/useBook.ts | 14 +++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/editor/positions.ts b/src/editor/positions.ts index c804ce0..2fbbd7d 100644 --- a/src/editor/positions.ts +++ b/src/editor/positions.ts @@ -25,3 +25,23 @@ export function savePosition(bookId: string, chapterId: string, position: Chapte (all[bookId] ||= {})[chapterId] = position; localStorage.setItem(KEY, JSON.stringify(all)); } + +const ACTIVE_KEY = "margin-active-chapter"; + +function readActive(): Record { + try { + return JSON.parse(localStorage.getItem(ACTIVE_KEY) || "{}") as Record; + } catch { + return {}; + } +} + +export function loadActiveChapter(bookId: string): string | null { + return readActive()[bookId] ?? null; +} + +export function saveActiveChapter(bookId: string, chapterId: string): void { + const all = readActive(); + all[bookId] = chapterId; + localStorage.setItem(ACTIVE_KEY, JSON.stringify(all)); +} diff --git a/src/store/useBook.ts b/src/store/useBook.ts index c356ba5..8b7f2b0 100644 --- a/src/store/useBook.ts +++ b/src/store/useBook.ts @@ -12,6 +12,7 @@ import { createPage, normalizeBook, } from "../model/book"; +import { loadActiveChapter, saveActiveChapter } from "../editor/positions"; export const COVER_ID = "__cover__"; @@ -50,7 +51,10 @@ export const useBook = create((set) => ({ setNotice: (message) => set({ notice: message }), openBook: (book) => { const normalized = normalizeBook(book); - set({ book: normalized, activeChapterId: normalized.chapters[0]?.id ?? "", dirty: false }); + 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 }), setActiveChapter: (id) => set({ activeChapterId: id }), @@ -132,3 +136,11 @@ export const useBook = create((set) => ({ set((s) => (s.book ? { dirty: true, book: { ...s.book, cover: { ...s.book.cover, ...patch } } } : {})), markSaved: () => set({ dirty: false }), })); + +let lastActive = ""; +useBook.subscribe((s) => { + if (s.book && s.activeChapterId && s.activeChapterId !== lastActive) { + lastActive = s.activeChapterId; + saveActiveChapter(s.book.id, s.activeChapterId); + } +});