polish(desktop): non-destructive Change Instance + recovery enter/exit logs

UX bug found during smoke testing: clicking Change Instance immediately
deleted the saved instance URL and showed an empty picker, with no way
back if the user changed their mind.

Fix:
- Don't clearInstanceUrl() in recovery action 'change-instance' — picker
  is now non-destructive
- Picker pre-fills the input with the current saved URL when present
- Cancel button (shown only when a saved URL exists) returns to current
  instance via idempotent setInstanceUrl re-save
- Header copy switches to 'Switch instance' / 'Cancel to stay' framing
  when a saved URL is present
- URL only overwrites on explicit Connect to a different instance

Also: add console.log enter/exit lines in enterRecoveryMode and the
clear-recovery-state action handlers, so smoke-test scripts can grep
stderr for recovery activity without UI introspection.

Spec + docs/systems/desktop.md updated.
This commit is contained in:
Jannis Braun
2026-05-03 13:16:55 +02:00
parent cff9a8e2cf
commit fff39f8d76
3 changed files with 86 additions and 3 deletions
+7 -1
View File
@@ -298,6 +298,7 @@ export function setOnQuitRequested(cb: (() => void) | null): void {
export function enterRecoveryMode(reason: { code: RecoveryReasonCode; detail: string }): void {
recoveryStore.update({ mode: 'recovery', reason });
console.log(`[recovery] entered: ${reason.code}${reason.detail}`);
if (recoveryStore.isInRecoveryMode()) {
// Already in recovery — state.reason updated for display, no re-navigation.
@@ -361,6 +362,7 @@ export function handleRecoveryAction(action: RecoveryAction): void {
// fails, did-fail-load re-enters recovery. If it stalls, boot timer fires.
recoveryStore.markRecoveryExited();
recoveryStore.update({ mode: 'normal', reason: null });
console.log('[recovery] exited (reload)');
if (!url) {
mainWindowRef?.loadFile(getPickerPath());
return;
@@ -385,9 +387,13 @@ export function handleRecoveryAction(action: RecoveryAction): void {
return;
}
case 'change-instance': {
clearInstanceUrl();
// Non-destructive: don't clear the saved URL here. The picker pre-fills it
// and offers Cancel — the URL is only overwritten when the user explicitly
// Connects to a different one. (clearInstanceUrl IPC remains for explicit
// "disconnect" operations from the web settings UI.)
recoveryStore.markRecoveryExited();
recoveryStore.update({ mode: 'normal', reason: null });
console.log('[recovery] exited (change-instance)');
mainWindowRef?.loadFile(getPickerPath());
// Ensure visible — tray clicks may happen with window hidden, and the
// recovery surface should also remain visible during the navigation.