Conversation
- Add modalStore with Map-based registry pattern (replaces 90+ field God Context) - Add uiStore with selector-based consumption - Delete ModalContext.tsx (845 lines removed) - Delete UILayoutContext.tsx (migrated to uiStore) - Add compatibility layer useModalActions() for seamless migration - Add 47 tests for modalStore (33 unit + 14 integration) - Add 43 tests for uiStore - Fix wdyr.dev.ts synchronous import issue
Resolves conflicts: - ModalContext.tsx: Keep deletion, add windowsWarning modal to modalStore instead - App.tsx: Auto-merged successfully Merge brings in: - Windows Warning Modal for Windows platform detection - Conductor Profile feature for personalized AI interactions - Daily backup system with 7-day rotation - Tab bar scroll improvements - SSH/Codex fixes - Various wizard and UI enhancements
compareVersions/parseVersion did not handle semver pre-release suffixes, causing versions like 0.15.0-rc.1 to compare as greater than 0.15.0 (the -rc suffix was silently dropped by parseInt and .1 became a fourth numeric segment). Users on a pre-release were never prompted to update to stable. Now correctly implements semver precedence: prerelease < stable. Also adds comprehensive logging to both update paths (GitHub API checker and electron-updater) so failures are visible in Maestro system logs.
Removed shadowed mockFsReaddirSync that masked the top-level vi.mock, causing getAvailableBackups tests to always see an empty directory. Fixed backup creation test to return false for the daily backup path so createDailyBackupIfNeeded actually proceeds to copy.
Resolve stats-db test conflict (comment reordering). All merged changes from main (quit-and-delete modal, platform-aware shortcuts, OpenCode model selection, notification logging, scroll fix, WAL checkpoint) are compatible with the Zustand modalStore/uiStore migration — no old context patterns introduced.
Code Review: PR #312 - Zustand State Management RefactorExecutive SummaryThis PR successfully refactors modal and UI layout state management from React Context to Zustand stores. The implementation is generally well-designed with strong type safety, comprehensive testing, and a thoughtful migration strategy. However, there are several critical issues that need to be addressed before merging. Recommendation: Request changes - fix the critical issues identified below. 1. CODE QUALITY & CRITICAL ISSUESIssue 1: Map Mutation Creates Unnecessary Re-renders (Performance)Location: Every store action creates a new Map instance, even when the modal's state hasn't changed. This defeats Zustand's shallow equality check and causes unnecessary re-renders. Fix: Add early returns for no-op operations to all modal actions ( Issue 2:
|
Address PR review feedback: - Issue #1: Add early returns in openModal, closeModal, and closeAll to skip Map recreation when state hasn't changed. Prevents unnecessary selector re-evaluations across all subscribers. - Issue #2: Add JSDoc to useModalActions() explaining that the ~40 selector subscriptions are intentionally transitional — identical to old Context behavior. Components should migrate to direct useModalStore(selectModalOpen(...)) calls for granular subscriptions. - Issue #5: Add 14 new tests covering no-op guard behavior (7 tests including render count verification), getModalActions() API surface and cross-call store sharing (3 tests), and useModalActions() reactive state and compatibility (4 tests). Issues #3 (memory leak) and #4 (type overloads) are not actionable: 47 fixed modal IDs is bounded, and ModalDataFor<T> already resolves to undefined for unmapped modals.
Code Review - PR #312: State Management Refactor (Context → Zustand)OverviewThis is an excellent refactor that addresses a critical performance bottleneck. The migration from React Context to Zustand stores is well-architected, thoroughly tested, and demonstrates strong engineering discipline. ✅ Strengths1. Architecture & Design ⭐
2. Test Coverage ⭐
3. Performance Impact
4. Code Quality
|
|
Summary
ModalContext(90+ boolean fields + setters) andUILayoutContext(30+ properties) with two Zustand stores:modalStore(registry pattern, 47 modal IDs) anduiStore(19 state properties, 24 actions)ModalProvider) from the component tree — Zustand stores are global singletons, no context nesting requireduseModalActions()) so downstream components can migrate incrementally from the olduseModalContext()APIMotivation
ModalContexthad grown to 90+ individual boolean fields with corresponding setters, meaning any modal open/close caused re-renders in every consumer.UILayoutContexthad a similar problem with 30+ layout properties. This was the single largest source of unnecessary re-renders in the renderer process.What changed
New:
modalStore(src/renderer/stores/modalStore.ts)Map<ModalId, { open, data }>replaces 90+ boolean fieldsModalDataMapinterface ensuresopenModal('settings', { tab })is compile-time checked (21 modals with associated data types)selectModalOpen('settings')subscribes to only that modal's state — no cross-modal re-rendersuseModalActions()returns the exact same API shape as the olduseModalContext(), enabling zero-diff migration for consumersNew:
uiStore(src/renderer/stores/uiStore.ts)setFoo(true)orsetFoo(prev => !prev))useUIStore.getState()works outside React (event handlers, utilities)Deleted
src/renderer/contexts/ModalContext.tsx(857 lines)src/renderer/contexts/UILayoutContext.tsx(320 lines)Modified
App.tsx— Destructures state fromuseModalActions()anduseUIStore()instead of context hooksmain.tsx— RemovedModalProviderfrom provider treeTest coverage
modalStore.test.ts— 829 lines (33 unit + 14 integration tests)uiStore.test.ts— 416 lines (comprehensive action/selector coverage)Before / After
useStatebooleansMap<ModalId, Entry>setSettingsModalOpen(true); setSettingsTab(tab)openModal('settings', { tab })ModalDataMapenforces correct typesuseModalStore.getState().openModal(...)Test plan
npm run lint) passes across all 3 tsconfig targets