feat(windows): persistance et restauration des fenêtres au redémarrage (#40)

Persiste l'état des fenêtres (layout/position) et le restaure au
relancement de l'application. Découpage hexagonal :
- domaine : modèle et port d'état des fenêtres (layout, ports)
- application : use cases de persistance/restauration
- infrastructure : store window_state (adapter de persistance)
- présentation : câblage app-tauri (state, commands, lib)
Couvert par des tests ciblés domaine/application/infrastructure.

Depend de #39 (fermeture des fenêtres auxiliaires).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 08:00:20 +02:00
parent 55087b5d9b
commit 2eb51d3335
15 changed files with 887 additions and 40 deletions

View File

@ -0,0 +1,60 @@
//! Integration tests for [`FsWindowStateStore`] against a real temp directory.
use std::path::PathBuf;
use std::sync::Arc;
use domain::layout::{PersistedWindowKind, PersistedWindowState, WindowStateSnapshot};
use domain::ports::{FileSystem, WindowStateStore};
use infrastructure::{FsWindowStateStore, LocalFileSystem};
use uuid::Uuid;
struct TempDir(PathBuf);
impl TempDir {
fn new() -> Self {
let path = std::env::temp_dir().join(format!("idea-window-store-{}", Uuid::new_v4()));
std::fs::create_dir_all(&path).unwrap();
Self(path)
}
fn app_data_dir(&self) -> String {
self.0.to_string_lossy().into_owned()
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
#[tokio::test]
async fn window_state_save_then_load_roundtrips() {
let tmp = TempDir::new();
let fs: Arc<dyn FileSystem> = Arc::new(LocalFileSystem::new());
let store = FsWindowStateStore::new(fs, tmp.app_data_dir());
assert_eq!(
store.load_window_state().await.unwrap(),
WindowStateSnapshot::default()
);
let snapshot = WindowStateSnapshot::new(vec![PersistedWindowState {
label: "main".to_owned(),
kind: PersistedWindowKind::Main,
panel: None,
project_id: None,
url: None,
visible: true,
maximized: true,
fullscreen: false,
outer_position: None,
outer_size: None,
monitor: None,
last_focused_at: None,
}]);
store.save_window_state(&snapshot).await.unwrap();
assert_eq!(store.load_window_state().await.unwrap(), snapshot);
assert!(tmp.0.join("windows.json").exists());
}