import { useState, useEffect, lazy, Suspense, useRef, KeyboardEvent, } from "react"; import { Card, Text, Textarea, Button, Stack, Group, Paper, } from "@mantine/core"; import { IconSend } from "@tabler/icons-react"; import { ChatMessage, generateChatResponse, } from "../../modules/textGeneration"; import { addLogEntry } from "../../modules/logEntries"; import { usePubSub } from "create-pubsub/react"; import { settingsPubSub } from "../../modules/pubSub"; import { match } from "ts-pattern"; const FormattedMarkdown = lazy(() => import("./FormattedMarkdown")); export default function ChatInterface({ initialQuery, initialResponse, }: { initialQuery: string; initialResponse: string; }) { const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); const [isGenerating, setIsGenerating] = useState(false); const [streamedResponse, setStreamedResponse] = useState(""); const latestResponseRef = useRef(""); const [settings] = usePubSub(settingsPubSub); useEffect(() => { setMessages([ { role: "user", content: initialQuery }, { role: "assistant", content: initialResponse }, ]); }, [initialQuery, initialResponse]); const handleSend = async () => { if (input.trim() === "" || isGenerating) return; const newMessages = [...messages, { role: "user", content: input }]; setMessages(newMessages); setInput(""); setIsGenerating(true); setStreamedResponse(""); latestResponseRef.current = ""; try { addLogEntry("User sent a follow-up question"); await generateChatResponse(newMessages, (partialResponse) => { setStreamedResponse(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 { setIsGenerating(false); setStreamedResponse(""); } }; const handleKeyDown = (event: KeyboardEvent) => { match([event, settings.enterToSubmit]) .with([{ code: "Enter", shiftKey: false }, true], () => { event.preventDefault(); handleSend(); }) .with([{ code: "Enter", shiftKey: true }, false], () => { event.preventDefault(); handleSend(); }) .otherwise(() => undefined); }; return ( Follow-up questions {messages.slice(2).length > 0 && ( {messages.slice(2).map((message, index) => ( {message.content} ))} {isGenerating && streamedResponse.length > 0 && ( {streamedResponse} )} )}