Modes: a mode that no longer matches the features reads "Custom (based on <mode>)" and offers "Re-apply <mode>"

A mode is a starting point, never a lock and never applied again on its own. modeStatus() (runtime)
compares the flags with the mode's preset: custom, what re-applying turns on and off, and which of
those the preset gained in an update since it was applied. mode_set keeps the preset of that moment
(modes.json "preset", flag names only, bounded; forgotten when the mode changes). Settings, the
first-run screen and the app menu's Mode row show Custom; Re-apply lists what it turns on and off
before it writes anything; a preset that grew is mentioned once on the mode row (Review), never applied.
Tests: exact match, changed by the owner, preset grew, choice made before the preset was kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CRTkafa 2026-10-01 03:33:41 +07:00
parent b75246bef8
commit 26658e14ae
9 changed files with 288 additions and 27 deletions

View file

@ -794,12 +794,14 @@ async fn mode_get(webview: Webview, app: AppHandle) -> Result<ModeState, String>
Ok(page(&app)?.modes.clone())
}
/// Remembers the chosen mode (or none) and that the first-run screen was answered. The preset's
/// flags are the shell's to apply (`features_set`); this only records the choice.
/// Remembers the chosen mode (or none), that the first-run screen was answered, and what the
/// mode's preset was when it was applied (`preset`, flag names to on/off: data for the shell's
/// "custom" and "this mode has new features" notes). The preset's flags are the shell's to apply
/// (`features_set`); this only records the choice.
#[tauri::command]
async fn mode_set(webview: Webview, app: AppHandle, mode: Option<String>, onboarded: bool) -> Result<ModeState, String> {
async fn mode_set(webview: Webview, app: AppHandle, mode: Option<String>, onboarded: bool, preset: Option<std::collections::BTreeMap<String, bool>>) -> Result<ModeState, String> {
shell_only(&webview)?;
let next = page(&app)?.modes.chosen(mode, onboarded)?;
let next = page(&app)?.modes.chosen(mode, onboarded, preset)?;
write_json(&app, MODES_FILE, &next)?;
page(&app)?.modes = next.clone();
Ok(next)