[UX] Add save indicator for per-view API configuration changes
Problem
When a user modifies their API profile settings in SettingsView, there's no visual indication that these changes are stored in view-local state (not global state). If the user switches tabs without explicitly saving, their view-local changes may be lost on extension reload or overwritten by another tab's changes.
Evidence
Code locations:
SettingsView.tsx:136: const [isChangeDetected, setChangeDetected] = useState(false) — tracks SettingsView panel changes, but NOT view-local persistence state
ClineProvider.ts:499-515: saveViewState() persists to view-specific keys (__view_state_{id}_mode, etc.)
SettingsView.tsx:452: vscode.postMessage({ type: "upsertApiConfiguration", ... }) — writes to global state, not view-local
Reproduction:
1. Tab A (sidebar): User opens SettingsView → changes API key for OpenRouter profile
2. isChangeDetected = true (SettingsView tracks this)
3. User clicks "Save" → upsertApiConfiguration message sent → global state updated
4. BUT: viewLocalState.apiConfiguration may NOT be synced yet (depends on timing)
5. If user switches to Tab B before sync completes, Tab A's changes are still in view-local buffer
6. On extension reload, if view-specific keys aren't persisted, Tab A loses its API config
Root Cause
SettingsView.isChangeDetected tracks panel-level changes (fields modified within the SettingsView UI), but doesn't track whether those changes have been:
- Persisted to view-local state (
saveViewState())
- Synced to global state (
syncViewStateToGlobal())
There's no "unsaved" indicator that tells users their API config changes are only stored in session-scoped view-local state.
Proposed Fix
Option A: Unsaved badge in SettingsView header (recommended)
// In SettingsView.tsx, add to header area:
{isChangeDetected && !viewLocalStateSynced && (
<Tooltip content="API configuration changes are stored per-tab and will be lost on extension reload">
<Badge variant="warning" size="sm">Unsaved</Badge>
</Tooltip>
)}
// Add state tracking:
const [viewLocalStateSynced, setViewLocalStateSynced] = useState(true)
useEffect(() => {
if (isChangeDetected) {
setViewLocalStateSynced(false)
} else {
// Sync to view-local state after save completes
setViewLocalStateSynced(true)
}
}, [isChangeDetected])
Option B: Save/discard dialog on tab switch (future enhancement)
- When user switches tabs with unsaved view-local changes, show a confirmation dialog:
"You have unsaved API configuration changes in this tab. Save before switching?"
[Save] [Discard] [Cancel]
Option C: Visual distinction for view-local vs global state
- Add a subtle indicator (e.g., dot icon) next to the API profile selector when
viewLocalState.apiConfiguration !== global apiConfiguration
- Tooltip: "This tab uses a custom API configuration"
Scope
- Impact: SettingsView users in parallel mode — especially those who modify API keys/profiles per-tab
- Risk: Low — adding UI indicators doesn't change behavior, just improves awareness
- Priority: Medium (prevents data loss confusion)
Design Considerations
- Badge placement: Top-right of SettingsView header or next to the "Save" button
- Tooltip text: Clear explanation that changes are per-tab and session-scoped
- Color coding: Yellow/warning for unsaved, green/checkmark for synced
- Auto-sync option: Consider auto-saving view-local state on
upsertApiConfiguration message to reduce confusion
Related
[UX] Add save indicator for per-view API configuration changes
Problem
When a user modifies their API profile settings in SettingsView, there's no visual indication that these changes are stored in view-local state (not global state). If the user switches tabs without explicitly saving, their view-local changes may be lost on extension reload or overwritten by another tab's changes.
Evidence
Code locations:
SettingsView.tsx:136:const [isChangeDetected, setChangeDetected] = useState(false)— tracks SettingsView panel changes, but NOT view-local persistence stateClineProvider.ts:499-515:saveViewState()persists to view-specific keys (__view_state_{id}_mode, etc.)SettingsView.tsx:452:vscode.postMessage({ type: "upsertApiConfiguration", ... })— writes to global state, not view-localReproduction:
Root Cause
SettingsView.isChangeDetectedtracks panel-level changes (fields modified within the SettingsView UI), but doesn't track whether those changes have been:saveViewState())syncViewStateToGlobal())There's no "unsaved" indicator that tells users their API config changes are only stored in session-scoped view-local state.
Proposed Fix
Option A: Unsaved badge in SettingsView header (recommended)
Option B: Save/discard dialog on tab switch (future enhancement)
Option C: Visual distinction for view-local vs global state
viewLocalState.apiConfiguration !== global apiConfigurationScope
Design Considerations
upsertApiConfigurationmessage to reduce confusionRelated