Commit Graph

5 Commits

Author SHA1 Message Date
loveheaven 7f57af8f90 perf(cfi): bucket booknotes per chapter and batch-collapse location matcher (#4561)
* perf(cfi): bucket booknotes per chapter and batch-collapse location matcher

When iterating a list of CFIs against the same currentLocation (Annotator
on every page turn, useSearchNav, useBooknotesNav), the standalone
isCfiInLocation collapses the location twice per CFI. With 1000+
booknotes -- which a heavy user reported -- that's 2000 CFI parses
per page turn. The foliate epubcfi.js chunk showed up as ~15% of
self time in Bottom-Up profiles of the release Android build.

Fix:
- createCfiLocationMatcher(location) collapses once and returns a
  matches(cfi) predicate that reuses the cached bounds. O(N) calls
  become 1 collapse + N compares.
- getCfiSpinePrefix(cfi) extracts the spine path via pure string ops
  (no CFI.parse round-trip) for use as a chapter bucket key.
- Annotator builds annotationIndex = { bySection, globals } via
  useMemo([config.booknotes]) once when booknotes change, not per
  page turn. The progress-driven effect then only scans the current
  chapter's bucket -- ~50 CFIs in a typical book instead of all 1000.
  globals are pre-filtered too.
- useSearchNav / useBooknotesNav switch to the batched matcher for
  the same reason.

Includes parity tests covering empty/malformed inputs, equality
shortcut, prefix shortcut, in-range, and out-of-range cases.

* fix(annotator): keep note-only annotations in the per-chapter bucket

The booknote bucketing gated entries on `item.style`, which dropped
note-only annotations (a `note` with no highlight style/color, created
via the Notebook flow) from the per-relocate re-apply path. Their note
bubble was no longer redrawn on relocate or when booknotes changed while
a section stayed rendered.

Restore the original two-list semantics: bucket on style OR note, then
classify per location (annotations need a style, notes need a note).

Extract the logic into a dedicated, unit-tested `annotationIndex` module
(buildAnnotationIndex + selectLocationAnnotations) instead of inlining it
in Annotator, matching the reader/utils domain-named convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Huang Xin <chrox.huang@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 18:06:13 +02:00
Huang Xin eed84a059a fix(search): should be able to terminate search when sidebar is invisible (#2863) 2026-01-04 17:10:49 +01:00
Huang Xin 604ef65719 perf(search): use cache for search results (#2861) 2026-01-04 12:56:40 +01:00
Huang Xin 8a263235ed feat(search): add annotations navigation bar, closes #2060 (#2849) 2026-01-03 14:57:10 +01:00
Huang Xin a5e09e8454 feat(search): add search results navigation bar, closes #1183 (#2842)
Add a floating navigation component that appears when search results exist.
The component includes:
   - Top bar: displays search term and current section with TOC and close buttons
   - Bottom bar: search results, previous, and next navigation buttons
   - Page-based navigation using isCfiInLocation to skip between pages with results
2026-01-03 08:10:23 +01:00