feat(reader): send context_text with progress for reflowable EPUBs

The web reader now captures ~100 chars of visible text from the
foliate relocate event's range and includes it as context_text in
the progress PUT body for reflowable formats.

This enables the server's reverseByTextSearch fallback in
ConvertStandardToCRE, which is critical for single-file EPUBs
(e.g. 1984) where foliate emits coarse or fake-section CFIs that
CREngine cannot directly resolve. Previously only the KOReader
plugin sent context_text; the web reader's omission left the
fallback unusable, causing percentage-based position estimation
that was off by ~1 page.

Changes:
- Add contextText state property (line 387)
- Extract visible text from e.detail.range in relocate handler,
  normalize whitespace, and slice to 100 chars (lines 508-512)
- Include context_text in saveProgress PUT body for reflowable
  EPUBs only, alongside epubcfi (line 575)

The server-side chain was already wired: media.go accepts it,
progress.go stores it, and koreader.go passes it to the CFI
converter. No Go changes needed.
This commit is contained in:
2026-06-07 11:45:33 -04:00
parent 9bd23fc1d7
commit e584200369
+7
View File
@@ -384,6 +384,7 @@ document.addEventListener("alpine:init", () => {
mediaItemId: "" as string,
saveTimeout: null as ReturnType<typeof setTimeout> | null,
initTime: 0 as number,
contextText: "" as string,
readingTheme: "light" as string,
readingMode: "light" as string,
readingFont: "literata" as string,
@@ -504,6 +505,11 @@ document.addEventListener("alpine:init", () => {
slider.value = fraction;
slider.title = progressStr;
}
const range = e.detail.range as Range | undefined;
if (range) {
const text = range.toString().replace(/\s+/g, " ").trim();
this.contextText = text ? text.slice(0, 100) : "";
}
this.debouncedSaveProgress(fraction, location, cfi);
});
const slider = document.getElementById(
@@ -566,6 +572,7 @@ document.addEventListener("alpine:init", () => {
};
if (!this.isFixedLayout) {
body.epubcfi = cfi || "";
body.context_text = this.contextText || "";
}
const tocItem = this.lastRelocateDetail?.tocItem;