import { useState, useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { TauriService, Runner } from "../../services/TauriService"; import { usePlatform } from "../../hooks/usePlatform"; import { useConfig, useAudio, useGame } from "../../context/LauncherContext"; interface SetupViewProps { onComplete: () => void; } const SetupView: React.FC = ({ onComplete }) => { const { isLinux, isMac } = usePlatform(); const { username, setUsername, setHasCompletedSetup, profile, setVfxEnabled: setConfigVfx, setRpcEnabled: setConfigRpc, setLinuxRunner, linuxRunner: configLinuxRunner, vfxEnabled: configVfx, rpcEnabled: configRpc, animationsEnabled, } = useConfig(); const { playPressSound, playSfx } = useAudio(); const { editions } = useGame(); const titleImage = editions.find(e => e.id === profile)?.titleImage || "/images/MenuTitle.png"; const [currentStep, setCurrentStep] = useState(0); const [focusIndex, setFocusIndex] = useState(0); const [tempUsername, setTempUsername] = useState(username); const [runners, setRunners] = useState([]); const [selectedRunner, setSelectedRunner] = useState(""); const [isSettingUpRuntime, setIsSettingUpRuntime] = useState(false); const [setupProgress, setSetupProgress] = useState<{ stage: string; message: string; percent?: number } | null>(null); const [runtimeAlreadyInstalled, setRuntimeAlreadyInstalled] = useState(false); const [enableVfx, setEnableVfx] = useState(configVfx); const [enableDiscordRPC, setEnableDiscordRPC] = useState(configRpc); const totalSteps = 4; useEffect(() => { if (isLinux || isMac) { TauriService.getAvailableRunners().then(availableRunners => { setRunners(availableRunners); if (configLinuxRunner && availableRunners.find(r => r.id === configLinuxRunner)) { setSelectedRunner(configLinuxRunner); } }); } if (isMac) { checkMacOSRuntime(); const unlisten = TauriService.onMacosProgress((progress) => { setSetupProgress(progress); }); return () => { unlisten.then(f => f?.()); }; } }, [isLinux, isMac]); const checkMacOSRuntime = async () => { try { const runtimeCheck = await TauriService.checkMacOSRuntimeInstalledFast(); setRuntimeAlreadyInstalled(runtimeCheck); if (runtimeCheck) { localStorage.setItem('lce-macos-runtime-installed', 'true'); } else { localStorage.removeItem('lce-macos-runtime-installed'); } } catch { setRuntimeAlreadyInstalled(false); localStorage.removeItem('lce-macos-runtime-installed'); } }; const handleRunnerSelect = (runnerId: string) => { playPressSound(); setSelectedRunner(runnerId); }; const handleNext = async () => { playPressSound(); if (currentStep === 0) { setUsername(tempUsername); setCurrentStep(1); setFocusIndex(0); } else if (currentStep === 1) { if (isLinux && selectedRunner) setLinuxRunner(selectedRunner); setCurrentStep(2); setFocusIndex(0); } else if (currentStep === 2) { setConfigVfx(enableVfx); setConfigRpc(enableDiscordRPC); setCurrentStep(3); setFocusIndex(0); } else if (currentStep === 3) { playSfx("levelup.ogg"); setHasCompletedSetup(true); onComplete(); } }; const handleBack = () => { playPressSound(); if (currentStep > 0) { setCurrentStep(currentStep - 1); setFocusIndex(0); } }; useEffect(() => { const handleKey = (e: KeyboardEvent) => { let count = 0; if (currentStep === 0) count = 2; else if (currentStep === 1) { if (isLinux) count = runners.length + 2; else if (isMac) count = 3; else count = 2; } else if (currentStep === 2) count = 4; else if (currentStep === 3) count = 2; if (e.key === "ArrowDown" || e.key === "Tab") { e.preventDefault(); setFocusIndex((prev) => (prev + 1) % count); } else if (e.key === "ArrowUp") { e.preventDefault(); setFocusIndex((prev) => (prev - 1 + count) % count); } else if (e.key === "Enter") { if (currentStep === 0) { if (focusIndex === 0) handleNext(); else if (focusIndex === 1) handleNext(); } else if (currentStep === 1) { if (isLinux) { if (focusIndex < runners.length) handleRunnerSelect(runners[focusIndex].id); else if (focusIndex === runners.length) handleBack(); else if (focusIndex === runners.length + 1) handleNext(); } else if (isMac) { if (focusIndex === 0) handleMacosSetup(); else if (focusIndex === 1) handleBack(); else if (focusIndex === 2) handleNext(); } else { if (focusIndex === 0) handleBack(); else if (focusIndex === 1) handleNext(); } } else if (currentStep === 2) { if (focusIndex === 0) { setEnableVfx(!enableVfx); playPressSound(); } else if (focusIndex === 1) { setEnableDiscordRPC(!enableDiscordRPC); playPressSound(); } else if (focusIndex === 2) handleBack(); else if (focusIndex === 3) handleNext(); } else if (currentStep === 3) { if (focusIndex === 0) handleBack(); else if (focusIndex === 1) handleNext(); } } }; window.addEventListener("keydown", handleKey); return () => window.removeEventListener("keydown", handleKey); }, [currentStep, focusIndex, runners, enableVfx, enableDiscordRPC, isLinux, isMac, tempUsername]); const handleMacosSetup = async () => { playPressSound(); setIsSettingUpRuntime(true); setSetupProgress({ stage: "preparing", message: "Preparing macOS runtime setup...", percent: 0 }); try { await TauriService.setupMacosRuntime(); setSetupProgress({ stage: "completed", message: "Setup completed successfully!", percent: 100 }); localStorage.setItem('lce-macos-runtime-installed', 'true'); setRuntimeAlreadyInstalled(true); setTimeout(() => { setCurrentStep(2); setIsSettingUpRuntime(false); setSetupProgress(null); }, 2000); } catch (e) { setSetupProgress({ stage: "error", message: `Setup failed: ${e}`, percent: 0 }); setIsSettingUpRuntime(false); } }; const canProceed = () => { if (currentStep === 0) return tempUsername.trim().length > 0; if (currentStep === 1 && isMac) return runtimeAlreadyInstalled; return true; }; const stepTitles = ["Welcome", "Compatibility", "Preferences", "Ready"]; const navBtnStyle = (isFocused: boolean) => ({ backgroundImage: isFocused ? "url('/images/button_highlighted.png')" : "url('/images/Button_Background.png')", backgroundSize: "100% 100%", imageRendering: "pixelated" as const, }); return (
Emerald Legacy
{Array.from({ length: totalSteps }, (_, i) => (
))}

{stepTitles[currentStep]}

{currentStep === 0 && (

Let's configure your launcher

{tempUsername.trim().length === 0 && (

A username is required to continue

)}
)} {currentStep === 1 && isMac && (

{runtimeAlreadyInstalled ? "Compatibility runtime is already installed" : "Emerald needs a compatibility runtime to run on macOS" }

{runtimeAlreadyInstalled ? "✓" : "⚠"}

{runtimeAlreadyInstalled ? "Runtime Detected" : "Runtime Not Detected"}

{runtimeAlreadyInstalled ? "Ready to use — you can proceed." : "You must install the runtime before proceeding."}

{setupProgress && (

{setupProgress.stage}

{setupProgress.message}

{setupProgress.percent !== undefined && (
)}
)}
)} {currentStep === 1 && isLinux && (

Choose your preferred compatibility layer

{runners.length === 0 ? (

No compatible runners found. Please install Wine or Proton.

) : (
{runners.map((runner, idx) => ( ))}
)}

You can change this later in settings

)} {currentStep === 1 && !isMac && !isLinux && (

Everything is ready to go!

Windows Native Support

Emerald Legacy runs natively on Windows without additional requirements.

)} {currentStep === 2 && (

Choose your preferred launcher settings

You can change these later in settings

)} {currentStep === 3 && (

Emerald Launcher is now configured and ready to use!

Username {tempUsername}
{isMac && (
Runtime Ready
)} {isLinux && selectedRunner && (
Runner {runners.find(r => r.id === selectedRunner)?.name}
)}
Click Effects {enableVfx
Discord RPC {enableDiscordRPC
)}
{currentStep > 0 ? ( ) : (
)}
); }; export default SetupView;