Skip to content

Commit

Permalink
Merge pull request #1475 from ManishMadan2882/main
Browse files Browse the repository at this point in the history
Highlight Similar substrings in search results
  • Loading branch information
dartpain authored Dec 9, 2024
2 parents e798d18 + 7c3f80f commit 35e8e2d
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 53 deletions.
61 changes: 34 additions & 27 deletions extensions/react-widget/src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React from 'react'
import styled, { keyframes, createGlobalStyle, ThemeProvider } from 'styled-components';
import styled, { ThemeProvider } from 'styled-components';
import { WidgetCore } from './DocsGPTWidget';
import { SearchBarProps } from '@/types';
import { getSearchResults } from '../requests/searchAPI'
import { Result } from '@/types';
import MarkdownIt from 'markdown-it';
import DOMPurify from 'dompurify';
import { getOS } from '../utils/helper'
import { getOS, preprocessSearchResultsToHTML } from '../utils/helper'
const themes = {
dark: {
bg: '#000',
Expand Down Expand Up @@ -116,7 +115,7 @@ const ResultWrapper = styled.div`
const Markdown = styled.div`
line-height:20px;
font-size: 12px;
word-break: break-all;
white-space: pre-wrap;
pre {
padding: 8px;
width: 90%;
Expand Down Expand Up @@ -147,17 +146,18 @@ word-break: break-all;
code:not(pre code) {
border-radius: 6px;
padding: 4px 4px;
font-size: 12px;
display: inline-block;
padding: 2px 2px;
margin: 2px;
font-size: 10px;
display: inline;
background-color: #646464;
color: #fff ;
}
img{
max-width: 50%;
}
code {
white-space: pre-wrap ;
overflow-wrap: break-word;
word-break: break-all;
overflow-x: auto;
}
a{
color: #007ee6;
Expand Down Expand Up @@ -291,6 +291,8 @@ export const SearchBar = ({
}, 500);

return () => {
console.log(results);

abortController.abort();
clearTimeout(debounceTimeout.current ?? undefined);
};
Expand Down Expand Up @@ -341,22 +343,27 @@ export const SearchBar = ({
(results.length > 0 ?
results.map((res, key) => {
const containsSource = res.source !== 'local';
return (
<ResultWrapper
key={key}
onClick={() => {
if (!containsSource) return;
window.open(res.source, '_blank', 'noopener, noreferrer')
}}
className={containsSource ? "contains-source" : ""}>
<Title>{res.title}</Title>
<Content>
<Markdown
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(md.render((res.text).substring(0, 256) + "...")) }}
/>
</Content>
</ResultWrapper>
)
const filteredResults = preprocessSearchResultsToHTML(res.text,input)
if (filteredResults)
return (
<ResultWrapper
key={key}
onClick={() => {
if (!containsSource) return;
window.open(res.source, '_blank', 'noopener, noreferrer')
}}
className={containsSource ? "contains-source" : ""}>
<Title>{res.title}</Title>
<Content>
<Markdown
dangerouslySetInnerHTML={{ __html: filteredResults }}
/>
</Content>
</ResultWrapper>
)
else {
setResults((prevItems) => prevItems.filter((_, index) => index !== key));
}
})
:
<NoResults>No results</NoResults>
Expand Down
110 changes: 85 additions & 25 deletions extensions/react-widget/src/utils/helper.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,87 @@
import MarkdownIt from "markdown-it";
import DOMPurify from "dompurify";
export const getOS = () => {
const platform = window.navigator.platform;
const userAgent = window.navigator.userAgent || window.navigator.vendor;

if (/Mac/i.test(platform)) {
return 'mac';
const platform = window.navigator.platform;
const userAgent = window.navigator.userAgent || window.navigator.vendor;

if (/Mac/i.test(platform)) {
return 'mac';
}

if (/Win/i.test(platform)) {
return 'win';
}

if (/Linux/i.test(platform) && !/Android/i.test(userAgent)) {
return 'linux';
}

if (/Android/i.test(userAgent)) {
return 'android';
}

if (/iPhone|iPad|iPod/i.test(userAgent)) {
return 'ios';
}

return 'other';
};

export const preprocessSearchResultsToHTML = (text: string, keyword: string) => {
const md = new MarkdownIt();
const htmlString = md.render(text);

// Container for processed HTML
const filteredResults = document.createElement("div");
filteredResults.innerHTML = htmlString;

if (!processNode(filteredResults, keyword.trim())) return null;

return filteredResults.innerHTML.trim() ? filteredResults.outerHTML : null;
};



// Recursive function to process nodes
const processNode = (node: Node, keyword: string): boolean => {

const keywordRegex = new RegExp(`(${keyword})`, "gi");
if (node.nodeType === Node.TEXT_NODE) {
const textContent = node.textContent || "";

if (textContent.toLowerCase().includes(keyword.toLowerCase())) {
const highlightedHTML = textContent.replace(
keywordRegex,
`<mark>$1</mark>`
);
const tempContainer = document.createElement("div");
tempContainer.innerHTML = highlightedHTML;

// Replace the text node with highlighted content
while (tempContainer.firstChild) {
node.parentNode?.insertBefore(tempContainer.firstChild, node);
}
node.parentNode?.removeChild(node);

return true;
}
if (/Win/i.test(platform)) {
return 'win';
}

if (/Linux/i.test(platform) && !/Android/i.test(userAgent)) {
return 'linux';
}

if (/Android/i.test(userAgent)) {
return 'android';
}

if (/iPhone|iPad|iPod/i.test(userAgent)) {
return 'ios';
}

return 'other';
};

return false;
} else if (node.nodeType === Node.ELEMENT_NODE) {

const children = Array.from(node.childNodes);
let hasKeyword = false;

children.forEach((child) => {
if (!processNode(child, keyword)) {
node.removeChild(child);
} else {
hasKeyword = true;
}
});

return hasKeyword;
}

return false;
};
2 changes: 1 addition & 1 deletion extensions/react-widget/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
/* Linting */
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": true,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true,
/* The "typeRoots" configuration specifies the locations where
TypeScript looks for type definitions (.d.ts files) to
Expand Down

0 comments on commit 35e8e2d

Please sign in to comment.