diff --git a/docs/TECHNICAL.md b/docs/TECHNICAL.md index 123abe5..c04084f 100644 --- a/docs/TECHNICAL.md +++ b/docs/TECHNICAL.md @@ -78,6 +78,7 @@ All files are stored under `library/` (relative to the app working directory, ma `GET /api/library` runs in fast-path mode by default (DB-only, no full disk rescan). For a forced sync: `GET /api/library?rescan=true` or `POST /library/rescan`. `include_file_info=true` is optional for file size/mtime enrichment. +ETag caching: response includes `ETag: "{count}-{max_updated_at_unix}"` and `Cache-Control: no-cache`. Client sends `If-None-Match`; server returns `304 Not Modified` when nothing changed. `/api/home` returns: - `continue_reading` @@ -164,6 +165,18 @@ Home read sections are ordered oldest-first: Publish flow: all chapters are run through `normalize_wysiwyg_html()`, then `build_epub()` produces an EPUB 2.0 ZIP. The file path is computed via `make_rel_path(media_type="epub", …)`. The book is inserted into the library with `needs_review=True`. The draft is deleted on success. +### `routers/following.py` +- `GET /following` — Following page (author URL management) +- `GET /api/following` — all distinct library authors with URL (if set), book count, and last-added date +- `POST /api/following/{author_name}` — set or clear URL for an author (empty `url` removes the record) + +`GET /api/following` returns one entry per non-archived author: +```json +{ "name": "Author Name", "book_count": 5, "last_added": "2026-03-27T…", "url": "https://…" } +``` + +URL is stored in the `authors` table (`name` unique, `url`, `created_at`, `updated_at`). + ### `routers/backup.py` - `GET /backup` — backup page - `GET /api/backup/credentials` — Dropbox settings (includes `app_key_configured` flag) @@ -256,6 +269,8 @@ Dropbox settings are managed via the web UI on `/backup`. - Bookmarks: saved per book via `POST /library/bookmarks/{filename}`; shown in Library sidebar section; navigated via `?bm_ch=N&bm_scroll=F` URL params on reader page. - Convert page: after loading metadata, if a book with the same title+author already exists in the library, a warning banner is shown (with a link to the existing book); user can still proceed with conversion. Check is done server-side in `/preload` response (`already_exists`, `existing_books`). - Duplicates view (`#duplicates`): groups non-archived books by `(title, author)` (case-insensitive); shows only groups with ≥ 2 copies; counter in sidebar shows total number of duplicate books. Detection is entirely client-side from the existing library data. +- Incomplete view (`#incomplete`): shows all non-archived books where `publication_status` is not `Complete` (Ongoing, Hiatus, or blank); sidebar counter included. +- Following page (`/following`): dedicated page in its own sidebar section between Library and Tools; shows all library authors with their external URL; two tabs — Following (authors with URL set) and All Authors; inline URL editing with keyboard support (Enter = save, Escape = cancel); clicking Visit opens the external URL in a new tab. Author URLs are stored in the `authors` table. Sidebar counter shows number of followed authors. - Book Builder (`/builder`): create EPUB books from scratch; drafts stored in `builder_drafts` (JSONB chapters); contenteditable editor with toolbar (bold/italic/underline/blockquote/author-note/scene-break/normalize); autosave every 30 s + Ctrl+S; publish normalizes HTML via `normalize_wysiwyg_html()` and builds EPUB via `build_epub()`. --- @@ -277,9 +292,13 @@ Dropbox settings are managed via the web UI on `/backup`. --- ## Performance Notes -- Library load is optimized for large datasets: - - `list_library_json()` uses pre-aggregation for `reading_sessions`. +- Library load is optimized for large datasets (1000+ books): + - `list_library_json()` uses `json_agg` in the main query to inline tags per book — eliminates a separate `SELECT * FROM book_tags` query and Python merge loop. - `has_cached_cover` is provided directly via SQL join instead of full cache fetch. + - `reading_sessions` is pre-aggregated in a subquery. + - ETag on `/api/library`: cheap `COUNT + MAX(updated_at)` query before full load; `304 Not Modified` on cache hit. +- Front-end rendering uses `IntersectionObserver` to defer both cover image loading and placeholder canvas drawing until cards enter the viewport — prevents hundreds of simultaneous HTTP requests and canvas operations on initial render. +- `renderBooksGrid`, `renderDuplicatesView`, `renderSeriesDetail` all use a single DOM pass: cover `` and `` are set up via `card.querySelector` immediately after `innerHTML` is set, eliminating a second full iteration with `document.getElementById` calls. - Additional migration indexes: - `idx_library_sort_coalesce` - `idx_library_needs_review` diff --git a/docs/changelog-develop.md b/docs/changelog-develop.md index f51d636..26a616d 100644 --- a/docs/changelog-develop.md +++ b/docs/changelog-develop.md @@ -3,6 +3,22 @@ This file tracks changes on the `develop` line. `changelog.md` can later be used for release summaries. +## 2026-03-28 (2) +- Performance: library page now loads instantly for large collections (1000+ books) + - `IntersectionObserver` defers both cover image loading and placeholder canvas drawing until cards enter the viewport — eliminates hundreds of upfront canvas ops that blocked the initial render + - `ETag` caching on `/library/list`: server returns `304 Not Modified` when nothing changed, client skips JSON parse and re-download + - Single DOM pass in `renderBooksGrid`, `renderDuplicatesView`, `renderSeriesDetail`: canvas and img set up via `card.querySelector` immediately after `innerHTML`, removing a second iteration with `document.getElementById` per card + - `book_tags` joined via `json_agg` in the main `list_library_json()` query, eliminating a separate `SELECT * FROM book_tags` query and Python merge loop + - `loadLibrary` now shows an error message instead of staying stuck on "Loading…" when the fetch or render fails + +## 2026-03-28 (1) +- Added Following page (`/following`): track external author URLs outside Library and Tools + - New `authors` table: `name` (unique), `url`, `created_at`, `updated_at` + - New `routers/following.py`: `GET /following` page, `GET /api/following` (all authors + URL + book count + last added), `POST /api/following/{name}` (set/clear URL) + - Sidebar: new Following section between Library and Tools; counter shows number of followed authors + - Following page: two tabs — Following (authors with URL) and All Authors; inline URL editing with Enter/Escape keyboard support; Visit button opens external URL in a new tab; author name links to library author view +- Added Incomplete view to Library (`#incomplete`): shows all non-archived books where `publication_status ≠ Complete`; sidebar counter included; entry placed after New in the Library section + ## 2026-03-27 (1) - Convert page: duplicate warning shown after loading metadata when a book with the same title+author already exists in the library; warning includes a link to the existing book; user can still proceed with conversion - Library: added Duplicates section to sidebar (between Rated and Statistics); counter shows total number of books that are part of a duplicate group (same title+author, case-insensitive); Duplicates view groups books by title+author with a subheading per group