/** * Root Application Component * Handles routing and global providers */ import { Routes, Route, useNavigate, useLocation } from 'react-router-dom'; import { Component, useEffect } from 'react'; import type { ErrorInfo, ReactNode } from 'react'; import { Toaster } from 'sonner'; import i18n from './i18n'; import { MainLayout } from './components/layout/MainLayout'; import { TooltipProvider } from '@/components/ui/tooltip'; import { Dashboard } from './pages/Dashboard'; import { Chat } from './pages/Chat'; import { Channels } from './pages/Channels'; import { Skills } from './pages/Skills'; import { Cron } from './pages/Cron'; import { Settings } from './pages/Settings'; import { Setup } from './pages/Setup'; import { useSettingsStore } from './stores/settings'; import { useGatewayStore } from './stores/gateway'; /** * Error Boundary to catch and display React rendering errors */ class ErrorBoundary extends Component< { children: ReactNode }, { hasError: boolean; error: Error | null } > { constructor(props: { children: ReactNode }) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error: Error) { return { hasError: true, error }; } componentDidCatch(error: Error, info: ErrorInfo) { console.error('React Error Boundary caught error:', error, info); } render() { if (this.state.hasError) { return (

Something went wrong

            {this.state.error?.message}
            {'\n\n'}
            {this.state.error?.stack}
          
); } return this.props.children; } } function App() { const navigate = useNavigate(); const location = useLocation(); const theme = useSettingsStore((state) => state.theme); const language = useSettingsStore((state) => state.language); const setupComplete = useSettingsStore((state) => state.setupComplete); const initGateway = useGatewayStore((state) => state.init); // Sync i18n language with persisted settings on mount useEffect(() => { if (language && language !== i18n.language) { i18n.changeLanguage(language); } }, [language]); // Initialize Gateway connection on mount useEffect(() => { initGateway(); }, [initGateway]); // Redirect to setup wizard if not complete useEffect(() => { if (!setupComplete && !location.pathname.startsWith('/setup')) { navigate('/setup'); } }, [setupComplete, location.pathname, navigate]); // Listen for navigation events from main process useEffect(() => { const handleNavigate = (...args: unknown[]) => { const path = args[0]; if (typeof path === 'string') { navigate(path); } }; const unsubscribe = window.electron.ipcRenderer.on('navigate', handleNavigate); return () => { if (typeof unsubscribe === 'function') { unsubscribe(); } }; }, [navigate]); // Apply theme useEffect(() => { const root = window.document.documentElement; root.classList.remove('light', 'dark'); if (theme === 'system') { const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; root.classList.add(systemTheme); } else { root.classList.add(theme); } }, [theme]); return ( {/* Setup wizard (shown on first launch) */} } /> {/* Main application routes */} }> } /> } /> } /> } /> } /> } /> {/* Global toast notifications */} ); } export default App;