/* eslint-disable @typescript-eslint/no-explicit-any */ import { useState, useRef } from "react"; import classNames from "classnames"; import { toast } from "sonner"; import { useLocalStorage, useUpdateEffect } from "react-use"; import { ArrowUp, ChevronDown } from "lucide-react"; import { FaStopCircle } from "react-icons/fa"; import Login from "../login/login"; import { defaultHTML } from "../../../utils/consts"; import SuccessSound from "./../../assets/success.mp3"; import Settings from "../settings/settings"; import ProModal from "../pro-modal/pro-modal"; import { Button } from "../ui/button"; // @ts-expect-error not needed import { MODELS } from "../../../utils/providers"; import Loading from "../loading/loading"; import { HtmlHistory } from "../../../utils/types"; import InviteFriends from "../invite-friends/invite-friends"; import ReImagine from "../re-imagine/re-imagine"; function AskAI({ html, setHtml, onScrollToBottom, isAiWorking, setisAiWorking, onNewPrompt, onSuccess, }: { html: string; setHtml: (html: string) => void; onScrollToBottom: () => void; isAiWorking: boolean; onNewPrompt: (prompt: string) => void; htmlHistory?: HtmlHistory[]; setisAiWorking: React.Dispatch>; onSuccess: (h: string, p: string, n?: number[][]) => void; }) { const refThink = useRef(null); const [open, setOpen] = useState(false); const [prompt, setPrompt] = useState(""); const [hasAsked, setHasAsked] = useState(false); const [previousPrompt, setPreviousPrompt] = useState(""); const [provider, setProvider] = useLocalStorage("provider", "auto"); const [model, setModel] = useLocalStorage("model", MODELS[0].value); const [openProvider, setOpenProvider] = useState(false); const [providerError, setProviderError] = useState(""); const [openProModal, setOpenProModal] = useState(false); const [think, setThink] = useState(undefined); const [openThink, setOpenThink] = useState(false); const [isThinking, setIsThinking] = useState(true); const [controller, setController] = useState(null); const audio = new Audio(SuccessSound); audio.volume = 0.5; const callAi = async (redesignMarkdown?: string) => { if (isAiWorking) return; if (!redesignMarkdown && !prompt.trim()) return; setisAiWorking(true); setProviderError(""); setThink(""); setOpenThink(false); setIsThinking(true); let contentResponse = ""; let thinkResponse = ""; let lastRenderTime = 0; const isFollowUp = html !== defaultHTML; const abortController = new AbortController(); setController(abortController); try { onNewPrompt(prompt); if (isFollowUp && !redesignMarkdown) { const request = await fetch("/api/ask-ai", { method: "PUT", body: JSON.stringify({ prompt, provider, previousPrompt, html, }), headers: { "Content-Type": "application/json", }, signal: abortController.signal, }); if (request && request.body) { const res = await request.json(); if (!request.ok) { if (res.openLogin) { setOpen(true); } else if (res.openSelectProvider) { setOpenProvider(true); setProviderError(res.message); } else if (res.openProModal) { setOpenProModal(true); } else { toast.error(res.message); } setisAiWorking(false); return; } setHtml(res.html); toast.success("AI responded successfully"); setPreviousPrompt(prompt); setPrompt(""); setisAiWorking(false); onSuccess(res.html, prompt, res.updatedLines); audio.play(); } } else { const request = await fetch("/api/ask-ai", { method: "POST", body: JSON.stringify({ prompt, provider, model, redesignMarkdown, }), headers: { "Content-Type": "application/json", }, signal: abortController.signal, }); if (request && request.body) { if (!request.ok) { const res = await request.json(); if (res.openLogin) { setOpen(true); } else if (res.openSelectProvider) { setOpenProvider(true); setProviderError(res.message); } else if (res.openProModal) { setOpenProModal(true); } else { toast.error(res.message); } setisAiWorking(false); return; } const reader = request.body.getReader(); const decoder = new TextDecoder("utf-8"); const selectedModel = MODELS.find( (m: { value: string }) => m.value === model ); let contentThink: string | undefined = undefined; const read = async () => { const { done, value } = await reader.read(); if (done) { toast.success("AI responded successfully"); setPreviousPrompt(prompt); setPrompt(""); setisAiWorking(false); setHasAsked(true); audio.play(); // Now we have the complete HTML including , so set it to be sure const finalDoc = contentResponse.match( /[\s\S]*<\/html>/ )?.[0]; if (finalDoc) { setHtml(finalDoc); } onSuccess(finalDoc ?? contentResponse, prompt); return; } const chunk = decoder.decode(value, { stream: true }); thinkResponse += chunk; if (selectedModel?.isThinker) { const thinkMatch = thinkResponse.match(/[\s\S]*/)?.[0]; if (thinkMatch && !thinkResponse?.includes("")) { if ((contentThink?.length ?? 0) < 3) { setOpenThink(true); } setThink(thinkMatch.replace("", "").trim()); contentThink += chunk; return read(); } } contentResponse += chunk; const newHtml = contentResponse.match( /[\s\S]*/ )?.[0]; if (newHtml) { setIsThinking(false); let partialDoc = newHtml; if ( partialDoc.includes("") && !partialDoc.includes("") ) { partialDoc += "\n"; } if ( partialDoc.includes("") ) { partialDoc += "\n"; } if (!partialDoc.includes("")) { partialDoc += "\n"; } // Throttle the re-renders to avoid flashing/flicker const now = Date.now(); if (now - lastRenderTime > 300) { setHtml(partialDoc); lastRenderTime = now; } if (partialDoc.length > 200) { onScrollToBottom(); } } read(); }; read(); } } } catch (error: any) { setisAiWorking(false); toast.error(error.message); if (error.openLogin) { setOpen(true); } } }; const stopController = () => { if (controller) { controller.abort(); setController(null); setisAiWorking(false); setThink(""); setOpenThink(false); setIsThinking(false); } }; useUpdateEffect(() => { if (refThink.current) { refThink.current.scrollTop = refThink.current.scrollHeight; } }, [think]); useUpdateEffect(() => { if (!isThinking) { setOpenThink(false); } }, [isThinking]); return (
{think && (
{ setOpenThink(!openThink); }} >

{isThinking ? "DeepSite is thinking..." : "DeepSite's plan"}

{think}

)}
{isAiWorking && (

AI is {isThinking ? "thinking" : "coding"}...{" "}

Stop generation
)} setPrompt(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { callAi(); } }} />
callAi(md)} />
setOpen(false)} >

You reached the limit of free AI usage. Please login to continue.

setOpenProModal(false)} />
); } export default AskAI;