mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-30 17:13:15 +00:00
@@ -4,11 +4,8 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import DragFileUpload from '../assets/DragFileUpload.svg';
|
||||
import newChatIcon from '../assets/openNewChat.svg';
|
||||
import ShareIcon from '../assets/share.svg';
|
||||
import MessageInput from '../components/MessageInput';
|
||||
import { useMediaQuery } from '../hooks';
|
||||
import { ShareConversationModal } from '../modals/ShareConversationModal';
|
||||
import { ActiveState } from '../models/misc';
|
||||
import {
|
||||
selectConversationId,
|
||||
@@ -42,7 +39,6 @@ export default function Conversation() {
|
||||
const conversationId = useSelector(selectConversationId);
|
||||
const selectedAgent = useSelector(selectSelectedAgent);
|
||||
|
||||
const [input, setInput] = useState<string>('');
|
||||
const [uploadModalState, setUploadModalState] =
|
||||
useState<ActiveState>('INACTIVE');
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
@@ -146,19 +142,19 @@ export default function Conversation() {
|
||||
};
|
||||
|
||||
const handleQuestionSubmission = (
|
||||
updatedQuestion?: string,
|
||||
question?: string,
|
||||
updated?: boolean,
|
||||
indx?: number,
|
||||
) => {
|
||||
if (updated === true) {
|
||||
handleQuestion({ question: updatedQuestion as string, index: indx });
|
||||
} else if (input && status !== 'loading') {
|
||||
handleQuestion({ question: question as string, index: indx });
|
||||
} else if (question && status !== 'loading') {
|
||||
if (lastQueryReturnedErr) {
|
||||
dispatch(
|
||||
updateQuery({
|
||||
index: queries.length - 1,
|
||||
query: {
|
||||
prompt: input,
|
||||
prompt: question,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -168,10 +164,9 @@ export default function Conversation() {
|
||||
});
|
||||
} else {
|
||||
handleQuestion({
|
||||
question: input,
|
||||
question,
|
||||
});
|
||||
}
|
||||
setInput('');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -184,10 +179,6 @@ export default function Conversation() {
|
||||
);
|
||||
};
|
||||
|
||||
const newChat = () => {
|
||||
if (queries && queries.length > 0) resetConversation();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (queries.length) {
|
||||
queries[queries.length - 1].error && setLastQueryReturnedErr(true);
|
||||
@@ -196,50 +187,6 @@ export default function Conversation() {
|
||||
}, [queries[queries.length - 1]]);
|
||||
return (
|
||||
<div className="flex h-full flex-col justify-end gap-1">
|
||||
{conversationId && queries.length > 0 && (
|
||||
<div className="absolute right-20 top-4">
|
||||
<div className="mt-2 flex items-center gap-4">
|
||||
{isMobile && queries.length > 0 && (
|
||||
<button
|
||||
title="Open New Chat"
|
||||
onClick={() => {
|
||||
newChat();
|
||||
}}
|
||||
className="rounded-full p-2 hover:bg-bright-gray dark:hover:bg-[#28292E]"
|
||||
>
|
||||
<img
|
||||
className="h-5 w-5 filter dark:invert"
|
||||
alt="NewChat"
|
||||
src={newChatIcon}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
title="Share"
|
||||
onClick={() => {
|
||||
setShareModalState(true);
|
||||
}}
|
||||
className="rounded-full p-2 hover:bg-bright-gray dark:hover:bg-[#28292E]"
|
||||
>
|
||||
<img
|
||||
className="h-5 w-5 filter dark:invert"
|
||||
alt="share"
|
||||
src={ShareIcon}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{isShareModalOpen && (
|
||||
<ShareConversationModal
|
||||
close={() => {
|
||||
setShareModalState(false);
|
||||
}}
|
||||
conversationId={conversationId}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ConversationMessages
|
||||
handleQuestion={handleQuestion}
|
||||
handleQuestionSubmission={handleQuestionSubmission}
|
||||
@@ -258,9 +205,9 @@ export default function Conversation() {
|
||||
</label>
|
||||
<input {...getInputProps()} id="file-upload" />
|
||||
<MessageInput
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onSubmit={handleQuestionSubmission}
|
||||
onSubmit={(text) => {
|
||||
handleQuestionSubmission(text);
|
||||
}}
|
||||
loading={status === 'loading'}
|
||||
showSourceButton={selectedAgent ? false : true}
|
||||
showToolButton={selectedAgent ? false : true}
|
||||
|
||||
@@ -49,7 +49,7 @@ const ConversationBubble = forwardRef<
|
||||
feedback?: FEEDBACK;
|
||||
handleFeedback?: (feedback: FEEDBACK) => void;
|
||||
thought?: string;
|
||||
sources?: { title: string; text: string; source: string }[];
|
||||
sources?: { title: string; text: string; link: string }[];
|
||||
toolCalls?: ToolCallsType[];
|
||||
retryBtn?: React.ReactElement;
|
||||
questionNumber?: number;
|
||||
@@ -233,7 +233,7 @@ const ConversationBubble = forwardRef<
|
||||
{DisableSourceFE ||
|
||||
type === 'ERROR' ||
|
||||
sources?.length === 0 ||
|
||||
sources?.some((source) => source.source === 'None') ? null : !sources &&
|
||||
sources?.some((source) => source.link === 'None') ? null : !sources &&
|
||||
chunks !== '0' &&
|
||||
selectedDocs ? (
|
||||
<div className="mb-4 flex flex-col flex-wrap items-start self-start lg:flex-nowrap">
|
||||
@@ -300,14 +300,14 @@ const ConversationBubble = forwardRef<
|
||||
</p>
|
||||
<div
|
||||
className={`mt-[14px] flex flex-row items-center gap-[6px] underline-offset-2 ${
|
||||
source.source && source.source !== 'local'
|
||||
source.link && source.link !== 'local'
|
||||
? 'hover:text-[#007DFF] hover:underline dark:hover:text-[#48A0FF]'
|
||||
: ''
|
||||
}`}
|
||||
onClick={() =>
|
||||
source.source && source.source !== 'local'
|
||||
source.link && source.link !== 'local'
|
||||
? window.open(
|
||||
source.source,
|
||||
source.link,
|
||||
'_blank',
|
||||
'noopener, noreferrer',
|
||||
)
|
||||
@@ -322,13 +322,13 @@ const ConversationBubble = forwardRef<
|
||||
<p
|
||||
className="mt-[2px] truncate text-xs"
|
||||
title={
|
||||
source.source && source.source !== 'local'
|
||||
? source.source
|
||||
source.link && source.link !== 'local'
|
||||
? source.link
|
||||
: source.title
|
||||
}
|
||||
>
|
||||
{source.source && source.source !== 'local'
|
||||
? source.source
|
||||
{source.link && source.link !== 'local'
|
||||
? source.link
|
||||
: source.title}
|
||||
</p>
|
||||
</div>
|
||||
@@ -339,7 +339,7 @@ const ConversationBubble = forwardRef<
|
||||
onMouseOver={() => setActiveTooltip(index)}
|
||||
onMouseOut={() => setActiveTooltip(null)}
|
||||
>
|
||||
<p className="max-h-[164px] overflow-y-auto break-words rounded-md text-sm">
|
||||
<p className="line-clamp-6 max-h-[164px] overflow-hidden text-ellipsis break-words rounded-md text-sm">
|
||||
{source.text}
|
||||
</p>
|
||||
</div>
|
||||
@@ -649,50 +649,68 @@ const ConversationBubble = forwardRef<
|
||||
});
|
||||
|
||||
type AllSourcesProps = {
|
||||
sources: { title: string; text: string; source: string }[];
|
||||
sources: { title: string; text: string; link?: string }[];
|
||||
};
|
||||
|
||||
function AllSources(sources: AllSourcesProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleCardClick = (link: string) => {
|
||||
if (link && link !== 'local') {
|
||||
window.open(link, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<div className="w-full">
|
||||
<p className="text-left text-xl">{`${sources.sources.length} Sources`}</p>
|
||||
<p className="text-left text-xl">{`${sources.sources.length} ${t('conversation.sources.title')}`}</p>
|
||||
<div className="mx-1 mt-2 h-[0.8px] w-full rounded-full bg-[#C4C4C4]/40 lg:w-[95%]"></div>
|
||||
</div>
|
||||
<div className="mt-6 flex h-[90%] w-60 flex-col items-center gap-4 overflow-y-auto sm:w-80">
|
||||
{sources.sources.map((source, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="min-h-32 w-full rounded-[20px] bg-gray-1000 p-4 dark:bg-[#28292E]"
|
||||
>
|
||||
<span className="flex flex-row">
|
||||
{sources.sources.map((source, index) => {
|
||||
const isExternalSource = source.link && source.link !== 'local';
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`group/card relative w-full rounded-[20px] bg-gray-1000 p-4 transition-colors hover:bg-[#F1F1F1] dark:bg-[#28292E] dark:hover:bg-[#2C2E3C] ${
|
||||
isExternalSource ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
onClick={() =>
|
||||
isExternalSource && source.link && handleCardClick(source.link)
|
||||
}
|
||||
>
|
||||
<p
|
||||
title={source.title}
|
||||
className="ellipsis-text break-words text-left text-sm font-semibold"
|
||||
className={`ellipsis-text break-words text-left text-sm font-semibold ${
|
||||
isExternalSource
|
||||
? 'group-hover/card:text-purple-30 dark:group-hover/card:text-[#8C67D7]'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
{`${index + 1}. ${source.title}`}
|
||||
{isExternalSource && (
|
||||
<img
|
||||
src={Link}
|
||||
alt="External Link"
|
||||
className={`ml-1 inline h-3 w-3 object-fill dark:invert ${
|
||||
isExternalSource
|
||||
? 'group-hover/card:contrast-[50%] group-hover/card:hue-rotate-[235deg] group-hover/card:invert-[31%] group-hover/card:saturate-[752%] group-hover/card:sepia-[80%] group-hover/card:filter'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
{source.source && source.source !== 'local' ? (
|
||||
<img
|
||||
src={Link}
|
||||
alt={'Link'}
|
||||
className="h-3 w-3 cursor-pointer object-fill"
|
||||
onClick={() =>
|
||||
window.open(source.source, '_blank', 'noopener, noreferrer')
|
||||
}
|
||||
></img>
|
||||
) : null}
|
||||
</span>
|
||||
<p className="mt-3 max-h-16 overflow-y-auto break-words rounded-md text-left text-xs text-black dark:text-chinese-silver">
|
||||
{source.text}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
<p className="mt-3 line-clamp-4 break-words rounded-md text-left text-xs text-black dark:text-chinese-silver">
|
||||
{source.text}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ConversationBubble;
|
||||
|
||||
function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
|
||||
|
||||
@@ -190,7 +190,7 @@ export default function ConversationMessages({
|
||||
ref={conversationRef}
|
||||
onWheel={handleUserScrollInterruption}
|
||||
onTouchMove={handleUserScrollInterruption}
|
||||
className="flex h-full w-full justify-center overflow-y-auto sm:pt-12"
|
||||
className="flex h-full w-full justify-center overflow-y-auto will-change-scroll sm:pt-6 lg:pt-12"
|
||||
>
|
||||
{queries.length > 0 && !hasScrolledToLast && (
|
||||
<button
|
||||
|
||||
@@ -35,7 +35,6 @@ export const SharedConversation = () => {
|
||||
const apiKey = useSelector(selectClientAPIKey);
|
||||
const status = useSelector(selectStatus);
|
||||
|
||||
const [input, setInput] = useState('');
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
|
||||
@@ -76,15 +75,15 @@ export const SharedConversation = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleQuestionSubmission = () => {
|
||||
if (input && status !== 'loading') {
|
||||
const handleQuestionSubmission = (question?: string) => {
|
||||
if (question && status !== 'loading') {
|
||||
if (lastQueryReturnedErr) {
|
||||
// update last failed query with new prompt
|
||||
dispatch(
|
||||
updateQuery({
|
||||
index: queries.length - 1,
|
||||
query: {
|
||||
prompt: input,
|
||||
prompt: question,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -93,9 +92,8 @@ export const SharedConversation = () => {
|
||||
isRetry: true,
|
||||
});
|
||||
} else {
|
||||
handleQuestion({ question: input });
|
||||
handleQuestion({ question });
|
||||
}
|
||||
setInput('');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -156,10 +154,12 @@ export const SharedConversation = () => {
|
||||
<div className="flex w-full max-w-[1200px] flex-col items-center gap-4 pb-2 md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12">
|
||||
{apiKey ? (
|
||||
<MessageInput
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onSubmit={() => handleQuestionSubmission()}
|
||||
onSubmit={(text) => {
|
||||
handleQuestionSubmission(text);
|
||||
}}
|
||||
loading={status === 'loading'}
|
||||
showSourceButton={false}
|
||||
showToolButton={false}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
|
||||
@@ -43,7 +43,7 @@ export interface Query {
|
||||
conversationId?: string | null;
|
||||
title?: string | null;
|
||||
thought?: string;
|
||||
sources?: { title: string; text: string; source: string }[];
|
||||
sources?: { title: string; text: string; link: string }[];
|
||||
tool_calls?: ToolCallsType[];
|
||||
error?: string;
|
||||
attachments?: { fileName: string; id: string }[];
|
||||
|
||||
@@ -72,7 +72,7 @@ export const fetchSharedAnswer = createAsyncThunk<Answer, { question: string }>(
|
||||
dispatch(sharedConversationSlice.actions.setStatus('failed'));
|
||||
dispatch(
|
||||
sharedConversationSlice.actions.raiseError({
|
||||
index: state.conversation.queries.length - 1,
|
||||
index: state.sharedConversation.queries.length - 1,
|
||||
message: data.error,
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user