diff --git a/src/interface/web/app/chat/page.tsx b/src/interface/web/app/chat/page.tsx index b672091d..ecd10a4f 100644 --- a/src/interface/web/app/chat/page.tsx +++ b/src/interface/web/app/chat/page.tsx @@ -49,6 +49,7 @@ interface ChatBodyDataProps { isChatSideBarOpen: boolean; setIsChatSideBarOpen: (open: boolean) => void; isActive?: boolean; + isParentProcessing?: boolean; } function ChatBodyData(props: ChatBodyDataProps) { @@ -166,7 +167,7 @@ function ChatBodyData(props: ChatBodyDataProps) { isLoggedIn={props.isLoggedIn} sendMessage={(message) => setMessage(message)} sendImage={(image) => setImages((prevImages) => [...prevImages, image])} - sendDisabled={processingMessage} + sendDisabled={props.isParentProcessing || false} chatOptionsData={props.chatOptionsData} conversationId={conversationId} isMobileWidth={props.isMobileWidth} @@ -203,6 +204,7 @@ export default function Chat() { const [abortMessageStreamController, setAbortMessageStreamController] = useState(null); const [triggeredAbort, setTriggeredAbort] = useState(false); + const [shouldSendWithInterrupt, setShouldSendWithInterrupt] = useState(false); const { locationData, locationDataError, locationDataLoading } = useIPLocationData() || { locationData: { @@ -239,6 +241,7 @@ export default function Chat() { if (triggeredAbort) { abortMessageStreamController?.abort(); handleAbortedMessage(); + setShouldSendWithInterrupt(true); setTriggeredAbort(false); } }, [triggeredAbort]); @@ -335,18 +338,21 @@ export default function Chat() { currentMessage.completed = true; setMessages([...messages]); - setQueryToProcess(""); setProcessQuerySignal(false); } async function chat() { localStorage.removeItem("message"); - if (!queryToProcess || !conversationId) return; + if (!queryToProcess || !conversationId) { + setProcessQuerySignal(false); + return; + } const chatAPI = "/api/chat?client=web"; const chatAPIBody = { q: queryToProcess, conversation_id: conversationId, stream: true, + interrupt: shouldSendWithInterrupt, ...(locationData && { city: locationData.city, region: locationData.region, @@ -358,6 +364,9 @@ export default function Chat() { ...(uploadedFiles && { files: uploadedFiles }), }; + // Reset the flag after using it + setShouldSendWithInterrupt(false); + const response = await fetch(chatAPI, { method: "POST", headers: { @@ -481,6 +490,7 @@ export default function Chat() { isChatSideBarOpen={isChatSideBarOpen} setIsChatSideBarOpen={setIsChatSideBarOpen} isActive={authenticatedData?.is_active} + isParentProcessing={processQuerySignal} /> diff --git a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx index 5448d8ce..e2234701 100644 --- a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx +++ b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx @@ -180,13 +180,7 @@ export const ChatInputArea = forwardRef((pr }, [props.isResearchModeEnabled]); function onSendMessage() { - if (imageUploaded) { - setImageUploaded(false); - setImagePaths([]); - imageData.forEach((data) => props.sendImage(data)); - } - if (!message.trim()) return; - + if (!message.trim() && imageData.length === 0) return; if (!props.isLoggedIn) { setLoginRedirectMessage( "Hey there, you need to be signed in to send messages to Khoj AI", @@ -195,6 +189,17 @@ export const ChatInputArea = forwardRef((pr return; } + // If currently processing, trigger abort first + if (props.sendDisabled) { + props.setTriggeredAbort(true); + } + + if (imageUploaded) { + setImageUploaded(false); + setImagePaths([]); + imageData.forEach((data) => props.sendImage(data)); + } + let messageToSend = message.trim(); // Check if message starts with an explicit slash command const startsWithSlashCommand = @@ -657,7 +662,7 @@ export const ChatInputArea = forwardRef((pr