Fixed an issue where clicking the language switcher would cause a brief flash
and fail to persist the language change. The root cause was a dependency cycle
in useSettingsForm where readPersistedLanguage depended on i18n.language,
causing the initialization effect to re-run and reset state whenever the
language changed.
Changed the dependency from [i18n.language] to [i18n] since the i18n object
itself is stable and doesn't change when the language changes, while the
function can still access the current language value via closure.
Before optimization:
- McpPanel.tsx: 298 lines (component + business logic)
After optimization:
- McpPanel.tsx: 234 lines (-21%, UI focused)
- useMcpActions.ts: 137 lines (business logic)
Benefits:
✅ Separation of concerns: UI vs business logic
✅ Reusability: MCP operations can be used in other components
✅ Testability: business logic can be tested independently
✅ Consistency: follows same pattern as useProviderActions
✅ Optimistic updates: toggle enabled status with rollback on error
✅ Unified error handling: all MCP errors use toast notifications
Technical details:
- Extract reload, toggleEnabled, saveServer, deleteServer
- Implement optimistic UI updates for toggle
- Centralize error handling and toast messages
- Remove duplicate error handling code from component
Before optimization:
- useSettings.ts: 516 lines (single monolithic hook)
After optimization:
- useSettingsForm.ts: 158 lines (form state management)
- useDirectorySettings.ts: 297 lines (directory management)
- useSettingsMetadata.ts: 95 lines (metadata management)
- useSettings.ts: 215 lines (composition layer)
- Total: 765 lines (+249 lines, but with clear separation of concerns)
Benefits:
✅ Single Responsibility Principle: each hook focuses on one domain
✅ Testability: independent hooks are easier to unit test
✅ Reusability: specialized hooks can be reused in other components
✅ Maintainability: reduced cognitive load per file
✅ Zero breaking changes: SettingsDialog auto-adapted to new interface
Technical details:
- useSettingsForm: pure form state + language sync
- useDirectorySettings: directory selection/reset + default value computation
- useSettingsMetadata: config path + portable mode + restart flag
- useSettings: composition layer + save logic + reset logic
- Remove unused useDarkMode hook (now using shadcn theme-provider)
- Clean up MCP components (remove redundant code)
- Add restart API to settings
- Minor type improvements
- refactor(settings): remove the VS Code auto-sync toggle from Settings UI
- feat(provider-list): enable auto-sync after "Apply to VS Code"; disable after "Remove"
- chore(prettier): run Prettier on changed files
- verify: typecheck and renderer build pass
- Files
- added: src/hooks/useVSCodeAutoSync.ts
- modified: src/App.tsx
- modified: src/components/ProviderList.tsx
- modified: src/components/SettingsModal.tsx
- Notes
- Auto-sync now defaults to enabled for new users (stored in localStorage; existing saved state is respected).
- No settings toggle is shown; manual Apply/Remove in the list still works as before.
- refactor(ProviderList): use shared base_url helpers
- refactor(App): reuse shared base_url helpers for VS Code sync
- fix(auto-sync): global shared VS Code auto-apply state (localStorage + event broadcast)
- feat(tray): auto-apply to VS Code on Codex provider-switched when enabled
- behavior: manual Apply enables auto-sync; manual Remove disables; official providers clear managed keys only
- chore(typecheck): pass pnpm typecheck
- Add useDarkMode hook for managing theme state and persistence
- Integrate dark mode toggle button in app header
- Update all components with dark variant styles using Tailwind v4
- Create centralized style utilities for consistent theming
- Support system color scheme preference as fallback
- Store user preference in localStorage for persistence