User: I'm having major performance issues with my AI assistant interface website, as if I get a long message response and then try and type something in my input area, it is almost impossible to type anything as it is very slow. How can I fix this?<br><br>Code:&nbsp;<code fileName="ChatInterface.js" notes="">// src/components/ChatInterface.js
import React, { useState, useContext, useRef, useEffect, useCallback } from &quot;react&quot;;
import { ChatContext } from &quot;../context/ChatContext&quot;;
import Message from &quot;./Message&quot;;
import CodeModal from &quot;./CodeModal&quot;;
import InlineCodeEditor from &quot;./InlineCodeEditor&quot;;
import { escapeHtml } from &quot;../utils/escapeHtml&quot;;

function ChatInterface() {
  const {
    chats,
    currentChatId,
    addMessage,
    updateChat,
    selectedModelId,
    models,
  } = useContext(ChatContext);
  const currentChat = chats.find((chat) =&gt; chat.id === currentChatId);
  const [message, setMessage] = useState(&quot;&quot;);
  const [isTyping, setIsTyping] = useState(false);
  const messagesEndRef = useRef(null);
  const [isCodeModalOpen, setIsCodeModalOpen] = useState(false);
  const [currentEditingCode, setCurrentEditingCode] = useState(null);
  const [isSystemPromptOpen, setIsSystemPromptOpen] = useState(false);

  const addCodeAttachment = ({ codeContent, fileName, notes }) =&gt; {
    const escapedCode = escapeHtml(codeContent);
    const escapedFileName = escapeHtml(fileName || &quot;untitled&quot;);
    const escapedNotes = escapeHtml(notes || &quot;&quot;);
    const codeTag = `&lt;code fileName=&quot;${escapedFileName}&quot; notes=&quot;${escapedNotes}&quot;&gt;${escapedCode}&lt;/code&gt;`;
    setMessage((prev) =&gt; prev + codeTag);
  };

  const handleAttachCode = useCallback((codeSegment = null) =&gt; {
    setCurrentEditingCode(codeSegment);
    setIsCodeModalOpen(true);
  }, []);

  const handleCodeSave = (updatedCode) =&gt; {
    if (currentEditingCode) {
      // Update existing code
      const escapedCode = escapeHtml(updatedCode.codeContent);
      const escapedFileName = escapeHtml(updatedCode.fileName || &quot;untitled&quot;);
      const escapedNotes = escapeHtml(updatedCode.notes || &quot;&quot;);

      // Improved regex to handle multi-line and special characters
      const regex = new RegExp(
        `&lt;code\\s+fileName=&quot;${currentEditingCode.fileName}&quot;\\s+notes=&quot;${currentEditingCode.notes}&quot;&gt;([\\s\\S]*?)&lt;\\/code&gt;`
      );

      const updatedMessage = message.replace(
        regex,
        `&lt;code fileName=&quot;${escapedFileName}&quot; notes=&quot;${escapedNotes}&quot;&gt;${escapedCode}&lt;/code&gt;`
      );
      setMessage(updatedMessage);
    } else {
      // Add new code
      addCodeAttachment(updatedCode);
    }
    setIsCodeModalOpen(false);
    setCurrentEditingCode(null);
  };

  const handleSend = useCallback(async () =&gt; {
    if (!message.trim() || !currentChatId || !selectedModelId) return;

    // Retrieve the system prompt from the current chat
    const systemPrompt = currentChat.systemPrompt || &quot;&quot;;

    // Combine the system prompt with the user message
    // Ensure there&#039;s a separator (like a newline) for clarity
    const fullMessage = systemPrompt ? `${systemPrompt}\n${message}` : message;

    const userMessage = { sender: &quot;user&quot;, text: message };
    addMessage(currentChatId, userMessage);
    setMessage(&quot;&quot;);
    scrollToBottom();

    // Set typing indicator
    setIsTyping(true);

    try {
      const response = await fetch(&quot;http://localhost:8000/api/chat&quot;, {
        method: &quot;POST&quot;,
        headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
        body: JSON.stringify({
          session_id: currentChatId,
          text: fullMessage,
          model_id: selectedModelId, // Ensure this is correctly set
        }),
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(
          errorData.detail || &quot;Failed to get response from the server.&quot;
        );
      }

      const data = await response.json();
      const responseText = data.response;
      const responseModelId = data.model_id; // Extract model_id from response

      const botMessage = {
        sender: &quot;bot&quot;,
        text: responseText,
        model_id: responseModelId, // Include model_id
      };
      addMessage(currentChatId, botMessage);
      scrollToBottom();
    } catch (error) {
      console.error(&quot;Error:&quot;, error);
      const errorMessage = {
        sender: &quot;bot&quot;,
        text: &quot;Sorry, something went wrong. Please try again.&quot;,
      };
      addMessage(currentChatId, errorMessage);
      scrollToBottom();
    } finally {
      // Remove typing indicator
      setIsTyping(false);
    }
  });

  const handleKeyDown = useCallback(
    (e) =&gt; {
      if (e.key === &quot;Enter&quot; &amp;&amp; !e.shiftKey) {
        e.preventDefault();
        handleSend();
      }
    },
    [handleSend]
  );

  const handleEditorKeyDown = useCallback((e) =&gt; {
    if (e.key === &#039;Enter&#039; &amp;&amp; !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  }, [handleSend]);


  const scrollToBottom = useCallback(() =&gt; {
    messagesEndRef.current?.scrollIntoView({ behavior: &quot;smooth&quot; });
  }, []);

  if (!currentChat) {
    return (
      &lt;div className=&quot;flex-1 p-4&quot;&gt;No active chat. Please start a new chat.&lt;/div&gt;
    );
  }

  const handleSystemPromptChange = (e) =&gt; {
    const newPrompt = e.target.value;
    updateChat(currentChatId, { systemPrompt: newPrompt });
  };

  const toggleSystemPrompt = () =&gt; {
    setIsSystemPromptOpen(!isSystemPromptOpen);
  };

  return (
    &lt;div className=&quot;flex flex-col h-screen&quot;&gt;
      &lt;div className=&quot;flex-1 p-32 pt-8 pb-8 overflow-y-auto bg-[#eeeeee] dark:bg-[#161618]&quot;&gt;
        {/* Removed Model Selector Dropdown */}

        {/* Chat Messages */}
        {currentChat.messages.map((msg, index) =&gt; (
          &lt;Message
            key={index}
            chatId={currentChatId}
            messageIndex={index}
            sender={msg.sender}
            text={msg.text}
            model_id={msg.model_id} // Pass model_id
          /&gt;
        ))}
        {isTyping &amp;&amp; (
          &lt;div className=&quot;mb-4 flex justify-start&quot;&gt;
            &lt;div className=&quot;max-w-xs md:max-w-md lg:max-w-lg p-3 rounded-lg bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200 flex items-center&quot;&gt;
              &lt;div className=&quot;flex space-x-1 mr-2&quot;&gt;
                &lt;div className=&quot;w-2 h-2 bg-gray-600 rounded-full animate-bounce-custom animation-delay-0&quot;&gt;&lt;/div&gt;
                &lt;div className=&quot;w-2 h-2 bg-gray-600 rounded-full animate-bounce-custom animation-delay-200&quot;&gt;&lt;/div&gt;
                &lt;div className=&quot;w-2 h-2 bg-gray-600 rounded-full animate-bounce-custom animation-delay-400&quot;&gt;&lt;/div&gt;
              &lt;/div&gt;
              &lt;span className=&quot;text-sm&quot;&gt;Echo is typing...&lt;/span&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        )}
        &lt;div ref={messagesEndRef} /&gt;
      &lt;/div&gt;
      &lt;div
        id=&quot;message_area&quot;
        className=&quot;p-4 border-t dark:border-gray-700 bg-white dark:bg-[#161618]&quot;
      &gt;
        &lt;button
          onClick={toggleSystemPrompt}
          className=&quot;mb-2 text-sm text-blue-500 hover:underline focus:outline-none&quot;
        &gt;
          {isSystemPromptOpen ? &quot;Hide System Prompt&quot; : &quot;Show System Prompt&quot;}
        &lt;/button&gt;
        {isSystemPromptOpen &amp;&amp; (
          &lt;textarea
            className=&quot;w-full p-2 border rounded-lg resize-none dark:bg-gray-800 dark:text-gray-200 mb-2&quot;
            rows=&quot;3&quot;
            placeholder=&quot;Enter system prompt...&quot;
            value={currentChat.systemPrompt}
            onChange={handleSystemPromptChange}
          /&gt;
        )}
        &lt;div className=&quot;flex flex-row&quot;&gt;
          &lt;div className=&quot;flex-grow&quot;&gt;
            &lt;InlineCodeEditor
              value={message}
              onChange={setMessage}
              onAttachCode={handleAttachCode}
              onKeyDown={handleEditorKeyDown}
            /&gt;
          &lt;/div&gt;
          &lt;div className=&quot;flex flex-col flex-shrink-0 items-start pl-4 space-y-2&quot;&gt;
            &lt;button
              onClick={() =&gt; setIsCodeModalOpen(true)}
              className=&quot;bg-blue-500 text-white px-3 py-2 rounded-lg hover:bg-blue-600 flex-grow w-full&quot;
            &gt;
              Attach Code
            &lt;/button&gt;
            &lt;button
              className=&quot;bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 flex-grow mt-2 w-full&quot;
              onClick={handleSend}
            &gt;
              Send
            &lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      {isCodeModalOpen &amp;&amp; (
        &lt;CodeModal
          onClose={() =&gt; {
            setIsCodeModalOpen(false);
            setCurrentEditingCode(null);
          }}
          onSave={handleCodeSave}
          initialData={currentEditingCode}
        /&gt;
      )}
    &lt;/div&gt;
  );
}

export default ChatInterface;
</code><code fileName="InlineCodeEditor.js" notes="">// src/components/InlineCodeEditor.js
import React, { useRef, useEffect, useCallback } from &#039;react&#039;;
import CodeAttachmentBubble from &#039;./CodeAttachmentBubble&#039;;
import { renderCodeBubble } from &#039;../utils/renderCodeBubble&#039;;

function InlineCodeEditor({ value, onChange, onAttachCode }) {
  const editorRef = useRef(null);

  useEffect(() =&gt; {
    if (editorRef.current &amp;&amp; editorRef.current.innerHTML !== formatContent(value)) {
      editorRef.current.innerHTML = formatContent(value);
    }
  }, [value]);

  const formatContent = (content) =&gt; {
    return content.replace(/&lt;code([^&gt;]*)&gt;([\s\S]*?)&lt;\/code&gt;/g, (match, attributes, code) =&gt; {
      const fileName = attributes.match(/fileName=&quot;([^&quot;]*)&quot;/)?.[1] || &#039;Untitled&#039;;
      const notes = attributes.match(/notes=&quot;([^&quot;]*)&quot;/)?.[1] || &#039;&#039;;
      // Pass the actual codeContent to renderCodeBubble
      return renderCodeBubble(fileName, notes, code);
    });
  };

  const handleInput = useCallback(() =&gt; {
    if (editorRef.current) {
      const content = editorRef.current.innerHTML;
      const plainText = content.replace(/&lt;span class=&quot;code-bubble&quot;[^&gt;]*&gt;[^&lt;]*&lt;\/span&gt;/g, (match) =&gt; {
        const fileName = match.match(/data-filename=&quot;([^&quot;]*)&quot;/)?.[1];
        const notes = match.match(/data-notes=&quot;([^&quot;]*)&quot;/)?.[1];
        const codeContentEscaped = match.match(/data-codecontent=&quot;([^&quot;]*)&quot;/)?.[1];
        // Unescape the codeContent
        const codeContent = codeContentEscaped ? decodeURIComponent(codeContentEscaped) : &#039;&#039;;
        return `&lt;code fileName=&quot;${fileName}&quot; notes=&quot;${notes}&quot;&gt;${codeContent}&lt;/code&gt;`;
      });
      onChange(plainText);
    }
  }, [onChange]);

  const handleKeyDown = useCallback((e) =&gt; {
    if (e.key === &#039;Enter&#039;) {
      if (e.shiftKey) {
        // Allow Shift+Enter to insert a newline
        // Let the event propagate naturally
      } else {
        // Prevent default behavior and emit a custom event or handle sending the message
        // Here, you might want to trigger the send action if needed
        // For InlineCodeEditor, we&#039;ll allow the parent to handle message sending
        // by not preventing default here
      }
    }
  }, []);

  const handlePaste = (e) =&gt; {
    e.preventDefault();
    const text = e.clipboardData.getData(&#039;text/plain&#039;);
    document.execCommand(&#039;insertText&#039;, false, text);
  };

  const handleBubbleClick = useCallback((e) =&gt; {
    if (e.target.classList.contains(&#039;code-bubble&#039;)) {
      const fileName = e.target.getAttribute(&#039;data-filename&#039;);
      const notes = e.target.getAttribute(&#039;data-notes&#039;);
      const codeContentEscaped = e.target.getAttribute(&#039;data-codecontent&#039;);
      const codeContent = codeContentEscaped ? decodeURIComponent(codeContentEscaped) : &#039;&#039;;
      onAttachCode({ fileName, notes, codeContent });
    }
  }, [onAttachCode]);

  return (
    &lt;div className=&quot;relative border rounded-lg p-2 bg-white dark:bg-[#18181b] text-black dark:text-white&quot;&gt;
      &lt;div
        ref={editorRef}
        className=&quot;w-full min-h-[100px] max-h-[300px] overflow-y-auto p-2 focus:outline-none break-all&quot;
        contentEditable
        onInput={handleInput}
        onKeyDown={handleKeyDown}
        onPaste={handlePaste}
        onClick={handleBubbleClick}
        role=&quot;textbox&quot;
        aria-multiline=&quot;true&quot;
        aria-label=&quot;Code Editor&quot;
      /&gt;
    &lt;/div&gt;
  );
}

export default InlineCodeEditor;
</code><code fileName="MarkdownRenderer.js" notes="">import React, { useContext, useState } from &#039;react&#039;;
import ReactMarkdown from &#039;react-markdown&#039;;
import { Prism as SyntaxHighlighter } from &#039;react-syntax-highlighter&#039;;
import { coldarkDark } from &#039;react-syntax-highlighter/dist/esm/styles/prism&#039;;
import { CodeContext } from &#039;../context/CodeContext&#039;;
import { ExternalLinkIcon, ClipboardCopyIcon } from &#039;@heroicons/react/outline&#039;;
import { CopyToClipboard } from &#039;react-copy-to-clipboard&#039;;

const MarkdownRenderer = ({ text }) =&gt; {
  const { showCode } = useContext(CodeContext);
  const [copiedCode, setCopiedCode] = useState({}); // Track copied state per code block

  return (
    &lt;ReactMarkdown
      components={{
        code({ node, inline, className, children, ...props }) {
          const match = /language-(\w+)/.exec(className || &#039;&#039;);
          if (!inline &amp;&amp; match) {
            const language = match[1];
            const codeContent = String(children).replace(/\n$/, &#039;&#039;);
            const key = node.position.start.line; // Unique key per block based on line number

            const handleCopy = () =&gt; {
              setCopiedCode((prev) =&gt; ({ ...prev, [key]: true }));
              setTimeout(() =&gt; {
                setCopiedCode((prev) =&gt; ({ ...prev, [key]: false }));
              }, 2000);
            };

            return (
              &lt;div className=&quot;relative my-4&quot;&gt;
                &lt;SyntaxHighlighter style={coldarkDark} language={language} PreTag=&quot;div&quot; {...props}&gt;
                  {codeContent}
                &lt;/SyntaxHighlighter&gt;
                &lt;div className=&quot;absolute top-2 right-2 flex space-x-2&quot;&gt;
                  &lt;CopyToClipboard text={codeContent} onCopy={handleCopy}&gt;
                    &lt;button
                      className=&quot;bg-gray-700 bg-opacity-50 text-white p-1 rounded hover:bg-opacity-75 flex items-center&quot;
                      title=&quot;Copy Code&quot;
                    &gt;
                      &lt;ClipboardCopyIcon className=&quot;h-4 w-4&quot; /&gt;
                      {copiedCode[key] &amp;&amp; &lt;span className=&quot;ml-1 text-xs&quot;&gt;Copied!&lt;/span&gt;}
                    &lt;/button&gt;
                  &lt;/CopyToClipboard&gt;
                  &lt;button
                    className=&quot;bg-gray-700 bg-opacity-50 text-white p-1 rounded hover:bg-opacity-75 flex items-center&quot;
                    onClick={() =&gt;
                      showCode({
                        codeContent: codeContent,
                        fileName: &#039;codeblock&#039;,
                        notes: &#039;&#039;,
                      })
                    }
                    title=&quot;Pop Out Code&quot;
                  &gt;
                    &lt;ExternalLinkIcon className=&quot;h-4 w-4&quot; /&gt;
                  &lt;/button&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            );
          } else {
            return (
              &lt;code className={className} {...props}&gt;
                {children}
              &lt;/code&gt;
            );
          }
        },
        h1: ({ node, ...props }) =&gt; &lt;h1 style={{ fontSize: &#039;2.5em&#039;, marginBottom: &#039;1em&#039;, marginTop: &#039;1.25em&#039; }} {...props} /&gt;,
        h2: ({ node, ...props }) =&gt; &lt;h2 style={{ fontSize: &#039;2em&#039;, marginBottom: &#039;0.5em&#039;, marginTop: &#039;0.75em&#039; }} {...props} /&gt;,
        h3: ({ node, ...props }) =&gt; &lt;h3 style={{ fontSize: &#039;1.75em&#039;, marginBottom: &#039;0.5em&#039;, marginTop: &#039;0.75em&#039; }} {...props} /&gt;,
        h4: ({ node, ...props }) =&gt; &lt;h4 style={{ fontSize: &#039;1.5em&#039;, marginBottom: &#039;0.5em&#039;, marginTop: &#039;0.75em&#039; }} {...props} /&gt;,
        h5: ({ node, ...props }) =&gt; &lt;h5 style={{ fontSize: &#039;1.25em&#039;, marginBottom: &#039;0.5em&#039; }} {...props} /&gt;,
        h6: ({ node, ...props }) =&gt; &lt;h6 style={{ fontSize: &#039;1.1em&#039;, marginBottom: &#039;0.5em&#039; }} {...props} /&gt;,
        hr: ({ node, ...props }) =&gt; &lt;hr style={{ marginTop: &#039;1.5em&#039;, marginBottom: &#039;1em&#039; }} {...props} /&gt;,
        li: ({ node, ...props }) =&gt; &lt;li style={{ marginLeft: &#039;0.5em&#039; }} {...props} /&gt;,
      }}
    &gt;
      {text}
    &lt;/ReactMarkdown&gt;
  );
};

export default MarkdownRenderer;</code><code fileName="Message.js" notes="">// src/components/Message.js
import React, { useState, useContext } from &#039;react&#039;;
import MarkdownRenderer from &#039;./MarkdownRenderer&#039;;
import { CopyToClipboard } from &#039;react-copy-to-clipboard&#039;;
import { ClipboardCopyIcon } from &#039;@heroicons/react/solid&#039;;
import { CodeContext } from &#039;../context/CodeContext&#039;;
import CodeAttachmentBubble from &#039;./CodeAttachmentBubble&#039;;
import CodeModal from &#039;./CodeModal&#039;;
import { ChatContext } from &#039;../context/ChatContext&#039;;

function Message({ chatId, messageIndex, sender, text, model_id }) {
  const [copied, setCopied] = useState(false);
  const { showCode } = useContext(CodeContext);
  const [isEditModalOpen, setIsEditModalOpen] = useState(false);
  const [currentAttachment, setCurrentAttachment] = useState(null);
  const { updateMessage } = useContext(ChatContext); // Ensure ChatContext is imported
  const { models } = useContext(ChatContext);  // Access models from context

  const modelName = model_id 
    ? models.find((model) =&gt; model.id === model_id)?.name || &quot;Unknown Model&quot; 
    : null;

  const handleCopy = () =&gt; {
    setCopied(true);
    setTimeout(() =&gt; setCopied(false), 2000);
  };

  const safeText = String(text);

  // Function to extract code attachments
  const extractCodeAttachments = (text) =&gt; {
    const codeRegex = /&lt;code(?:\s+fileName=&quot;([^&quot;]*)&quot;)?(?:\s+notes=&quot;([^&quot;]*)&quot;)?&gt;([\s\S]*?)&lt;\/code&gt;/g;
    let match;
    const attachments = [];
    while ((match = codeRegex.exec(text)) !== null) {
      attachments.push({
        fileName: match[1] || &#039;Untitled&#039;,
        notes: match[2] || &#039;&#039;,
        codeContent: match[3],
      });
    }
    return attachments;
  };

  const codeAttachments = extractCodeAttachments(safeText);
  const cleanedText = safeText.replace(/&lt;code(?:\s+fileName=&quot;[^&quot;]*&quot;)?(?:\s+notes=&quot;[^&quot;]*&quot;)?&gt;[\s\S]*?&lt;\/code&gt;/g, &#039;&#039;);

  // Handlers for editing and deleting attachments
  const handleEdit = (attachment) =&gt; {
    setCurrentAttachment(attachment);
    setIsEditModalOpen(true);
  };

  const handleDelete = (attachment) =&gt; {
    // Implement deletion logic
    // For example, remove the attachment&#039;s &lt;code&gt; block from the message text
    const updatedText = text.replace(
      `&lt;code fileName=&quot;${attachment.fileName}&quot; notes=&quot;${attachment.notes}&quot;&gt;${attachment.codeContent}&lt;/code&gt;`,
      &#039;&#039;
    );
    updateMessage(chatId, messageIndex, { text: updatedText });
  };

  const handleUpdateAttachment = (updatedAttachment) =&gt; {
    // Replace the old &lt;code&gt; block with the updated one
    const oldCodeBlockRegex = new RegExp(
      `&lt;code(?:\\s+fileName=&quot;${updatedAttachment.fileName || &#039;Untitled&#039;}&quot;)?(?:\\s+notes=&quot;${updatedAttachment.notes || &#039;&#039;}&quot;)?&gt;[\\s\\S]*?&lt;\\/code&gt;`,
      &#039;g&#039;
    );
    const newCodeBlock = `&lt;code fileName=&quot;${updatedAttachment.fileName}&quot; notes=&quot;${updatedAttachment.notes}&quot;&gt;${updatedAttachment.codeContent}&lt;/code&gt;`;
    const updatedText = text.replace(oldCodeBlockRegex, newCodeBlock);
    updateMessage(chatId, messageIndex, { text: updatedText });
  };

  return (
    &lt;&gt;
      &lt;div className={`mb-4 flex ${sender === &#039;user&#039; ? &#039;justify-end&#039; : &#039;justify-start&#039;}`}&gt;
        &lt;div
          className={`max-w-xs md:max-w-md lg:max-w-5xl p-5 rounded-xl ${
            sender === &#039;user&#039;
              ? &#039;bg-zinc-950 text-white&#039;
              : &#039;bg-gray-200 dark:bg-[#282828] text-gray-800 dark:text-gray-200&#039;
          }`}
        &gt;
          {sender === &#039;bot&#039; &amp;&amp; modelName &amp;&amp; (
            &lt;div className=&quot;mb-2 text-sm text-gray-500 dark:text-gray-400&quot;&gt;
              &lt;strong&gt;Model:&lt;/strong&gt; {modelName}
            &lt;/div&gt;
          )}

          &lt;MarkdownRenderer text={cleanedText} /&gt;
          &lt;div className=&quot;mt-2 flex flex-wrap&quot;&gt;
            {codeAttachments.map((attachment, index) =&gt; (
              &lt;CodeAttachmentBubble
                key={index}
                attachment={attachment}
                onEdit={() =&gt; handleEdit(attachment)}
                onDelete={() =&gt; handleDelete(attachment)}
              /&gt;
            ))}
          &lt;/div&gt;
          &lt;CopyToClipboard text={text} onCopy={handleCopy}&gt;
            &lt;button className=&quot;mt-2 text-sm text-gray-500 hover:text-gray-700 flex items-center&quot;&gt;
              &lt;ClipboardCopyIcon className=&quot;h-5 w-5 mr-1&quot; /&gt; {copied ? &#039;Copied!&#039; : &#039;Copy&#039;}
            &lt;/button&gt;
          &lt;/CopyToClipboard&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      {isEditModalOpen &amp;&amp; currentAttachment &amp;&amp; (
        &lt;CodeModal
          onClose={() =&gt; setIsEditModalOpen(false)}
          onSave={handleUpdateAttachment}
          initialData={currentAttachment}
        /&gt;
      )}
    &lt;/&gt;
  );
}

export default Message;</code><code fileName="ChatContext.js" notes="">// src/context/ChatContext.js
import React, { createContext, useState, useEffect } from &#039;react&#039;;
import { v4 as uuidv4 } from &#039;uuid&#039;;

export const ChatContext = createContext();

export const ChatProvider = ({ children }) =&gt; {
  const [chats, setChats] = useState(() =&gt; {
    const saved = localStorage.getItem(&#039;chats&#039;);
    return saved ? JSON.parse(saved) : [];
  });

  useEffect(() =&gt; {
    const saved = localStorage.getItem(&#039;chats&#039;);
    if (saved) {
      const parsedChats = JSON.parse(saved).map(chat =&gt; ({
        ...chat,
        messages: chat.messages.map(msg =&gt; ({
          ...msg,
          text: String(msg.text) // Ensure text is string
        }))
      }));
      setChats(parsedChats);
    } else {
      setChats([]);
    }
  }, []);

  const [currentChatId, setCurrentChatId] = useState(() =&gt; {
    const saved = localStorage.getItem(&#039;currentChatId&#039;);
    return saved ? JSON.parse(saved) : null;
  });

  const [models, setModels] = useState([]); // List of available models
  const [selectedModelId, setSelectedModelId] = useState(() =&gt; {
    const saved = localStorage.getItem(&#039;selectedModelId&#039;);
    return saved ? JSON.parse(saved) : null;
  });

  // Fetch models from the backend on component mount
  useEffect(() =&gt; {
    fetch(&#039;http://localhost:8000/api/models&#039;)
      .then((res) =&gt; {
        if (!res.ok) {
          throw new Error(`HTTP error! Status: ${res.status}`);
        }
        return res.json();
      })
      .then((data) =&gt; {
        console.log(&#039;Fetched models:&#039;, data); // Debugging line
        if (Array.isArray(data) &amp;&amp; data.length &gt; 0 &amp;&amp; !selectedModelId) {
          setSelectedModelId(data[0].id); // Select the first model by default
        }
        setModels(data);
      })
      .catch((error) =&gt; {
        console.error(&#039;Error fetching models:&#039;, error);
        setModels([]); // Prevent `.map` errors
      });
  }, []);

  // Persist chats to localStorage
  useEffect(() =&gt; {
    localStorage.setItem(&#039;chats&#039;, JSON.stringify(chats));
  }, [chats]);

  // Persist currentChatId to localStorage
  useEffect(() =&gt; {
    localStorage.setItem(&#039;currentChatId&#039;, JSON.stringify(currentChatId));
  }, [currentChatId]);

  // Persist selectedModelId to localStorage
  useEffect(() =&gt; {
    if (selectedModelId) {
      localStorage.setItem(&#039;selectedModelId&#039;, JSON.stringify(selectedModelId));
    }
  }, [selectedModelId]);

  const addChat = (name = `Chat ${chats.length + 1}`) =&gt; {
    const newChat = {
      id: uuidv4(),
      name: name,
      messages: [],
      systemPrompt: &#039;&#039;,
      model_id: selectedModelId, // Associate with selected model
    };
    setChats((prev) =&gt; [newChat, ...prev]);
    setCurrentChatId(newChat.id);
  };  

  const updateChat = (id, updatedChat) =&gt; {
    console.log(`Updating chat with ID: ${id}`, updatedChat); // Add this line
    setChats((prevChats) =&gt;
      prevChats.map((chat) =&gt; (chat.id === id ? { ...chat, ...updatedChat } : chat))
    );
  };

  const addMessage = (id, message) =&gt; {
    setChats((prevChats) =&gt;
      prevChats.map((chat) =&gt;
        chat.id === id
          ? { ...chat, messages: [...chat.messages, { ...message, text: String(message.text) }] }
          : chat
      )
    );
  };

  const deleteChat = (id) =&gt; {
    setChats((prevChats) =&gt; prevChats.filter((chat) =&gt; chat.id !== id));
    // If the deleted chat was the current one, reset currentChatId
    if (id === currentChatId) {
      setCurrentChatId(null);
    }
  };

  const clearChats = () =&gt; {
    setChats([]);
    setCurrentChatId(null);
  };

  const updateMessage = (chatId, messageIndex, updatedMessage) =&gt; {
    setChats((prevChats) =&gt;
      prevChats.map((chat) =&gt;
        chat.id === chatId
          ? {
              ...chat,
              messages: chat.messages.map((msg, idx) =&gt;
                idx === messageIndex
                  ? { ...msg, text: String(updatedMessage.text) }
                  : msg
              ),
            }
          : chat
      )
    );
  };

  const addModel = (model) =&gt; {
    setModels((prev) =&gt; [...prev, model]);
  };

  const removeModel = (modelId) =&gt; {
    setModels((prev) =&gt; prev.filter((model) =&gt; model.id !== modelId));
    // Optionally, handle chat associations if the removed model was in use
    if (selectedModelId === modelId) {
      setSelectedModelId(models.length &gt; 0 ? models[0].id : null);
    }
  };

  return (
    &lt;ChatContext.Provider
      value={{
        chats,
        currentChatId,
        setCurrentChatId,
        addChat,
        updateChat,
        addMessage,
        updateMessage,
        deleteChat,
        clearChats,
        models,
        addModel,
        removeModel,
        selectedModelId,
        setSelectedModelId,
      }}
    &gt;
      {children}
    &lt;/ChatContext.Provider&gt;
  );
};
</code>