fix(reader): clean chapter title display and declutter audio panel
- Strip leading digit prefix (e.g. "6Chapter 6 → Chapter 6") and content after first newline (scraped date artifacts) from chapter titles - Add "CHAPTER N" eyebrow label above the h1 for clear hierarchy - Show date_label as small muted text in the meta row - Remove double-border / mt-6 gap from standard AudioPlayer inside the chapter page's collapsible panel (was rendering two nested boxes) - Remove redundant "Audio Narration" label (toggle already says "Listen") Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1024,15 +1024,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- ── Standard player ─────────────────────────────────────────────────────── -->
|
<!-- ── Standard player ─────────────────────────────────────────────────────── -->
|
||||||
<div class="mt-6 p-4 rounded-lg bg-(--color-surface-2) border border-(--color-border)">
|
<div class="p-4">
|
||||||
<div class="flex items-center justify-between gap-2 mb-3">
|
<div class="flex items-center justify-end gap-2 mb-3">
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<svg class="w-4 h-4 text-(--color-brand)" fill="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path d="M12 3v10.55A4 4 0 1014 17V7h4V3h-6z"/>
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm text-(--color-text) font-medium">{m.reader_audio_narration()}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Voice selector button -->
|
<!-- Voice selector button -->
|
||||||
{#if voices.length > 0}
|
{#if voices.length > 0}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -287,6 +287,15 @@
|
|||||||
html ? (html.replace(/<[^>]*>/g, '').match(/\S+/g)?.length ?? 0) : 0
|
html ? (html.replace(/<[^>]*>/g, '').match(/\S+/g)?.length ?? 0) : 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Strip scraper artifacts from chapter titles:
|
||||||
|
// - Leading digit(s) prefixed before "Chapter" (e.g. "6Chapter 6 : ...")
|
||||||
|
// - Everything after the first newline (often includes a scraped date)
|
||||||
|
const cleanTitle = $derived.by(() => {
|
||||||
|
let t = (data.chapter.title || '').split('\n')[0].trim();
|
||||||
|
t = t.replace(/^\d+(?=Chapter)/i, '').trim();
|
||||||
|
return t || `Chapter ${data.chapter.number}`;
|
||||||
|
});
|
||||||
|
|
||||||
// Audio panel: auto-open if this chapter is already loaded/playing in the store
|
// Audio panel: auto-open if this chapter is already loaded/playing in the store
|
||||||
// svelte-ignore state_referenced_locally
|
// svelte-ignore state_referenced_locally
|
||||||
let audioExpanded = $state(
|
let audioExpanded = $state(
|
||||||
@@ -301,7 +310,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{data.chapter.title || m.reader_chapter_n({ n: String(data.chapter.number) })} — {data.book.title} — libnovel</title>
|
<title>{cleanTitle} — {data.book.title} — libnovel</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<!-- Reading progress bar (scroll mode, fixed at top of viewport) -->
|
<!-- Reading progress bar (scroll mode, fixed at top of viewport) -->
|
||||||
@@ -364,8 +373,11 @@
|
|||||||
|
|
||||||
<!-- Chapter heading + meta + language switcher -->
|
<!-- Chapter heading + meta + language switcher -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
|
<p class="text-xs font-semibold text-(--color-brand) uppercase tracking-widest mb-1.5">
|
||||||
|
{m.reader_chapter_n({ n: String(data.chapter.number) })}
|
||||||
|
</p>
|
||||||
<h1 class="text-xl font-bold text-(--color-text) leading-snug">
|
<h1 class="text-xl font-bold text-(--color-text) leading-snug">
|
||||||
{data.chapter.title || m.reader_chapter_n({ n: String(data.chapter.number) })}
|
{cleanTitle}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex items-center flex-wrap gap-x-3 gap-y-1.5 mt-2">
|
<div class="flex items-center flex-wrap gap-x-3 gap-y-1.5 mt-2">
|
||||||
{#if wordCount > 0}
|
{#if wordCount > 0}
|
||||||
@@ -375,6 +387,9 @@
|
|||||||
~{Math.max(1, Math.round(wordCount / 200))} min
|
~{Math.max(1, Math.round(wordCount / 200))} min
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if data.chapter.date_label}
|
||||||
|
<span class="text-(--color-muted) text-xs opacity-60">{data.chapter.date_label}</span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<!-- Language switcher (inline, compact) -->
|
<!-- Language switcher (inline, compact) -->
|
||||||
{#if !data.isPreview}
|
{#if !data.isPreview}
|
||||||
@@ -483,7 +498,7 @@
|
|||||||
<AudioPlayer
|
<AudioPlayer
|
||||||
slug={data.book.slug}
|
slug={data.book.slug}
|
||||||
chapter={data.chapter.number}
|
chapter={data.chapter.number}
|
||||||
chapterTitle={data.chapter.title || m.reader_chapter_n({ n: String(data.chapter.number) })}
|
chapterTitle={cleanTitle}
|
||||||
bookTitle={data.book.title}
|
bookTitle={data.book.title}
|
||||||
cover={data.book.cover}
|
cover={data.book.cover}
|
||||||
nextChapter={data.next}
|
nextChapter={data.next}
|
||||||
|
|||||||
Reference in New Issue
Block a user