setInput(e.target.value)}
- onSubmit={() => handleQuestionSubmission()}
+ onSubmit={(text) => handleQuestionSubmission(text)}
loading={status === 'loading'}
showSourceButton={selectedAgent ? false : true}
showToolButton={selectedAgent ? false : true}
diff --git a/frontend/src/agents/SharedAgent.tsx b/frontend/src/agents/SharedAgent.tsx
index 98827f1d..8c7e76c9 100644
--- a/frontend/src/agents/SharedAgent.tsx
+++ b/frontend/src/agents/SharedAgent.tsx
@@ -91,22 +91,18 @@ export default function SharedAgent() {
);
const handleQuestionSubmission = (
- updatedQuestion?: string,
+ question?: string,
updated?: boolean,
indx?: number,
) => {
- if (
- updated === true &&
- updatedQuestion !== undefined &&
- indx !== undefined
- ) {
+ if (updated === true && question !== undefined && indx !== undefined) {
handleQuestion({
- question: updatedQuestion,
+ question,
index: indx,
isRetry: false,
});
- } else if (input.trim() && status !== 'loading') {
- const currentInput = input.trim();
+ } else if (question && status !== 'loading') {
+ const currentInput = question.trim();
if (lastQueryReturnedErr && queries.length > 0) {
const lastQueryIndex = queries.length - 1;
handleQuestion({
@@ -183,9 +179,7 @@ export default function SharedAgent() {
setInput(e.target.value)}
- onSubmit={() => handleQuestionSubmission()}
+ onSubmit={(text) => handleQuestionSubmission(text)}
loading={status === 'loading'}
showSourceButton={sharedAgent ? false : true}
showToolButton={sharedAgent ? false : true}
diff --git a/frontend/src/components/ActionButtons.tsx b/frontend/src/components/ActionButtons.tsx
new file mode 100644
index 00000000..3bd71515
--- /dev/null
+++ b/frontend/src/components/ActionButtons.tsx
@@ -0,0 +1,89 @@
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import newChatIcon from '../assets/openNewChat.svg';
+import ShareIcon from '../assets/share.svg';
+import { ShareConversationModal } from '../modals/ShareConversationModal';
+import { useState } from 'react';
+import { selectConversationId } from '../preferences/preferenceSlice';
+import { useDispatch } from 'react-redux';
+import { AppDispatch } from '../store';
+import {
+ setConversation,
+ updateConversationId,
+} from '../conversation/conversationSlice';
+
+interface ActionButtonsProps {
+ className?: string;
+ showNewChat?: boolean;
+ showShare?: boolean;
+}
+
+import { useNavigate } from 'react-router-dom';
+
+export default function ActionButtons({
+ className = '',
+ showNewChat = true,
+ showShare = true,
+}: ActionButtonsProps) {
+ const { t } = useTranslation();
+ const dispatch = useDispatch();
+ const conversationId = useSelector(selectConversationId);
+ const [isShareModalOpen, setShareModalState] = useState(false);
+ const navigate = useNavigate();
+
+ const newChat = () => {
+ dispatch(setConversation([]));
+ dispatch(
+ updateConversationId({
+ query: { conversationId: null },
+ }),
+ );
+ navigate('/');
+ };
+ return (
+
+
+ {showNewChat && (
+
+ )}
+
+ {showShare && conversationId && (
+ <>
+
+ {isShareModalOpen && (
+
setShareModalState(false)}
+ conversationId={conversationId}
+ />
+ )}
+ >
+ )}
+ {/* */}
+
+
+ );
+}
diff --git a/frontend/src/components/MessageInput.tsx b/frontend/src/components/MessageInput.tsx
index 60cd4b81..79487188 100644
--- a/frontend/src/components/MessageInput.tsx
+++ b/frontend/src/components/MessageInput.tsx
@@ -30,9 +30,7 @@ import SourcesPopup from './SourcesPopup';
import ToolsPopup from './ToolsPopup';
type MessageInputProps = {
- value: string;
- onChange: (e: React.ChangeEvent) => void;
- onSubmit: () => void;
+ onSubmit: (text: string) => void;
loading: boolean;
showSourceButton?: boolean;
showToolButton?: boolean;
@@ -40,8 +38,6 @@ type MessageInputProps = {
};
export default function MessageInput({
- value,
- onChange,
onSubmit,
loading,
showSourceButton = true,
@@ -50,6 +46,7 @@ export default function MessageInput({
}: MessageInputProps) {
const { t } = useTranslation();
const [isDarkTheme] = useDarkTheme();
+ const [value, setValue] = useState('');
const inputRef = useRef(null);
const sourceButtonRef = useRef(null);
const toolButtonRef = useRef(null);
@@ -232,6 +229,11 @@ export default function MessageInput({
handleInput();
}, []);
+ const handleChange = (e: React.ChangeEvent) => {
+ setValue(e.target.value);
+ handleInput();
+ };
+
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
@@ -248,7 +250,10 @@ export default function MessageInput({
};
const handleSubmit = () => {
- onSubmit();
+ if (value.trim() && !loading) {
+ onSubmit(value);
+ setValue('');
+ }
};
return (
@@ -274,11 +279,11 @@ export default function MessageInput({
dispatch(removeAttachment(attachment.id));
}
}}
- aria-label="Remove attachment"
+ aria-label={t('conversation.attachments.remove')}
>

@@ -334,7 +339,7 @@ export default function MessageInput({
id="message-input"
ref={inputRef}
value={value}
- onChange={onChange}
+ onChange={handleChange}
tabIndex={1}
placeholder={t('inputPlaceholder')}
className="inputbox-style no-scrollbar w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-t-[23px] bg-lotion px-4 py-3 text-base leading-tight opacity-100 focus:outline-none dark:bg-transparent dark:text-bright-gray dark:placeholder-bright-gray dark:placeholder-opacity-50 sm:px-6 sm:py-5"
@@ -398,7 +403,7 @@ export default function MessageInput({
className="mr-1 h-3.5 w-3.5 sm:mr-1.5 sm:h-4 sm:w-4"
/>
- Attach
+ {t('conversation.attachments.attach')}
-
{/* Additional badges can be added here in the future */}
diff --git a/frontend/src/components/SourcesPopup.tsx b/frontend/src/components/SourcesPopup.tsx
index 6846cf84..83966730 100644
--- a/frontend/src/components/SourcesPopup.tsx
+++ b/frontend/src/components/SourcesPopup.tsx
@@ -81,12 +81,6 @@ export default function SourcesPopup({
return () => window.removeEventListener('resize', updatePosition);
}, [isOpen, anchorRef]);
- const handleEmptyDocumentSelect = () => {
- dispatch(setSelectedDocs(null));
- handlePostDocumentSelect(null);
- onClose();
- };
-
const handleClickOutside = (event: MouseEvent) => {
if (
popupRef.current &&
@@ -153,14 +147,24 @@ export default function SourcesPopup({
<>
{filteredOptions?.map((option: any, index: number) => {
if (option.model === embeddingsName) {
+ const isSelected =
+ selectedDocs &&
+ (option.id
+ ? selectedDocs.id === option.id
+ : selectedDocs.date === option.date);
+
return (
{
- dispatch(setSelectedDocs(option));
- handlePostDocumentSelect(option);
- onClose();
+ if (isSelected) {
+ dispatch(setSelectedDocs(null));
+ handlePostDocumentSelect(null);
+ } else {
+ dispatch(setSelectedDocs(option));
+ handlePostDocumentSelect(option);
+ }
}}
>
![]()
- {selectedDocs &&
- (option.id
- ? selectedDocs.id === option.id // For documents with MongoDB IDs
- : selectedDocs.date === option.date) && ( // For preloaded sources
-

- )}
+ {isSelected && (
+

+ )}
);
}
return null;
})}
-
-

-
- {t('none')}
-
-
- {selectedDocs === null && (
-

- )}
-
-
>
) : (
diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx
index 21fc3a1f..753a3725 100644
--- a/frontend/src/conversation/Conversation.tsx
+++ b/frontend/src/conversation/Conversation.tsx
@@ -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
('');
const [uploadModalState, setUploadModalState] =
useState('INACTIVE');
const [files, setFiles] = useState([]);
@@ -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 (
- {conversationId && queries.length > 0 && (
-
-
- {isMobile && queries.length > 0 && (
-
- )}
-
-
-
- {isShareModalOpen && (
-
{
- setShareModalState(false);
- }}
- conversationId={conversationId}
- />
- )}
-
- )}
-
setInput(e.target.value)}
- onSubmit={handleQuestionSubmission}
+ onSubmit={(text) => {
+ handleQuestionSubmission(text);
+ }}
loading={status === 'loading'}
showSourceButton={selectedAgent ? false : true}
showToolButton={selectedAgent ? false : true}
diff --git a/frontend/src/conversation/ConversationBubble.tsx b/frontend/src/conversation/ConversationBubble.tsx
index 2f9891b5..920005e3 100644
--- a/frontend/src/conversation/ConversationBubble.tsx
+++ b/frontend/src/conversation/ConversationBubble.tsx
@@ -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 ? (
@@ -300,14 +300,14 @@ const ConversationBubble = forwardRef<
- 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<
- {source.source && source.source !== 'local'
- ? source.source
+ {source.link && source.link !== 'local'
+ ? source.link
: source.title}
@@ -339,7 +339,7 @@ const ConversationBubble = forwardRef<
onMouseOver={() => setActiveTooltip(index)}
onMouseOut={() => setActiveTooltip(null)}
>
-
+
{source.text}
@@ -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 (
-
{`${sources.sources.length} Sources`}
+
{`${sources.sources.length} ${t('conversation.sources.title')}`}
- {sources.sources.map((source, index) => (
-
-
+ {sources.sources.map((source, index) => {
+ const isExternalSource = source.link && source.link !== 'local';
+ return (
+
+ isExternalSource && source.link && handleCardClick(source.link)
+ }
+ >
{`${index + 1}. ${source.title}`}
+ {isExternalSource && (
+
+ )}
- {source.source && source.source !== 'local' ? (
-

- window.open(source.source, '_blank', 'noopener, noreferrer')
- }
- >
- ) : null}
-
-
- {source.text}
-
-
- ))}
+
+ {source.text}
+
+
+ );
+ })}
);
}
-
export default ConversationBubble;
function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
diff --git a/frontend/src/conversation/ConversationMessages.tsx b/frontend/src/conversation/ConversationMessages.tsx
index 2369dbc6..5c2150a6 100644
--- a/frontend/src/conversation/ConversationMessages.tsx
+++ b/frontend/src/conversation/ConversationMessages.tsx
@@ -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 && (