feat: add nav store persistence and tmp file cleanup

Completes persistence story and crash recovery hardening.

Nav store persistence:
- Wraps nav store with zustand persist middleware
- Persists activeView to localStorage under "mc-nav-store"
- Remembers which view you were on across sessions

Bridge tmp file cleanup (src-tauri/src/data/bridge.rs):
- New cleanup_tmp_files() method removes orphaned .json.tmp files
- Called on startup to clean up from crashes during save_map()
- Logs cleaned files for debugging
- Returns count of files removed

The atomic write pattern (write to .tmp, then rename) can leave
orphan files if the app crashes between write and rename. This
cleanup ensures we don't accumulate stale tmp files over time.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
teernisse
2026-02-26 09:56:09 -05:00
parent 6c04c2efe7
commit 5059eb008a
2 changed files with 39 additions and 4 deletions

View File

@@ -216,6 +216,33 @@ impl<L: LoreCli, B: BeadsCli> Bridge<L, B> {
}
}
/// Clean up orphaned .tmp files from interrupted atomic writes.
///
/// Called on startup to remove any .json.tmp files left behind from
/// crashes during save_map(). Returns the number of files cleaned up.
pub fn cleanup_tmp_files(&self) -> Result<usize, BridgeError> {
if !self.data_dir.exists() {
return Ok(0);
}
let mut cleaned = 0;
for entry in fs::read_dir(&self.data_dir)? {
let entry = entry?;
let path = entry.path();
if path.extension().is_some_and(|e| e == "tmp") {
tracing::info!("Cleaning up orphaned tmp file: {:?}", path);
fs::remove_file(&path)?;
cleaned += 1;
}
}
if cleaned > 0 {
tracing::info!("Cleaned up {} orphaned tmp file(s)", cleaned);
}
Ok(cleaned)
}
/// Save the mapping file atomically (write to .tmp, then rename)
pub fn save_map(&self, map: &GitLabBeadMap) -> Result<(), BridgeError> {
fs::create_dir_all(&self.data_dir)?;