The bookmarks list was rendering "Manga bookmark <date>" with no
indication of which manga the bookmark referred to. The data is
already in the DB — the list query just wasn't pulling it.
Backend:
- BookmarkSummary gains manga_title (String) and
manga_cover_image_path (Option<String>). Populated by an INNER JOIN
on `mangas` in `repo::bookmark::list_for_user`. The JOIN is INNER
because `bookmarks.manga_id` has ON DELETE CASCADE, so a bookmark
cannot outlive its manga. Chapter LEFT JOIN unchanged.
- The existing list_me_enriches_chapter_bookmarks_with_chapter_number
test now also asserts manga_title is populated for both chapter-
and manga-level bookmarks, and that manga_cover_image_path is null
when no cover was uploaded.
Frontend:
- Bookmark type carries optional manga_title and
manga_cover_image_path (optional because POST /bookmarks returns
the bare Bookmark, not the enriched summary).
- /bookmarks page redesigned as a grid: cover thumbnail (64×96 with
a placeholder when no cover) on the left, then the manga title (as
the primary link), then either "Chapter N — page M" linked to the
reader, "(chapter removed)" for orphan chapter bookmarks, or
"Whole manga" for manga-level bookmarks. Bookmark date moves to a
subdued footer.
- E2E fixtures track the enriched shape returned by the list endpoint
(vs. the bare Bookmark returned by POST). The toggle test now
asserts the manga title appears on the bookmarks card after the
bookmark is created.
Also: tighten .gitignore. `/data` only catches the compose volume
root; the dev backend writes to `/backend/data` (default STORAGE_DIR
is `./data/storage` relative to backend cwd), so local uploads were
showing as untracked. Adding `/backend/data` keeps test uploads out
of the index.
Lockstep version bump to 0.11.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The reader route is keyed on chapter number (URL `/manga/{id}/chapter/{n}`,
loaded via `Number(params.n)`), but the bookmarks list was building
hrefs from `chapter_id` (a UUID). Following any chapter bookmark
produced a NaN load on the reader page.
Fix at the API layer so every consumer of /me/bookmarks gets the
information without a follow-up round-trip per bookmark.
- domain::BookmarkSummary: new type, `Bookmark` plus
`chapter_number: Option<i32>`. Populated by a LEFT JOIN on chapters
so manga-level bookmarks come back with `chapter_number = null` and
chapter-level ones get the value. `Bookmark` itself stays minimal
for POST / DELETE responses.
- repo::bookmark::list_for_user returns Vec<BookmarkSummary>.
- api::bookmarks::list_me returns PagedResponse<BookmarkSummary>.
- Frontend `Bookmark` type carries an optional `chapter_number`.
- /bookmarks page builds `/manga/{manga_id}/chapter/{chapter_number}`
for chapter bookmarks, falling back to the manga overview if the
chapter has been deleted out from under the bookmark (chapter_id is
ON DELETE SET NULL, so this is a real edge case).
New test asserts both branches of the JOIN: a chapter-level bookmark
comes back with the right chapter_number and page, a manga-level one
has a null chapter_number.
Lockstep version bump to 0.9.2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Set up Mangalord with a Rust/axum backend, SvelteKit frontend, Postgres,
and Docker Compose deployment. Establishes the architecture and TDD
patterns the project will extend:
- Hexagonal-ish backend layering (domain / repo / storage / api) with
a pluggable Storage trait (LocalStorage today, S3 as a future impl).
- Initial migration: users, mangas, chapters, bookmarks.
- Vertical slice for mangas (list, search, create, get) with
#[sqlx::test] integration coverage and storage unit tests.
- SvelteKit frontend using Svelte 5 runes, typed API client, Vitest
unit tests and Playwright e2e with route mocking.
- CLAUDE.md documenting layering, TDD/git/SemVer workflow rules, and
extension points (tags, fulltext search, OCR, S3, auth).
- Project-scoped .claude/settings.json with permission allowlist for
the toolchain (git, cargo, npm/vite, docker, psql, gh, doc fetches).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>