* Applies search highlighting to a single line for virtualized rendering. * * @param html - The syntax-highlighted HTML string * @param searchQuery - The search query to highlight * @param currentMatchIndex - Index of the current match (for distinct highlighting) * @param globalMatchOffset - Cu
( html: string, searchQuery: string, currentMatchIndex: number, globalMatchOffset: number )
| 759 | * @returns Object containing highlighted HTML and count of matches in this line |
| 760 | */ |
| 761 | function applySearchHighlightingToLine( |
| 762 | html: string, |
| 763 | searchQuery: string, |
| 764 | currentMatchIndex: number, |
| 765 | globalMatchOffset: number |
| 766 | ): { html: string; matchesInLine: number } { |
| 767 | if (!searchQuery.trim()) return { html, matchesInLine: 0 } |
| 768 | |
| 769 | const escaped = escapeRegex(searchQuery) |
| 770 | const regex = new RegExp(`(${escaped})`, 'gi') |
| 771 | const parts = html.split(/(<[^>]+>)/g) |
| 772 | let matchesInLine = 0 |
| 773 | |
| 774 | const result = parts |
| 775 | .map((part) => { |
| 776 | if (part.startsWith('<') && part.endsWith('>')) { |
| 777 | return part |
| 778 | } |
| 779 | return part.replace(regex, (match) => { |
| 780 | const globalIndex = globalMatchOffset + matchesInLine |
| 781 | const isCurrentMatch = globalIndex === currentMatchIndex |
| 782 | matchesInLine++ |
| 783 | |
| 784 | const bgClass = isCurrentMatch |
| 785 | ? 'bg-[var(--highlight-search-active)] text-[var(--text-primary)]' |
| 786 | : 'bg-[#FCD34D]/40 dark:bg-[#FCD34D]/30' |
| 787 | |
| 788 | return `<mark class="${bgClass} rounded-xs" data-search-match>${match}</mark>` |
| 789 | }) |
| 790 | }) |
| 791 | .join('') |
| 792 | |
| 793 | return { html: result, matchesInLine } |
| 794 | } |
| 795 | |
| 796 | /** |
| 797 | * Props for the Code.Viewer component (readonly code display). |
no test coverage detected