Files
sencho/frontend/src/App.tsx
T
Anso b5810a9b55 feat: add Reduced motion setting and polish chrome, files, and stack-detail (#1501)
A batch of UI/UX polish:

- New independent "Reduced motion" appearance setting (separate from Reduced
  effects). Drives framer-motion via MotionConfig and clamps CSS transitions via
  data-motion on <html>; toasts are unaffected. Defaults off (OS preference
  still honored).
- Stack-detail Files tab: rename "Files & Volumes" to "Files", add a persisted
  word-wrap toggle to the file viewer (default on), and add a fullscreen toggle
  that collapses the Command Center + Logs column so the editor fills the width.
- Create Stack > From Git: remove the nested scroll clamp so the deploy toggle
  and footer are reachable.
- Fleet: full-width tab band with icon-only Refresh / Export Dossier, icon-only
  Check-for-updates / Add-node on the Overview toolbar, theme-aware empty-state
  headings (calm drops the italic), and fix the Actions card body overlapping
  the action-row divider.
- Snapshots: restyle Restore and Restore all to the ghost button design used by
  View / Preview / Download, and right-align the per-stack Restore.
- Settings sidebar: App Store gradient active style and standard font size.
- Compose Doctor: dismiss the high-risk banner (and clear the tab dot) until the
  findings change, via a shared fingerprint-keyed hook.
- Stack-detail Storage: link the "no recent fleet snapshot" warning to the Fleet
  Snapshots tab (FleetView tabs are now controlled to support the deep link).
2026-06-28 06:18:02 -04:00

79 lines
2.4 KiB
TypeScript

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';
/** 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 (
<MotionConfig reducedMotion={reducedMotion ? 'always' : 'user'}>
{children}
</MotionConfig>
);
}
function AppContent() {
const { appStatus, isAuthenticated, needsSetup, completeSetup } = useAuth();
if (appStatus === 'loading') {
return (
<div className="min-h-screen flex items-center justify-center app-canvas">
<div className="text-muted-foreground">Loading...</div>
</div>
);
}
if (needsSetup) {
return <Setup onComplete={completeSetup} />;
}
if (appStatus === 'mfaChallenge') {
return <MfaChallenge />;
}
if (!isAuthenticated) {
return <Login />;
}
return (
<MotionProvider>
<NodeProvider>
<LicenseProvider>
<EditorLayout />
{/* 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. */}
<DeployFeedbackPortal />
</LicenseProvider>
</NodeProvider>
</MotionProvider>
);
}
function App() {
return (
<AuthProvider>
<DeployFeedbackProvider>
<AppContent />
</DeployFeedbackProvider>
<ToastContainer />
</AuthProvider>
);
}
export default App;