import { useState, useEffect } from 'react'; import { AppShell, Loader, Center, Stack, Text } from '@mantine/core'; import { DownloadScreen } from '@/components/screens/Download'; import { LaunchScreen } from '@/components/screens/Launch'; import { InterfaceScreen } from '@/components/screens/Interface'; import { WelcomeScreen } from '@/components/screens/Welcome'; import { UpdateAvailableModal } from '@/components/UpdateAvailableModal'; import { SettingsModal } from '@/components/settings/SettingsModal'; import { EjectConfirmModal } from '@/components/EjectConfirmModal'; import { ScreenTransition } from '@/components/ScreenTransition'; import { TitleBar } from '@/components/TitleBar'; import { ErrorBoundary } from '@/components/ErrorBoundary'; import { useUpdateChecker } from '@/hooks/useUpdateChecker'; import { useKoboldVersions } from '@/hooks/useKoboldVersions'; import { useModalStore } from '@/stores/modal'; import { safeExecute } from '@/utils/logger'; import { TITLEBAR_HEIGHT } from '@/constants'; import type { DownloadItem } from '@/types/electron'; import type { InterfaceTab, FrontendPreference, Screen } from '@/types'; export const App = () => { const [currentScreen, setCurrentScreen] = useState(null); const [hasInitialized, setHasInitialized] = useState(false); const [activeInterfaceTab, setActiveInterfaceTab] = useState('terminal'); const [frontendPreference, setFrontendPreference] = useState('koboldcpp'); const { modals, setModalOpen } = useModalStore(); const { updateInfo: binaryUpdateInfo, showUpdateModal, checkForUpdates, dismissUpdate, } = useUpdateChecker(); const { handleDownload: sharedHandleDownload, downloading, downloadProgress, } = useKoboldVersions(); const setCurrentScreenWithTransition = (screen: Screen) => { setCurrentScreen(screen); }; useEffect(() => { const checkInstallation = async () => { await safeExecute(async () => { const [currentVersion, hasSeenWelcome, preference] = await Promise.all([ window.electronAPI.kobold.getCurrentVersion(), window.electronAPI.config.get('hasSeenWelcome') as Promise, window.electronAPI.config.get( 'frontendPreference' ) as Promise, ]); setFrontendPreference(preference || 'koboldcpp'); if (!hasSeenWelcome) { setCurrentScreenWithTransition('welcome'); } else if (currentVersion) { setCurrentScreenWithTransition('launch'); } else { setCurrentScreenWithTransition('download'); } if (currentVersion) { setTimeout(() => { checkForUpdates(); }, 2000); } }, 'Error checking installation:'); setHasInitialized(true); }; checkInstallation(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleBinaryUpdate = async (download: DownloadItem) => { await safeExecute(async () => { const success = await sharedHandleDownload({ item: download, isUpdate: true, wasCurrentBinary: true, }); if (success) { dismissUpdate(); } }, 'Failed to update binary:'); }; const handleDownloadComplete = async () => { await safeExecute(async () => { await window.electronAPI.kobold.getCurrentVersion(); }, 'Error refreshing versions after download:'); setTimeout(() => { setCurrentScreenWithTransition('launch'); }, 100); }; const handleLaunch = () => { setActiveInterfaceTab('terminal'); setCurrentScreenWithTransition('interface'); }; const handleBackToLaunch = () => { setCurrentScreenWithTransition('launch'); }; const handleEject = async () => { const skipEjectConfirmation = await window.electronAPI.config.get( 'skipEjectConfirmation' ); if (skipEjectConfirmation) { performEject(); } else { setModalOpen('ejectConfirm', true); } }; const performEject = () => { window.electronAPI.kobold.stopKoboldCpp(); handleBackToLaunch(); }; const handleEjectConfirm = async (skipConfirmation: boolean) => { if (skipConfirmation) { await window.electronAPI.config.set('skipEjectConfirmation', true); } performEject(); }; const handleWelcomeComplete = async () => { await window.electronAPI.config.set('hasSeenWelcome', true); const versions = await window.electronAPI.kobold.getInstalledVersions(); if (versions.length > 0) { setCurrentScreenWithTransition('launch'); } else { setCurrentScreenWithTransition('download'); } }; return ( setModalOpen('settings', true)} frontendPreference={frontendPreference} /> {currentScreen === null ? (
Loading...
) : ( <> )}
{ setModalOpen('settings', false); const preference = await safeExecute( () => window.electronAPI.config.get( 'frontendPreference' ) as Promise, 'Failed to load frontend preference:' ); setFrontendPreference(preference || 'koboldcpp'); }} currentScreen={currentScreen || undefined} /> setModalOpen('ejectConfirm', false)} onConfirm={handleEjectConfirm} />
); };