import { Button, Card, Group, Paper, Stack, Text, Textarea, } from "@mantine/core"; import { IconSend } from "@tabler/icons-react"; import { usePubSub } from "create-pubsub/react"; import type { ChatMessage } from "gpt-tokenizer/GptEncoding"; import { type ChangeEvent, type KeyboardEvent, Suspense, lazy, useEffect, useRef, useState, } from "react"; import { handleEnterKeyDown } from "../../modules/keyboard"; import { addLogEntry } from "../../modules/logEntries"; import { settingsPubSub } from "../../modules/pubSub"; import { generateChatResponse } from "../../modules/textGeneration"; const FormattedMarkdown = lazy(() => import("./FormattedMarkdown")); const CopyIconButton = lazy(() => import("./CopyIconButton")); interface ChatState { input: string; isGenerating: boolean; streamedResponse: string; } export default function ChatInterface({ initialQuery, initialResponse, }: { initialQuery: string; initialResponse: string; }) { const [messages, setMessages] = useState([]); const [state, setState] = useState({ input: "", isGenerating: false, streamedResponse: "", }); const latestResponseRef = useRef(""); const [settings] = usePubSub(settingsPubSub); useEffect(() => { setMessages([ { role: "user", content: initialQuery }, { role: "assistant", content: initialResponse }, ]); }, [initialQuery, initialResponse]); const handleSend = async () => { if (state.input.trim() === "" || state.isGenerating) return; const newMessages: ChatMessage[] = [ ...messages, { role: "user", content: state.input }, ]; setMessages(newMessages); setState((prev) => ({ ...prev, input: "", isGenerating: true, streamedResponse: "", })); latestResponseRef.current = ""; try { addLogEntry("User sent a follow-up question"); await generateChatResponse(newMessages, (partialResponse) => { setState((prev) => ({ ...prev, streamedResponse: partialResponse })); latestResponseRef.current = partialResponse; }); setMessages((prevMessages) => [ ...prevMessages, { role: "assistant", content: latestResponseRef.current }, ]); addLogEntry("AI responded to follow-up question"); } catch (error) { addLogEntry(`Error generating chat response: ${error}`); setMessages((prevMessages) => [ ...prevMessages, { role: "assistant", content: "Sorry, I encountered an error while generating a response.", }, ]); } finally { setState((prev) => ({ ...prev, isGenerating: false, streamedResponse: "", })); } }; const handleInputChange = (event: ChangeEvent) => { const input = event.target.value; setState((prev) => ({ ...prev, input })); }; const handleKeyDown = (event: KeyboardEvent) => { handleEnterKeyDown(event, settings, handleSend); }; const getChatContent = () => { return messages .slice(2) .map( (msg, index) => `${index + 1}. ${msg.role?.toUpperCase()}\n\n${msg.content}`, ) .join("\n\n"); }; return ( Follow-up questions {messages.length > 2 && ( )} {messages.slice(2).length > 0 && ( {messages.slice(2).map((message, index) => ( {message.content} ))} {state.isGenerating && state.streamedResponse.length > 0 && ( {state.streamedResponse} )} )}