Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
6c04851
feat: add in-book search for epubs
chongfun Jun 27, 2026
2d8854e
feat: highlight search matches
chongfun Jun 28, 2026
7f0b17a
fix: resolve typos in Polish and Ukrainian translations
chongfun Jun 28, 2026
5396fc2
fix: enforce Section cache bounds, handle error cleanups, and extend …
chongfun Jun 28, 2026
749219f
fix: pass isChapterBoundary flag during pageContainsText scanning
chongfun Jun 28, 2026
59f9ccb
fix: align search viewport, footnote preview, highlights, and pace ti…
chongfun Jun 28, 2026
0834ceb
fix: display toast instead of popup for search messages
chongfun Jun 28, 2026
d0181f6
fix: undo isChapterBoundary change
chongfun Jun 28, 2026
22bc9e2
refactor: extract KMP matching logic into SearchMatcher
chongfun Jun 29, 2026
7ec9a2c
refactor: optimize search architecture with batched reads and unified…
chongfun Jun 29, 2026
f341da9
perf: optimize search highlight KMP priming
chongfun Jun 29, 2026
c601156
feat: add diacritic-insensitive search matching
chongfun Jun 29, 2026
0f07f7a
fix(ui): enforce full screen refresh on search completion
chongfun Jun 29, 2026
8377314
fix(epub): skip unsupported codepoints and accumulate separator bytes…
chongfun Jun 29, 2026
3c720bf
fix(search): validate query length in EpubReaderSearchActivity to pre…
chongfun Jun 29, 2026
357ef17
fix(search): save and restore full SearchMatcher state on cache-repai…
chongfun Jun 29, 2026
8886231
fix: reset pending separators on KMP match restart
chongfun Jun 29, 2026
fd159d7
fix: use bounded strnlen for search query validation
chongfun Jun 29, 2026
c5b2141
feat(search): support multi-character diacritic folding
chongfun Jun 29, 2026
cd7c4f9
docs(search): update architecture doc for Unicode and folding support
chongfun Jun 29, 2026
97f6171
refactor: encapsulate SearchMatcher state and optimize compile()
chongfun Jun 29, 2026
078c2b8
refactor: extract SearchHighlighter and consolidate utilities
chongfun Jun 29, 2026
320fdd0
docs: update search architecture to include SearchHighlighter
chongfun Jun 29, 2026
15cbfa0
fix: conditionally highlight search results only on matching page
chongfun Jun 29, 2026
3bee5bb
fix: sum unique source-codepoint widths for search highlighting
chongfun Jun 29, 2026
f1158e4
fix: safely multiplex Section file handle to prevent search crash
chongfun Jun 29, 2026
2068d77
docs: correct search scan loop to 50-page chunk cadence
chongfun Jun 29, 2026
840aa02
fix: widen search match-width accounting to avoid uint8_t overflow
chongfun Jun 29, 2026
f5ca125
perf: reuse scanForward page-LUT buffer to avoid per-chunk heap churn
chongfun Jun 29, 2026
77f71e2
refactor: unify search query limit on SearchMatcher::MAX_QUERY_BYTES
chongfun Jun 29, 2026
9656a6c
fix: never return a colliding /Read destination path
chongfun Jun 29, 2026
a8831b9
refactor: remove dead getTextFromSectionFile()
chongfun Jun 29, 2026
7d9717c
fix: guard empty /Read destination path before rename()
chongfun Jun 29, 2026
373ef9d
refactor: drop duplicate calculateReaderViewport helper
chongfun Jun 29, 2026
965f332
refactor: share isSearchSeparator via AsciiCase.h
chongfun Jun 29, 2026
eb625aa
fix: distinguish corrupt-cache from transient I/O in search scan
chongfun Jun 29, 2026
2776dee
perf: reuse a member for the per-chunk matcher snapshot
chongfun Jun 29, 2026
59bc377
refactor: share word-highlight geometry between search and clippings
chongfun Jun 29, 2026
5bf715e
fix: handle the highlighter's cross-page priming scan failure
chongfun Jun 29, 2026
4536047
perf: allocate search highlight buffers lazily and release them
chongfun Jun 29, 2026
645976a
perf: memoize search highlight ranges per page+query
chongfun Jun 29, 2026
f51f609
refactor: merge reader toasts into one shared slot
chongfun Jun 29, 2026
046af12
refactor: name section-header trailer offsets
chongfun Jun 29, 2026
f5d97d0
docs: update stale pageContainsText references to scanForward
chongfun Jun 29, 2026
087c8d5
fix: don't carry separator width into a restarted search match
chongfun Jun 29, 2026
6d510d0
refactor: split search scan/serialize into their own translation units
chongfun Jun 29, 2026
62d38d3
refactor: bundle Section search-scan state into one struct
chongfun Jun 29, 2026
9db6f8a
refactor: extract search route planning into SearchRoute::plan
chongfun Jun 29, 2026
39c7455
fix: include spine in search highlight cache key
chongfun Jun 29, 2026
d1795c0
fix: derive search highlight colors from reader theme
chongfun Jun 29, 2026
7d85ffb
fix: widen paragraph/li LUT bounds checks to 64-bit
chongfun Jun 29, 2026
e4fed12
docs: correct scanForward contract and document codepoint dropping
chongfun Jun 29, 2026
aa4fbc5
fix: don't treat footnote preview as the real loaded section in search
chongfun Jun 29, 2026
d6516fe
docs: fix relative library/source links in search-architecture
chongfun Jun 29, 2026
6c88dbc
docs: add blank lines around CHANGELOG Unreleased headings
chongfun Jun 29, 2026
f97d107
fix: guard lastSearchQuery copy against oversized query
chongfun Jun 29, 2026
04a89b4
fix: classify search header failures as IoError vs CorruptCache
chongfun Jun 29, 2026
cd1d45e
fix: bound search text records against the page LUT offset
chongfun Jun 29, 2026
21deac3
fix: clear footnote preview when jumping to a search match
chongfun Jun 29, 2026
b7a02de
fix: harden search text-record offset bounds and LUT read error class
chongfun Jun 29, 2026
caf8335
fix: reserve search highlight buffers despite std::string SSO capacity
chongfun Jun 29, 2026
b0a7a73
fix: release section cache file and reset matcher after search-highli…
chongfun Jun 29, 2026
ae96862
refactor: dedup search/reader helpers and drop dead code
chongfun Jun 29, 2026
1b5feec
refactor: highlight search matches from the scan's reported byte span
chongfun Jun 29, 2026
7c6e6e2
fix: respect spaces in book search while keeping hyphenation fuzzy
chongfun Jun 30, 2026
87955fc
docs: correct search-architecture figures
chongfun Jun 30, 2026
984f8c5
fix: carry dropped non-Latin codepoint width into search match span
chongfun Jun 30, 2026
15c10c0
fix: invalidate cached search highlight span on section re-layout
chongfun Jun 30, 2026
2f5f09b
fix: treat case-only query differences as the same book search
chongfun Jun 30, 2026
90399c1
docs: collapse unreleased search changelog into one feature entry
chongfun Jun 30, 2026
97bea7d
fix: gate repeat search on full query normalization, not ASCII case
chongfun Jun 30, 2026
7a375e8
fix: enforce whole-word boundaries in book search matcher
chongfun Jun 30, 2026
81bf88c
test: add whole-word search matcher regression suite
chongfun Jun 30, 2026
43566bd
test: link EpdFontFamily into DifferentialRoundingTest cmake target
chongfun Jun 30, 2026
e7556e3
chore: build and run gtest unit suite in CI
chongfun Jun 30, 2026
42a8101
revert: drop unrelated /Read destination-collision refinement
chongfun Jun 30, 2026
f89f543
ci: harden checkout action for unit tests
chongfun Jun 30, 2026
9df71a3
test: add regression test for NFC/NFD equivalence in search queries
chongfun Jun 30, 2026
b73f90c
fix: return IoError for unexpected short reads in text payload
chongfun Jun 30, 2026
bb3ab95
docs: update search architecture numbers
chongfun Jun 30, 2026
d273cb8
perf: batch text reads during search to reduce SPI overhead
chongfun Jun 30, 2026
9f0307a
docs: fix stale buffer[j] comments after textBuf rename
chongfun Jun 30, 2026
340ccc9
ci: harden checkout action across all jobs
chongfun Jun 30, 2026
e0f73fd
perf: add opt-in SEARCH_PROFILE instrumentation for scan I/O vs CPU
chongfun Jun 30, 2026
324be54
perf: throttle search progress repaints by wall-clock to cut refresh …
chongfun Jun 30, 2026
afa1777
perf: log firstOfSection in SEARCH_PROFILE to separate cold-file from…
chongfun Jun 30, 2026
9a01fcf
fix: avoid full-screen flash on no-results search
chongfun Jun 30, 2026
ef1f89d
feat: show indexing popup during cold-cache search builds
chongfun Jun 30, 2026
6b54ca6
docs: note cold-search indexing popup and add text-only index future …
chongfun Jun 30, 2026
d9cb73c
chore: remove SEARCH_PROFILE instrumentation now that search latency …
chongfun Jun 30, 2026
7398885
fix: seed search progress throttle from initial paint to avoid an ext…
chongfun Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ jobs:
clang-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: recursive

