import { useState, useEffect } from 'react'; import { GenerationForm } from '@/components/Generation/GenerationForm'; import { HistoryTable } from '@/components/History/HistoryTable'; import { ConnectionForm } from '@/components/ServerSettings/ConnectionForm'; import { ServerStatus } from '@/components/ServerSettings/ServerStatus'; import { UpdateStatus } from '@/components/ServerSettings/UpdateStatus'; import { ModelManagement } from '@/components/ServerSettings/ModelManagement'; import { Toaster } from '@/components/ui/toaster'; import { ProfileList } from '@/components/VoiceProfiles/ProfileList'; import { Sidebar } from '@/components/Sidebar'; import { AudioPlayer } from '@/components/AudioPlayer/AudioPlayer'; import { UpdateNotification } from '@/components/UpdateNotification'; import { isTauri, startServer, setupWindowCloseHandler } from '@/lib/tauri'; // Track if server is starting to prevent duplicate starts let serverStarting = false; function App() { const [activeTab, setActiveTab] = useState('main'); const [serverReady, setServerReady] = useState(false); // Setup window close handler and auto-start server when running in Tauri (production only) useEffect(() => { if (!isTauri()) { return; } // Setup window close handler to check setting and stop server if needed // This works in both dev and prod, but will only stop server if it was started by the app setupWindowCloseHandler().catch((error) => { console.error('Failed to setup window close handler:', error); }); // Only auto-start server in production mode // In dev mode, user runs server separately if (!import.meta.env?.PROD) { console.log('Dev mode: Skipping auto-start of server (run it separately)'); setServerReady(true); // Mark as ready so UI doesn't show loading screen // Mark that server was not started by app (so we don't try to stop it on close) // @ts-expect-error - adding property to window window.__voiceboxServerStartedByApp = false; return; } // Auto-start server in production if (serverStarting) { return; } serverStarting = true; console.log('Production mode: Starting bundled server...'); startServer(false) .then(() => { console.log('Server is ready'); setServerReady(true); // Mark that we started the server (so we know to stop it on close) // @ts-expect-error - adding property to window window.__voiceboxServerStartedByApp = true; }) .catch((error) => { console.error('Failed to auto-start server:', error); serverStarting = false; // @ts-expect-error - adding property to window window.__voiceboxServerStartedByApp = false; }); // Cleanup: stop server on actual unmount (not StrictMode remount) // Note: Window close is handled separately in Tauri Rust code return () => { // Window close event handles server shutdown based on setting serverStarting = false; }; }, []); // Show loading screen while server is starting in Tauri if (isTauri() && !serverReady) { return (

Starting server...

); } return (
{activeTab === 'settings' ? (
{isTauri() && }
) : ( // Main view: Profiles top left, Generator bottom left, History right
{/* Left Column */}
{/* Profiles - Top Left */}
{/* Generator - Bottom Left */}
{/* Right Column - History */}
)}
{/* Audio Player - always visible except on settings */} {activeTab !== 'settings' && }
); } export default App;