mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
adding responsive markdown response, error alert
This commit is contained in:
1650
extensions/react-widget/package-lock.json
generated
1650
extensions/react-widget/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,9 @@
|
||||
"clsx": "^2.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"tailwind-merge": "^2.2.1",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
@@ -24,6 +27,7 @@
|
||||
"@types/node": "^20.11.19",
|
||||
"@types/react": "^18.2.55",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"@types/react-syntax-highlighter": "^15.5.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
|
||||
@@ -1,34 +1,17 @@
|
||||
"use client";
|
||||
import { Fragment, useEffect, useRef, useState } from 'react'
|
||||
import { PaperPlaneIcon } from '@radix-ui/react-icons';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { PaperPlaneIcon, RocketIcon, ExclamationTriangleIcon } from '@radix-ui/react-icons';
|
||||
import { Input } from './ui/input';
|
||||
import { Button } from './ui/button';
|
||||
import { ScrollArea, ScrollBar } from './ui/scroll-area'
|
||||
import { ScrollArea } from './ui/scroll-area'
|
||||
import { Alert, AlertTitle, AlertDescription } from './ui/alert';
|
||||
import Dragon from '../assets/cute-docsgpt.svg'
|
||||
import MessageIcon from '../assets/message.svg'
|
||||
import Cancel from '../assets/cancel.svg'
|
||||
import { Doc, Query } from '@/models/customTypes';
|
||||
import { Query } from '@/models/customTypes';
|
||||
import { fetchAnswerStreaming } from '@/requests/streamingApi';
|
||||
//import './style.css'
|
||||
|
||||
interface HistoryItem {
|
||||
prompt: string;
|
||||
response: string;
|
||||
}
|
||||
interface Message {
|
||||
type: 'PROMPT' | 'RESPONSE' | 'ERROR',
|
||||
message: string
|
||||
id: string | null
|
||||
}
|
||||
interface FetchAnswerStreamingProps {
|
||||
question?: string;
|
||||
apiKey?: string;
|
||||
selectedDocs?: string;
|
||||
history?: HistoryItem[];
|
||||
conversationId?: string | null;
|
||||
apiHost?: string;
|
||||
onEvent?: (event: MessageEvent) => void;
|
||||
}
|
||||
import Response from './Response';
|
||||
|
||||
type Status = 'idle' | 'loading' | 'failed';
|
||||
|
||||
@@ -50,28 +33,7 @@ export const DocsGPTWidget = ({ apiHost = 'https://gptcloud.arc53.com', selectDo
|
||||
});
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const [status, setStatus] = useState<Status>('idle');
|
||||
const [queries, setQueries] = useState<Query[]>([
|
||||
{
|
||||
prompt: 'dasasfafa fafajfiaf agad gagadjga gadgadgadijgaf',
|
||||
response: 'dkadfafadfa fadfafa fa df adgdfaeye5uttr sr s srt rssr '
|
||||
},
|
||||
{
|
||||
prompt: 'dasasfafa fafajfiaf agad gagadjga gadgadgadijgaf',
|
||||
response: 'dkadfafadfa fadfafa fa df adgdfaeye5uttr sr s srt rssr '
|
||||
},
|
||||
{
|
||||
prompt: 'dasasfafa fafajfiaf agad gagadjga gadgadgadijgaf',
|
||||
response: 'dkadfafadfa fadfafa fa df adgdfaeye5uttr sr s srt rssr '
|
||||
},
|
||||
{
|
||||
prompt: 'dasasfafa fafajfiaf agad gagadjga gadgadgadijgaf',
|
||||
response: 'dkadfafadfa fadfafa fa df adgdfaeye5uttr sr s srt rssr '
|
||||
},
|
||||
{
|
||||
prompt: 'LAST PROMPT',
|
||||
response: 'dkadfafadfa fadfafa fa df adgdfaeye5uttr sr s srt rssr '
|
||||
}
|
||||
])
|
||||
const [queries, setQueries] = useState<Query[]>([])
|
||||
const [conversationId, setConversationId] = useState<string | null>(null)
|
||||
//const selectDocs = 'local/1706.03762.pdf/'
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -83,7 +45,7 @@ export const DocsGPTWidget = ({ apiHost = 'https://gptcloud.arc53.com', selectDo
|
||||
};
|
||||
useEffect(() => {
|
||||
scrollIntoView();
|
||||
}, [queries.length, queries[queries.length - 1].response]);
|
||||
}, [queries.length, queries[queries.length - 1]?.response]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('docsGPTChatState', chatState);
|
||||
@@ -185,7 +147,7 @@ export const DocsGPTWidget = ({ apiHost = 'https://gptcloud.arc53.com', selectDo
|
||||
<div className='h-full'>
|
||||
<ScrollArea className='h-72 rounded-md border'>
|
||||
{
|
||||
queries?.map((query, index) => {
|
||||
queries.length > 0 ? queries?.map((query, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
{
|
||||
@@ -196,14 +158,37 @@ export const DocsGPTWidget = ({ apiHost = 'https://gptcloud.arc53.com', selectDo
|
||||
</div>
|
||||
}
|
||||
{
|
||||
query.response && <div ref={(index === queries.length - 1) ? scrollRef : null} className='flex justify-start m-2 '>
|
||||
query.response ? <div ref={(index === queries.length - 1) ? scrollRef : null} className='flex justify-start m-2 '>
|
||||
<p className='dark:bg-[#38383B] max-w-[80%] dark:text-white block p-2 rounded-lg'>
|
||||
{query.response}
|
||||
<Response message={query.response}/>
|
||||
</p>
|
||||
</div>
|
||||
: <div className='max-w-[80%] m-2'>
|
||||
{
|
||||
query.error ? <Alert className='border-red-700 text-red-700' variant="destructive">
|
||||
<ExclamationTriangleIcon color='red' className="h-4 w-4" />
|
||||
<AlertTitle>Network Error</AlertTitle>
|
||||
<AlertDescription>
|
||||
Something went wrong !
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
: <div>
|
||||
add loader here
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</Fragment>)
|
||||
})
|
||||
: <div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-5/6 bg-gradient-to-br dark:from-[#5AF0EC] dark:to-[#ff1bf4] rounded-lg mx-2 p-[1px]'>
|
||||
<Alert className='dark:bg-[#222327] mx-0'>
|
||||
<RocketIcon className="h-4 w-4" />
|
||||
<AlertTitle>Welcome to DocsGPT !</AlertTitle>
|
||||
<AlertDescription>
|
||||
This is a chatbot that uses the GPT-3, Faiss and LangChain to answer questions.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
}
|
||||
</ScrollArea>
|
||||
<form
|
||||
|
||||
12
extensions/react-widget/src/components/Response.module.css
Normal file
12
extensions/react-widget/src/components/Response.module.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.list p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.list li:not(:first-child) {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.list li > .list {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
88
extensions/react-widget/src/components/Response.tsx
Normal file
88
extensions/react-widget/src/components/Response.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import classes from './Response.module.css'
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter/dist/esm/light-async';
|
||||
interface typeProps {
|
||||
message:string
|
||||
}
|
||||
const Response = (props:typeProps) => {
|
||||
return (
|
||||
<ReactMarkdown
|
||||
className="whitespace-pre-wrap break-words max-w-72"
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
code({ node, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
|
||||
return match ? (
|
||||
<SyntaxHighlighter
|
||||
PreTag="div"
|
||||
wrapLines={true}
|
||||
lineProps={{style: {width:'',overflowX:'scroll'}}}
|
||||
language={match[1]}
|
||||
style={vscDarkPlus}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
<code className={className ? className : ''} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
ul({ children }) {
|
||||
return (
|
||||
<ul
|
||||
className={`list-inside list-disc whitespace-normal pl-4 ${classes.list}`}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
ol({ children }) {
|
||||
return (
|
||||
<ol
|
||||
className={`list-inside list-decimal whitespace-normal pl-4 ${classes.list}`}
|
||||
>
|
||||
{children}
|
||||
</ol>
|
||||
);
|
||||
},
|
||||
table({ children }) {
|
||||
return (
|
||||
<div className="relative overflow-x-auto rounded-lg border">
|
||||
<table className="w-full text-left text-sm text-gray-700">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
thead({ children }) {
|
||||
return (
|
||||
<thead className="text-xs uppercase text-gray-900 [&>.table-row]:bg-gray-50">
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
},
|
||||
tr({ children }) {
|
||||
return (
|
||||
<tr className="table-row border-b odd:bg-white even:bg-gray-50">
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="px-6 py-3">{children}</td>;
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="px-6 py-3">{children}</th>;
|
||||
},
|
||||
}}
|
||||
>
|
||||
{props.message}
|
||||
</ReactMarkdown>
|
||||
|
||||
)
|
||||
}
|
||||
export default Response
|
||||
59
extensions/react-widget/src/components/ui/alert.tsx
Normal file
59
extensions/react-widget/src/components/ui/alert.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Alert.displayName = "Alert"
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertTitle.displayName = "AlertTitle"
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDescription.displayName = "AlertDescription"
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
||||
Reference in New Issue
Block a user