mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
inititated shadcn
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
"use client";
|
||||
import {useEffect, useRef, useState} from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { PaperPlaneIcon } from '@radix-ui/react-icons';
|
||||
import { Input } from './ui/input';
|
||||
import { Button } from './ui/button';
|
||||
|
||||
//import './style.css'
|
||||
|
||||
interface HistoryItem {
|
||||
@@ -33,7 +37,7 @@ function fetchAnswerStreaming({
|
||||
history = [],
|
||||
conversationId = null,
|
||||
apiHost = '',
|
||||
onEvent = () => {console.log("Event triggered, but no handler provided.");}
|
||||
onEvent = () => { console.log("Event triggered, but no handler provided."); }
|
||||
}: FetchAnswerStreamingProps): Promise<void> {
|
||||
let docPath = 'default';
|
||||
if (selectedDocs) {
|
||||
@@ -107,140 +111,169 @@ function fetchAnswerStreaming({
|
||||
});
|
||||
}
|
||||
|
||||
export const DocsGPTWidget = ({ apiHost = 'https://gptcloud.arc53.com', selectDocs = 'default', apiKey = 'docsgpt-public'}) => {
|
||||
// processing states
|
||||
const [chatState, setChatState] = useState<ChatStates>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return localStorage.getItem('docsGPTChatState') as ChatStates || ChatStates.Init;
|
||||
}
|
||||
return ChatStates.Init;
|
||||
});
|
||||
|
||||
const [answer, setAnswer] = useState<string>('');
|
||||
|
||||
//const selectDocs = 'local/1706.03762.pdf/'
|
||||
const answerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (answerRef.current) {
|
||||
const element = answerRef.current;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
}
|
||||
}, [answer]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('docsGPTChatState', chatState);
|
||||
}, [chatState]);
|
||||
|
||||
|
||||
|
||||
// submit handler
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
setAnswer('')
|
||||
e.preventDefault()
|
||||
// get question
|
||||
setChatState(ChatStates.Processing)
|
||||
setTimeout(() => {
|
||||
setChatState(ChatStates.Answer)
|
||||
}, 800)
|
||||
const inputElement = e.currentTarget[0] as HTMLInputElement;
|
||||
const questionValue = inputElement.value;
|
||||
|
||||
fetchAnswerStreaming({
|
||||
question: questionValue,
|
||||
apiKey: apiKey,
|
||||
selectedDocs: selectDocs,
|
||||
history: [],
|
||||
conversationId: null,
|
||||
apiHost: apiHost,
|
||||
onEvent: (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
// check if the 'end' event has been received
|
||||
if (data.type === 'end') {
|
||||
setChatState(ChatStates.Answer)
|
||||
} else if (data.type === 'source') {
|
||||
// check if data.metadata exists
|
||||
let result;
|
||||
if (data.metadata && data.metadata.title) {
|
||||
const titleParts = data.metadata.title.split('/');
|
||||
result = {
|
||||
title: titleParts[titleParts.length - 1],
|
||||
text: data.doc,
|
||||
};
|
||||
} else {
|
||||
result = { title: data.doc, text: data.doc };
|
||||
}
|
||||
console.log(result)
|
||||
|
||||
} else if (data.type === 'id') {
|
||||
console.log(data.id);
|
||||
} else {
|
||||
const result = data.answer;
|
||||
// set answer by appending answer
|
||||
setAnswer(prevAnswer => prevAnswer + result);
|
||||
}
|
||||
},
|
||||
});
|
||||
export const DocsGPTWidget = ({ apiHost = 'https://gptcloud.arc53.com', selectDocs = 'default', apiKey = 'docsgpt-public' }) => {
|
||||
// processing states
|
||||
const [chatState, setChatState] = useState<ChatStates>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return localStorage.getItem('docsGPTChatState') as ChatStates || ChatStates.Init;
|
||||
}
|
||||
return ChatStates.Init;
|
||||
});
|
||||
|
||||
const [answer, setAnswer] = useState<string>('');
|
||||
|
||||
//const selectDocs = 'local/1706.03762.pdf/'
|
||||
const answerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (answerRef.current) {
|
||||
const element = answerRef.current;
|
||||
element.scrollTop = element.scrollHeight;
|
||||
}
|
||||
}, [answer]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('docsGPTChatState', chatState);
|
||||
}, [chatState]);
|
||||
|
||||
|
||||
|
||||
// submit handler
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
setAnswer('')
|
||||
e.preventDefault()
|
||||
// get question
|
||||
setChatState(ChatStates.Processing)
|
||||
setTimeout(() => {
|
||||
setChatState(ChatStates.Answer)
|
||||
}, 800)
|
||||
const inputElement = e.currentTarget[0] as HTMLInputElement;
|
||||
const questionValue = inputElement.value;
|
||||
|
||||
fetchAnswerStreaming({
|
||||
question: questionValue,
|
||||
apiKey: apiKey,
|
||||
selectedDocs: selectDocs,
|
||||
history: [],
|
||||
conversationId: null,
|
||||
apiHost: apiHost,
|
||||
onEvent: (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
// check if the 'end' event has been received
|
||||
if (data.type === 'end') {
|
||||
setChatState(ChatStates.Answer)
|
||||
} else if (data.type === 'source') {
|
||||
// check if data.metadata exists
|
||||
let result;
|
||||
if (data.metadata && data.metadata.title) {
|
||||
const titleParts = data.metadata.title.split('/');
|
||||
result = {
|
||||
title: titleParts[titleParts.length - 1],
|
||||
text: data.doc,
|
||||
};
|
||||
} else {
|
||||
result = { title: data.doc, text: data.doc };
|
||||
}
|
||||
console.log(result)
|
||||
|
||||
} else if (data.type === 'id') {
|
||||
console.log(data.id);
|
||||
} else {
|
||||
const result = data.answer;
|
||||
// set answer by appending answer
|
||||
setAnswer(prevAnswer => prevAnswer + result);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dark widget-container">
|
||||
<div onClick={() => setChatState(ChatStates.Init)}
|
||||
className={`${chatState !== 'minimized' ? 'hidden' : ''} cursor-pointer`}>
|
||||
<div className="mr-2 mb-2 w-20 h-20 rounded-full overflow-hidden dark:divide-gray-700 border dark:border-gray-700 bg-gradient-to-br from-gray-100/80 via-white to-white dark:from-gray-900/80 dark:via-gray-900 dark:to-gray-900 font-sans shadow backdrop-blur-sm flex items-center justify-center">
|
||||
<img
|
||||
src="https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png"
|
||||
alt="DocsGPT"
|
||||
className="cursor-pointer hover:opacity-50 h-14"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={` ${chatState !== 'minimized' ? '' : 'hidden'} divide-y dark:divide-gray-700 rounded-md border dark:border-gray-700 bg-gradient-to-br from-gray-100/80 via-white to-white dark:from-gray-900/80 dark:via-gray-900 dark:to-gray-900 font-sans shadow backdrop-blur-sm`} style={{ width: '18rem', transform: 'translateY(0%) translateZ(0px)' }}>
|
||||
<div>
|
||||
<img
|
||||
src="https://d3dg1063dc54p9.cloudfront.net/exit.svg"
|
||||
alt="Exit"
|
||||
className="cursor-pointer hover:opacity-50 h-2 absolute top-0 right-0 m-2 white-filter"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setChatState(ChatStates.Minimized);
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-center gap-2 p-3">
|
||||
<div className={`${chatState === 'init' ? '' :
|
||||
chatState === 'processing' ? '' :
|
||||
chatState === 'typing' ? '' :
|
||||
'hidden'} flex-1`}>
|
||||
<h3 className="text-sm font-bold text-gray-700 dark:text-gray-200">Need help with documentation?</h3>
|
||||
<p className="mt-1 text-xs text-gray-400 dark:text-gray-500">DocsGPT AI assistant will help you with docs</p>
|
||||
</div>
|
||||
<div id="docsgpt-answer" ref={answerRef} className={`${chatState !== 'answer' ? 'hidden' : ''}`}>
|
||||
<p className="mt-1 text-sm text-gray-600 dark:text-white text-left">{answer}</p>
|
||||
</div>
|
||||
<div className="dark widget-container">
|
||||
<div onClick={() => setChatState(ChatStates.Init)}
|
||||
className={`${chatState !== 'minimized' ? 'hidden' : ''} cursor-pointer`}>
|
||||
<div className="mr-2 mb-2 w-20 h-20 rounded-full overflow-hidden dark:divide-gray-700 border dark:border-gray-700 bg-gradient-to-br from-gray-100/80 via-white to-white dark:from-gray-900/80 dark:via-gray-900 dark:to-gray-900 font-sans shadow backdrop-blur-sm flex items-center justify-center">
|
||||
<img
|
||||
src="https://d3dg1063dc54p9.cloudfront.net/cute-docsgpt.png"
|
||||
alt="DocsGPT"
|
||||
className="cursor-pointer hover:opacity-50 h-14"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<button onClick={() => setChatState(ChatStates.Typing)}
|
||||
className={`flex w-full justify-center px-5 py-3 text-sm text-gray-800 font-bold dark:text-white transition duration-300 hover:bg-gray-100 rounded-b dark:hover:bg-gray-800/70 ${chatState !== 'init' ? 'hidden' : ''}`}>
|
||||
Ask DocsGPT
|
||||
</button>
|
||||
{ (chatState === 'typing' || chatState === 'answer') && (
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="relative w-full m-0" style={{ opacity: 1 }}>
|
||||
<input type="text"
|
||||
className="w-full bg-transparent px-5 py-3 pr-8 text-sm text-gray-700 dark:text-white focus:outline-none" placeholder="What do you want to do?" />
|
||||
<button className="absolute text-gray-400 dark:text-gray-500 text-sm inset-y-0 right-2 -mx-2 px-2" type="submit" >Submit</button>
|
||||
</form>
|
||||
)}
|
||||
<p className={`${chatState !== 'processing' ? 'hidden' : ''} flex w-full justify-center px-5 py-3 text-sm text-gray-800 font-bold dark:text-white transition duration-300 rounded-b`}>
|
||||
Processing<span className="dot-animation">.</span><span className="dot-animation delay-200">.</span><span className="dot-animation delay-400">.</span>
|
||||
</p>
|
||||
<div className={` ${chatState !== 'minimized' ? '' : 'hidden'} divide-y dark:divide-gray-700 rounded-md border dark:border-gray-700 bg-gradient-to-br from-gray-100/80 via-white to-white dark:from-gray-900/80 dark:via-gray-900 dark:to-gray-900 font-sans shadow backdrop-blur-sm`} style={{ width: '18rem', transform: 'translateY(0%) translateZ(0px)' }}>
|
||||
<div>
|
||||
<img
|
||||
src="https://d3dg1063dc54p9.cloudfront.net/exit.svg"
|
||||
alt="Exit"
|
||||
className="cursor-pointer hover:opacity-50 h-2 absolute top-0 right-0 m-2 white-filter"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setChatState(ChatStates.Minimized);
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-center gap-2 p-3">
|
||||
<div className={`${chatState === 'init' ? '' :
|
||||
chatState === 'processing' ? '' :
|
||||
chatState === 'typing' ? '' :
|
||||
'hidden'} flex-1`}>
|
||||
<h3 className="text-sm font-bold text-gray-700 dark:text-gray-200">Need help with documentation?</h3>
|
||||
<p className="mt-1 text-xs text-gray-400 dark:text-gray-500">DocsGPT AI assistant will help you with docs</p>
|
||||
</div>
|
||||
<div id="docsgpt-answer" ref={answerRef} className={`${chatState !== 'answer' ? 'hidden' : ''}`}>
|
||||
<p className="mt-1 text-sm text-gray-600 dark:text-white text-left">{answer}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<button onClick={() => setChatState(ChatStates.Typing)}
|
||||
className={`flex w-full justify-center px-5 py-3 text-sm text-gray-800 font-bold dark:text-white transition duration-300 hover:bg-gray-100 rounded-b dark:hover:bg-gray-800/70 ${chatState !== 'init' ? 'hidden' : ''}`}>
|
||||
Ask DocsGPT
|
||||
</button>
|
||||
{(chatState === 'typing' || chatState === 'answer') && (
|
||||
<div>
|
||||
<div>
|
||||
<div className='flex justify-start m-2 '>
|
||||
<p className='dark:bg-slate-600 max-w-[80%] dark:text-white block p-2 rounded-lg'>
|
||||
Hi, How can I help you today ?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end m-2 '>
|
||||
<p className='dark:bg-blue-500 max-w-[80%] dark:text-black block p-2 rounded-lg '>
|
||||
Hey, I am having trouble with my account !
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-start m-2 '>
|
||||
<p className='dark:bg-slate-600 max-w-[80%] dark:text-white block p-2 rounded-lg'>
|
||||
What seems to be the problem ?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end m-2 '>
|
||||
<p className='dark:bg-blue-500 max-w-[80%] dark:text-black block p-2 rounded-lg '>
|
||||
I can't login
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="relative w-full m-0" style={{ opacity: 1 }}>
|
||||
<div className='p-2 flex justify-between'>
|
||||
<Input type='text'
|
||||
className="w-[85%] h-8 bg-transparent px-5 py-4 text-sm text-gray-700 dark:text-white focus:outline-none" placeholder="What do you want to do?" />
|
||||
<Button className="text-gray-400 dark:text-gray-500 dark:bg-blue-700 dark:hover:bg-blue-600 text-sm inset-y-0 px-2" type="submit" ><PaperPlaneIcon className='text-white' /></Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
<p className={`${chatState !== 'processing' ? 'hidden' : ''} flex w-full justify-center px-5 py-3 text-sm text-gray-800 font-bold dark:text-white transition duration-300 rounded-b`}>
|
||||
Processing<span className="dot-animation">.</span><span className="dot-animation delay-200">.</span><span className="dot-animation delay-400">.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user