# 015 — EPUB bookmarks and notes (physical-book UX)

**Status:** Implemented  
**Created:** 2026-06-04

## Objective

Let users select passage text while reading an EPUB, save a bookmark (with optional thought), list markers in chapter order, and jump back to the exact location. Global view groups markers by book in a Reddit-style thread layout.

## Fix: AnnotationsProvider

`AnnotationsProvider` wraps all routes in [`App.tsx`](frontend/src/App.tsx) so `/bookmarks-notes` and the reader can use `useAnnotations()`.

## Data model

`Bookmark` (annotations app) references **`books.EBook`**, not catalog `Book`:

| Field | Purpose |
|-------|---------|
| `ebook` | FK to uploaded ebook |
| `epub_cfi` | EPUB CFI anchor |
| `chapter_index` | Spine index for sort order |
| `chapter_title` | Display label |
| `location_text` | Selected passage quote |
| `content` | Optional user thought (empty = bookmark only) |
| `page` | Legacy display field (`chapter_index + 1`) |

Unique: `(user, ebook, epub_cfi)`.

Default API ordering: `chapter_index`, `epub_cfi`.

Legacy `Note` model remains for old API; new UX uses `Bookmark.content` only.

## Reader flow

```mermaid
sequenceDiagram
  participant User
  participant EpubView
  participant Popover
  participant API
  User->>EpubView: Select text
  EpubView->>Popover: Show near selection
  User->>Popover: Save optional thought
  Popover->>API: POST /annotations/bookmarks/
```

1. Text selection via epub.js `selected` event and content `mouseup` hook.
2. [`SelectionPopover`](frontend/src/components/reader/SelectionPopover.tsx) — floating UI, optional textarea.
3. Toolbar bookmark icon opens [`BookMarkersPanel`](frontend/src/components/reader/BookMarkersPanel.tsx) (current ebook only).
4. “Go to passage” sets reader `location` to stored CFI (`/read/:id` with router state).

## Global page (`/bookmarks-notes`)

[`MarkerThreadsView`](frontend/src/components/annotations/MarkerThreadsView.tsx):

- Groups markers by ebook (collapsible book rows).
- Within each book: chapter order, passage as blockquote, thought indented below (Reddit-style).
- Optional filter: `/bookmarks-notes/:ebookId`.

## API

- `GET /api/annotations/bookmarks/?ebook={id}`
- `POST /api/annotations/bookmarks/` — body: `ebook`, `epub_cfi`, `chapter_index`, `chapter_title`, `location_text`, `content`

## i18n

New keys under `annotations.*` (EN/ES): `saveMarker`, `thoughtPlaceholder`, `bookmarkOnly`, `selectTextHint`, `goToPassage`, `inBookPanel`, `markerCount`, etc.

## Verification

1. `/bookmarks-notes` loads without provider error.
2. Select text in reader → popover → save with/without thought.
3. Markers appear in reader panel and global page under correct book, in chapter order.
4. “Go to passage” opens the correct location.

See also [016 — Bookmark reading anchor](016-bookmark-reading-anchor.md) for preserving reading position while peeking at bookmarks.

## Out of scope

- PDF selection
- Multiple replies per passage
- Migrating legacy `Note` rows into `Bookmark`
