@@ -1891,12 +1891,14 @@ def get_log_css():
18911891}
18921892
18931893/* Highlight matching text */
1894- .search-highlight {
1894+ mark .search-highlight {
18951895 background-color: #ffeb3b;
18961896 font-weight: bold;
1897+ padding: 0;
1898+ border-radius: 2px;
18971899}
18981900
1899- body.dark-mode .search-highlight {
1901+ body.dark-mode mark .search-highlight {
19001902 background-color: #ffa000;
19011903 color: #000;
19021904}
@@ -2364,7 +2366,7 @@ def get_logfile_js(filter_type):
23642366 // Re-apply filter to include new entries
23652367 setTimeout(() => {{
23662368 filterLogEntries();
2367- }}, 10 );
2369+ }}, 50 );
23682370 }}
23692371
23702372 // Auto-scroll to top for new entries (since newest are at top)
@@ -2393,7 +2395,7 @@ def get_logfile_js(filter_type):
23932395 const searchTerm = document.getElementById('logSearchInput').value.toLowerCase();
23942396 const rows = document.querySelectorAll('#logTableBody tr[data-line]');
23952397 const statusDiv = document.getElementById('searchStatus');
2396-
2398+
23972399 let visibleCount = 0;
23982400 let totalCount = rows.length;
23992401
@@ -2402,10 +2404,7 @@ def get_logfile_js(filter_type):
24022404 rows.forEach(row => {{
24032405 row.classList.remove('log-entry-hidden');
24042406 // Remove any existing highlights
2405- const cells = row.querySelectorAll('td');
2406- cells.forEach(cell => {{
2407- cell.innerHTML = cell.innerHTML.replace(/<mark class="search-highlight">(.*?)<\\ /mark>/gi, '$1');
2408- }});
2407+ clearHighlights(row);
24092408 visibleCount++;
24102409 }});
24112410 statusDiv.textContent = '';
@@ -2415,36 +2414,89 @@ def get_logfile_js(filter_type):
24152414 // Filter and highlight entries
24162415 rows.forEach(row => {{
24172416 const rowText = row.textContent.toLowerCase();
2418-
2417+
24192418 if (rowText.includes(searchTerm)) {{
24202419 row.classList.remove('log-entry-hidden');
24212420 visibleCount++;
2422-
2423- // Highlight matching text
2424- const cells = row.querySelectorAll('td');
2425- cells.forEach(cell => {{
2426- // First remove any existing highlights
2427- cell.innerHTML = cell.innerHTML.replace(/<mark class="search-highlight">(.*?)<\\ /mark>/gi, '$1');
2428-
2429- // Then add new highlights
2430- const regex = new RegExp(`(${{escapeRegExp(searchTerm)}})`, 'gi');
2431- cell.innerHTML = cell.innerHTML.replace(regex, '<mark class="search-highlight">$1</mark>');
2432- }});
2421+
2422+ // Clear existing highlights first
2423+ clearHighlights(row);
2424+
2425+ // Apply new highlights
2426+ highlightTextInElement(row, searchTerm);
24332427 }} else {{
24342428 row.classList.add('log-entry-hidden');
2435- // Remove highlights from hidden rows
2436- const cells = row.querySelectorAll('td');
2437- cells.forEach(cell => {{
2438- cell.innerHTML = cell.innerHTML.replace(/<mark class="search-highlight">(.*?)<\\ /mark>/gi, '$1');
2439- }});
2429+ // Clear highlights from hidden rows
2430+ clearHighlights(row);
24402431 }}
24412432 }});
24422433
24432434 // Update status
24442435 statusDiv.textContent = `Showing ${{visibleCount}} of ${{totalCount}} entries`;
24452436 }}
24462437
2447- // Clear search function
2438+ // Clear all highlights in an element
2439+ function clearHighlights(element) {{
2440+ const highlightedElements = element.querySelectorAll('.search-highlight');
2441+ highlightedElements.forEach(highlighted => {{
2442+ const parent = highlighted.parentNode;
2443+ parent.replaceChild(document.createTextNode(highlighted.textContent), highlighted);
2444+ parent.normalize(); // Merge adjacent text nodes
2445+ }});
2446+ }}
2447+
2448+ // Highlight text in an element without corrupting HTML
2449+ function highlightTextInElement(element, searchTerm) {{
2450+ const walker = document.createTreeWalker(
2451+ element,
2452+ NodeFilter.SHOW_TEXT,
2453+ null,
2454+ false
2455+ );
2456+
2457+ const textNodes = [];
2458+ let node;
2459+ while (node = walker.nextNode()) {{
2460+ textNodes.push(node);
2461+ }}
2462+
2463+ textNodes.forEach(textNode => {{
2464+ const text = textNode.textContent;
2465+ const lowerText = text.toLowerCase();
2466+ const lowerSearchTerm = searchTerm.toLowerCase();
2467+
2468+ if (lowerText.includes(lowerSearchTerm)) {{
2469+ const parent = textNode.parentNode;
2470+ const fragment = document.createDocumentFragment();
2471+
2472+ let lastIndex = 0;
2473+ let index = lowerText.indexOf(lowerSearchTerm, 0);
2474+
2475+ while (index !== -1) {{
2476+ // Add text before the match
2477+ if (index > lastIndex) {{
2478+ fragment.appendChild(document.createTextNode(text.substring(lastIndex, index)));
2479+ }}
2480+
2481+ // Add highlighted match
2482+ const mark = document.createElement('mark');
2483+ mark.className = 'search-highlight';
2484+ mark.textContent = text.substring(index, index + searchTerm.length);
2485+ fragment.appendChild(mark);
2486+
2487+ lastIndex = index + searchTerm.length;
2488+ index = lowerText.indexOf(lowerSearchTerm, lastIndex);
2489+ }}
2490+
2491+ // Add remaining text
2492+ if (lastIndex < text.length) {{
2493+ fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
2494+ }}
2495+
2496+ parent.replaceChild(fragment, textNode);
2497+ }}
2498+ }});
2499+ }} // Clear search function
24482500 function clearLogSearch() {{
24492501 document.getElementById('logSearchInput').value = '';
24502502 filterLogEntries();
0 commit comments