diff --git a/package.json b/package.json index 13522b2d8..6f58c157f 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "lint": "^0.8.19", + "lucide-react": "^0.451.0", "prism-react-renderer": "^2.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/components/ChatBot.js b/src/components/ChatBot.js new file mode 100644 index 000000000..aa0c3b99f --- /dev/null +++ b/src/components/ChatBot.js @@ -0,0 +1,222 @@ +import React, {useState, useCallback, useRef, useEffect} from "react"; +import {MessageCircle, X} from "lucide-react"; + +function textFormat(text) { + let block = 0; + let code = ``; + let lang = ""; + let heading = 2; + let output = ``; + + for (let i = 0; i < text.length; i++) { + if (text.substring(i, i + 3) === "```") { + i += 2; + if (block === 0) { + block = 1; + lang = ""; + while (i < text.length && text.charAt(i) !== "\n") { + lang += text.charAt(i++); + } + } else { + output += code; + code = ``; + lang = ""; + block = 0; + } + } else if (block === 0) { + if (text.substring(i, i + 3) === "###") { + i += 2; + heading = 3; + output += `\n\n`; + } else if (text.substring(i, i + 2) === "##") { + i += 1; + heading = 2; + output += `\n\n`; + } else if (text.substring(i, i + 1) == "#") { + heading = 1; + output += `\n\n`; + } else if (text.substring(i, i + 1) == "]") { + output += ": "; + } else if (text.substring(i, i + 1) == "[") { + output += ""; + } else if ( + text.substring(i, i + 1) == "(" || + text.substring(i, i + 1) == ")" + ) { + output += `"`; + } else if (text.substring(i, i + 1) == "<") { + i++; + while ( + i < text.length && + text.substring(i, i + 2) != "/>" && + text.substring(i, i + 1) != ">" + ) { + i++; + } + } else if (text.substring(i, i + 2) === "**") { + i += 1; + } else if (text.substring(i, i + 2) === "* ") { + i += 1; + output += `\n• `; + } else if (text.charAt(i) === "\n") { + output += `\n`; + } else { + output += text.charAt(i); + } + } else { + code += text.charAt(i); + } + } + + return output; +} + +export default function ChatBot() { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const messagesEndRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({behavior: "smooth"}); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); + + const sendMessage = useCallback(async () => { + if (!input.trim() || isLoading) return; + + const userMessage = {text: input, sender: "user"}; + setMessages((prevMessages) => [...prevMessages, userMessage]); + setIsLoading(true); + + try { + const apiRes = await fetch( + "https://keploy-api.abhishekkushwaha.me/chat", + { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({question: input}), + } + ); + + if (!apiRes.ok) { + throw new Error(`HTTP error! status: ${apiRes.status}`); + } + + const {answer} = await apiRes.json(); + + setMessages((prevMessages) => [ + ...prevMessages, + {text: answer, sender: "bot"}, + ]); + } catch (error) { + console.error("Error fetching response from Keploy API", error); + setMessages((prevMessages) => [ + ...prevMessages, + { + text: "Sorry, there was an error processing your request.", + sender: "bot", + }, + ]); + } finally { + setIsLoading(false); + setInput(""); + } + }, [input, isLoading]); + + const handleKeyPress = useCallback( + (e) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + }, + [sendMessage] + ); + + const toggleChat = useCallback(() => { + setIsOpen((prev) => !prev); + }, []); + + return ( +