Fix chapter reader nav bar: use hamburger icon for back-to-book, show chapter date below title
Some checks failed
CI / Lint (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled

- Replace bare ← back-to-book link with ☰ (hamburger) icon to eliminate
  the visual double-arrow confusion with the ← Prev chapter button
- Export SplitChapterTitle in writer package and call it in handleChapter
  to split the raw heading into (title, date) — fixes concatenated title+date bug
- Display ChapterDate as a smaller muted line below the chapter title
  in the center nav button
- Bump nav bar height from h-12 to h-14 to accommodate two-line center button
This commit is contained in:
Admin
2026-03-01 22:03:47 +05:00
parent ca33f8c3cf
commit 7e4bb87da0
2 changed files with 18 additions and 12 deletions

View File

@@ -1379,19 +1379,19 @@ const chapterTmpl = `
<!-- aria-hidden keeps it out of accessibility trees / Reader Mode heuristics --> <!-- aria-hidden keeps it out of accessibility trees / Reader Mode heuristics -->
<nav id="reader-header" aria-hidden="true" <nav id="reader-header" aria-hidden="true"
style="position:sticky;top:0;z-index:50;background:#09090b;border-bottom:1px solid #27272a;"> style="position:sticky;top:0;z-index:50;background:#09090b;border-bottom:1px solid #27272a;">
<div class="max-w-2xl mx-auto px-4 flex items-center gap-2 h-12"> <div class="max-w-2xl mx-auto px-4 flex items-center gap-2 h-14">
<!-- Back to book --> <!-- Back to book (book/list icon, visually distinct from ← Prev) -->
<a href="/books/{{.Slug}}" <a href="/books/{{.Slug}}"
hx-get="/books/{{.Slug}}" hx-get="/books/{{.Slug}}"
hx-target="#main-content" hx-target="#main-content"
hx-push-url="true" hx-push-url="true"
hx-swap="innerHTML" hx-swap="innerHTML"
title="Chapter list" title="Back to chapter list"
aria-label="Back to chapter list" aria-label="Back to chapter list"
style="flex-shrink:0;padding:0.375rem 0.5rem;border-radius:0.5rem;color:#a1a1aa;font-size:0.875rem;text-decoration:none;transition:color 0.15s;" style="flex-shrink:0;padding:0.375rem 0.5rem;border-radius:0.5rem;color:#a1a1aa;font-size:1rem;text-decoration:none;transition:color 0.15s;line-height:1;"
onmouseover="this.style.color='#fbbf24'" onmouseout="this.style.color='#a1a1aa'"> onmouseover="this.style.color='#fbbf24'" onmouseout="this.style.color='#a1a1aa'">
&#8592; &#9776;
</a> </a>
<!-- Prev chapter --> <!-- Prev chapter -->
@@ -1416,8 +1416,11 @@ const chapterTmpl = `
onclick="toggleChapterList()" onclick="toggleChapterList()"
title="Jump to chapter" title="Jump to chapter"
aria-label="Jump to chapter" aria-label="Jump to chapter"
style="flex:1;min-width:0;display:flex;align-items:center;justify-content:center;gap:0.375rem;overflow:hidden;background:transparent;border:none;cursor:pointer;padding:0.375rem 0.25rem;"> style="flex:1;min-width:0;display:flex;align-items:center;justify-content:center;gap:0.375rem;overflow:hidden;background:transparent;border:none;cursor:pointer;padding:0.25rem 0.25rem;">
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.8125rem;color:#a1a1aa;">{{.Title}}</span> <span style="min-width:0;display:flex;flex-direction:column;align-items:center;overflow:hidden;">
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.8125rem;color:#a1a1aa;max-width:100%;">{{.Title}}</span>
{{if .ChapterDate}}<span style="font-size:0.6875rem;color:#52525b;white-space:nowrap;">{{.ChapterDate}}</span>{{end}}
</span>
<span style="flex-shrink:0;font-size:0.625rem;color:#52525b;" aria-hidden="true">&#9660;</span> <span style="flex-shrink:0;font-size:0.625rem;color:#52525b;" aria-hidden="true">&#9660;</span>
</button> </button>
@@ -2166,6 +2169,7 @@ func (s *Server) handleChapter(w http.ResponseWriter, r *http.Request) {
prevN, nextN := adjacentChapters(chapters, n) prevN, nextN := adjacentChapters(chapters, n)
title := firstHeading(raw, fmt.Sprintf("Chapter %d", n)) title := firstHeading(raw, fmt.Sprintf("Chapter %d", n))
chapterTitle, chapterDate := writer.SplitChapterTitle(title)
t := template.Must(template.New("chapter").Parse(chapterTmpl)) t := template.Must(template.New("chapter").Parse(chapterTmpl))
var buf bytes.Buffer var buf bytes.Buffer
@@ -2176,6 +2180,7 @@ func (s *Server) handleChapter(w http.ResponseWriter, r *http.Request) {
NextN int NextN int
ChapterN int ChapterN int
Title string Title string
ChapterDate string
AllChapters interface{} AllChapters interface{}
Voices []string Voices []string
DefaultVoice string DefaultVoice string
@@ -2185,13 +2190,14 @@ func (s *Server) handleChapter(w http.ResponseWriter, r *http.Request) {
PrevN: prevN, PrevN: prevN,
NextN: nextN, NextN: nextN,
ChapterN: n, ChapterN: n,
Title: title, Title: chapterTitle,
ChapterDate: chapterDate,
AllChapters: chapters, AllChapters: chapters,
Voices: kokoroVoices, Voices: kokoroVoices,
DefaultVoice: s.kokoroVoice, DefaultVoice: s.kokoroVoice,
}) })
s.respond(w, r, title, buf.String()) s.respond(w, r, chapterTitle, buf.String())
} }
// ─── helpers ────────────────────────────────────────────────────────────────── // ─── helpers ──────────────────────────────────────────────────────────────────

View File

@@ -219,12 +219,12 @@ func chapterTitle(path string, n int) (title, date string) {
continue continue
} }
line = strings.TrimPrefix(line, "# ") line = strings.TrimPrefix(line, "# ")
return splitChapterTitle(line) return SplitChapterTitle(line)
} }
return fmt.Sprintf("Chapter %d", n), "" return fmt.Sprintf("Chapter %d", n), ""
} }
// splitChapterTitle separates the human-readable chapter name from the // SplitChapterTitle separates the human-readable chapter name from the
// trailing relative-date string that novelfire.net appends to the heading. // trailing relative-date string that novelfire.net appends to the heading.
// Examples of raw heading text (after stripping "# "): // Examples of raw heading text (after stripping "# "):
// //
@@ -233,7 +233,7 @@ func chapterTitle(path string, n int) (title, date string) {
// //
// The pattern is: optional leading number+whitespace, then the real title, // The pattern is: optional leading number+whitespace, then the real title,
// then a date that matches /\d+\s+(second|minute|hour|day|week|month|year)s?\s+ago$/ // then a date that matches /\d+\s+(second|minute|hour|day|week|month|year)s?\s+ago$/
func splitChapterTitle(raw string) (title, date string) { func SplitChapterTitle(raw string) (title, date string) {
// Strip a leading chapter-number index that novelfire sometimes prepends. // Strip a leading chapter-number index that novelfire sometimes prepends.
// It looks like "1 " or "12 " at the very start. // It looks like "1 " or "12 " at the very start.
raw = strings.TrimSpace(raw) raw = strings.TrimSpace(raw)