Skip to content

feat(core): search a publication's text - #28

Closed
devaryakjha wants to merge 7 commits into
mainfrom
claude/project-thread-eheuhr-search
Closed

devaryakjha wants to merge 7 commits into
mainfrom
claude/project-thread-eheuhr-search

Conversation

@devaryakjha

Copy link
Copy Markdown
Contributor

Requested by Arya · project thread

Before

The reader had no search. An app wanting to find text in an open book had to read every chapter itself and had no way to turn a hit into a location the navigator could open.

After

Publication.search(for:options:) and ReaderController.search(for:options:) find a query and return Locator values in reading order. Each result carries the matched text with some words around it in text, the chapter's title when the table of contents names it, and a progression through its resource. ReaderController.go(to:) opens the page holding the match, because the context is a slice of the same flat text the reader script uses for anchors. SearchOptions sets case and diacritic sensitivity and a result limit (1000 by default).

  • EPUB, FB2, MOBI, TXT and Markdown search their HTML resources on every platform, including Linux.
  • PDF searches through PDFKit in PDFModule, with a page=N fragment per result and the outline's title for the page.
  • Comics find nothing, since they have no text.
  • The controller drops results when the session changes while searching.

Visual highlighting of a match on the page isn't part of this; it needs a decorations feature in the reader script and comes with the controls PR.

How

  • HTMLText.flatten in ChekReaderCore reads an HTML or XHTML document into the body's text with whitespace collapsed, skipping head, script, style, comments and declarations, and decoding entities. It does what the script's textIndex plus normalized do, so a result's before and highlight are exactly what goToText looks for.
  • ReaderFormatModule gains an optional search hook with a default returning nil, so only PDF overrides it.
  • PDFFixture.document can now put a line of text on chosen pages, which the PDF search test uses.
  • Tests: HTMLTextTests (what the flattening keeps and drops), SearchTests in the EPUB target (hits across chapters with context, case, diacritics, limit, blank query), searchesTheOpenPublication in ReaderControllerTests, findsTextOnPages in PDFModuleTests.

Built and tested here with Swift 6.4.1 on Linux (swift build --build-tests && swift test --skip-build, 180 tests pass). The PDF path and Apple targets weren't built here, since there's no Xcode in this environment; the Apple CI job covers them.


Generated by Claude Code

Publication.search(for:options:) finds a query in reflowable formats by
reading each chapter's markup into the same flat text the reader script
indexes, so every result is a Locator that go(to:) opens at the page
holding the match, with the matched text and some words around it. PDF
searches through PDFKit in its module, with a page fragment and the
chapter's title from the outline. ReaderController.search(for:options:)
drops results when the session changes while searching.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfwUCp8GqHWUdTK7EhZCT5
@devaryakjha devaryakjha self-assigned this Oct 21, 2026
findString returned a different set of selections than the page text
holds on the Apple CI runner, so results came out with the wrong pages.
Searching PDFPage.string with the shared matcher gives the same results
and context as reflowable formats, and the page is set on each match.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfwUCp8GqHWUdTK7EhZCT5
xcodebuild's console doesn't print expectation comments, but it does
print a thrown error's description.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfwUCp8GqHWUdTK7EhZCT5
Publication.search finds a format's module through the registry, and
the PDF test opened the publication through the module directly, so
the search fell through to the markup search and found nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfwUCp8GqHWUdTK7EhZCT5
PDFKit reads only the text inside a page's box. The fixture's pages
were 200 points wide, so a 12-point line was cut at 33 characters and
the search found two matches of three. Pages are letter-sized now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfwUCp8GqHWUdTK7EhZCT5
devaryakjha added a commit that referenced this pull request Oct 21, 2026
Lands four pull requests in one:

- #28 feat(core): search a publication's text. Publication.search
  finds a query in reflowable formats and PDFs, returning Locators
  that go(to:) opens at the match, with the text around it.
- #29 feat(ui): standard controls, accessibility and a String Catalog.
  ReaderView draws a title bar, table of contents, search and
  appearance sheets and a position slider; every control is labelled
  for VoiceOver, and user-facing strings are localizable.
- #30 feat(archives): read CBR and CB7 through libarchive, in the
  optional ChekReaderArchives product.
- #27 Point the README at the 0.1.0 release instead of main.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfwUCp8GqHWUdTK7EhZCT5

Copy link
Copy Markdown
Contributor Author

Landed on main in 1f74f28 through #27, which carried this branch. Closing.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants