diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx index d9efd98c..e6ebaf0d 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx @@ -23,9 +23,8 @@ const HistoricalMessage = ({ return (
{ + return ( + (prevProps.message === nextProps.message) && + (prevProps.isLastMessage === nextProps.isLastMessage) && + (prevProps.chatId === nextProps.chatId) + ); + } +); \ No newline at end of file diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx index 859f8417..0b28ac58 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx @@ -12,20 +12,33 @@ import AvailableAgentsButton, { useAvailableAgents, } from "./AgentMenu"; import TextSizeButton from "./TextSizeMenu"; + +export const PROMPT_INPUT_EVENT = 'set_prompt_input'; export default function PromptInput({ - message, submit, onChange, inputDisabled, buttonDisabled, sendCommand, }) { + const [promptInput, setPromptInput] = useState(''); const { showAgents, setShowAgents } = useAvailableAgents(); const { showSlashCommand, setShowSlashCommand } = useSlashCommands(); const formRef = useRef(null); const textareaRef = useRef(null); const [_, setFocused] = useState(false); + // To prevent too many re-renders we remotely listen for updates from the parent + // via an event cycle. Otherwise, using message as a prop leads to a re-render every + // change on the input. + function handlePromptUpdate(e) { setPromptInput(e?.detail ?? ''); } + useEffect(() => { + if (!!window) window.addEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate); + return () => ( + window?.removeEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate) + ) + }, []); + useEffect(() => { if (!inputDisabled && textareaRef.current) { textareaRef.current.focus(); @@ -102,6 +115,7 @@ export default function PromptInput({ watchForSlash(e); watchForAt(e); adjustTextArea(e); + setPromptInput(e.target.value) }} onKeyDown={captureEnter} required={true} @@ -111,7 +125,7 @@ export default function PromptInput({ setFocused(false); adjustTextArea(e); }} - value={message} + value={promptInput} className="cursor-text max-h-[100px] md:min-h-[40px] mx-2 md:mx-0 py-2 w-full text-[16px] md:text-md text-white bg-transparent placeholder:text-white/60 resize-none active:outline-none focus:outline-none flex-grow" placeholder={"Send a message"} /> diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx index 7d2850bd..07608a7f 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import ChatHistory from "./ChatHistory"; -import PromptInput from "./PromptInput"; +import PromptInput, { PROMPT_INPUT_EVENT } from "./PromptInput"; import Workspace from "@/models/workspace"; import handleChat, { ABORT_STREAM_EVENT } from "@/utils/chat"; import { isMobile } from "react-device-detect"; @@ -20,10 +20,19 @@ export default function ChatContainer({ workspace, knownHistory = [] }) { const [chatHistory, setChatHistory] = useState(knownHistory); const [socketId, setSocketId] = useState(null); const [websocket, setWebsocket] = useState(null); + + // Maintain state of message from whatever is in PromptInput const handleMessageChange = (event) => { setMessage(event.target.value); }; + // Emit an update to the sate of the prompt input without directly + // passing a prop in so that it does not re-render constantly. + function setMessageEmit(messageContent = '') { + setMessage(messageContent); + window.dispatchEvent(new CustomEvent(PROMPT_INPUT_EVENT, { detail: messageContent })) + } + const handleSubmit = async (event) => { event.preventDefault(); if (!message || message === "") return false; @@ -41,14 +50,14 @@ export default function ChatContainer({ workspace, knownHistory = [] }) { ]; setChatHistory(prevChatHistory); - setMessage(""); + setMessageEmit(""); setLoadingResponse(true); }; const sendCommand = async (command, submit = false) => { if (!command || command === "") return false; if (!submit) { - setMessage(command); + setMessageEmit(command); return; } @@ -65,7 +74,7 @@ export default function ChatContainer({ workspace, knownHistory = [] }) { ]; setChatHistory(prevChatHistory); - setMessage(""); + setMessageEmit(""); setLoadingResponse(true); }; @@ -208,7 +217,6 @@ export default function ChatContainer({ workspace, knownHistory = [] }) { sendCommand={sendCommand} />