"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({ 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 [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 [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, previousPrompt, selectedElementHtml, selectedFiles ); if (result?.error) { handleError(result.error, result.message); return; } if (result?.success) { setPreviousPrompt(prompt); 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) { setPreviousPrompt(prompt); 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) { setPreviousPrompt(prompt); 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 ? "KAICoder is thinking..." : "KAICoders's plan"}

{think}

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

{isUploading ? "Uploading images..." : `AI is ${isThinking ? "thinking" : "coding"}...`}

{isAiWorking && (
Stop generation
)}
)}