"use client"; /* eslint-disable @typescript-eslint/no-explicit-any */ import { useState, useMemo, useRef } from "react"; import classNames from "classnames"; import { toast } from "sonner"; import { useLocalStorage, useUpdateEffect } from "react-use"; import { ArrowUp, ChevronDown, Crosshair } from "lucide-react"; import { FaStopCircle } from "react-icons/fa"; import ProModal from "@/components/pro-modal"; import { Button } from "@/components/ui/button"; import { MODELS } from "@/lib/providers"; import { HtmlHistory, Page, Project } from "@/types"; // import { InviteFriends } from "@/components/invite-friends"; import { Settings } from "@/components/editor/ask-ai/settings"; import { LoginModal } from "@/components/login-modal"; import { ReImagine } from "@/components/editor/ask-ai/re-imagine"; import Loading from "@/components/loading"; import { Checkbox } from "@/components/ui/checkbox"; import { Tooltip, TooltipTrigger } from "@/components/ui/tooltip"; import { TooltipContent } from "@radix-ui/react-tooltip"; import { SelectedHtmlElement } from "./selected-html-element"; import { FollowUpTooltip } from "./follow-up-tooltip"; import { isTheSameHtml } from "@/lib/compare-html-diff"; import { useCallAi } from "@/hooks/useCallAi"; import { SelectedFiles } from "./selected-files"; import { Uploader } from "./uploader"; export function AskAI({ isNew, project, images, currentPage, previousPrompts, onScrollToBottom, isAiWorking, setisAiWorking, isEditableModeEnabled = false, pages, htmlHistory, selectedElement, setSelectedElement, selectedFiles, setSelectedFiles, setIsEditableModeEnabled, onNewPrompt, onSuccess, setPages, setCurrentPage, }: { project?: Project | null; currentPage: Page; images?: string[]; pages: Page[]; onScrollToBottom: () => void; previousPrompts: string[]; isAiWorking: boolean; onNewPrompt: (prompt: string) => void; htmlHistory?: HtmlHistory[]; setisAiWorking: React.Dispatch>; isNew?: boolean; onSuccess: (page: Page[], p: string, n?: number[][]) => void; isEditableModeEnabled: boolean; setIsEditableModeEnabled: React.Dispatch>; selectedElement?: HTMLElement | null; setSelectedElement: React.Dispatch>; selectedFiles: string[]; setSelectedFiles: React.Dispatch>; setPages: React.Dispatch>; setCurrentPage: React.Dispatch>; }) { const refThink = useRef(null); const [open, setOpen] = useState(false); const [prompt, setPrompt] = 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 [openThink, setOpenThink] = useState(false); const [isThinking, setIsThinking] = useState(true); const [think, setThink] = useState(""); const [isFollowUp, setIsFollowUp] = useState(true); const [isUploading, setIsUploading] = useState(false); const [files, setFiles] = useState(images ?? []); const { callAiNewProject, callAiFollowUp, callAiNewPage, stopController, audio: hookAudio, } = useCallAi({ onNewPrompt, onSuccess, onScrollToBottom, setPages, setCurrentPage, currentPage, pages, isAiWorking, setisAiWorking, }); const selectedModel = useMemo(() => { return MODELS.find((m: { value: string }) => m.value === model); }, [model]); const callAi = async (redesignMarkdown?: string) => { if (isAiWorking) return; if (!redesignMarkdown && !prompt.trim()) return; if (isFollowUp && !redesignMarkdown && !isSameHtml) { // Use follow-up function for existing projects const selectedElementHtml = selectedElement ? selectedElement.outerHTML : ""; const result = await callAiFollowUp( prompt, model, provider, previousPrompts, selectedElementHtml, selectedFiles ); if (result?.error) { handleError(result.error, result.message); return; } if (result?.success) { setPrompt(""); } } else if (isFollowUp && pages.length > 1 && isSameHtml) { const result = await callAiNewPage( prompt, model, provider, currentPage.path, [ ...(previousPrompts ?? []), ...(htmlHistory?.map((h) => h.prompt) ?? []), ] ); if (result?.error) { handleError(result.error, result.message); return; } if (result?.success) { setPrompt(""); } } else { const result = await callAiNewProject( prompt, model, provider, redesignMarkdown, handleThink, () => { setIsThinking(false); } ); if (result?.error) { handleError(result.error, result.message); return; } if (result?.success) { setPrompt(""); if (selectedModel?.isThinker) { setModel(MODELS[0].value); } } } }; const handleThink = (think: string) => { setThink(think); setIsThinking(true); setOpenThink(true); }; const handleError = (error: string, message?: string) => { switch (error) { case "login_required": setOpen(true); break; case "provider_required": setOpenProvider(true); setProviderError(message || ""); break; case "pro_required": setOpenProModal(true); break; case "api_error": toast.error(message || "An error occurred"); break; case "network_error": toast.error(message || "Network error occurred"); break; default: toast.error("An unexpected error occurred"); } }; useUpdateEffect(() => { if (refThink.current) { refThink.current.scrollTop = refThink.current.scrollHeight; } }, [think]); useUpdateEffect(() => { if (!isThinking) { setOpenThink(false); } }, [isThinking]); const isSameHtml = useMemo(() => { return isTheSameHtml(currentPage.html); }, [currentPage.html]); return (
{think && (
{ setOpenThink(!openThink); }} >

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

{think}

)} setSelectedFiles((prev) => prev.filter((f) => f !== file)) } /> {selectedElement && (
setSelectedElement(null)} />
)}
{(isAiWorking || isUploading) && (

{isUploading ? ( "Uploading images..." ) : isAiWorking && !isSameHtml ? ( "AI is working..." ) : ( {[ "D", "e", "e", "p", "S", "i", "t", "e", " ", "i", "s", " ", "T", "h", "i", "n", "k", "i", "n", "g", ".", ".", ".", " ", "W", "a", "i", "t", " ", "a", " ", "m", "o", "m", "e", "n", "t", ".", ".", ".", ].map((char, index) => ( {char === " " ? "\u00A0" : char} ))} )}

{isAiWorking && (
Stop generation
)}
)}