- uses: actions/setup-python@v6
Expand All @@ -36,8 +37,9 @@ jobs:
cppcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: recursive

- uses: actions/setup-python@v6
Expand Down Expand Up @@ -67,8 +69,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: recursive

- uses: actions/setup-python@v6
Expand Down Expand Up @@ -116,6 +119,26 @@ jobs:
.pio/build/tiny/firmware-tiny.bin
if-no-files-found: error

unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: false
submodules: recursive

- name: Verify CMake
run: cmake --version

- name: Configure test suite
run: cmake -S test -B test/build -DCMAKE_BUILD_TYPE=Release

- name: Build test suite
run: cmake --build test/build -j"$(nproc)"

- name: Run test suite
run: ctest --test-dir test/build --output-on-failure

# This job is used as the PR required actions check, allows for changes to other steps in the future without breaking
# PR requirements.
test-status:
Expand All @@ -124,6 +147,7 @@ jobs:
- build
- clang-format
- cppcheck
- unit-tests
if: always()
runs-on: ubuntu-latest
steps:
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Changelog

## [Unreleased]

### Added

- In-book search: find a word or phrase within the current EPUB and jump to a match, with the matched words highlighted on the page. Matching respects word boundaries while still finding words broken by hyphenation across a line or page.
Comment thread
chongfun marked this conversation as resolved.

