import type { ReactNode } from 'react'; import { MotionConfig } from 'motion/react'; import { AuthProvider, useAuth } from './context/AuthContext'; import { useReducedMotion } from './hooks/use-theme'; import { NodeProvider } from './context/NodeContext'; import { LicenseProvider } from './context/LicenseContext'; import { Login } from './components/Login'; import { Setup } from './components/Setup'; import EditorLayout from './components/EditorLayout'; import { MfaChallenge } from './components/MfaChallenge'; import { DeployFeedbackProvider } from './context/DeployFeedbackContext'; import { DeployFeedbackPortal } from './components/DeployFeedbackPortal'; import { ToastContainer } from './components/ui/toast'; import { Button } from './components/ui/button'; import { AlertCircle, RefreshCw } from 'lucide-react'; /** Gates framer-motion animations on the "Reduced motion" appearance setting. * 'always' suppresses transform/layout motion app-wide; 'user' defers to the OS * prefers-reduced-motion. Sonner toasts do not use framer-motion, so they are * unaffected. Subscribes only to the motion flag to avoid re-rendering the app * tree on unrelated theme changes. */ function MotionProvider({ children }: { children: ReactNode }) { const reducedMotion = useReducedMotion(); return ( {children} ); } function AppContent() { const { appStatus, isAuthenticated, needsSetup, completeSetup, permissionsStatus, retryPermissions } = useAuth(); if (appStatus === 'loading') { return (
Loading...
); } if (needsSetup) { return ; } if (appStatus === 'mfaChallenge') { return ; } if (!isAuthenticated) { return ; } return ( {permissionsStatus === 'error' && (
Permission controls are unavailable. Changes remain disabled until access is verified.
)} {/* Portal lives inside LicenseProvider so the editor surface and its portalled overlays can read license state via useLicense(). Outer DeployFeedbackProvider is still an ancestor through App. */}
); } function App() { return ( ); } export default App;