diff --git a/templates/audio_reader.templ b/templates/audio_reader.templ index 17cf52a3..a6996cb8 100644 --- a/templates/audio_reader.templ +++ b/templates/audio_reader.templ @@ -155,8 +155,17 @@ templ AudioReader(user User, metadata ReaderMetadata) { style="background-color: var(--accent); color: var(--bg-primary);" aria-label="Play or pause" > - ▶ - ❚❚ + + + + + + -

+
- +
@@ -443,12 +452,12 @@ templ AudioReader(user User, metadata ReaderMetadata) { style="background-color: var(--bg-secondary); border: 1px solid var(--border);" >

Bookmark at 0:00

- + rows="3" + placeholder="Optional note — markdown welcome (e.g. **great quote**, list, [link](https://…))" + class="input mb-2 resize-y" + >
diff --git a/templates/audio_reader_templ.go b/templates/audio_reader_templ.go index 97433c51..c6918af0 100644 --- a/templates/audio_reader_templ.go +++ b/templates/audio_reader_templ.go @@ -172,7 +172,7 @@ func AudioReader(user User, metadata ReaderMetadata) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "

—

0:00 0:00
Chapters

No chapters detected for this audiobook.

Bookmarks

No bookmarks yet — tap the button to save your position.

Bookmark at 0:00

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "

—

0:00 0:00
Chapters

No chapters detected for this audiobook.

Bookmarks

No bookmarks yet — tap the button to save your position.

Bookmark at 0:00

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/web/src/audio.ts b/web/src/audio.ts index 608afa61..49cdb415 100644 --- a/web/src/audio.ts +++ b/web/src/audio.ts @@ -13,6 +13,7 @@ // tracks) — the mobile-web native feel from handoff item 6. import { Alpine } from "./alpine"; +import { renderNoteHtml } from "./markdown"; import { showToast } from "./toast"; type AudioChapter = { index: number; title: string; start: number; end: number }; @@ -375,6 +376,12 @@ Alpine.data("audioReader", () => ({ } }, + // Markdown notes: the drawer renders sanitized HTML from the same + // pipeline the reader's annotations use; editing stays raw markdown. + bookmarkNoteHtml(b: Record): string { + return renderNoteHtml(String(b.notes ?? "")); + }, + bookmarkSeconds(position: string): number { const n = parseInt(String(position).slice("audio:".length), 10); return Number.isFinite(n) ? n : 0; diff --git a/web/src/dashboard.ts b/web/src/dashboard.ts index b2ea6bd0..52830b51 100644 --- a/web/src/dashboard.ts +++ b/web/src/dashboard.ts @@ -286,29 +286,61 @@ const BOOK_OPEN_ICON = '' + '' + ""; +const HEADPHONES_ICON = + '"; +const COMIC_ICON = + '"; +const MANGA_ICON = + '"; +// 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; + return ""; +} + function renderBookCard(book: BookInfo): string { const coverUrl = book.cover_image_path || "/static/placeholder-book.svg"; const actionHref = book.has_conflict ? `/media/${book.media_item_id}` : `/readers/${book.media_item_id}`; + const isAudio = book.format_group === "audiobook"; const actionLabel = book.has_conflict ? `Resolve progress conflict for ${book.title}` - : `Read ${book.title}`; + : isAudio + ? `Listen to ${book.title}` + : `Read ${book.title}`; const actionTitle = book.has_conflict ? "Resolve progress conflict" - : "Read"; + : isAudio + ? "Listen" + : "Read"; + const actionIcon = isAudio ? HEADPHONES_ICON : BOOK_OPEN_ICON; + const badgeIcon = badgeIconFor(book.format_group || ""); return `
diff --git a/web/src/types/api.d.ts b/web/src/types/api.d.ts index ad84ff20..fbb308fa 100644 --- a/web/src/types/api.d.ts +++ b/web/src/types/api.d.ts @@ -102,6 +102,7 @@ interface BookInfo { author: string; cover_image_path: string; has_conflict: boolean; + format_group: string; } // Dashboard type definitions