perf: reduce duplicate app initialization and heavy view imports

Deduplicates concurrent app initialization in the config store
Lazy-loads VS Code-only app surfaces
Avoids broad view barrel imports for better chunk isolation
This commit is contained in:
Bohdan Triapitsyn
2026-05-06 02:43:13 +03:00
parent 170d334921
commit dbc06dd6fa
7 changed files with 71 additions and 79 deletions
+17 -32
View File
@@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import { MainLayout } from '@/components/layout/MainLayout'; import { MainLayout } from '@/components/layout/MainLayout';
import { VSCodeLayout } from '@/components/layout/VSCodeLayout'; import { ChatView } from '@/components/views/ChatView';
import { AgentManagerView } from '@/components/views/agent-manager';
import { ChatView } from '@/components/views';
import { FireworksProvider } from '@/contexts/FireworksContext'; import { FireworksProvider } from '@/contexts/FireworksContext';
import { Toaster } from '@/components/ui/sonner'; import { Toaster } from '@/components/ui/sonner';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -65,6 +63,14 @@ const OnboardingScreen = lazyWithChunkRecovery(() =>
import('@/components/onboarding/OnboardingScreen').then((m) => ({ default: m.OnboardingScreen })), import('@/components/onboarding/OnboardingScreen').then((m) => ({ default: m.OnboardingScreen })),
); );
const VSCodeLayoutLazy = lazyWithChunkRecovery(() =>
import('@/components/layout/VSCodeLayout').then((m) => ({ default: m.VSCodeLayout })),
);
const AgentManagerViewLazy = lazyWithChunkRecovery(() =>
import('@/components/views/agent-manager').then((m) => ({ default: m.AgentManagerView })),
);
const AboutDialogWrapper: React.FC = () => { const AboutDialogWrapper: React.FC = () => {
const isAboutDialogOpen = useUIStore((s) => s.isAboutDialogOpen); const isAboutDialogOpen = useUIStore((s) => s.isAboutDialogOpen);
const setAboutDialogOpen = useUIStore((s) => s.setAboutDialogOpen); const setAboutDialogOpen = useUIStore((s) => s.setAboutDialogOpen);
@@ -236,7 +242,6 @@ function App({ apis }: AppProps) {
: null; : null;
}); });
const appReadyDispatchedRef = React.useRef(false); const appReadyDispatchedRef = React.useRef(false);
const initializationInFlightRef = React.useRef(false);
const embeddedSessionChat = React.useMemo<EmbeddedSessionChatConfig | null>(() => readEmbeddedSessionChatConfig(), []); const embeddedSessionChat = React.useMemo<EmbeddedSessionChatConfig | null>(() => readEmbeddedSessionChatConfig(), []);
const embeddedBackgroundWorkEnabled = !embeddedSessionChat || isEmbeddedVisible; const embeddedBackgroundWorkEnabled = !embeddedSessionChat || isEmbeddedVisible;
const isMcpOAuthCallback = React.useMemo(() => isMcpOAuthCallbackPath(), []); const isMcpOAuthCallback = React.useMemo(() => isMcpOAuthCallbackPath(), []);
@@ -383,24 +388,12 @@ function App({ apis }: AppProps) {
}, [setPlanModeEnabled]); }, [setPlanModeEnabled]);
React.useEffect(() => { React.useEffect(() => {
const init = async () => {
// VS Code runtime bootstraps config + sessions after the managed OpenCode instance reports "connected". // VS Code runtime bootstraps config + sessions after the managed OpenCode instance reports "connected".
// Doing the default initialization here can race with startup and lead to one-shot failures. // Doing the default initialization here can race with startup and lead to one-shot failures.
if (isVSCodeRuntime) { if (isVSCodeRuntime) {
return; return;
} }
if (initializationInFlightRef.current) { void initializeApp();
return;
}
initializationInFlightRef.current = true;
try {
await initializeApp();
} finally {
initializationInFlightRef.current = false;
}
};
init();
}, [initializeApp, isVSCodeRuntime]); }, [initializeApp, isVSCodeRuntime]);
React.useEffect(() => { React.useEffect(() => {
@@ -423,18 +416,8 @@ function App({ apis }: AppProps) {
setInitRetryExhausted(false); setInitRetryExhausted(false);
return; return;
} }
if (initializationInFlightRef.current) {
retryTimer = setTimeout(retryInitialization, BASE_DELAY_MS);
return;
}
retryCount += 1; retryCount += 1;
initializationInFlightRef.current = true;
try {
await state.initializeApp(); await state.initializeApp();
} finally {
initializationInFlightRef.current = false;
}
const next = useConfigStore.getState(); const next = useConfigStore.getState();
if (!active) return; if (!active) return;
@@ -760,15 +743,13 @@ function App({ apis }: AppProps) {
}, []); }, []);
const handleManualInitRetry = React.useCallback(async () => { const handleManualInitRetry = React.useCallback(async () => {
if (manualInitRetrying || initializationInFlightRef.current) return; if (manualInitRetrying) return;
setInitRetryExhausted(false); setInitRetryExhausted(false);
setManualInitRetrying(true); setManualInitRetrying(true);
initializationInFlightRef.current = true;
try { try {
await useConfigStore.getState().initializeApp(); await useConfigStore.getState().initializeApp();
} finally { } finally {
initializationInFlightRef.current = false;
setManualInitRetrying(false); setManualInitRetrying(false);
} }
@@ -882,7 +863,9 @@ function App({ apis }: AppProps) {
<TooltipProvider delayDuration={300} skipDelayDuration={150}> <TooltipProvider delayDuration={300} skipDelayDuration={150}>
<div className="h-full text-foreground bg-background"> <div className="h-full text-foreground bg-background">
<SyncAppEffects embeddedBackgroundWorkEnabled={embeddedBackgroundWorkEnabled} /> <SyncAppEffects embeddedBackgroundWorkEnabled={embeddedBackgroundWorkEnabled} />
<AgentManagerView /> <React.Suspense fallback={<div className="h-full" />}>
<AgentManagerViewLazy />
</React.Suspense>
<Toaster /> <Toaster />
</div> </div>
</TooltipProvider> </TooltipProvider>
@@ -900,7 +883,9 @@ function App({ apis }: AppProps) {
<TooltipProvider delayDuration={300} skipDelayDuration={150}> <TooltipProvider delayDuration={300} skipDelayDuration={150}>
<div className="h-full text-foreground bg-background"> <div className="h-full text-foreground bg-background">
<SyncAppEffects embeddedBackgroundWorkEnabled={embeddedBackgroundWorkEnabled} /> <SyncAppEffects embeddedBackgroundWorkEnabled={embeddedBackgroundWorkEnabled} />
<VSCodeLayout /> <React.Suspense fallback={<div className="h-full" />}>
<VSCodeLayoutLazy />
</React.Suspense>
<Toaster /> <Toaster />
</div> </div>
</TooltipProvider> </TooltipProvider>
@@ -4,7 +4,9 @@ import { RiArrowLeftRightLine, RiChat4Line, RiCloseLine, RiDonutChartFill, RiFil
import { FileTypeIcon } from '@/components/icons/FileTypeIcon'; import { FileTypeIcon } from '@/components/icons/FileTypeIcon';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip'; import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip';
import { DiffView, FilesView, PlanView } from '@/components/views'; import { DiffView } from '@/components/views/DiffView';
import { FilesView } from '@/components/views/FilesView';
import { PlanView } from '@/components/views/PlanView';
import { useThemeSystem } from '@/contexts/useThemeSystem'; import { useThemeSystem } from '@/contexts/useThemeSystem';
import { openExternalUrl } from '@/lib/url'; import { openExternalUrl } from '@/lib/url';
import { copyTextToClipboard } from '@/lib/clipboard'; import { copyTextToClipboard } from '@/lib/clipboard';
@@ -25,7 +25,7 @@ import { cn } from '@/lib/utils';
import { isDesktopShell } from '@/lib/desktop'; import { isDesktopShell } from '@/lib/desktop';
import { lazyWithChunkRecovery } from '@/lib/chunkLoadRecovery'; import { lazyWithChunkRecovery } from '@/lib/chunkLoadRecovery';
import { ChatView } from '@/components/views'; import { ChatView } from '@/components/views/ChatView';
// Heavy views loaded on-demand to reduce initial bundle parse time. // Heavy views loaded on-demand to reduce initial bundle parse time.
const PlanView = lazyWithChunkRecovery(() => import('@/components/views/PlanView').then(m => ({ default: m.PlanView }))); const PlanView = lazyWithChunkRecovery(() => import('@/components/views/PlanView').then(m => ({ default: m.PlanView })));
@@ -3,7 +3,7 @@ import { RiBookletLine, RiFolder3Line, RiGitBranchLine } from '@remixicon/react'
import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip'; import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip';
import { ProjectNotesTodoPanel } from '@/components/session/ProjectNotesTodoPanel'; import { ProjectNotesTodoPanel } from '@/components/session/ProjectNotesTodoPanel';
import { GitView } from '@/components/views'; import { GitView } from '@/components/views/GitView';
import { useGitStore } from '@/stores/useGitStore'; import { useGitStore } from '@/stores/useGitStore';
import { useProjectsStore } from '@/stores/useProjectsStore'; import { useProjectsStore } from '@/stores/useProjectsStore';
import { useDirectoryStore } from '@/stores/useDirectoryStore'; import { useDirectoryStore } from '@/stores/useDirectoryStore';
@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { ErrorBoundary } from '../ui/ErrorBoundary'; import { ErrorBoundary } from '../ui/ErrorBoundary';
import { SessionSidebar } from '@/components/session/SessionSidebar'; import { SessionSidebar } from '@/components/session/SessionSidebar';
import { ChatView } from '@/components/views'; import { ChatView } from '@/components/views/ChatView';
import { useSessionUIStore } from '@/sync/session-ui-store'; import { useSessionUIStore } from '@/sync/session-ui-store';
import { useViewportStore } from '@/sync/viewport-store'; import { useViewportStore } from '@/sync/viewport-store';
import { useSessions, useDirectorySync, useSessionMessages, useSessionMessagesResolved } from '@/sync/sync-context'; import { useSessions, useDirectorySync, useSessionMessages, useSessionMessagesResolved } from '@/sync/sync-context';
+1 -8
View File
@@ -1,9 +1,2 @@
/** Entry-level views only. Avoid barrel re-exports that pull heavy modules into the main chunk. */
export { ChatView } from './ChatView'; export { ChatView } from './ChatView';
export { PlanView } from './PlanView';
export { GitView } from './GitView';
export { DiffView, useDiffFileCount } from './DiffView';
export { TerminalView } from './TerminalView';
export { FilesView } from './FilesView';
export { SettingsView } from './SettingsView';
export { SettingsWindow } from './SettingsWindow';
export { MultiRunWindow } from './MultiRunWindow';
+12
View File
@@ -583,6 +583,7 @@ declare global {
// In-flight dedup: prevent concurrent duplicate loadProviders/loadAgents calls for the same directory // In-flight dedup: prevent concurrent duplicate loadProviders/loadAgents calls for the same directory
const _inFlightProviders = new Map<string, Promise<void>>(); const _inFlightProviders = new Map<string, Promise<void>>();
const _inFlightAgents = new Map<string, Promise<boolean>>(); const _inFlightAgents = new Map<string, Promise<boolean>>();
let _initializeAppInFlight: Promise<void> | null = null;
export const useConfigStore = create<ConfigStore>()( export const useConfigStore = create<ConfigStore>()(
devtools( devtools(
@@ -1965,6 +1966,11 @@ export const useConfigStore = create<ConfigStore>()(
}, },
initializeApp: async () => { initializeApp: async () => {
if (_initializeAppInFlight) {
return _initializeAppInFlight;
}
const run = (async () => {
try { try {
const debug = streamDebugEnabled(); const debug = streamDebugEnabled();
if (debug) console.log("Starting app initialization..."); if (debug) console.log("Starting app initialization...");
@@ -2002,6 +2008,12 @@ export const useConfigStore = create<ConfigStore>()(
lastDisconnectReason: 'init_error', lastDisconnectReason: 'init_error',
}); });
} }
})().finally(() => {
_initializeAppInFlight = null;
});
_initializeAppInFlight = run;
return run;
}, },
getCurrentProvider: () => { getCurrentProvider: () => {