(feat:search bar) initiating seach bar

This commit is contained in:
ManishMadan2882
2024-11-14 04:52:15 +05:30
parent 541a6417b7
commit 9409e4498f
8 changed files with 257 additions and 15 deletions

View File

@@ -32,7 +32,7 @@
"scripts": {
"build": "parcel build src/main.tsx --public-url ./",
"build:react": "parcel build src/index.ts",
"dev": "parcel src/index.html -p 3000",
"dev": "parcel -p 3000",
"test": "jest",
"lint": "eslint",
"check": "tsc --noEmit",

View File

@@ -1,11 +1,11 @@
import React from "react"
import {DocsGPTWidget} from "./components/DocsGPTWidget"
const App = () => {
import {SearchBar} from "./components/SearchBar"
export const App = () => {
return (
<div>
<SearchBar/>
<DocsGPTWidget/>
</div>
)
}
export default App
}

View File

@@ -0,0 +1,177 @@
import React from 'react'
import styled, { keyframes, createGlobalStyle } 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';
const Main = styled.div`
font-family: sans-serif;
`
const TextField = styled.input`
padding: 8px;
border-radius: 8px;
display: inline;
color: rgb(107 114 128);
outline: none;
border: 2px solid transparent;
background-color: rgba(0, 0, 0, .05);;
&:focus {
outline: #467f95; /* remove default outline */
border:2px solid skyblue; /* change border color on focus */
box-shadow: 0px 0px 2px skyblue; /* add a red box shadow on focus */
}
`
const Container = styled.div`
position: relative;
display: inline-block;
`
const SearchResults = styled.div`
position: absolute;
background-color: white;
opacity: 90%;
border: 1px solid rgba(0, 0, 0, .1);
border-radius: 12px;
padding: 20px;
width: 576px;
z-index: 100;
height: 25vh;
overflow-y: auto;
top: 45px;
scrollbar-color: lab(48.438 0 0 / 0.4) rgba(0, 0, 0, 0);
scrollbar-gutter: stable;
scrollbar-width: thin;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(16px);
`
const Title = styled.h3`
font-size: 12px;
color: rgb(107, 114, 128);
padding-bottom: 4px;
font-weight: 600;
text-transform: uppercase;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
`
const Content = styled.div`
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
`
const Markdown = styled.div`
font-size: 12px;
pre {
padding: 8px;
width: 90%;
font-size: 12px;
border-radius: 6px;
overflow-x: auto;
background-color: #1B1C1F;
color: #fff ;
}
h1,h2 {
font-size: 16px;
font-weight: 600;
color: rgb(31, 41, 55);
}
h3 {
font-size: 14px;
}
p {
margin: 0px;
line-height: 1.35rem;
}
code:not(pre code) {
border-radius: 6px;
padding: 4px 4px;
font-size: 12px;
display: inline-block;
background-color: #646464;
color: #fff ;
}
code {
white-space: pre-wrap ;
overflow-wrap: break-word;
word-break: break-all;
}
ul{
padding:0px;
list-style-position: inside;
}
`
export const SearchBar = ({
apiKey = "79bcbf0e-3dd1-4ac3-b893-e41b3d40ec8d",
apiHost = "http://127.0.0.1:7091",
theme = "dark"
}: SearchBarProps) => {
const [input, setInput] = React.useState("")
const [isWidgetOpen, setIsWidgetOpen] = React.useState<boolean>(false);
const inputRef = React.useRef<HTMLInputElement>(null)
const [results, setResults] = React.useState<Result[]>([])
React.useEffect(() => {
input.length > 0 ?
getSearchResults(input, apiKey, apiHost)
.then((data) => setResults(data))
.catch((err) => console.log(err))
:
setResults([])
}, [input])
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
setIsWidgetOpen(true);
}
};
const handleClose = () => {
setIsWidgetOpen(false);
}
const md = new MarkdownIt();
return (
<Main>
<Container>
<TextField
ref={inputRef}
onKeyDown={(e) => handleKeyDown(e)}
placeholder='Search here or Ask DocsGPT'
value={input}
onChange={(e) => setInput(e.target.value)}
/>
{
results.length > 0 && (
<SearchResults>
{results.map((res) => (
<div>
<Title>{res.title}</Title>
<Content>
<Markdown
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(md.render(res.text)) }}
/>
</Content>
</div>
))
}
</SearchResults>
)
}
</Container>
<WidgetCore
theme={theme}
apiHost={apiHost}
apiKey={apiKey}
prefilledQuery={input}
isOpen={isWidgetOpen}
handleClose={handleClose} size={'large'}
/>
</Main>
)
}

View File

@@ -9,11 +9,11 @@
<body>
<div id="app"></div>
<script type="module" src="main.tsx"></script>
<script type="module" src="../dist/main.js"></script>
<script type="module">
<!-- <script type="module">
window.onload = function() {
renderDocsGPTWidget('app');
renderSearchBar('app')
}
</script>
</script> -->
</body>
</html>

View File

@@ -1 +1,2 @@
export { DocsGPTWidget } from "./components/DocsGPTWidget";
export {SearchBar} from "./components/SearchBar"
export { DocsGPTWidget } from "./components/DocsGPTWidget";

View File

@@ -1,12 +1,25 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { DocsGPTWidget } from './components/DocsGPTWidget';
import { createRoot } from "react-dom/client";
import { App } from "./App";
import { DocsGPTWidget } from './components/DocsGPTWidget';
import { SearchBar } from './components/SearchBar';
import React from "react";
if (typeof window !== 'undefined') {
const renderWidget = (elementId: string, props = {}) => {
const root = createRoot(document.getElementById(elementId) as HTMLElement);
root.render(<DocsGPTWidget {...props} />);
};
const renderSearchBar = (elementId: string, props = {}) => {
const root = createRoot(document.getElementById(elementId) as HTMLElement);
root.render(<SearchBar {...props} />);
};
(window as any).renderDocsGPTWidget = renderWidget;
(window as any).renderSearchBar = renderSearchBar;
}
export { DocsGPTWidget };
const container = document.getElementById("app") as HTMLElement;
const root = createRoot(container)
root.render(<App />);
export { DocsGPTWidget };
export { SearchBar }

View File

@@ -0,0 +1,34 @@
import { Result } from "@/types";
async function getSearchResults(question: string, apiKey:string, apiHost:string): Promise<Result[]> {
const payload = {
question,
api_key:apiKey
};
try {
const response = await fetch(`${apiHost}/api/search`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}
const data: Result[] = await response.json();
return data;
} catch (error) {
console.error("Failed to fetch documents:", error);
throw error;
}
}
export {
getSearchResults
}

View File

@@ -32,5 +32,22 @@ export interface WidgetProps {
buttonText?:string;
buttonBg?:string;
collectFeedback?:boolean;
deafultOpen?: boolean;
}
defaultOpen?: boolean;
}
export interface WidgetCoreProps extends WidgetProps {
widgetRef?:React.RefObject<HTMLDivElement> | null;
handleClose?:React.MouseEventHandler | undefined;
isOpen:boolean;
prefilledQuery?: string;
}
export interface SearchBarProps {
apiHost?: string;
apiKey?: string;
theme?:THEME
}
export interface Result {
text:string;
title:string
}