feat(koreader): one conversion route for every feature; bookmarks get web CFIs
Release / build-and-push (push) Successful in 2m14s
Release / build-and-push (push) Successful in 2m14s
Progress, highlights, notes, and bookmarks entered position conversion through three different doors: progress converted inline with an uncached converter, annotations through the facade, bookmarks not at all (the raw xpointer was stored verbatim, cfi_position stayed empty, and the web drawer's goToBookmark silently no-ops on cfi-less entries). Unify on the facade (ConvertToCanonical/ConvertFromCanonical): - annotationEpub context resolved once per push: media item + EPUB path shared by every annotation instead of re-fetched per entry - progress forward: the inline block becomes one facade call; non-reflowable formats pass through unchanged, and the cached converter stops re-parsing the book on every sync - progress reverse: convertCFIToXPointer delegates to reverseConvertCFI, keeping the stored percentage in play for the fallback ladder - bookmarks (bulk progress and /sync-bookmarks): pos0 resolves structural-only — bookmark text is a display label, never book text, so no context is supplied; webUsableCFI stores the result only for structural/exact epubcfi landings, discarding href/percentage results rather than storing dead drawer links. Also records percentage_location and origin_source on the legacy endpoint. - percentages thread through: highlights/notes/bookmarks pass the device percentage or the derived section percentage instead of a hardcoded 0, so the last-resort fallback lands near the true position instead of the document start - extendCFIByLength end-derivation now also fires on structural starts (it had silently stopped matching when the structural rung began landing starts with precision 'structural' rather than 'exact') Tests: the drop-cap xpointer through the facade with empty context (the bookmark scenario) must land structurally, not doc-start; webUsableCFI table covers the store/discard gate.
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
wsync "bookhoard/internal/sync"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -50,3 +52,53 @@ func TestKOReaderProgressRequest_DeletedAnnotationsOmitted(t *testing.T) {
|
||||
assert.Empty(t, req.Books[0].DeletedHighlights)
|
||||
assert.Empty(t, req.Books[0].DeletedBookmarks)
|
||||
}
|
||||
|
||||
// The web drawer navigates bookmarks by CFI, so only high-confidence
|
||||
// conversions may be stored: href/percentage/fallback results would
|
||||
// become dead links. This is the sole gate for KOReader→web bookmark
|
||||
// positions (bookmark text is a label, never book text, so the
|
||||
// conversion runs structural-only with empty context).
|
||||
func TestWebUsableCFI(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
loc wsync.CanonicalLocator
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "structural landing stored",
|
||||
loc: wsync.CanonicalLocator{CFI: "epubcfi(/6/52!/4/28/2/1:0)", Precision: "structural", Percentage: 0.52},
|
||||
expected: "epubcfi(/6/52!/4/28/2/1:0)",
|
||||
},
|
||||
{
|
||||
name: "exact text-search landing stored",
|
||||
loc: wsync.CanonicalLocator{CFI: "epubcfi(/6/52!/4/28/2/1:0)", Precision: "exact", Percentage: 0.52},
|
||||
expected: "epubcfi(/6/52!/4/28/2/1:0)",
|
||||
},
|
||||
{
|
||||
name: "percentage guess discarded",
|
||||
loc: wsync.CanonicalLocator{CFI: "epubcfi(/6/52!/4/2/1:0)", Precision: "percentage", Percentage: 0.52},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "fallback passthrough (raw xpointer) discarded",
|
||||
loc: wsync.CanonicalLocator{CFI: "/body/DocFragment[2]/body/p[3]", Precision: "fallback", Percentage: 0.52},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "section href discarded (not a CFI)",
|
||||
loc: wsync.CanonicalLocator{CFI: "ch10.xhtml", Precision: "section", Percentage: 0.52},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "structural but non-CFI value discarded",
|
||||
loc: wsync.CanonicalLocator{CFI: "ch10.xhtml#h1", Precision: "structural", Percentage: 0.52},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.expected, webUsableCFI(tc.loc))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user