Guard unsaved edits against refresh and failed apply; coalesce undo

- Refresh (r) no longer clears the dirty flag or clobbers in-progress
  edits: it skips with a status message while changes are uncommitted,
  keeping the unsaved-changes quit guard intact.
- Config-tab apply keeps dirty set until the hyprctl eval and
  monitors.json write both succeed, so a failed apply no longer drops
  the modified marker.
- Undo snapshots coalesce runs of nudges/value cycles into one step via
  burst tracking in AppState, so 20 px of nudges undo as a single step.
- Replace the `date` subprocess timestamp with an in-process ISO 8601
  formatter (Hinnant days-from-civil); remove dead row_height code.
This commit is contained in:
Breadway 2026-08-30 18:34:41 +08:00
parent 40d28d3ec4
commit 8e0b95899c
5 changed files with 135 additions and 39 deletions

View file

@ -173,11 +173,19 @@ async fn run(
}
crossterm::event::KeyCode::Char('r') => match monitor::load_monitors().await {
Ok(monitors) => {
state.monitors = monitors;
state.layout.clamp_selected(state.monitors.len());
state.dirty = false;
state.active_profile = None;
state.set_status("Monitors refreshed.", StatusLevel::Success);
if state.dirty {
// Don't clobber unsaved edits (or silently drop the
// unsaved-changes quit guard) on a refresh.
state.set_status(
"Refresh skipped: unsaved changes present.",
StatusLevel::Info,
);
} else {
state.monitors = monitors;
state.layout.clamp_selected(state.monitors.len());
state.active_profile = None;
state.set_status("Monitors refreshed.", StatusLevel::Success);
}
}
Err(e) => {
state.set_status(format!("Refresh failed: {}", e), StatusLevel::Error);