-
Notifications
You must be signed in to change notification settings - Fork 536
Description
Problem
When multiple tabs are opened in the desktop app, there is noticeable lag when closing tabs. This creates a poor user experience, especially when users have many tabs open and want to close several of them.
Investigation Findings
After analyzing the tab management code, I identified the following root causes:
1. Middleware Cascade Causing Multiple Re-renders (Critical - ~80% of the issue)
The tab store uses 3 stacked middleware layers: restoreMiddleware → lifecycleMiddleware → navigationMiddleware
When close() is called:
- Initial
set()inbasic.ts:137-141triggers ALL middleware restoreMiddlewaredetects closed tabs, calls anotherset()atrestore.ts:82- This second
set()triggers the ENTIRE middleware chain again navigationMiddlewaremay call a thirdset()if flags changed (navigation.ts:263)
Impact: Closing 5 tabs results in ~15 state updates → 15 React re-renders
2. O(n²) Duplicate Computation (High)
Both restoreMiddleware and lifecycleMiddleware independently compute closed tabs using Array.filter + Array.some:
// restore.ts:72-75
const closedTabs = prevTabs.filter(
(prevTab) => !nextTabs.some((nextTab) => nextTab.slotId === prevTab.slotId)
);
// lifecycle.ts:70-72
const closedTabs = prevTabs.filter(
(prevTab) => !nextTabs.some((nextTab) => isSameTab(prevTab, nextTab))
);This O(n²) computation happens on EVERY set() call, including cascading ones.
3. Heavy onClose Handlers (~10-13% of the issue)
Session tabs trigger triggerEnhancementOnClose which performs:
- TinyBase index queries
- JSON parsing of transcript words
- AI task generation
These operations run synchronously in the middleware callback, blocking the UI thread.
4. Reorder.Group Animations (~2% of the issue)
The motion/react Reorder.Group component triggers layout animations on every tab array change, adding to the perceived lag.
Relevant Files
apps/desktop/src/store/zustand/tabs/lifecycle.tsapps/desktop/src/store/zustand/tabs/restore.tsapps/desktop/src/store/zustand/tabs/navigation.tsapps/desktop/src/store/zustand/tabs/basic.tsapps/desktop/src/components/main/body/sessions/index.tsxapps/desktop/src/components/main/body/index.tsx
Recommended Fixes (Prioritized by Impact)
Priority 1: Batch Middleware State Updates (~87% improvement)
Combine all middleware into a single pass that:
- Computes closed tabs ONCE
- Applies all state changes (closedTabs, navigation flags) in a single
set()call - Calls lifecycle handlers AFTER all state is settled
Priority 2: Defer Heavy onClose Handlers (~10-13% improvement)
Use queueMicrotask or setTimeout to defer heavy operations:
closedTabs.forEach((tab) => {
queueMicrotask(() => {
nextState.onClose?.(tab);
});
});Priority 3: Optimize Tab Comparison (~3% improvement)
Use a Set for O(n) lookup instead of O(n²):
const nextTabSlotIds = new Set(nextTabs.map(t => t.slotId));
const closedTabs = prevTabs.filter(prevTab => !nextTabSlotIds.has(prevTab.slotId));Priority 4: Reduce Reorder Animations (~2% improvement)
Reduce transition duration or disable animations during rapid close operations.
Acceptance Criteria
- Tab closing feels responsive even with 10+ tabs open
- No visible lag when closing multiple tabs in quick succession
- Existing functionality (restore closed tabs, navigation history) continues to work
- No data loss when closing session tabs
Labels
product/desktop, area/frontend, performance
Metadata
Metadata
Assignees
Labels
Type
Projects
Status