172 lines
5.7 KiB
TypeScript
172 lines
5.7 KiB
TypeScript
"use client";
|
|
import { useState, useEffect, useMemo } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { toast } from "sonner";
|
|
import { Loader2, Download, CheckCircle } from "lucide-react";
|
|
import { useSelector } from "react-redux";
|
|
import { RootState } from "@/store/store";
|
|
import { handleSaveLLMConfig } from "@/utils/storeHelpers";
|
|
import LLMProviderSelection from "./LLMSelection";
|
|
import {
|
|
checkIfSelectedOllamaModelIsPulled,
|
|
pullOllamaModel,
|
|
} from "@/utils/providerUtils";
|
|
import { LLMConfig } from "@/types/llm_config";
|
|
import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
|
|
import { usePathname } from "next/navigation";
|
|
import OnBoardingSlidebar from "./OnBoarding/OnBoardingSlidebar";
|
|
import OnBoardingHeader from "./OnBoarding/OnBoardingHeader";
|
|
import ModeSelectStep from "./OnBoarding/ModeSelectStep";
|
|
import PresentonMode from "./OnBoarding/PresentonMode";
|
|
import GenerationWithImage from "./OnBoarding/GenerationWithImage";
|
|
import FinalStep from "./OnBoarding/FinalStep";
|
|
|
|
// Button state interface
|
|
interface ButtonState {
|
|
isLoading: boolean;
|
|
isDisabled: boolean;
|
|
text: string;
|
|
showProgress: boolean;
|
|
progressPercentage?: number;
|
|
status?: string;
|
|
}
|
|
|
|
const getTaperedSideOffset = (offset: number, top: number) => {
|
|
const taperMultiplier = Math.max(0.72, 1.85 - top * 0.012);
|
|
return Math.min(29, Number((offset * taperMultiplier).toFixed(2)));
|
|
};
|
|
|
|
export default function Home() {
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const [step, setStep] = useState<number>(1)
|
|
const [selectedMode, setSelectedMode] = useState<string>("presenton")
|
|
const config = useSelector((state: RootState) => state.userConfig);
|
|
const [llmConfig, setLlmConfig] = useState<LLMConfig>(config.llm_config);
|
|
|
|
const [downloadingModel, setDownloadingModel] = useState<{
|
|
name: string;
|
|
size: number | null;
|
|
downloaded: number | null;
|
|
status: string;
|
|
done: boolean;
|
|
} | null>(null);
|
|
const [showDownloadModal, setShowDownloadModal] = useState<boolean>(false);
|
|
const [buttonState, setButtonState] = useState<ButtonState>({
|
|
isLoading: false,
|
|
isDisabled: false,
|
|
text: "Save Configuration",
|
|
showProgress: false
|
|
});
|
|
|
|
const canChangeKeys = config.can_change_keys;
|
|
const downloadProgress = useMemo(() => {
|
|
if (downloadingModel && downloadingModel.downloaded !== null && downloadingModel.size !== null) {
|
|
return Math.round((downloadingModel.downloaded / downloadingModel.size) * 100);
|
|
}
|
|
return 0;
|
|
}, [downloadingModel?.downloaded, downloadingModel?.size]);
|
|
|
|
const handleSaveConfig = async () => {
|
|
trackEvent(MixpanelEvent.Home_SaveConfiguration_Button_Clicked, { pathname });
|
|
try {
|
|
setButtonState(prev => ({
|
|
...prev,
|
|
isLoading: true,
|
|
isDisabled: true,
|
|
text: "Saving Configuration..."
|
|
}));
|
|
// API: save config
|
|
trackEvent(MixpanelEvent.Home_SaveConfiguration_API_Call);
|
|
// API CALL: save config
|
|
await handleSaveLLMConfig(llmConfig);
|
|
|
|
if (llmConfig.LLM === "ollama" && llmConfig.OLLAMA_MODEL) {
|
|
// API: check model pulled
|
|
trackEvent(MixpanelEvent.Home_CheckOllamaModelPulled_API_Call);
|
|
const isPulled = await checkIfSelectedOllamaModelIsPulled(llmConfig.OLLAMA_MODEL);
|
|
if (!isPulled) {
|
|
setShowDownloadModal(true);
|
|
// API: download model
|
|
trackEvent(MixpanelEvent.Home_DownloadOllamaModel_API_Call);
|
|
await handleModelDownload();
|
|
}
|
|
}
|
|
toast.info("Configuration saved successfully");
|
|
setButtonState(prev => ({
|
|
...prev,
|
|
isLoading: false,
|
|
isDisabled: false,
|
|
text: "Save Configuration"
|
|
}));
|
|
// Track navigation from -> to
|
|
trackEvent(MixpanelEvent.Navigation, { from: pathname, to: "/upload" });
|
|
router.push("/upload");
|
|
} catch (error) {
|
|
toast.info(error instanceof Error ? error.message : "Failed to save configuration");
|
|
setButtonState(prev => ({
|
|
...prev,
|
|
isLoading: false,
|
|
isDisabled: false,
|
|
text: "Save Configuration"
|
|
}));
|
|
}
|
|
};
|
|
|
|
const handleModelDownload = async () => {
|
|
try {
|
|
await pullOllamaModel(llmConfig.OLLAMA_MODEL!, setDownloadingModel);
|
|
}
|
|
finally {
|
|
setDownloadingModel(null);
|
|
setShowDownloadModal(false);
|
|
}
|
|
};
|
|
|
|
|
|
useEffect(() => {
|
|
if (downloadingModel && downloadingModel.downloaded !== null && downloadingModel.size !== null) {
|
|
const percentage = Math.round(((downloadingModel.downloaded / downloadingModel.size) * 100));
|
|
setButtonState({
|
|
isLoading: true,
|
|
isDisabled: true,
|
|
text: `Downloading Model (${percentage}%)`,
|
|
showProgress: true,
|
|
progressPercentage: percentage,
|
|
status: downloadingModel.status
|
|
});
|
|
}
|
|
|
|
if (downloadingModel && downloadingModel.done) {
|
|
setTimeout(() => {
|
|
setShowDownloadModal(false);
|
|
setDownloadingModel(null);
|
|
toast.info("Model downloaded successfully!");
|
|
}, 2000);
|
|
}
|
|
}, [downloadingModel]);
|
|
|
|
useEffect(() => {
|
|
if (!canChangeKeys) {
|
|
router.push("/upload");
|
|
}
|
|
}, [canChangeKeys, router]);
|
|
|
|
if (!canChangeKeys) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
|
|
<div className="flex min-h-screen ">
|
|
<OnBoardingSlidebar step={step} />
|
|
<main className="w-full pl-20 pr-8 pb-5 max-w-[1440px] mx-auto relative z-10">
|
|
<OnBoardingHeader currentStep={step} setStep={setStep} />
|
|
{step === 1 && <ModeSelectStep selectedMode={selectedMode} setStep={setStep} setSelectedMode={setSelectedMode} />}
|
|
{step === 2 && selectedMode === "presenton" && <PresentonMode currentStep={step} setStep={setStep} />}
|
|
{step === 2 && selectedMode === "image" && <GenerationWithImage />}
|
|
{step === 3 && <FinalStep />}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|