mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
Merge pull request #1475 from ManishMadan2882/main
Highlight Similar substrings in search results
This commit is contained in:
@@ -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',
|
||||
@@ -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%;
|
||||
@@ -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;
|
||||
@@ -291,6 +291,8 @@ export const SearchBar = ({
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
console.log(results);
|
||||
|
||||
abortController.abort();
|
||||
clearTimeout(debounceTimeout.current ?? undefined);
|
||||
};
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
const platform = window.navigator.platform;
|
||||
const userAgent = window.navigator.userAgent || window.navigator.vendor;
|
||||
|
||||
if (/Mac/i.test(platform)) {
|
||||
return 'mac';
|
||||
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';
|
||||
}
|
||||
return false;
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
|
||||
if (/Linux/i.test(platform) && !/Android/i.test(userAgent)) {
|
||||
return 'linux';
|
||||
}
|
||||
const children = Array.from(node.childNodes);
|
||||
let hasKeyword = false;
|
||||
|
||||
if (/Android/i.test(userAgent)) {
|
||||
return 'android';
|
||||
}
|
||||
children.forEach((child) => {
|
||||
if (!processNode(child, keyword)) {
|
||||
node.removeChild(child);
|
||||
} else {
|
||||
hasKeyword = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (/iPhone|iPad|iPod/i.test(userAgent)) {
|
||||
return 'ios';
|
||||
}
|
||||
|
||||
return 'other';
|
||||
};
|
||||
return hasKeyword;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user