feat: optimize IPC initialization and context management for improved app performance
- Deferred IPC-heavy initialization to occur after the first paint to prevent app freezing on Windows. - Staggered notification listener setup to avoid saturating the UV thread pool during startup. - Updated context system initialization to be lazy, ensuring local context is always ready without upfront costs. - Enhanced data fetching sequence to reduce simultaneous IPC calls, improving overall responsiveness.
This commit is contained in:
parent
1a928a68de
commit
736ec470d9
3 changed files with 33 additions and 24 deletions
|
|
@ -23,27 +23,28 @@ export const App = (): React.JSX.Element => {
|
|||
}
|
||||
}, []);
|
||||
|
||||
// Initialize context system first, then notification listeners.
|
||||
// Staggered to avoid flooding the main process with 6+ simultaneous IPC
|
||||
// calls at startup, which saturates the UV thread pool on Windows.
|
||||
// Defer IPC-heavy initialization to after the first paint.
|
||||
// On Windows, firing 6+ IPC calls simultaneously at startup saturates the
|
||||
// UV thread pool (4 threads by default), causing the app to freeze.
|
||||
// Context system init is skipped here — local context is ready by default,
|
||||
// and SSH context is initialized lazily when SSH connects (see below).
|
||||
useEffect(() => {
|
||||
let notificationCleanup: (() => void) | undefined;
|
||||
|
||||
void useStore
|
||||
.getState()
|
||||
.initializeContextSystem()
|
||||
.finally(() => {
|
||||
// Start notification listeners after context system is ready
|
||||
notificationCleanup = initializeNotificationListeners();
|
||||
});
|
||||
|
||||
return () => notificationCleanup?.();
|
||||
let cleanup: (() => void) | undefined;
|
||||
const timer = setTimeout(() => {
|
||||
cleanup = initializeNotificationListeners();
|
||||
}, 100);
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
cleanup?.();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Refresh available contexts when SSH connection state changes
|
||||
// Initialize context system lazily when SSH connection state changes.
|
||||
// Local-only users never pay the cost of IndexedDB init + context IPC calls.
|
||||
useEffect(() => {
|
||||
if (!api.ssh?.onStatus) return;
|
||||
const cleanup = api.ssh.onStatus(() => {
|
||||
void useStore.getState().initializeContextSystem();
|
||||
void useStore.getState().fetchAvailableContexts();
|
||||
});
|
||||
return cleanup;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,18 @@ export function initializeNotificationListeners(): () => void {
|
|||
cleanupFns.push(() => {
|
||||
useStore.getState().unsubscribeProvisioningProgress();
|
||||
});
|
||||
void useStore.getState().fetchTeams();
|
||||
// Stagger IPC data fetches to avoid saturating the UV thread pool on Windows.
|
||||
// Each fetch triggers file I/O in the main process; firing them all at once
|
||||
// blocks all 4 default UV threads simultaneously, freezing the app.
|
||||
void useStore
|
||||
.getState()
|
||||
.fetchTeams()
|
||||
.finally(() => {
|
||||
void useStore.getState().fetchNotifications();
|
||||
if (api.cliInstaller) {
|
||||
void useStore.getState().fetchCliStatus();
|
||||
}
|
||||
});
|
||||
const pendingSessionRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
const pendingProjectRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
let teamRefreshTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
|
@ -168,8 +179,7 @@ export function initializeNotificationListeners(): () => void {
|
|||
}
|
||||
}
|
||||
|
||||
// Fetch after listeners are attached so startup events do not get overwritten by a stale response.
|
||||
void useStore.getState().fetchNotifications();
|
||||
// fetchNotifications() is called in the staggered init chain above (after fetchTeams).
|
||||
|
||||
/**
|
||||
* Check if a session is visible in any pane (not just the focused pane's active tab).
|
||||
|
|
@ -363,10 +373,7 @@ export function initializeNotificationListeners(): () => void {
|
|||
}
|
||||
}
|
||||
|
||||
// Auto-check CLI status on startup
|
||||
if (api.cliInstaller) {
|
||||
void useStore.getState().fetchCliStatus();
|
||||
}
|
||||
// fetchCliStatus() is called in the staggered init chain above (after fetchTeams).
|
||||
|
||||
// Listen for CLI installer progress events from main process
|
||||
let cliCompletedRevertTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
|
|
|||
|
|
@ -226,11 +226,12 @@ function captureSnapshot(state: AppState, contextId: string): ContextSnapshot {
|
|||
// =============================================================================
|
||||
|
||||
export const createContextSlice: StateCreator<AppState, [], [], ContextSlice> = (set, get) => ({
|
||||
// Initial state
|
||||
// Initial state — local context is always ready, no initialization needed.
|
||||
// initializeContextSystem() is called lazily when SSH connects.
|
||||
activeContextId: 'local',
|
||||
isContextSwitching: false,
|
||||
targetContextId: null,
|
||||
contextSnapshotsReady: false,
|
||||
contextSnapshotsReady: true,
|
||||
availableContexts: [{ id: 'local', type: 'local' as const }],
|
||||
|
||||
// Initialize context system (called once on app mount)
|
||||
|
|
|
|||
Loading…
Reference in a new issue