Merge branch 'arc53:main' into main
@ -0,0 +1,27 @@
|
||||
You are a DocsGPT bot assistant by Arc53 that provides help with programming libraries. You give thorough answers with code examples.
|
||||
Given the following extracted parts of a long document and a question, create a final answer with references ("SOURCES").
|
||||
ALWAYS return a "SOURCES" part in your answer. You can also remeber things from previous questions and use them in your answer.
|
||||
|
||||
QUESTION: How to merge tables in pandas?
|
||||
=========
|
||||
Content: pandas provides various facilities for easily combining together Series or DataFrame with various kinds of set logic for the indexes and relational algebra functionality in the case of join / merge-type operations.
|
||||
Source: 28-pl
|
||||
Content: pandas provides a single function, merge(), as the entry point for all standard database join operations between DataFrame or named Series objects: \n\npandas.merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False, validate=None)
|
||||
Source: 30-pl
|
||||
=========
|
||||
FINAL ANSWER: To merge two tables in pandas, you can use the pd.merge() function. The basic syntax is: \n\npd.merge(left, right, on, how) \n\nwhere left and right are the two tables to merge, on is the column to merge on, and how is the type of merge to perform. \n\nFor example, to merge the two tables df1 and df2 on the column 'id', you can use: \n\npd.merge(df1, df2, on='id', how='inner')
|
||||
SOURCES: 28-pl 30-pl
|
||||
|
||||
QUESTION: {historyquestion}
|
||||
=========
|
||||
CONTENT:
|
||||
SOURCE:
|
||||
=========
|
||||
FINAL ANSWER: {historyanswer}
|
||||
SOURCES:
|
||||
|
||||
QUESTION: {question}
|
||||
=========
|
||||
{summaries}
|
||||
=========
|
||||
FINAL ANSWER:
|
@ -1,71 +1,54 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useMediaQuery } from '../hooks';
|
||||
import { selectIsMenuOpen } from '../store';
|
||||
|
||||
//TODO - Add hyperlinks to text
|
||||
//TODO - Styling
|
||||
|
||||
export default function About() {
|
||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||
const isMenuOpen = useSelector(selectIsMenuOpen);
|
||||
|
||||
return (
|
||||
//Parent div for all content shown through App.tsx routing needs to have this styling. Might change when state management is updated.
|
||||
<div
|
||||
className={`${
|
||||
isMobile
|
||||
? isMenuOpen
|
||||
? 'mt-80'
|
||||
: 'mt-16'
|
||||
: isMenuOpen
|
||||
? 'md:ml-72 lg:ml-96'
|
||||
: 'ml-16'
|
||||
} h-full w-full p-6 transition-all`}
|
||||
>
|
||||
<article className="mx-auto my-auto flex w-full max-w-6xl flex-col gap-6 rounded-lg bg-gray-100 p-6 text-jet lg:p-10 xl:p-16">
|
||||
<p className="text-3xl font-semibold">About DocsGPT 🦖</p>
|
||||
<p className="mt-4 text-xl font-bold">
|
||||
Find the information in your documentation through AI-powered
|
||||
open-source chatbot. Powered by GPT-3, Faiss and LangChain.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<p className="text-lg">
|
||||
If you want to add your own documentation, please follow the
|
||||
instruction below:
|
||||
</p>
|
||||
<p className="mt-4 text-lg">
|
||||
1. Navigate to{' '}
|
||||
<span className="bg-gray-200 italic"> /application</span> folder
|
||||
</p>
|
||||
<p className="mt-4 text-lg">
|
||||
2. Install dependencies from{' '}
|
||||
<span className="bg-gray-200 italic">
|
||||
pip install -r requirements.txt
|
||||
</span>
|
||||
</p>
|
||||
<p className="mt-4 text-lg">
|
||||
3. Prepare a <span className="bg-gray-200 italic">.env</span> file.
|
||||
Copy <span className="bg-gray-200 italic">.env_sample</span> and
|
||||
create <span className="bg-gray-200 italic">.env</span> with your
|
||||
OpenAI API token
|
||||
</p>
|
||||
<p className="mt-4 text-lg">
|
||||
4. Run the app with{' '}
|
||||
<span className="bg-gray-200 italic">python app.py</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-lg">
|
||||
Currently It uses python pandas documentation, so it will respond to
|
||||
information relevant to pandas. If you want to train it on different
|
||||
documentation - please follow this guide.
|
||||
</p>
|
||||
|
||||
<p className="mt-4 text-lg">
|
||||
If you want to launch it on your own server - follow this guide.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
//TODO - Add hyperlinks to text
|
||||
//TODO - Styling
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
//Parent div for all content shown through App.tsx routing needs to have this styling. Might change when state management is updated.
|
||||
<div className="grid min-h-screen">
|
||||
<article className=" mx-auto my-auto flex w-full max-w-6xl flex-col place-items-center gap-6 rounded-lg bg-gray-100 p-6 text-jet lg:p-10 xl:p-16">
|
||||
<p className="text-3xl font-semibold">About DocsGPT 🦖</p>
|
||||
<p className="mt-4 text-xl font-bold">
|
||||
Find the information in your documentation through AI-powered
|
||||
open-source chatbot. Powered by GPT-3, Faiss and LangChain.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<p className="text-lg">
|
||||
If you want to add your own documentation, please follow the
|
||||
instruction below:
|
||||
</p>
|
||||
<p className="mt-4 text-lg">
|
||||
1. Navigate to{' '}
|
||||
<span className="bg-gray-200 italic"> /application</span> folder
|
||||
</p>
|
||||
<p className="mt-4 text-lg">
|
||||
2. Install dependencies from{' '}
|
||||
<span className="bg-gray-200 italic">
|
||||
pip install -r requirements.txt
|
||||
</span>
|
||||
</p>
|
||||
<p className="mt-4 text-lg">
|
||||
3. Prepare a <span className="bg-gray-200 italic">.env</span> file.
|
||||
Copy <span className="bg-gray-200 italic">.env_sample</span> and
|
||||
create <span className="bg-gray-200 italic">.env</span> with your
|
||||
OpenAI API token
|
||||
</p>
|
||||
<p className="mt-4 text-lg">
|
||||
4. Run the app with{' '}
|
||||
<span className="bg-gray-200 italic">python app.py</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-lg">
|
||||
Currently It uses python pandas documentation, so it will respond to
|
||||
information relevant to pandas. If you want to train it on different
|
||||
documentation - please follow this guide.
|
||||
</p>
|
||||
|
||||
<p className="mt-4 text-lg">
|
||||
If you want to launch it on your own server - follow this guide.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,18 +1,29 @@
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import Navigation from './components/Navigation';
|
||||
import Conversation from './components/Conversation/Conversation';
|
||||
import APIKeyModal from './components/APIKeyModal';
|
||||
import About from './components/About';
|
||||
import Navigation from './Navigation';
|
||||
import Conversation from './conversation/Conversation';
|
||||
import About from './About';
|
||||
import { useState } from 'react';
|
||||
import { ActiveState } from './models/misc';
|
||||
|
||||
export default function App() {
|
||||
const [navState, setNavState] = useState<ActiveState>('ACTIVE');
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-col transition-all md:flex-row">
|
||||
<APIKeyModal />
|
||||
<Navigation />
|
||||
<Routes>
|
||||
<Route path="/" element={<Conversation />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
</Routes>
|
||||
<div className="min-h-full min-w-full">
|
||||
<Navigation
|
||||
navState={navState}
|
||||
setNavState={(val: ActiveState) => setNavState(val)}
|
||||
/>
|
||||
<div
|
||||
className={`transition-all duration-200 ${
|
||||
navState === 'ACTIVE' ? 'ml-0 md:ml-72 lg:ml-96' : ' ml-0 md:ml-16'
|
||||
}`}
|
||||
>
|
||||
<Routes>
|
||||
<Route path="/" element={<Conversation />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
export default function Avatar({
|
||||
avatar,
|
||||
size,
|
||||
}: {
|
||||
avatar: string;
|
||||
size?: 'SMALL' | 'MEDIUM' | 'LARGE';
|
||||
}) {
|
||||
return <div>{avatar}</div>;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
export default function Hero({ className = '' }: { className?: string }) {
|
||||
return (
|
||||
<div className={`flex flex-col ${className}`}>
|
||||
<p className="mb-10 text-center text-4xl font-semibold">
|
||||
DocsGPT <span className="text-3xl">🦖</span>
|
||||
</p>
|
||||
<p className="mb-3 text-center">
|
||||
Welcome to DocsGPT, your technical documentation assistant!
|
||||
</p>
|
||||
<p className="mb-3 text-center">
|
||||
Enter a query related to the information in the documentation you
|
||||
selected to receive and we will provide you with the most relevant
|
||||
answers.
|
||||
</p>
|
||||
<p className="text-center">
|
||||
Start by entering your query in the input field below and we will do the
|
||||
rest!
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import Arrow1 from './assets/arrow.svg';
|
||||
import Hamburger from './assets/hamburger.svg';
|
||||
import Key from './assets/key.svg';
|
||||
import Info from './assets/info.svg';
|
||||
import Link from './assets/link.svg';
|
||||
import { ActiveState } from './models/misc';
|
||||
import APIKeyModal from './preferences/APIKeyModal';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectApiKeyStatus } from './preferences/preferenceSlice';
|
||||
import { useState } from 'react';
|
||||
|
||||
//TODO - Need to replace Chat button to open secondary nav with scrollable past chats option and new chat at top
|
||||
//TODO - Need to add Discord and Github links
|
||||
|
||||
export default function Navigation({
|
||||
navState,
|
||||
setNavState,
|
||||
}: {
|
||||
navState: ActiveState;
|
||||
setNavState: (val: ActiveState) => void;
|
||||
}) {
|
||||
const isApiKeySet = useSelector(selectApiKeyStatus);
|
||||
const [apiKeyModalState, setApiKeyModalState] = useState<ActiveState>(
|
||||
isApiKeySet ? 'INACTIVE' : 'ACTIVE',
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`${
|
||||
navState === 'INACTIVE' && '-ml-96 md:-ml-60 lg:-ml-80'
|
||||
} fixed z-10 flex h-full w-72 flex-col border-r-2 border-gray-100 bg-gray-50 transition-all duration-200 lg:w-96`}
|
||||
>
|
||||
<div className={'h-16 w-full border-b-2 border-gray-100'}>
|
||||
<button
|
||||
className="float-right mr-5 mt-5 h-5 w-5"
|
||||
onClick={() =>
|
||||
setNavState(navState === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE')
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={Arrow1}
|
||||
alt="menu toggle"
|
||||
className={`${
|
||||
navState === 'INACTIVE' ? 'rotate-180' : 'rotate-0'
|
||||
} m-auto w-3 transition-all duration-200`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-grow border-b-2 border-gray-100"></div>
|
||||
|
||||
<div className="flex h-16 flex-col border-b-2 border-gray-100">
|
||||
<div
|
||||
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
|
||||
onClick={() => {
|
||||
setApiKeyModalState('ACTIVE');
|
||||
}}
|
||||
>
|
||||
<img src={Key} alt="key" className="ml-2 w-6" />
|
||||
<p className="my-auto text-eerie-black">Reset Key</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-48 flex-col border-b-2 border-gray-100">
|
||||
<NavLink
|
||||
to="/about"
|
||||
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
|
||||
>
|
||||
<img src={Info} alt="info" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">About</p>
|
||||
</NavLink>
|
||||
|
||||
<div className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100">
|
||||
<img src={Link} alt="link" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">Discord</p>
|
||||
</div>
|
||||
|
||||
<div className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100">
|
||||
<img src={Link} alt="link" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">Github</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="fixed mt-5 ml-6 h-6 w-6 md:hidden"
|
||||
onClick={() => setNavState('ACTIVE')}
|
||||
>
|
||||
<img src={Hamburger} alt="menu toggle" className="w-7" />
|
||||
</button>
|
||||
<APIKeyModal
|
||||
modalState={apiKeyModalState}
|
||||
setModalState={setApiKeyModalState}
|
||||
isCancellable={isApiKeySet}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
Before Width: | Height: | Size: 200 B After Width: | Height: | Size: 200 B |
Before Width: | Height: | Size: 391 B After Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 273 B |
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 337 B |
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 293 B |
@ -0,0 +1,3 @@
|
||||
<svg width="21" height="18" viewBox="0 0 21 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.00999999 18L21 9L0.00999999 0L0 7L15 9L0 11L0.00999999 18Z" fill="black" fill-opacity="0.54"/>
|
||||
</svg>
|
After Width: | Height: | Size: 210 B |
@ -1,63 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
setApiKey,
|
||||
toggleApiKeyModal,
|
||||
selectIsApiKeyModalOpen,
|
||||
} from '../store';
|
||||
|
||||
export default function APIKeyModal({}) {
|
||||
//TODO - Add form validation?
|
||||
//TODO - Connect to backend
|
||||
//TODO - Add link to OpenAI API Key page
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const isApiModalOpen = useSelector(selectIsApiKeyModalOpen);
|
||||
const [key, setKey] = useState('');
|
||||
const [formError, setFormError] = useState(false);
|
||||
|
||||
function handleSubmit() {
|
||||
if (key.length < 1) {
|
||||
setFormError(true);
|
||||
return;
|
||||
}
|
||||
dispatch(setApiKey(key));
|
||||
dispatch(toggleApiKeyModal());
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
isApiModalOpen ? 'visible' : 'hidden'
|
||||
} absolute z-30 h-screen w-screen bg-gray-alpha`}
|
||||
>
|
||||
<article className="mx-auto mt-24 flex w-[90vw] max-w-lg flex-col gap-4 rounded-lg bg-white p-6 shadow-lg">
|
||||
<p className="text-xl text-jet">OpenAI API Key</p>
|
||||
<p className="text-lg leading-5 text-gray-500">
|
||||
Before you can start using DocsGPT we need you to provide an API key
|
||||
for llm. Currently, we support only OpenAI but soon many more. You can
|
||||
find it here.
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
className="h-10 w-full border-b-2 border-jet focus:outline-none"
|
||||
value={key}
|
||||
maxLength={100}
|
||||
placeholder="API Key"
|
||||
onChange={(e) => setKey(e.target.value)}
|
||||
/>
|
||||
<div className="flex justify-between">
|
||||
{formError && (
|
||||
<p className="text-sm text-red-500">Please enter a valid API key</p>
|
||||
)}
|
||||
<button
|
||||
onClick={() => handleSubmit()}
|
||||
className="ml-auto h-10 w-20 rounded-lg bg-violet-800 text-white transition-all hover:bg-violet-700"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { useMediaQuery } from '../../hooks';
|
||||
import { selectIsMenuOpen } from '../../store';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
export default function Conversation() {
|
||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||
const isMenuOpen = useSelector(selectIsMenuOpen);
|
||||
|
||||
return (
|
||||
//Parent div for all content shown through App.tsx routing needs to have this styling.
|
||||
<div
|
||||
className={`${
|
||||
isMobile
|
||||
? isMenuOpen
|
||||
? 'mt-80'
|
||||
: 'mt-16'
|
||||
: isMenuOpen
|
||||
? 'md:ml-72 lg:ml-96'
|
||||
: 'ml-16'
|
||||
} h-full w-full p-6 transition-all`}
|
||||
>
|
||||
Docs GPT Chat Placeholder
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { useMediaQuery } from '../hooks';
|
||||
import {
|
||||
toggleApiKeyModal,
|
||||
selectIsMenuOpen,
|
||||
toggleIsMenuOpen,
|
||||
} from '../store';
|
||||
import Arrow1 from '../imgs/arrow.svg';
|
||||
import Hamburger from '../imgs/hamburger.svg';
|
||||
import Key from '../imgs/key.svg';
|
||||
import Info from '../imgs/info.svg';
|
||||
import Link from '../imgs/link.svg';
|
||||
import Exit from '../imgs/exit.svg';
|
||||
|
||||
//TODO - Need to replace Chat button to open secondary nav with scrollable past chats option and new chat at top
|
||||
//TODO - Need to add Discord and Github links
|
||||
|
||||
function MobileNavigation({}) {
|
||||
const dispatch = useDispatch();
|
||||
const isMenuOpen = useSelector(selectIsMenuOpen);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
isMenuOpen ? 'border-b-2 border-gray-100' : 'h-16'
|
||||
} fixed flex w-full flex-col bg-gray-50 transition-all`}
|
||||
>
|
||||
<div className="h-16 w-full border-b-2 border-gray-100">
|
||||
{isMenuOpen ? (
|
||||
<>
|
||||
<button
|
||||
className="mt-5 ml-6 h-6 w-6"
|
||||
onClick={() => dispatch(toggleIsMenuOpen())}
|
||||
>
|
||||
<img src={Exit} alt="menu toggle" className="w-5" />
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
className="mt-5 ml-6 h-6 w-6"
|
||||
onClick={() => dispatch(toggleIsMenuOpen())}
|
||||
>
|
||||
<img src={Hamburger} alt="menu toggle" className="w-7" />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{isMenuOpen && (
|
||||
<nav className="my-4 flex flex-col">
|
||||
<NavLink
|
||||
to="/"
|
||||
className="flex h-12 cursor-pointer gap-4 rounded-md px-6 hover:bg-gray-100"
|
||||
>
|
||||
<img src={Info} alt="info" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">Chat</p>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/about"
|
||||
className="flex h-12 cursor-pointer gap-4 rounded-md px-6 hover:bg-gray-100"
|
||||
>
|
||||
<img src={Info} alt="info" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">About</p>
|
||||
</NavLink>
|
||||
<div className="flex h-12 cursor-pointer gap-4 rounded-md px-6 hover:bg-gray-100">
|
||||
<img src={Link} alt="info" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">Discord</p>
|
||||
</div>
|
||||
<div className="flex h-12 cursor-pointer gap-4 rounded-md px-6 hover:bg-gray-100">
|
||||
<img src={Link} alt="info" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">Github</p>
|
||||
</div>
|
||||
<div
|
||||
className="flex h-12 cursor-pointer gap-4 rounded-md px-6 hover:bg-gray-100"
|
||||
onClick={() => dispatch(toggleApiKeyModal())}
|
||||
>
|
||||
<img src={Key} alt="info" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">Reset Key</p>
|
||||
</div>
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DesktopNavigation() {
|
||||
const dispatch = useDispatch();
|
||||
const isMenuOpen = useSelector(selectIsMenuOpen);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
isMenuOpen ? 'w-72 lg:w-96' : 'w-16'
|
||||
} fixed flex h-screen flex-col border-r-2 border-gray-100 bg-gray-50 transition-all`}
|
||||
>
|
||||
<div
|
||||
className={`${
|
||||
isMenuOpen ? 'w-full' : 'w-16'
|
||||
} ml-auto h-16 border-b-2 border-gray-100`}
|
||||
>
|
||||
<button
|
||||
className="float-right mr-5 mt-5 h-5 w-5"
|
||||
onClick={() => dispatch(toggleIsMenuOpen())}
|
||||
>
|
||||
<img
|
||||
src={Arrow1}
|
||||
alt="menu toggle"
|
||||
className={`${
|
||||
isMenuOpen ? 'rotate-0' : 'rotate-180'
|
||||
} m-auto w-3 transition-all`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isMenuOpen && (
|
||||
<>
|
||||
<div className="flex-grow border-b-2 border-gray-100"></div>
|
||||
|
||||
<div className="flex h-16 flex-col border-b-2 border-gray-100">
|
||||
<div
|
||||
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
|
||||
onClick={() => dispatch(toggleApiKeyModal())}
|
||||
>
|
||||
<img src={Key} alt="key" className="ml-2 w-6" />
|
||||
<p className="my-auto text-eerie-black">Reset Key</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-48 flex-col border-b-2 border-gray-100">
|
||||
<NavLink
|
||||
to="/about"
|
||||
className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100"
|
||||
>
|
||||
<img src={Info} alt="info" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">About</p>
|
||||
</NavLink>
|
||||
|
||||
<div className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100">
|
||||
<img src={Link} alt="link" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">Discord</p>
|
||||
</div>
|
||||
|
||||
<div className="my-auto mx-4 flex h-12 cursor-pointer gap-4 rounded-md hover:bg-gray-100">
|
||||
<img src={Link} alt="link" className="ml-2 w-5" />
|
||||
<p className="my-auto text-eerie-black">Github</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Navigation() {
|
||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||
|
||||
if (isMobile) {
|
||||
return <MobileNavigation />;
|
||||
} else {
|
||||
return <DesktopNavigation />;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Hero from '../Hero';
|
||||
import ConversationBubble from './ConversationBubble';
|
||||
import ConversationInput from './ConversationInput';
|
||||
import { selectConversation } from './conversationSlice';
|
||||
|
||||
export default function Conversation() {
|
||||
const messages = useSelector(selectConversation);
|
||||
const endMessageRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => endMessageRef?.current?.scrollIntoView());
|
||||
|
||||
return (
|
||||
<div className="flex justify-center p-6">
|
||||
<div className="w-10/12 transition-all md:w-1/2">
|
||||
{messages.map((message, index) => {
|
||||
return (
|
||||
<ConversationBubble
|
||||
ref={index === messages.length - 1 ? endMessageRef : null}
|
||||
className="mb-7"
|
||||
key={index}
|
||||
message={message.text}
|
||||
type={message.type}
|
||||
></ConversationBubble>
|
||||
);
|
||||
})}
|
||||
{messages.length === 0 && <Hero className="mt-24"></Hero>}
|
||||
</div>
|
||||
<ConversationInput className="fixed bottom-2 w-10/12 md:w-[50%]"></ConversationInput>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { forwardRef } from 'react';
|
||||
import Avatar from '../Avatar';
|
||||
import { MESSAGE_TYPE } from './conversationModels';
|
||||
|
||||
const ConversationBubble = forwardRef<
|
||||
HTMLDivElement,
|
||||
{
|
||||
message: string;
|
||||
type: MESSAGE_TYPE;
|
||||
className: string;
|
||||
}
|
||||
>(function ConversationBubble({ message, type, className }, ref) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={`flex rounded-3xl ${
|
||||
type === 'QUESTION' ? '' : 'bg-gray-1000'
|
||||
} py-7 px-5 ${className}`}
|
||||
>
|
||||
<Avatar avatar={type === 'QUESTION' ? '👤' : '🦖'}></Avatar>
|
||||
<p className="ml-5">{message}</p>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default ConversationBubble;
|
@ -0,0 +1,21 @@
|
||||
import Send from './../assets/send.svg';
|
||||
|
||||
export default function ConversationInput({
|
||||
className,
|
||||
}: {
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={`${className} flex`}>
|
||||
<div
|
||||
contentEditable
|
||||
className={`min-h-5 border-000000 overflow-x-hidden; max-h-24 w-full overflow-y-auto rounded-xl border bg-white p-2 pr-9 opacity-100 focus:border-2 focus:outline-none`}
|
||||
></div>
|
||||
<img
|
||||
onClick={() => console.log('here')}
|
||||
src={Send}
|
||||
className="relative right-9"
|
||||
></img>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
export type MESSAGE_TYPE = 'QUESTION' | 'ANSWER';
|
||||
|
||||
export interface Message {
|
||||
text: string;
|
||||
type: MESSAGE_TYPE;
|
||||
}
|
||||
|
||||
export interface ConversationState {
|
||||
conversation: Message[];
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import store from '../store';
|
||||
import { ConversationState, Message } from './conversationModels';
|
||||
|
||||
// harcoding the initial state just for demo
|
||||
const initialState: ConversationState = {
|
||||
conversation: [
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{ text: 'ChatGPT is large learning model', type: 'ANSWER' },
|
||||
{ text: 'what is ChatGPT', type: 'QUESTION' },
|
||||
{
|
||||
text: 'ChatGPT is large learning model',
|
||||
type: 'ANSWER',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const conversationSlice = createSlice({
|
||||
name: 'conversation',
|
||||
initialState,
|
||||
reducers: {
|
||||
addMessage(state, action: PayloadAction<Message>) {
|
||||
state.conversation.push(action.payload);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { addMessage } = conversationSlice.actions;
|
||||
|
||||
type RootState = ReturnType<typeof store.getState>;
|
||||
export const selectConversation = (state: RootState) =>
|
||||
state.conversation.conversation;
|
||||
|
||||
export default conversationSlice.reducer;
|
@ -1,22 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export function useMediaQuery(query: string): boolean {
|
||||
const [matches, setMatches] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const media = window.matchMedia(query);
|
||||
|
||||
if (media.matches !== matches) {
|
||||
setMatches(media.matches);
|
||||
}
|
||||
|
||||
const listener = () => {
|
||||
setMatches(media.matches);
|
||||
};
|
||||
|
||||
media.addEventListener('resize', listener);
|
||||
return () => media.removeEventListener('resize', listener);
|
||||
}, [matches, query]);
|
||||
|
||||
return matches;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export type ActiveState = 'ACTIVE' | 'INACTIVE';
|
||||
|
||||
export type User = {
|
||||
avatar: string;
|
||||
};
|
@ -0,0 +1,83 @@
|
||||
import { useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { ActiveState } from '../models/misc';
|
||||
import { setApiKey } from './preferenceSlice';
|
||||
|
||||
export default function APIKeyModal({
|
||||
modalState,
|
||||
setModalState,
|
||||
isCancellable = true,
|
||||
}: {
|
||||
modalState: ActiveState;
|
||||
setModalState: (val: ActiveState) => void;
|
||||
isCancellable?: boolean;
|
||||
}) {
|
||||
const dispatch = useDispatch();
|
||||
const [key, setKey] = useState('');
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
function handleSubmit() {
|
||||
if (key.length <= 1) {
|
||||
setIsError(true);
|
||||
} else {
|
||||
dispatch(setApiKey(key));
|
||||
setModalState('INACTIVE');
|
||||
setKey('');
|
||||
setIsError(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
setKey('');
|
||||
setIsError(false);
|
||||
setModalState('INACTIVE');
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
modalState === 'ACTIVE' ? 'visible' : 'hidden'
|
||||
} absolute z-30 h-screen w-screen bg-gray-alpha`}
|
||||
>
|
||||
<article className="mx-auto mt-24 flex w-[90vw] max-w-lg flex-col gap-4 rounded-lg bg-white p-6 shadow-lg">
|
||||
<p className="text-xl text-jet">OpenAI API Key</p>
|
||||
<p className="text-lg leading-5 text-gray-500">
|
||||
Before you can start using DocsGPT we need you to provide an API key
|
||||
for llm. Currently, we support only OpenAI but soon many more. You can
|
||||
find it here.
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
className="h-10 w-full border-b-2 border-jet focus:outline-none"
|
||||
value={key}
|
||||
maxLength={100}
|
||||
placeholder="API Key"
|
||||
onChange={(e) => setKey(e.target.value)}
|
||||
/>
|
||||
<div className="flex flex-row-reverse">
|
||||
<div>
|
||||
<button
|
||||
onClick={() => handleSubmit()}
|
||||
className="ml-auto h-10 w-20 rounded-lg bg-violet-800 text-white transition-all hover:bg-violet-700"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
{isCancellable && (
|
||||
<button
|
||||
onClick={() => handleCancel()}
|
||||
className="ml-5 h-10 w-20 rounded-lg border border-violet-700 bg-white text-violet-800 transition-all hover:bg-violet-700 hover:text-white"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{isError && (
|
||||
<p className="mr-auto text-sm text-red-500">
|
||||
Please enter a valid API key
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import store from '../store';
|
||||
|
||||
interface Preference {
|
||||
apiKey: string;
|
||||
}
|
||||
|
||||
const initialState: Preference = {
|
||||
apiKey: '',
|
||||
};
|
||||
|
||||
export const prefSlice = createSlice({
|
||||
name: 'preference',
|
||||
initialState,
|
||||
reducers: {
|
||||
setApiKey: (state, action) => {
|
||||
state.apiKey = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setApiKey } = prefSlice.actions;
|
||||
export default prefSlice.reducer;
|
||||
|
||||
type RootState = ReturnType<typeof store.getState>;
|
||||
|
||||
export const selectApiKey = (state: RootState) => state.preference.apiKey;
|
||||
export const selectApiKeyStatus = (state: RootState) =>
|
||||
!!state.preference.apiKey;
|
@ -1,48 +1,12 @@
|
||||
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface State {
|
||||
isApiKeyModalOpen: boolean;
|
||||
apiKey: string;
|
||||
isMenuOpen: boolean;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
isApiKeyModalOpen: false,
|
||||
apiKey: '',
|
||||
isMenuOpen: false,
|
||||
};
|
||||
|
||||
export const slice = createSlice({
|
||||
name: 'app',
|
||||
initialState,
|
||||
reducers: {
|
||||
toggleApiKeyModal: (state) => {
|
||||
state.isApiKeyModalOpen = !state.isApiKeyModalOpen;
|
||||
console.log('showApiKeyModal', state.isApiKeyModalOpen);
|
||||
},
|
||||
setApiKey: (state, action: PayloadAction<string>) => {
|
||||
state.apiKey = action.payload;
|
||||
console.log('setApiKey', action.payload);
|
||||
},
|
||||
toggleIsMenuOpen: (state) => {
|
||||
state.isMenuOpen = !state.isMenuOpen;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { toggleApiKeyModal, setApiKey, toggleIsMenuOpen } = slice.actions;
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
app: slice.reducer,
|
||||
},
|
||||
});
|
||||
|
||||
type RootState = ReturnType<typeof store.getState>;
|
||||
|
||||
export const selectIsApiKeyModalOpen = (state: RootState) =>
|
||||
state.app.isApiKeyModalOpen;
|
||||
export const selectApiKey = (state: RootState) => state.app.apiKey;
|
||||
export const selectIsMenuOpen = (state: RootState) => state.app.isMenuOpen;
|
||||
|
||||
export default store;
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { conversationSlice } from './conversation/conversationSlice';
|
||||
import { prefSlice } from './preferences/preferenceSlice';
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
preference: prefSlice.reducer,
|
||||
conversation: conversationSlice.reducer,
|
||||
},
|
||||
});
|
||||
|
||||
export default store;
|
||||
|