diff --git a/frontend/components/JsonDisplay.tsx b/frontend/components/JsonDisplay.tsx index 79a859c..42a3ac3 100644 --- a/frontend/components/JsonDisplay.tsx +++ b/frontend/components/JsonDisplay.tsx @@ -1,6 +1,6 @@ import React, { useState, useRef, useEffect, useCallback } from 'react'; -import { CheckIcon, ChevronDownIcon, ClipboardIcon, SearchIcon, XIcon, ArrowUpwardIcon, ArrowDownwardIcon } from './icons/material-icons-imports'; +import { CheckIcon, ChevronDownIcon, ClipboardIcon, SearchIcon, XIcon, ArrowUpwardIcon, ArrowDownwardIcon, CaseSensitiveIcon, WholeWordIcon, RegexIcon, ImageIcon } from './icons/material-icons-imports'; // Search context for highlighting and navigation interface SearchState { @@ -11,29 +11,55 @@ interface SearchState { } // Helper function to highlight search matches in text -const highlightText = (text: string, searchQuery: string, isCurrentMatch: boolean = false): React.ReactNode => { - if (!searchQuery || !text.includes(searchQuery)) { +// Helper function to highlight search matches in text +const highlightText = (text: string, searchRegex: RegExp | null): React.ReactNode => { + if (!searchRegex || !text) { return text; } - const parts = text.split(new RegExp(`(${searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi')); - let matchIndex = 0; - - return parts.map((part, index) => { - if (part.toLowerCase() === searchQuery.toLowerCase()) { - const currentMatch = matchIndex++; - return ( - - {part} - - ); + // Create a local regex with 'g' flag to find all matches in this string + let localRegex: RegExp; + try { + localRegex = new RegExp(searchRegex.source, searchRegex.flags.includes('g') ? searchRegex.flags : searchRegex.flags + 'g'); + } catch (e) { + return text; + } + + const matches: { start: number; end: number; text: string }[] = []; + let match; + localRegex.lastIndex = 0; + + while ((match = localRegex.exec(text)) !== null) { + matches.push({ start: match.index, end: match.index + match[0].length, text: match[0] }); + if (match[0].length === 0) localRegex.lastIndex++; // Avoid infinite loop + } + + if (matches.length === 0) return text; + + const nodes: React.ReactNode[] = []; + let lastIndex = 0; + + matches.forEach((m, index) => { + if (m.start > lastIndex) { + nodes.push(text.substring(lastIndex, m.start)); } - return part; + nodes.push( + + {m.text} + + ); + lastIndex = m.end; }); + + if (lastIndex < text.length) { + nodes.push(text.substring(lastIndex)); + } + + return <>{nodes}>; }; // Component to render ObjectId with both click navigation and copy functionality @@ -129,10 +155,10 @@ const ObjectIdDisplay: React.FC<{ {/* Context Menu */} {showContextMenu && ( -
-
-
+