Skip to content

Search Result Rendering - The Deep Dive

  1. PDF Page Level Search Result Handling In PDFPage.tsx, we see the crucial connection between search matches and their visual representation:
const { textSearchMatches: searchResults, selectedTextSearchMatchIndex } = useTextSearchState();

The component filters and processes these search results specifically for each PDF page:

searchResults
  .filter((match): match is TextSearchTokenResult => "tokens" in match)  // Type guard to ensure token-based results
  .filter((match) => match.tokens[pageInfo.page.pageNumber - 1] !== undefined)  // Only show results for this page
  .map((match) => {
    const isHidden = match.id !== selectedTextSearchMatchIndex;
    return (
      <SearchResult
        key={match.id}
        total_results={searchResults.length}
        showBoundingBox={true}
        hidden={isHidden}
        pageInfo={updatedPageInfo}
        match={match}
      />
    );
  })

  1. Search Result Visualization Architecture The system uses a layered approach:
  2. The base layer is the PDF canvas (<PageCanvas>)
  3. Above it sits the <SelectionLayer> for user interactions
  4. Search results are rendered as <SearchResult> components in the same stack
  5. All of these are wrapped in a <CanvasWrapper> with position: relative to maintain proper positioning

  6. Token-Based Search Implementation The search results are specifically filtered for TextSearchTokenResult types, which contain:

  7. Token information for specific pages
  8. Page number information (start_page and end_page)
  9. Actual matching text content

  10. Coordinate System and Scaling The PDFPage component handles viewport scaling:

    const pageViewport = pageInfo.page.getViewport({ scale: 1 });
    const { zoomLevel } = useZoomLevel();
    

This scaling information is passed to search results to ensure they're properly positioned regardless of zoom level:

const updatedPageInfo = useMemo(() => {
  return new PDFPageInfo(
    pageInfo.page,
    pageInfo.tokens,
    zoomLevel,
    pageBounds
  );
}, [pageInfo.page, pageInfo.tokens, zoomLevel, pageBounds]);

  1. Search Result Highlighting and Navigation
  2. Each <SearchResult> component receives:
  3. The match data
  4. Whether it should be hidden (based on selectedTextSearchMatchIndex)
  5. The total number of results for context
  6. Updated page information for proper scaling and positioning

  7. State Management Flow

    User Types Search → 
      useSearchText (Jotai atom) → 
        Text Search Processing → 
          Results stored in textSearchMatches atom →
            PDFPage components filter relevant results →
              SearchResult components render highlights
    

  8. Performance Considerations The implementation uses several performance optimizations:

  9. useMemo for expensive calculations
  10. Filtering results per page to avoid unnecessary rendering
  11. Type guards to ensure type safety and proper handling of token-based results

  12. Visual Feedback Loop When a search result is selected:

  13. The selectedTextSearchMatchIndex is updated
  14. This triggers re-renders of SearchResult components
  15. The selected result becomes visible while others are hidden
  16. The document view scrolls to the selected result's position

The search functionality isn't just a simple text highlight - it's a carefully orchestrated system that works across multiple layers of the PDF rendering stack while maintaining performance and accuracy.