## [v1.3.4] - 2026-06-24

### Added
Expand Down
27 changes: 21 additions & 6 deletions docs/file-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,19 +229,20 @@ Binary layout:

## `section.bin`

### Version 41
### Version 42

Each file in `sections/*.bin` stores one laid-out spine section. The header is
also the cache-busting key: if any layout-affecting setting differs from the
current reader settings, the section is discarded and rebuilt.

Version 41 includes:
Version 42 includes:

- cache-busting fields for font, line compression, extra paragraph spacing,
forced paragraph indents, paragraph alignment, viewport size, hyphenation,
embedded CSS, image rendering mode, Bionic Reading, Guide Dots, and EPUB
render mode
- page offset LUT
- paired page/search-text offset LUT
- compact per-page text records used by bounded-memory in-book search
- anchor-to-page map for fragment and footnote navigation
- paragraph and list-item LUTs used by KOReader sync page refinement
- optional per-word Bionic Reading split metadata
Expand All @@ -252,14 +253,17 @@ Version 41 includes:
- per-page footnote entries
- per-page publisher page markers

See [In-Book Search Architecture](./search-architecture.md) for the
memory and SD-space trade-offs behind the search records.

ImHex pattern:

```c++
import std.mem;
import std.string;
import std.core;

#define EXPECTED_VERSION 41
#define EXPECTED_VERSION 42
#define MAX_STRING_LENGTH 65535
#define FOOTNOTE_NUMBER_LEN 32
#define FOOTNOTE_HREF_LEN 96
Expand Down Expand Up @@ -426,6 +430,17 @@ struct Page {
PublisherPageMarker publisherPageMarkers[publisherPageMarkerCount];
};

struct PageRecord {
Page page [[inline]];
u32 searchTextLength;
char searchText[searchTextLength] [[comment("Rendered words joined by ASCII spaces")]];
};

struct PageLutEntry {
u32 pageOffset [[comment("Serialized Page offset")]];
u32 searchTextOffset [[comment("searchTextLength field offset")]];
};

struct AnchorEntry {
String anchor;
u16 page;
Expand Down Expand Up @@ -467,14 +482,14 @@ struct SectionBin {
u32 paragraphLutOffset;
u32 listItemLutOffset;

Page pages[pageCount];
PageRecord pages[pageCount];

u32 currentOffset = $;
if (currentOffset != pageLutOffset) {
std::warning(std::format("Page LUT offset mismatch: expected 0x{:X}, got 0x{:X}", pageLutOffset, currentOffset));
}

u32 pageLut[pageCount] [[comment("Page data offsets")]];
PageLutEntry pageLut[pageCount] [[comment("Page and search-text offsets")]];

if (anchorMapOffset != 0) {
AnchorMap anchorMap @ anchorMapOffset;
Expand Down
Loading
Loading