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
This commit is contained in:
Huang Xin
2026-01-03 08:10:23 +01:00
committed by GitHub
parent 730ee21651
commit a5e09e8454
39 changed files with 514 additions and 89 deletions
@@ -50,7 +50,13 @@ export const annotationToolButtons = createAnnotationToolButtons([
tooltip: _('Annotate text after selection'),
Icon: BsPencilSquare,
},
{ type: 'search', label: _('Search'), tooltip: _('Search text after selection'), Icon: FiSearch },
{
type: 'search',
label: _('Search'),
tooltip: _('Search text after selection'),
Icon: FiSearch,
quickAction: true,
},
{
type: 'dictionary',
label: _('Dictionary'),
@@ -24,6 +24,7 @@ import { findTocItemBS } from '@/utils/toc';
import { throttle } from '@/utils/throttle';
import { runSimpleCC } from '@/utils/simplecc';
import { getWordCount } from '@/utils/word';
import { isCfiInLocation } from '@/utils/cfi';
import { getHighlightColorHex } from '../../utils/annotatorUtil';
import { annotationToolButtons } from './AnnotationTools';
import AnnotationPopup from './AnnotationPopup';
@@ -386,6 +387,9 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
handleDismissPopupAndSelection();
}, 0);
break;
case 'search':
handleSearch();
break;
case 'dictionary':
handleDictionary();
break;
@@ -460,16 +464,13 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
useEffect(() => {
if (!progress) return;
const { location } = progress;
const start = CFI.collapse(location);
const end = CFI.collapse(location, true);
const { booknotes = [] } = config;
const annotations = booknotes.filter(
(item) =>
!item.deletedAt &&
item.type === 'annotation' &&
item.style &&
CFI.compare(item.cfi, start) >= 0 &&
CFI.compare(item.cfi, end) <= 0,
isCfiInLocation(item.cfi, location),
);
const notes = booknotes.filter(
(item) =>
@@ -477,8 +478,7 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => {
item.type === 'annotation' &&
item.note &&
item.note.trim().length > 0 &&
CFI.compare(item.cfi, start) >= 0 &&
CFI.compare(item.cfi, end) <= 0,
isCfiInLocation(item.cfi, location),
);
try {
Promise.all(annotations.map((annotation) => view?.addAnnotation(annotation)));