feat(document-viewer): add PDF text search with highlight overlay#2159
feat(document-viewer): add PDF text search with highlight overlay#2159mx-kshitij wants to merge 1 commit into
Conversation
| } | ||
| } | ||
| } | ||
| // Search toggle button — pressed state via aria-pressed so Atlas hover/focus work normally |
There was a problem hiding this comment.
Does this code still need to be implemented or can it be removed?
|
|
||
| let active = true; | ||
|
|
||
| (async () => { |
There was a problem hiding this comment.
Can you wrap this in try/catch for better error handling?
| const textContent = await page.getTextContent(); | ||
|
|
||
| // Shared canvas for proportional character-width measurement. | ||
| const canvas = document.createElement("canvas"); |
There was a problem hiding this comment.
Canvas created every effect run. Low impact but cleaner to cache in a ref
| return () => { | ||
| active = false; | ||
| }; | ||
| }, [pdfDoc, currentPage, zoomLevel, matches, currentMatchIndex]); |
There was a problem hiding this comment.
currentMatchIndex in deps triggers full recalc on every prev/next click even when staying on same page. Remove from deps, return globalMatchIndex per rect instead of isCurrent, derive current state in JSX: rect.globalMatchIndex === currentMatchIndex
|
|
||
| const itemIndex = strItemIndex++; | ||
| const item = rawItem as { str: string; transform: number[]; width: number }; | ||
| const itemMatches = pageMatches.filter(m => m.itemIndex === itemIndex); |
There was a problem hiding this comment.
Docs with many matches will have lots of comparisons. Filter inside loop scans all matches for every item. Can you group matches upfront withMap.groupBy(pageMatches, m => m.itemIndex), then use lookup byItem.get(itemIndex) inside the loop.
Pull request type
Description
Adds in-document text search to the PDF viewer in
document-viewer-web.What changed:
PDFViewer.tsx— A search toggle button (magnifier icon) is added to the control bar. When activated, a secondary search bar appears below the controls with:"N of M"/"No results"/"Searching…"counter (aria-live).usePDFSearch.ts(new) — Custom hook that iterates all pages of the loadedPDFDocumentProxy, collects all case-insensitive substring matches asSearchMatchrecords (page, item index, char offsets), and exposesgoToNextMatch/goToPrevMatchwith automatic page navigation.usePDFHighlightPositions.ts(new) — Custom hook that convertsSearchMatchrecords on the current page into absolutely-positionedHighlightRectobjects. Usespage.getViewport()+ acanvascontext for proportional glyph-width measurement scaled to the actual rendered item width. Async effects are guarded with anactiveflag to prevent state updates after unmount/query change.BaseViewer.tsx— Added optionalSecondaryControlprop slot rendered between the toolbar and the page content.documentViewer.scss— Styles for.widget-document-viewer-search-bar,.widget-document-viewer-highlight-layer, and.widget-document-viewer-highlight(yellow for all matches, orange + outline for the current match). Uses Atlas UI tokens (--spacing-*,--border-color-default, etc.).documentViewerIcons.scss— AddedSearchicon (\e905) to theDocViewericon font map; broadened font-family scope so search-bar nav buttons also pick up the icon font.DocViewer.woff2— Updated to include the new search glyph.Why:
Users viewing multi-page PDFs had no way to locate specific text without scrolling manually. This brings standard Ctrl+F-style search behaviour to the widget, matching expectations from native PDF viewers.
What should be covered while testing?
"1 of N"."No results", no highlights.spanusesaria-live="polite").aria-pressed="true"when open.