M3.9: a browser session restored with unsaved changes stays modified

The web app reopens the last visit's project and, when it had unsaved changes, marked it
modified with `bump()`. Since M3.9 the modified mark follows the saved snapshot, and a bump on
the very project just opened counts as "back at the saved state", so restored changes looked
saved (no `*`, no unsaved-changes prompt). `Session::mark_unsaved` forgets the saved snapshot
instead: the project stays modified until saved, whatever is undone. Regression test included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bflatastic 2026-10-05 06:33:40 +07:00
parent b21e04b301
commit a12d4ae1fa
3 changed files with 23 additions and 1 deletions

View File

@ -167,7 +167,7 @@ pub fn restore(s: &mut Session) -> bool {
} }
if meta.dirty { if meta.dirty {
// Unsaved changes stay unsaved. // Unsaved changes stay unsaved.
s.bump(); s.mark_unsaved();
} }
SNAP.with(|c| c.set((s.revision, 0.0))); SNAP.with(|c| c.set((s.revision, 0.0)));
log::info!("restored the last session ({})", meta.path.as_deref().unwrap_or("untitled")); log::info!("restored the last session ({})", meta.path.as_deref().unwrap_or("untitled"));

View File

@ -635,6 +635,13 @@ impl Session {
self.saved_revision = self.revision; self.saved_revision = self.revision;
} }
/// The project has changes its file doesn't (a session restored with unsaved changes): it
/// stays modified until it is saved, whatever is undone.
pub fn mark_unsaved(&mut self) {
self.saved_project = Arc::new(Project::default());
self.bump();
}
pub fn active_comp(&self) -> Option<&Comp> { pub fn active_comp(&self) -> Option<&Comp> {
self.project.comp(self.state.active_comp?) self.project.comp(self.state.active_comp?)
} }

View File

@ -175,3 +175,18 @@ fn modified_mark_follows_undo_and_no_op_edits_record_nothing() {
assert!(!s.is_dirty(), "rolled back"); assert!(!s.is_dirty(), "rolled back");
let _ = std::fs::remove_file(path); let _ = std::fs::remove_file(path);
} }
#[test]
fn a_session_restored_with_unsaved_changes_stays_modified() {
let mut s = Session::default();
s.execute("comp.new", json!({"name": "H", "width": 64, "height": 64, "duration": 2})).unwrap();
let p = (*s.project).clone();
// The web app reopens the last visit's project, which had unsaved changes.
s.replace_project(p, None);
assert!(!s.is_dirty());
s.mark_unsaved();
assert!(s.is_dirty());
solid(&mut s, "A");
s.execute("edit.undo", json!({})).unwrap();
assert!(s.is_dirty(), "undo doesn't reach a saved state");
}