# Bookmark Feature Implementation ## Task Implement bookmark functionality for the e-reader, replacing existing "Coming soon" stubs with full add/remove bookmark, visual page indicator, and bookmark navigation features. ## Changes Made ### New Files Created - **`src/util/BookmarkStore.h`** / **`src/util/BookmarkStore.cpp`** - Bookmark persistence utility. Stores bookmarks as binary data (`bookmarks.bin`) per-book in the epub cache directory on SD card. Each bookmark is a (spineIndex, pageNumber) pair (4 bytes). Provides static methods: `load`, `save`, `addBookmark`, `removeBookmark`, `hasBookmark`. - **`src/activities/reader/EpubReaderBookmarkSelectionActivity.h`** / **`.cpp`** - New activity for the "Go to Bookmark" list UI, modeled on the existing chapter selection activity. Shows bookmark entries as "Chapter Title - Page N" with ButtonNavigator for scrolling. Selecting a bookmark navigates to that spine/page. ### Edited Files - **`src/activities/reader/EpubReaderMenuActivity.h`** - Added `REMOVE_BOOKMARK` to `MenuAction` enum. Changed `buildMenuItems()` to accept `isBookmarked` parameter; dynamically shows "Remove Bookmark" or "Add Bookmark" as the first menu item. - **`src/activities/reader/EpubReaderActivity.cpp`** - Main integration point: - Added includes for `BookmarkStore.h` and `EpubReaderBookmarkSelectionActivity.h` - Menu creation now computes `isBookmarked` state and passes it through - `ADD_BOOKMARK` handler: calls `BookmarkStore::addBookmark()`, shows "Bookmark added" popup - `REMOVE_BOOKMARK` handler (new): calls `BookmarkStore::removeBookmark()`, shows "Bookmark removed" popup - `GO_TO_BOOKMARK` handler: loads bookmarks, opens `EpubReaderBookmarkSelectionActivity` if any exist, falls back to Table of Contents if no bookmarks but TOC exists, otherwise returns to reader - `renderContents()`: draws a small bookmark ribbon (fillPolygon, 5-point shape) in the top-right corner when the current page is bookmarked ## Follow-up Changes (same session) ### Force half refresh on menu exit - `onReaderMenuBack()`: sets `pagesUntilFullRefresh = 1` so the next render uses `HALF_REFRESH` to clear menu/popup ghosting artifacts from the e-ink display. - `ADD_BOOKMARK` / `REMOVE_BOOKMARK` handlers: also set `pagesUntilFullRefresh = 1` after their popups. ### Bookmark snippet (first sentence) - `Bookmark` struct now includes a `snippet` string field storing the first sentence from the bookmarked page. - `BookmarkStore` binary format upgraded to v2: version marker byte (0xFF) + count + entries with variable-length snippet. Backward-compatible: reads v1 files (no snippets) gracefully. - `addBookmark()` now accepts an optional `snippet` parameter (max 120 chars). - `EpubReaderActivity::onReaderMenuConfirm(ADD_BOOKMARK)`: extracts the first sentence from the page by iterating PageLine elements and their TextBlock words, stopping at sentence-ending punctuation (.!?:). - `EpubReaderBookmarkSelectionActivity::getBookmarkLabel()`: displays bookmark as "Chapter Title - First sentence here - Page N". ## Follow-up Items - Test on device to verify bookmark ribbon sizing/positioning looks good across orientations - Consider caching bookmark state in memory to avoid SD reads on every page render (currently `hasBookmark` reads from SD each time in `renderContents`) - The bookmark selection list could potentially support deleting bookmarks directly from the list in a future iteration