feat(ui): add Bible Reader search - #396
Conversation
Replace beforeEach setup with per-test factories per docs/testing.md and consolidate language-range parsing into schemas/search.ts.
Search hits and the structural validator both need the same chapter/verse shape. parseUsfmReference owns that grammar so inverted ranges drop at the boundary instead of leaking into the reader.
The PRD's suggestion-suppression, stale-page, and dedupe rules are consequences of one SearchSession union plus a pure reducer, so the eight phases can be proven without timers or network.
Headless hosts get one phase and five commands. Debounce, language-range gating, and page commits stay inside the hook so UI never sees tokens or TanStack Query.
Content can scroll and dim only after the destination chapter is on screen. A seq mailbox plus a hold timer keeps focus separate from verse selection.
Hosts get a zero-required-prop search dialog that talks to useBibleSearch and navigates through one Root-owned verse-focus mailbox. English keys land here so the UI can ship; platform-localization still owns the canonical source.
The public helpers and Reader search UI are new surface on core, hooks, and ui, so they ship as minors together.
The first keystroke after submit left the submitted lane while debounce still held that query. Suggestions then re-fetched it and showed a spinner. Debounce null while submitted, and skip queriesRequest until the new input settles.
The hold-clear scroll listener sat on the renderer root, which does not scroll. Listen on the nearest overflow ancestor instead. Drop the unused focusedVerses return. Content never read it.
🦋 Changeset detectedLatest commit: 1773708 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| const verses: number[] = []; | ||
| for (let verse = verseStart; verse <= verseEnd; verse += 1) { | ||
| verses.push(verse); | ||
| } |
There was a problem hiding this comment.
When a search response contains a reference such as JHN.6.9007199254740992-9007199254740993, both endpoints pass validation but verse += 1 no longer advances at the safe-integer boundary, causing the application to hang while processing the response; sufficiently large lower ranges also exhaust memory.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/usfm-reference.ts
Line: 52-55
Comment:
**Range expansion can hang**
When a search response contains a reference such as `JHN.6.9007199254740992-9007199254740993`, both endpoints pass validation but `verse += 1` no longer advances at the safe-integer boundary, causing the application to hang while processing the response; sufficiently large lower ranges also exhaust memory.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| "youVersionPlatformLogoAriaLabel": "YouVersion Platform", | ||
| "signInAppNameFallback": "This app", | ||
| "untitledSerifFontName": "Untitled Serif" | ||
| "untitledSerifFontName": "Untitled Serif", | ||
| "bibleSearchAriaLabel": "Search the Bible", | ||
| "bibleSearchDialogTitle": "Search", | ||
| "bibleSearchPlaceholder": "Search verses", | ||
| "bibleSearchClearAriaLabel": "Clear search", | ||
| "bibleSearchTrendingHeading": "Trending", | ||
| "bibleSearchSuggestionsHeading": "Suggestions", | ||
| "bibleSearchNoVerseResults": "No verses matched this search.", |
There was a problem hiding this comment.
This directly edits an upstream-owned locale file, so the author-based locale-ownership check rejects the pull request. Add these English keys to platform-localization/sources/common/en.json and let the localization sync update this generated file.
Rule Used: Locale JSON is upstream-owned (platform-localization). CI locale-ownership job fails any PR that edits packages/ui/src/i18n/locales/ unless its author is platform-localization-pr-bot[bot]. The branch name is deliberately not part of the check (see do... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/i18n/locales/en.json
Line: 84-93
Comment:
**Locale ownership check fails**
This directly edits an upstream-owned locale file, so the author-based locale-ownership check rejects the pull request. Add these English keys to `platform-localization/sources/common/en.json` and let the localization sync update this generated file.
**Rule Used:** Locale JSON is upstream-owned (platform-localization). CI locale-ownership job fails any PR that edits packages/ui/src/i18n/locales/ unless its author is platform-localization-pr-bot\[bot\]. The branch name is deliberately not part of the check (see do... ([source](https://github.com/youversion/platform-sdk-react/blob/6d30613d98f72997736ded3d3967f17b26724762/greptile.json))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Why
Reader hosts have no way to search verses from
BibleReader. #395 (YPE-5750) shipsSearchClientand the query helpers. This PR puts a search dialog on the Toolbar and a headlessuseBibleSearchhook under it. A host that rendersBibleReader.Toolbargets search with no new props. A headless host gets the same session without the UI.The session is one
SearchSessionunion with an exclusive lane. Either the suggestion lane or the submitted lane is live, never both. A pure reducer owns every transition, so the PRD rules (no suggestions for a query that was just submitted, drop stale pages, dedupe pages by token) are tested without timers or a network.Closes YPE-5766. Parent: YPE-5622.
Scope
packages/core:parseUsfmReferenceandUsfmReference.isValidStructuralUsfmReferencenow delegates to it.packages/hooks:useSearchClient,useBibleSearch, theSearchSessionreducer ininternal/bible-search-session.ts, asearchClienttest seam onYouVersionContext, anduseDebouncepassesnullthrough without delay so a paused lane stops fetching.packages/ui:BibleReaderSearch, asearchprop onBibleReader.Toolbar('control'by default,'none'opts out),useTransientVerseFocus,navigateToVerseandverseFocuson the Reader context,useBibleReaderContextexported,yv-v-focusedstyles inbible-reader.css, and tenbibleSearch*keys inen.json.Tradeoffs
SearchSessionunion instead of two independent query hooks. Two hooks can both be live at once, and the PRD forbids that state. The union costs a reducer file and makes the forbidden state unrepresentable.seqmailbox onBibleReader.Root, separate from verse selection. Reusing selection would open the verse action popover on every search hit.usePassageinstead of one batch call. The search API returns references only, andusePassagealready caches per reference.Blast Radius
Every host that renders
BibleReader.Toolbargets a search button and one more grid column.search="none"restores the old layout. Core and hooks add exports only. The English keys land here so the UI can ship, andplatform-localizationstill owns the canonical source, so the locale-ownership CI check may flagen.jsonuntil that repo syncs.This PR stacks on #395. Retarget to
mainafter #395 merges.Verification
pnpm testin core, hooks, and ui is green.bible-search-session.test.tscovers every session phase through the reducer alone.pnpm typecheckis green after a coredistbuild..arena/ype-5766-search-ui/SYNTHESIS.md, local and not committed.Greptile Summary
Adds Bible Reader verse search across the core parser, search-session hooks, and toolbar UI.
useBibleSearchAPI.Confidence Score: 3/5
The PR is not safe to merge until the unbounded USFM range expansion and locale-ownership CI failure are resolved.
Search-response validation can still enter a non-progressing or extremely large synchronous range expansion, while the direct English locale edit remains guaranteed to fail the repository's author-based ownership gate.
Files Needing Attention: packages/core/src/usfm-reference.ts; packages/ui/src/i18n/locales/en.json
Important Files Changed
Sequence Diagram
Reviews (2): Last reviewed commit: "fix(ui): keep Search key on mobile after..." | Re-trigger Greptile