mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-12-01 17:43:15 +00:00
Merge branch 'arc53:main' into Fixes-#1260
This commit is contained in:
@@ -1,30 +1,31 @@
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import newChatIcon from '../assets/openNewChat.svg';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Hero from '../Hero';
|
||||
import ArrowDown from '../assets/arrow-down.svg';
|
||||
import newChatIcon from '../assets/openNewChat.svg';
|
||||
import Send from '../assets/send.svg';
|
||||
import SendDark from '../assets/send_dark.svg';
|
||||
import ShareIcon from '../assets/share.svg';
|
||||
import SpinnerDark from '../assets/spinner-dark.svg';
|
||||
import Spinner from '../assets/spinner.svg';
|
||||
import RetryIcon from '../components/RetryIcon';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Hero from '../Hero';
|
||||
import { useDarkTheme, useMediaQuery } from '../hooks';
|
||||
import { ShareConversationModal } from '../modals/ShareConversationModal';
|
||||
import { setConversation, updateConversationId } from './conversationSlice';
|
||||
import { selectConversationId } from '../preferences/preferenceSlice';
|
||||
import { AppDispatch } from '../store';
|
||||
import conversationService from '../api/services/conversationService';
|
||||
import ConversationBubble from './ConversationBubble';
|
||||
import { handleSendFeedback } from './conversationHandlers';
|
||||
import { FEEDBACK, Query } from './conversationModels';
|
||||
import ShareIcon from '../assets/share.svg';
|
||||
import {
|
||||
addQuery,
|
||||
fetchAnswer,
|
||||
selectQueries,
|
||||
selectStatus,
|
||||
setConversation,
|
||||
updateConversationId,
|
||||
updateQuery,
|
||||
} from './conversationSlice';
|
||||
|
||||
@@ -329,14 +330,14 @@ export default function Conversation() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex w-11/12 flex-col items-end self-center rounded-2xl bg-opacity-0 pb-1 sm:w-[62%] h-auto">
|
||||
<div className="flex w-11/12 flex-col items-end self-center rounded-2xl bg-opacity-0 z-3 sm:w-[62%] h-auto">
|
||||
<div className="flex w-full items-center rounded-[40px] border border-silver bg-white py-1 dark:bg-raisin-black">
|
||||
<textarea
|
||||
id="inputbox"
|
||||
ref={inputRef}
|
||||
tabIndex={1}
|
||||
placeholder={t('inputPlaceholder')}
|
||||
className={`inputbox-style h-16 w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-white pt-5 pb-[22px] text-base leading-tight opacity-100 focus:outline-none dark:bg-raisin-black dark:text-bright-gray`}
|
||||
className={`inputbox-style w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-transparent py-5 text-base leading-tight opacity-100 focus:outline-none dark:bg-transparent dark:text-bright-gray`}
|
||||
onInput={handleInput}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
import { forwardRef, useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
import DocsGPT3 from '../assets/cute_docsgpt3.svg';
|
||||
import Dislike from '../assets/dislike.svg?react';
|
||||
import Document from '../assets/document.svg';
|
||||
@@ -17,13 +19,13 @@ import Edit from '../assets/edit.svg';
|
||||
import Avatar from '../components/Avatar';
|
||||
import CopyButton from '../components/CopyButton';
|
||||
import Sidebar from '../components/Sidebar';
|
||||
import SpeakButton from '../components/TextToSpeechButton';
|
||||
import {
|
||||
selectChunks,
|
||||
selectSelectedDocs,
|
||||
} from '../preferences/preferenceSlice';
|
||||
import classes from './ConversationBubble.module.css';
|
||||
import { FEEDBACK, MESSAGE_TYPE } from './conversationModels';
|
||||
import SpeakButton from '../components/TextToSpeechButton';
|
||||
|
||||
const DisableSourceFE = import.meta.env.VITE_DISABLE_SOURCE_FE || false;
|
||||
|
||||
@@ -58,6 +60,7 @@ const ConversationBubble = forwardRef<
|
||||
},
|
||||
ref,
|
||||
) {
|
||||
// const bubbleRef = useRef<HTMLDivElement | null>(null);
|
||||
const chunks = useSelector(selectChunks);
|
||||
const selectedDocs = useSelector(selectSelectedDocs);
|
||||
const [isLikeHovered, setIsLikeHovered] = useState(false);
|
||||
@@ -209,7 +212,7 @@ const ConversationBubble = forwardRef<
|
||||
/>
|
||||
<p className="text-base font-semibold">Sources</p>
|
||||
</div>
|
||||
<div className="ml-3 mr-5 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
|
||||
<div className="fade-in ml-3 mr-5 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
|
||||
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
|
||||
{sources?.slice(0, 3)?.map((source, index) => (
|
||||
<div key={index} className="relative">
|
||||
@@ -258,7 +261,7 @@ const ConversationBubble = forwardRef<
|
||||
</div>
|
||||
{activeTooltip === index && (
|
||||
<div
|
||||
className={`absolute left-1/2 z-30 max-h-48 w-40 translate-x-[-50%] translate-y-[3px] rounded-xl bg-[#FBFBFB] p-4 text-black shadow-xl dark:bg-chinese-black dark:text-chinese-silver sm:w-56`}
|
||||
className={`absolute left-1/2 z-50 max-h-48 w-40 translate-x-[-50%] translate-y-[3px] rounded-xl bg-[#FBFBFB] p-4 text-black shadow-xl dark:bg-chinese-black dark:text-chinese-silver sm:w-56`}
|
||||
onMouseOver={() => setActiveTooltip(index)}
|
||||
onMouseOut={() => setActiveTooltip(null)}
|
||||
>
|
||||
@@ -299,14 +302,14 @@ const ConversationBubble = forwardRef<
|
||||
<p className="text-base font-semibold">Answer</p>
|
||||
</div>
|
||||
<div
|
||||
className={`ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[14px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
className={`fade-in-bubble ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[14px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
type === 'ERROR'
|
||||
? 'relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal text-red-3000 dark:border-red-2000 dark:text-white'
|
||||
: 'flex-col rounded-3xl'
|
||||
}`}
|
||||
>
|
||||
<ReactMarkdown
|
||||
className="whitespace-pre-wrap break-normal leading-normal"
|
||||
className="fade-in whitespace-pre-wrap break-normal leading-normal"
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
|
||||
@@ -17,9 +17,23 @@ const initialState: ConversationState = {
|
||||
|
||||
const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
|
||||
|
||||
let abortController: AbortController | null = null;
|
||||
export function handleAbort() {
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
abortController = null;
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchAnswer = createAsyncThunk<Answer, { question: string }>(
|
||||
'fetchAnswer',
|
||||
async ({ question }, { dispatch, getState, signal }) => {
|
||||
async ({ question }, { dispatch, getState }) => {
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
}
|
||||
abortController = new AbortController();
|
||||
const { signal } = abortController;
|
||||
|
||||
let isSourceUpdated = false;
|
||||
const state = getState() as RootState;
|
||||
if (state.preference) {
|
||||
|
||||
Reference in New Issue
Block a user