import { useEffect, useState } from "react"; import { invoke } from "@tauri-apps/api/core"; import { useToast } from "./Toast"; export function FirstRunWizard({ onComplete }: { onComplete: () => void }) { const [step, setStep] = useState("welcome"); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [models, setModels] = useState([]); const [selectedModel, setSelectedModel] = useState(""); // ponytail: wizard defaults to local Ollama; no API-key UI here (Settings covers remote). const apiUrl = "http://localhost:11434"; const apiKey = ""; const { addToast } = useToast(); useEffect(() => { if (step === "check") { checkOllama(); } }, [step]); async function checkOllama() { setLoading(true); setError(""); try { // Test connection to the default Ollama URL const result = await invoke<{ ok: boolean; models: string[]; error: string }>( "test_connection", { config: { api_url: apiUrl, api_key: apiKey, model: "", temperature: 0.7, max_tokens: 512, top_p: 0.9, image_api_url: "", embed_model: "" } } ); if (result.ok) { setModels(result.models); setStep("model"); } else { setError(result.error || "Failed to connect to Ollama"); setStep("error"); } } catch (e) { setError(String(e)); setStep("error"); } setLoading(false); } async function pullModel(model: string) { setLoading(true); setError(""); try { // We don't have a direct pull command, but we can suggest the user to run `ollama pull` in terminal. // For now, we'll just set the model in config and hope it exists. // Alternatively, we could invoke a generate command to trigger a pull? Not sure. // We'll just set the config and complete. await invoke("set_llm_config", { config: { api_url: apiUrl, api_key: apiKey, model: model, temperature: 0.7, max_tokens: 512, top_p: 0.9, image_api_url: "http://localhost:1234", embed_model: "nomic-embed-text", } }); addToast(`Model ${model} selected. You may need to run 'ollama pull ${model}' if not already downloaded.`, "success"); setStep("done"); } catch (e) { setError(String(e)); setStep("error"); } setLoading(false); } function skip() { // Skip wizard and go to settings onComplete(); } function back() { if (step === "model") { setStep("welcome"); } else if (step === "error") { setStep("welcome"); } else if (step === "done") { // Should not happen } } async function handleSubmit() { if (step === "welcome") { setStep("check"); return; } if (step === "model") { if (!selectedModel) { setError("Please select a model"); return; } await pullModel(selectedModel); return; } if (step === "done") { onComplete(); return; } } if (step === "welcome") { return (

Welcome to DM-Pal

To generate content, DM-Pal needs a language model. Let's set up a local model using Ollama (recommended for privacy and offline use).

{error && (

{error}

)}
); } if (step === "check") { return (

Checking for Ollama

We're checking if Ollama is running at {apiUrl}...

{loading ? (

Checking…

) : ( <> {error ? (

{error}

) : (

Ollama is running!

)}
)}
); } if (step === "model") { return (

Select a Model

Ollama is running. Choose a model to use for text generation.

{error && (

{error}

)}

If you don't see your model, you may need to download it first. In a terminal, run:{' '} ollama pull llama3.2

); } if (step === "done") { return (

Setup Complete

DM-Pal is now configured to use {selectedModel} for text generation. Image generation runs via a stable-diffusion.cpp `sd-server` at http://localhost:1234 (cross-platform). Embedding model for lore search is set to nomic-embed-text.

); } if (step === "error") { return (

Error

{error}

); } return null; }