Fix chapter reader nav bar: use hamburger icon for back-to-book, show chapter date below title
- 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:
@@ -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'">
|
||||||
←
|
☰
|
||||||
</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">▼</span>
|
<span style="flex-shrink:0;font-size:0.625rem;color:#52525b;" aria-hidden="true">▼</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 ──────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user