From 2f98f5ec1cd4bdf283cee90106248406cb3ef76a Mon Sep 17 00:00:00 2001 From: PJ Date: Mon, 22 Jun 2026 13:33:23 -0400 Subject: [PATCH] fix(editor): prune chapter positions on delete and guard quota Saved cursor/scroll and active-chapter entries accumulated per book forever and were never removed when a book was deleted. Add clearPositions and call it from the delete flow, and guard localStorage writes against quota or unavailable storage. Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk --- src/components/Library.tsx | 2 ++ src/editor/positions.ts | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/components/Library.tsx b/src/components/Library.tsx index 40a3bf9..7c2d914 100644 --- a/src/components/Library.tsx +++ b/src/components/Library.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import { type BookSummary, createAndOpenBook, deleteBook, exampleBook, listBooks, loadBook, saveBook } from "../library"; import type { Book } from "../model/book"; import { importEpub } from "../import/epub"; +import { clearPositions } from "../editor/positions"; import { isDesktop } from "../ipc"; import { ConfirmDialog } from "./ConfirmDialog"; import { Icon } from "./Icon"; @@ -38,6 +39,7 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) { const removeBook = async () => { if (!pendingDelete) return; await deleteBook(pendingDelete.id); + clearPositions(pendingDelete.id); setPendingDelete(null); refresh(); }; diff --git a/src/editor/positions.ts b/src/editor/positions.ts index 2fbbd7d..5355c69 100644 --- a/src/editor/positions.ts +++ b/src/editor/positions.ts @@ -16,6 +16,14 @@ function readAll(): Store { } } +function write(key: string, value: unknown): void { + try { + localStorage.setItem(key, JSON.stringify(value)); + } catch { + return; + } +} + export function loadPosition(bookId: string, chapterId: string): ChapterPosition | null { return readAll()[bookId]?.[chapterId] ?? null; } @@ -23,7 +31,7 @@ export function loadPosition(bookId: string, chapterId: string): ChapterPosition export function savePosition(bookId: string, chapterId: string, position: ChapterPosition): void { const all = readAll(); (all[bookId] ||= {})[chapterId] = position; - localStorage.setItem(KEY, JSON.stringify(all)); + write(KEY, all); } const ACTIVE_KEY = "margin-active-chapter"; @@ -43,5 +51,14 @@ export function loadActiveChapter(bookId: string): string | null { export function saveActiveChapter(bookId: string, chapterId: string): void { const all = readActive(); all[bookId] = chapterId; - localStorage.setItem(ACTIVE_KEY, JSON.stringify(all)); + write(ACTIVE_KEY, all); +} + +export function clearPositions(bookId: string): void { + const positions = readAll(); + const active = readActive(); + delete positions[bookId]; + delete active[bookId]; + write(KEY, positions); + write(ACTIVE_KEY, active); }