feat(mangas): add tag-similarity "Similar" section on manga detail

Add GET /v1/mangas/:id/similar returning the top 5 mangas ranked by
Jaccard tag overlap (shared / union), excluding self, as MangaCard items
in a plain { items } object. Surface them in a hidden-when-empty
"Similar" section on the manga detail page, reusing MangaCard.

Backend: new repo::manga::list_similar with a manga_tags self-join; a
shared cards_from_rows helper (also used by list_cards) that loads
authors+genres concurrently; single-sourced column list via MANGA_COLS +
manga_cols(alias) to replace the fragile SELECT_COLS string-splitting.

Frontend: getSimilarMangas client fn (guards a malformed body), loader
fetch with non-critical .catch fallback, and the catalog grid hoisted to
a shared global .manga-grid (lib/styles/tokens.css), de-duplicating the
per-route copies.

Bump version 0.62.0 -> 0.63.0 (backend + frontend in lockstep).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-13 15:48:15 +02:00
parent 6c901e64c9
commit 33f684887d
15 changed files with 423 additions and 54 deletions

View File

@@ -870,14 +870,8 @@
margin: var(--space-2) 0;
}
.manga-grid {
list-style: none;
padding: 0;
margin: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: var(--space-4);
}
/* `.manga-grid` is a shared global (see lib/styles/tokens.css), including
its four-per-row phone layout. */
/* Mobile-only helpers. Defaults flip below the 640px breakpoint
so the same chrome doesn't render twice. */
@@ -899,15 +893,5 @@
the 28rem cap is desktop-only. */
max-width: none;
}
.manga-grid {
/* Four covers per row on phones (per user preference) at a
tight gap. `minmax(0, 1fr)` is load-bearing — without it
the implicit `minmax(auto, 1fr)` lets grid cells grow to
their intrinsic min-content width and push the whole
grid past the viewport edge. */
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: var(--space-2);
}
}
</style>