diff --git a/frontend/src/components/EditorLayout.tsx b/frontend/src/components/EditorLayout.tsx index 1f77e226..89fedf83 100644 --- a/frontend/src/components/EditorLayout.tsx +++ b/frontend/src/components/EditorLayout.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useMemo, useCallback, Suspense } from 'react'; +import { useState, useEffect, useRef, useMemo, useCallback, lazy, Suspense } from 'react'; type Theme = 'light' | 'dark' | 'auto'; import { Editor } from '@/lib/monacoLoader'; @@ -8,7 +8,7 @@ import ErrorBoundary from './ErrorBoundary'; import HomeDashboard from './HomeDashboard'; import type { NotificationItem } from './dashboard/types'; import BashExecModal from './BashExecModal'; -import HostConsole from './HostConsole'; +import { Skeleton } from '@/components/ui/skeleton'; import { AdmiralGate } from './AdmiralGate'; import { CapabilityGate } from './CapabilityGate'; import ResourcesView from './ResourcesView'; @@ -46,12 +46,48 @@ import { LogViewer } from './LogViewer'; import StructuredLogViewer from './StructuredLogViewer'; import StackAnatomyPanel from './StackAnatomyPanel'; import { Sparkline } from './ui/sparkline'; -import { GlobalObservabilityView } from './GlobalObservabilityView'; -import { FleetView } from './FleetView'; -import { AuditLogView } from './AuditLogView'; -import ScheduledOperationsView, { type ScheduleTaskPrefill } from './ScheduledOperationsView'; -import AutoUpdateReadinessView from './AutoUpdateReadinessView'; -import { SecurityHistoryView } from './SecurityHistoryView'; +import type { ScheduleTaskPrefill } from './ScheduledOperationsView'; + +// Paid-tier views and the security-history overlay are loaded on demand. +// Their internal PaidGate / AdmiralGate / CapabilityGate wrappers render +// the upsell or capability-missing card with blurred children rather than +// short-circuiting, so a tier-locked or capability-missing operator +// opening one of these tabs still triggers the chunk fetch to render the +// blurred preview. The gate-short-circuit fix lives in a follow-up +// branch. What this lazy split closes is the much larger initial-bundle +// leak: every Community user used to download the full FleetView, +// AuditLogView, etc. on first page load even if they never clicked those +// tabs. After this change, the chunks fetch only on tab open. +// +// GlobalObservabilityView is a free-tier feature with no internal gate; +// it is split here purely for the bundle-size win, not for IP protection. +const HostConsole = lazy(() => import('./HostConsole')); +const GlobalObservabilityView = lazy(() => + import('./GlobalObservabilityView').then(m => ({ default: m.GlobalObservabilityView })), +); +const FleetView = lazy(() => + import('./FleetView').then(m => ({ default: m.FleetView })), +); +const AuditLogView = lazy(() => + import('./AuditLogView').then(m => ({ default: m.AuditLogView })), +); +const SecurityHistoryView = lazy(() => + import('./SecurityHistoryView').then(m => ({ default: m.SecurityHistoryView })), +); +const ScheduledOperationsView = lazy(() => import('./ScheduledOperationsView')); +const AutoUpdateReadinessView = lazy(() => import('./AutoUpdateReadinessView')); + +// Sized for the main workspace area (flex-1 with p-6 padding). Visible +// only during the brief window between an unlocked view's chunk request +// and its first render. +function ViewSkeleton() { + return ( +