import { useEffect, useState } from 'react'; import { useAppContext } from '../utils/app.context'; import StorageUtils from '../utils/storage'; import { useNavigate } from 'react-router'; import ChatMessage from './ChatMessage'; import { CanvasType, PendingMessage } from '../utils/types'; import { classNames } from '../utils/misc'; import CanvasPyInterpreter from './CanvasPyInterpreter'; export default function ChatScreen() { const { viewingConversation, sendMessage, isGenerating, stopGenerating, pendingMessages, canvasData, } = useAppContext(); const [inputMsg, setInputMsg] = useState(''); const navigate = useNavigate(); const currConvId = viewingConversation?.id ?? ''; const pendingMsg: PendingMessage | undefined = pendingMessages[currConvId]; const scrollToBottom = (requiresNearBottom: boolean) => { const mainScrollElem = document.getElementById('main-scroll'); if (!mainScrollElem) return; const spaceToBottom = mainScrollElem.scrollHeight - mainScrollElem.scrollTop - mainScrollElem.clientHeight; if (!requiresNearBottom || spaceToBottom < 50) { setTimeout( () => mainScrollElem.scrollTo({ top: mainScrollElem.scrollHeight }), 1 ); } }; // scroll to bottom when conversation changes useEffect(() => { scrollToBottom(false); }, [viewingConversation?.id]); const sendNewMessage = async () => { if (inputMsg.trim().length === 0 || isGenerating(currConvId)) return; const convId = viewingConversation?.id ?? StorageUtils.getNewConvId(); const lastInpMsg = inputMsg; setInputMsg(''); if (!viewingConversation) { // if user is creating a new conversation, redirect to the new conversation navigate(`/chat/${convId}`); } scrollToBottom(false); // auto scroll as message is being generated const onChunk = () => scrollToBottom(true); if (!(await sendMessage(convId, inputMsg, onChunk))) { // restore the input message if failed setInputMsg(lastInpMsg); } }; const hasCanvas = !!canvasData; return (