Files
automaker/docs/settings-api-migration.md
webdevcody 555523df38 refactor: remove kanbanCardDetailLevel from settings and UI components
- Eliminated kanbanCardDetailLevel from the SettingsService, app state, and various UI components including BoardView and BoardControls.
- Updated related hooks and API client to reflect the removal of kanbanCardDetailLevel.
- Cleaned up imports and props associated with kanbanCardDetailLevel across the codebase for improved clarity and maintainability.
2026-01-10 13:39:45 -05:00

216 lines
6.1 KiB
Markdown

# Settings API-First Migration
## Overview
This document summarizes the migration from localStorage-based settings persistence to an API-first approach. The goal was to ensure settings are consistent between Electron and web modes by using the server's `settings.json` as the single source of truth.
## Problem
Previously, settings were stored in two places:
1. **Browser localStorage** (via Zustand persist middleware) - isolated per browser/Electron instance
2. **Server files** (`{DATA_DIR}/settings.json`)
This caused settings drift between Electron and web modes since each had its own localStorage.
## Solution
All settings are now:
1. **Fetched from the server API** on app startup
2. **Synced back to the server API** when changed (with debouncing)
3. **No longer cached in localStorage** (persist middleware removed)
## Files Changed
### New Files
#### `apps/ui/src/hooks/use-settings-sync.ts`
New hook that:
- Waits for migration to complete before starting
- Subscribes to Zustand store changes
- Debounces sync to server (1000ms delay)
- Handles special case for `currentProjectId` (extracted from `currentProject` object)
### Modified Files
#### `apps/ui/src/store/app-store.ts`
- Removed `persist` middleware from Zustand store
- Added new state fields:
- `worktreePanelCollapsed: boolean`
- `lastProjectDir: string`
- `recentFolders: string[]`
- Added corresponding setter actions
#### `apps/ui/src/store/setup-store.ts`
- Removed `persist` middleware from Zustand store
#### `apps/ui/src/hooks/use-settings-migration.ts`
Complete rewrite to:
- Run in both Electron and web modes (not just Electron)
- Parse localStorage data and merge with server data
- Prefer server data, but use localStorage for missing arrays (projects, profiles, etc.)
- Export `waitForMigrationComplete()` for coordination with sync hook
- Handle `currentProjectId` to restore the currently open project
#### `apps/ui/src/App.tsx`
- Added `useSettingsSync` hook
- Wait for migration to complete before rendering router (prevents race condition)
- Show loading state while settings are being fetched
#### `apps/ui/src/routes/__root.tsx`
- Removed persist middleware hydration checks (no longer needed)
- Set `setupHydrated` to `true` by default
#### `apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx`
- Changed from localStorage to app store for `worktreePanelCollapsed`
#### `apps/ui/src/components/dialogs/file-browser-dialog.tsx`
- Changed from localStorage to app store for `recentFolders`
#### `apps/ui/src/lib/workspace-config.ts`
- Changed from localStorage to app store for `lastProjectDir`
#### `libs/types/src/settings.ts`
- Added `currentProjectId: string | null` to `GlobalSettings` interface
- Added to `DEFAULT_GLOBAL_SETTINGS`
## Settings Synced to Server
The following fields are synced to the server when they change:
```typescript
const SETTINGS_FIELDS_TO_SYNC = [
'theme',
'sidebarOpen',
'chatHistoryOpen',
'maxConcurrency',
'defaultSkipTests',
'enableDependencyBlocking',
'skipVerificationInAutoMode',
'useWorktrees',
'defaultPlanningMode',
'defaultRequirePlanApproval',
'muteDoneSound',
'enhancementModel',
'validationModel',
'phaseModels',
'enabledCursorModels',
'cursorDefaultModel',
'autoLoadClaudeMd',
'keyboardShortcuts',
'mcpServers',
'promptCustomization',
'projects',
'trashedProjects',
'currentProjectId',
'projectHistory',
'projectHistoryIndex',
'lastSelectedSessionByProject',
'worktreePanelCollapsed',
'lastProjectDir',
'recentFolders',
];
```
## Data Flow
### On App Startup
```
1. App mounts
└── Shows "Loading settings..." screen
2. useSettingsMigration runs
├── Waits for API key initialization
├── Reads localStorage data (if any)
├── Fetches settings from server API
├── Merges data (prefers server, uses localStorage for missing arrays)
├── Hydrates Zustand store (including currentProject from currentProjectId)
├── Syncs merged data back to server (if needed)
└── Signals completion via waitForMigrationComplete()
3. useSettingsSync initializes
├── Waits for migration to complete
├── Stores initial state hash
└── Starts subscribing to store changes
4. Router renders
├── Root layout reads currentProject (now properly set)
└── Navigates to /board if project was open
```
### On Settings Change
```
1. User changes a setting
└── Zustand store updates
2. useSettingsSync detects change
├── Debounces for 1000ms
└── Syncs to server via API
3. Server writes to settings.json
```
## Migration Logic
When merging localStorage with server data:
1. **Server has data** → Use server data as base
2. **Server missing arrays** (projects, mcpServers, etc.) → Use localStorage arrays
3. **Server missing objects** (lastSelectedSessionByProject) → Use localStorage objects
4. **Simple values** (lastProjectDir, currentProjectId) → Use localStorage if server is empty
## Exported Functions
### `useSettingsMigration()`
Hook that handles initial settings hydration. Returns:
- `checked: boolean` - Whether hydration is complete
- `migrated: boolean` - Whether data was migrated from localStorage
- `error: string | null` - Error message if failed
### `useSettingsSync()`
Hook that handles ongoing sync. Returns:
- `loaded: boolean` - Whether sync is initialized
- `syncing: boolean` - Whether currently syncing
- `error: string | null` - Error message if failed
### `waitForMigrationComplete()`
Returns a Promise that resolves when migration is complete. Used for coordination.
### `forceSyncSettingsToServer()`
Manually triggers an immediate sync to server.
### `refreshSettingsFromServer()`
Fetches latest settings from server and updates store.
## Testing
All 1001 server tests pass after these changes.
## Notes
- **sessionStorage** is still used for session-specific state (splash screen shown, auto-mode state)
- **Terminal layouts** are stored in the app store per-project (not synced to API - considered transient UI state)
- The server's `{DATA_DIR}/settings.json` is the single source of truth