User: Analyse this code:&nbsp;<ai-code fileName="ChatInterface.js" notes="">&#x2F;&#x2F; src&#x2F;components&#x2F;ChatInterface.js

import React, {
  useState,
  useContext,
  useRef,
  useEffect,
  useCallback,
} from &quot;react&quot;;
import { ChatContext } from &quot;..&#x2F;context&#x2F;ChatContext&quot;;
import Message from &quot;.&#x2F;Message&quot;;
import CodeModal from &quot;.&#x2F;CodeModal&quot;;
import InlineCodeEditor from &quot;.&#x2F;InlineCodeEditor&quot;;
import { escapeHtml } from &quot;..&#x2F;utils&#x2F;escapeHtml&quot;;
import { unescapeHtml } from &quot;..&#x2F;utils&#x2F;unescapeHtml&quot;; &#x2F;&#x2F; Import unescapeHtml

&#x2F;**
 * Component representing the chat interface where users can send messages and receive responses.
 *
 * @returns {JSX.Element} The ChatInterface component.
 *&#x2F;
function ChatInterface() {
  const {
    chats,
    currentChatId,
    addMessage,
    updateChat,
    selectedModelId,
    models,
  } &#x3D; useContext(ChatContext);

  const currentChat &#x3D; chats.find((chat) &#x3D;&gt; chat.id &#x3D;&#x3D;&#x3D; currentChatId);
  const [message, setMessage] &#x3D; useState(&quot;&quot;);
  const [isTyping, setIsTyping] &#x3D; useState(false);
  const messagesEndRef &#x3D; useRef(null);
  const [isCodeModalOpen, setIsCodeModalOpen] &#x3D; useState(false);
  const [currentEditingCode, setCurrentEditingCode] &#x3D; useState(null);
  const [isSystemPromptOpen, setIsSystemPromptOpen] &#x3D; useState(false);
  &#x2F;&#x2F; Removed unused state: codeAttachments

  &#x2F;**
   * Adds a code attachment within the message by embedding it as a &lt;code&gt; tag.
   *
   * @param {object} params - Parameters for the code attachment.
   * @param {string} params.codeContent - The actual code content.
   * @param {string} [params.fileName&#x3D;&#039;untitled&#039;] - The name of the code file.
   * @param {string} [params.notes&#x3D;&#039;&#039;] - Optional notes or comments.
   *&#x2F;
  const addCodeAttachment &#x3D; ({
    codeContent,
    fileName &#x3D; &quot;untitled&quot;,
    notes &#x3D; &quot;&quot;,
  }) &#x3D;&gt; {
    const escapedFileName &#x3D; escapeHtml(fileName);
    const escapedNotes &#x3D; escapeHtml(notes);
    &#x2F;&#x2F; Escape the codeContent to prevent tag conflicts
    const escapedCodeContent &#x3D; escapeHtml(codeContent);
    const codeTag &#x3D; &#x60;&lt;ai-code fileName&#x3D;&quot;${escapedFileName}&quot; notes&#x3D;&quot;${escapedNotes}&quot;&gt;${escapedCodeContent}&lt;&#x2F;ai-code&gt;&#x60;;
    setMessage((prev) &#x3D;&gt; prev + codeTag);
  };

  &#x2F;**
   * Opens the code modal for attaching or editing code segments.
   *
   * @param {object} [codeSegment&#x3D;null] - The code segment to edit, if any.
   *&#x2F;
  const handleAttachCode &#x3D; useCallback((codeSegment &#x3D; null) &#x3D;&gt; {
    setCurrentEditingCode(codeSegment);
    setIsCodeModalOpen(true);
  }, []);

  &#x2F;**
   * Handles saving of code attachments, either adding new or updating existing ones.
   *
   * @param {object} updatedCode - The updated code data.
   * @param {string} updatedCode.codeContent - The updated code content.
   * @param {string} updatedCode.fileName - The updated file name.
   * @param {string} updatedCode.notes - The updated notes.
   *&#x2F;
  const handleCodeSave &#x3D; useCallback(
    (updatedCode) &#x3D;&gt; {
      if (currentEditingCode) {
        &#x2F;&#x2F; Update existing code
        const escapedFileName &#x3D; escapeHtml(updatedCode.fileName || &quot;untitled&quot;);
        const escapedNotes &#x3D; escapeHtml(updatedCode.notes || &quot;&quot;);

        &#x2F;&#x2F; Use a more robust selector if possible to prevent regex issues
        const regex &#x3D; new RegExp(
          &#x60;&lt;code\\s+fileName&#x3D;&quot;${escapeHtml(
            currentEditingCode.fileName
          )}&quot;\\s+notes&#x3D;&quot;${escapeHtml(
            currentEditingCode.notes
          )}&quot;&gt;([\\s\\S]*?)&lt;\\&#x2F;code&gt;&#x60;
        );

        const updatedMessage &#x3D; message.replace(
          regex,
          &#x60;&lt;code fileName&#x3D;&quot;${escapedFileName}&quot; notes&#x3D;&quot;${escapedNotes}&quot;&gt;${updatedCode.codeContent}&lt;&#x2F;code&gt;&#x60;
        );
        setMessage(updatedMessage);
      } else {
        &#x2F;&#x2F; Add new code
        addCodeAttachment(updatedCode);
      }
      setIsCodeModalOpen(false);
      setCurrentEditingCode(null);
    },
    [currentEditingCode, message, escapeHtml, addCodeAttachment]
  );

  &#x2F;**
   * Sends the user&#039;s message to the backend and handles the response.
   *&#x2F;
  const handleSend &#x3D; useCallback(async () &#x3D;&gt; {
    if (!message.trim() || !currentChatId || !selectedModelId) return;
  
    &#x2F;&#x2F; Retrieve the system prompt from the current chat
    const systemPrompt &#x3D; currentChat.systemPrompt || &quot;&quot;;
  
    &#x2F;&#x2F; Combine the system prompt with the user message
    const fullMessage &#x3D; systemPrompt ? &#x60;${systemPrompt}\n${message}&#x60; : message;
  
    &#x2F;&#x2F; Parse the message to extract ai-code tags and unescape code content
    const parser &#x3D; new DOMParser();
    const doc &#x3D; parser.parseFromString(fullMessage, &#039;text&#x2F;html&#039;);
    const aiCodeElements &#x3D; doc.querySelectorAll(&#039;ai-code&#039;);
    
    aiCodeElements.forEach(element &#x3D;&gt; {
      const codeContent &#x3D; element.textContent; &#x2F;&#x2F; Already unescaped
      &#x2F;&#x2F; Here you can process or transform codeContent as needed
      &#x2F;&#x2F; For AI, you might want to replace the ai-code tag with the actual code block
      const codeBlock &#x3D; &#x60;\n\&#x60;\&#x60;\&#x60;\n${codeContent}\n\&#x60;\&#x60;\&#x60;\n&#x60;;
      element.outerHTML &#x3D; codeBlock;
    });
  
    const processedMessage &#x3D; doc.body.innerHTML;
  
    const userMessage &#x3D; { sender: &quot;user&quot;, text: processedMessage };
    addMessage(currentChatId, userMessage);
    setMessage(&quot;&quot;);
    scrollToBottom();

    &#x2F;&#x2F; Set typing indicator
    setIsTyping(true);

    try {
      const response &#x3D; await fetch(
        &#x60;${process.env.REACT_APP_BACKEND_URL}&#x2F;api&#x2F;chat&#x60;,
        {
          method: &quot;POST&quot;,
          headers: { &quot;Content-Type&quot;: &quot;application&#x2F;json&quot; },
          body: JSON.stringify({
            text: fullMessage, &#x2F;&#x2F; Send the full message with &lt;code&gt; tags intact
            model_id: selectedModelId,
          }),
        }
      );

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

      const data &#x3D; await response.json();
      const responseText &#x3D; data.response;
      const responseModelId &#x3D; data.model_id;

      const botMessage &#x3D; {
        sender: &quot;bot&quot;,
        text: responseText,
        model_id: responseModelId,
      };
      addMessage(currentChatId, botMessage);
      scrollToBottom();
    } catch (error) {
      console.error(&quot;Error:&quot;, error);
      const errorMessage &#x3D; {
        sender: &quot;bot&quot;,
        text: &quot;Sorry, something went wrong. Please try again.&quot;,
      };
      addMessage(currentChatId, errorMessage);
      scrollToBottom();
    } finally {
      &#x2F;&#x2F; Remove typing indicator
      setIsTyping(false);
    }
  }, [message, currentChatId, selectedModelId, addMessage, currentChat]);

  &#x2F;**
   * Handles the keydown event in the message input area.
   * Sends the message on Enter key without Shift.
   *
   * @param {object} e - The event object.
   *&#x2F;
  const handleKeyDown &#x3D; useCallback(
    (e) &#x3D;&gt; {
      if (e.key &#x3D;&#x3D;&#x3D; &quot;Enter&quot; &amp;&amp; !e.shiftKey) {
        e.preventDefault();
        handleSend();
      }
    },
    [handleSend]
  );

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

  &#x2F;**
   * Scrolls the chat to the bottom.
   *&#x2F;
  const scrollToBottom &#x3D; useCallback(() &#x3D;&gt; {
    messagesEndRef.current?.scrollIntoView({ behavior: &quot;smooth&quot; });
  }, []);

  &#x2F;**
   * Updates the system prompt in the current chat.
   *
   * @param {object} e - The event object.
   *&#x2F;
  const handleSystemPromptChange &#x3D; useCallback(
    (e) &#x3D;&gt; {
      const newPrompt &#x3D; e.target.value;
      updateChat(currentChatId, { systemPrompt: newPrompt });
    },
    [updateChat, currentChatId]
  );

  &#x2F;**
   * Toggles the visibility of the system prompt textarea.
   *&#x2F;
  const toggleSystemPrompt &#x3D; useCallback(() &#x3D;&gt; {
    setIsSystemPromptOpen((prev) &#x3D;&gt; !prev);
  }, []);

  &#x2F;&#x2F; Remove unused useState: codeAttachments

  if (!currentChat) {
    return (
      &lt;div className&#x3D;&quot;flex-1 p-4 text-gray-700 dark:text-gray-300&quot;&gt;
        No active chat. Please start a new chat.
      &lt;&#x2F;div&gt;
    );
  }

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

        {&#x2F;* Chat Messages *&#x2F;}
        {currentChat.messages.map((msg, index) &#x3D;&gt; (
          &lt;Message
            key&#x3D;{index}
            chatId&#x3D;{currentChatId}
            messageIndex&#x3D;{index}
            sender&#x3D;{msg.sender}
            text&#x3D;{msg.text}
            model_id&#x3D;{msg.model_id} &#x2F;&#x2F; Pass model_id
          &#x2F;&gt;
        ))}
        {isTyping &amp;&amp; (
          &lt;div className&#x3D;&quot;mb-4 flex justify-start&quot;&gt;
            &lt;div className&#x3D;&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&#x3D;&quot;flex space-x-1 mr-2&quot;&gt;
                &lt;div className&#x3D;&quot;w-2 h-2 bg-gray-600 rounded-full animate-bounce-custom animation-delay-0&quot;&gt;&lt;&#x2F;div&gt;
                &lt;div className&#x3D;&quot;w-2 h-2 bg-gray-600 rounded-full animate-bounce-custom animation-delay-200&quot;&gt;&lt;&#x2F;div&gt;
                &lt;div className&#x3D;&quot;w-2 h-2 bg-gray-600 rounded-full animate-bounce-custom animation-delay-400&quot;&gt;&lt;&#x2F;div&gt;
              &lt;&#x2F;div&gt;
              &lt;span className&#x3D;&quot;text-sm&quot;&gt;Echo is typing...&lt;&#x2F;span&gt;
            &lt;&#x2F;div&gt;
          &lt;&#x2F;div&gt;
        )}
        &lt;div ref&#x3D;{messagesEndRef} &#x2F;&gt;
      &lt;&#x2F;div&gt;
      &lt;div
        id&#x3D;&quot;message_area&quot;
        className&#x3D;&quot;p-4 border-t dark:border-gray-700 bg-white dark:bg-[#161618]&quot;
      &gt;
        &lt;button
          onClick&#x3D;{toggleSystemPrompt}
          className&#x3D;&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;&#x2F;button&gt;
        {isSystemPromptOpen &amp;&amp; (
          &lt;textarea
            className&#x3D;&quot;w-full p-2 border rounded-lg resize-none dark:bg-gray-800 dark:text-gray-200 mb-2&quot;
            rows&#x3D;&quot;3&quot;
            placeholder&#x3D;&quot;Enter system prompt...&quot;
            value&#x3D;{currentChat.systemPrompt}
            onChange&#x3D;{handleSystemPromptChange}
          &#x2F;&gt;
        )}
        &lt;div className&#x3D;&quot;flex flex-row&quot;&gt;
          &lt;div className&#x3D;&quot;flex-grow&quot;&gt;
            &lt;InlineCodeEditor
              value&#x3D;{message}
              onChange&#x3D;{setMessage}
              onAttachCode&#x3D;{handleAttachCode}
              onKeyDown&#x3D;{handleEditorKeyDown} &#x2F;&#x2F; Pass the keydown handler as a prop
            &#x2F;&gt;
          &lt;&#x2F;div&gt;
          &lt;div className&#x3D;&quot;flex flex-col items-end pl-4&quot;&gt;
            &lt;button
              onClick&#x3D;{() &#x3D;&gt; handleAttachCode(null)} &#x2F;&#x2F; Passing null for new attachment
              className&#x3D;&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;&#x2F;button&gt;
            &lt;button
              className&#x3D;&quot;bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600 flex-grow mt-2 w-full&quot;
              onClick&#x3D;{handleSend}
            &gt;
              Send
            &lt;&#x2F;button&gt;
          &lt;&#x2F;div&gt;
        &lt;&#x2F;div&gt;
      &lt;&#x2F;div&gt;
      {isCodeModalOpen &amp;&amp; (
        &lt;CodeModal
          onClose&#x3D;{() &#x3D;&gt; {
            setIsCodeModalOpen(false);
            setCurrentEditingCode(null);
          }}
          onSave&#x3D;{handleCodeSave}
          initialData&#x3D;{currentEditingCode}
        &#x2F;&gt;
      )}
    &lt;&#x2F;div&gt;
  );
}

export default ChatInterface;
</ai-code>