feat(annotations + player): bookmark rows in the banner, stacked volume panel, badge parity for comics/manga
Annotations banner: audio/text bookmarks join notes and highlights — third row kind (bookmark glyph indicator, H:MM:SS / Page N / Chapter N friendly labels), edit-note/delete actions branch by kind, and the banner's export now covers all annotations. The archived ledger (ListDeletedAnnotationsForBook) already tombstoned bookmarks equally and Restore/Purge take annotation_type=bookmark — verified live: delete → restore brings the row back with its note intact. Volume: the dragger sits BELOW the mute icon in the panel (stacked flyout shape) with the horizontal slider from the last round. Badges: the real fix for comics/manga — format_group only knows 'comic_archive' (manga_type is unreliable across sources), so the comic/manga split is resolved from the LIBRARY type server-side. handlers.MediaBadgeKind + BookInfo.Badge at every construction site (dashboard SSR + sections API, collections, series, and the bookshelf search grid — which was also missing FormatGroup entirely, so no card ever badged there). Both renderers (templ + dashboard.ts) switch on the resolved badge. Verified: All-Books grid renders 2 Comic + 3 Manga badges, dashboard the audiobook one. Infra: header.templ main.js src gains ?v=<stamp> — browsers keyed an intermediate bundle by URL and served it across rebuilds (the stale 'Add note' the user saw); bump the stamp when JS stale-shadows.
This commit is contained in:
1 parent
2c714dcb60
commit
89665fb2bc
19 files changed
+273
-92
No files matched your search
+16
-7
@@ -302,11 +302,20 @@ const MANGA_ICON =
|
||||
'<path d="M4 4h16v13a3 3 0 0 1-3 3H4z"></path>' +
|
||||
'<path d="M4 4v16M10 4h10v8"></path>' +
|
||||
"</svg>";
|
||||
// Media-type identifier for mixed grids: '' for books (unbadged default)
|
||||
function badgeIconFor(formatGroup: string): string {
|
||||
if (formatGroup === "audiobook") return HEADPHONES_ICON;
|
||||
if (formatGroup === "comic") return COMIC_ICON;
|
||||
if (formatGroup === "manga") return MANGA_ICON;
|
||||
// Media-type identifier for mixed grids, from the server-resolved badge
|
||||
// (format_group + library type — comic vs manga is a LIBRARY distinction):
|
||||
// '' for books (unbadged default)
|
||||
function badgeIconFor(badge: string): string {
|
||||
if (badge === "audiobook") return HEADPHONES_ICON;
|
||||
if (badge === "comic") return COMIC_ICON;
|
||||
if (badge === "manga") return MANGA_ICON;
|
||||
return "";
|
||||
}
|
||||
|
||||
function badgeTitleFor(badge: string): string {
|
||||
if (badge === "audiobook") return "Audiobook";
|
||||
if (badge === "comic") return "Comic";
|
||||
if (badge === "manga") return "Manga";
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -328,7 +337,7 @@ function renderBookCard(book: BookInfo): string {
|
||||
? "Listen"
|
||||
: "Read";
|
||||
const actionIcon = isAudio ? HEADPHONES_ICON : BOOK_OPEN_ICON;
|
||||
const badgeIcon = badgeIconFor(book.format_group || "");
|
||||
const badgeIcon = badgeIconFor(book.badge || "");
|
||||
|
||||
return `
|
||||
<div class="flex-shrink-0 w-36 sm:w-40 snap-start" data-media-item-id="${book.media_item_id}">
|
||||
@@ -340,7 +349,7 @@ function renderBookCard(book: BookInfo): string {
|
||||
class="w-full h-full object-cover"
|
||||
loading="lazy"
|
||||
onerror="this.src='/static/placeholder-book.svg'">
|
||||
${badgeIcon ? `<span class="absolute top-2 right-2 z-10 flex items-center justify-center h-7 w-7 rounded-full shadow-sm" style="background-color: color-mix(in srgb, var(--bg-primary) 78%, transparent); color: var(--text-primary); backdrop-filter: blur(3px);" title="${isAudio ? "Audiobook" : book.format_group === "comic" ? "Comic" : "Manga"}">${badgeIcon}</span>` : ""}
|
||||
${badgeIcon ? `<span class="absolute top-2 right-2 z-10 flex items-center justify-center h-7 w-7 rounded-full shadow-sm" style="background-color: color-mix(in srgb, var(--bg-primary) 78%, transparent); color: var(--text-primary); backdrop-filter: blur(3px);" title="${badgeTitleFor(book.badge || "")}">${badgeIcon}</span>` : ""}
|
||||
</div>
|
||||
<div class="book-card-meta">
|
||||
<h3 class="font-semibold text-sm leading-snug line-clamp-2" style="color: var(--text-primary)" title="${book.title}">
|
||||
|
||||
Reference in new issue
Block a user