feat(reader): add sticky progress bar with chapter ticks (#4707)

Add an always-visible, opt-in progress bar with chapter tick marks in the
persistent footer, so reading progress no longer disappears like the hover
footer slider does.

- New StickyProgressBar: a 1px rounded-border capsule with a fill and
  chapter tick marks; display-only, e-ink aware, and RTL safe. Ticks render
  inside the clipped track so the rounded ends crop them and they never
  exceed the border.
- Chapter ticks come from the TOC, mapped to spine-section start fractions
  (getChapterTickFractions); the first and last ticks are trimmed so they
  do not crowd the rounded ends.
- Thread the overall size-domain reading fraction through setProgress so the
  bar fill aligns with the tick domain.
- Footer layout: when enabled the bar grows on the left and the info widgets
  group to the right with even spacing; otherwise the existing layout is
  unchanged.
- Horizontal writing mode only; vertical keeps the current footer.
- Add the showStickyProgressBar view setting, a LayoutPanel toggle, and i18n.

Closes #1616.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-06-22 01:03:24 +08:00
committed by GitHub
parent 9735f497db
commit c781aeddaa
13 changed files with 402 additions and 7 deletions
@@ -76,6 +76,7 @@ interface ReaderStore {
pageinfo: PageInfo,
timeinfo: TimeInfo,
range: Range,
fraction: number,
) => void;
getProgress: (key: string) => BookProgress | null;
setView: (key: string, view: FoliateView) => void;
@@ -375,6 +376,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
pageinfo: PageInfo,
timeinfo: TimeInfo,
range: Range,
fraction: number,
) => {
const id = key.split('-')[0]!;
const bookData = useBookDataStore.getState().booksData[id];
@@ -436,6 +438,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
section,
pageinfo,
timeinfo,
fraction,
index: section.current,
range,
page: pageInfo.current + 1,