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:
@ -96,8 +96,8 @@ pub use store::{
|
||||
embedder_from_profile, index_token_size, onnx_model_is_cached, should_use_vector,
|
||||
AdaptiveMemoryRecall, BackgroundTaskReconcileReport, EmbedderEnvProbe, FsBackgroundTaskStore,
|
||||
FsEmbedderProfileStore, FsEmbedderPromptStore, FsLiveStateStore, FsMemoryStore,
|
||||
FsPermissionStore, FsProfileStore, FsProjectStore, FsSkillStore, FsTemplateStore, HashEmbedder,
|
||||
IdeaiContextStore, NaiveMemoryRecall, OnnxModelInfo, StubEmbedder, VectorMemoryRecall,
|
||||
DEFAULT_OLLAMA_BASE_URL, ONNX_CACHE_SUBDIR, RECOMMENDED_ONNX_MODELS, VECTOR_HTTP_ENABLED,
|
||||
VECTOR_ONNX_ENABLED,
|
||||
FsPermissionStore, FsProfileStore, FsProjectStore, FsSkillStore, FsTemplateStore,
|
||||
FsWindowStateStore, HashEmbedder, IdeaiContextStore, NaiveMemoryRecall, OnnxModelInfo,
|
||||
StubEmbedder, VectorMemoryRecall, DEFAULT_OLLAMA_BASE_URL, ONNX_CACHE_SUBDIR,
|
||||
RECOMMENDED_ONNX_MODELS, VECTOR_HTTP_ENABLED, VECTOR_ONNX_ENABLED,
|
||||
};
|
||||
|
||||
@ -15,6 +15,7 @@ mod project;
|
||||
mod skill;
|
||||
mod template;
|
||||
mod vector;
|
||||
mod window_state;
|
||||
|
||||
pub use background_task::{BackgroundTaskReconcileReport, FsBackgroundTaskStore};
|
||||
pub use context::IdeaiContextStore;
|
||||
@ -35,3 +36,4 @@ pub use project::FsProjectStore;
|
||||
pub use skill::FsSkillStore;
|
||||
pub use template::FsTemplateStore;
|
||||
pub use vector::{should_use_vector, AdaptiveMemoryRecall, VectorMemoryRecall};
|
||||
pub use window_state::FsWindowStateStore;
|
||||
|
||||
68
crates/infrastructure/src/store/window_state.rs
Normal file
68
crates/infrastructure/src/store/window_state.rs
Normal file
@ -0,0 +1,68 @@
|
||||
//! JSON-file implementation of the [`WindowStateStore`] port.
|
||||
//!
|
||||
//! The snapshot is machine-local and lives under the app-data directory:
|
||||
//! `<app_data_dir>/windows.json`.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use domain::layout::WindowStateSnapshot;
|
||||
use domain::ports::{FileSystem, RemotePath, StoreError, WindowStateStore};
|
||||
|
||||
/// File name of the persisted OS window-state snapshot.
|
||||
const WINDOWS_FILE: &str = "windows.json";
|
||||
|
||||
/// JSON-file implementation of [`WindowStateStore`].
|
||||
#[derive(Clone)]
|
||||
pub struct FsWindowStateStore {
|
||||
fs: Arc<dyn FileSystem>,
|
||||
app_data_dir: String,
|
||||
}
|
||||
|
||||
impl FsWindowStateStore {
|
||||
/// Builds the store from an injected filesystem and app-data directory.
|
||||
#[must_use]
|
||||
pub fn new(fs: Arc<dyn FileSystem>, app_data_dir: impl Into<String>) -> Self {
|
||||
Self {
|
||||
fs,
|
||||
app_data_dir: app_data_dir.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn path(&self) -> RemotePath {
|
||||
let base = self.app_data_dir.trim_end_matches(['/', '\\']);
|
||||
RemotePath::new(format!("{base}/{WINDOWS_FILE}"))
|
||||
}
|
||||
|
||||
async fn ensure_dir(&self) -> Result<(), StoreError> {
|
||||
let dir = RemotePath::new(self.app_data_dir.trim_end_matches(['/', '\\']).to_owned());
|
||||
self.fs
|
||||
.create_dir_all(&dir)
|
||||
.await
|
||||
.map_err(|e| StoreError::Io(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WindowStateStore for FsWindowStateStore {
|
||||
async fn save_window_state(&self, snapshot: &WindowStateSnapshot) -> Result<(), StoreError> {
|
||||
self.ensure_dir().await?;
|
||||
let bytes = serde_json::to_vec_pretty(snapshot)
|
||||
.map_err(|e| StoreError::Serialization(e.to_string()))?;
|
||||
self.fs
|
||||
.write(&self.path(), &bytes)
|
||||
.await
|
||||
.map_err(|e| StoreError::Io(e.to_string()))
|
||||
}
|
||||
|
||||
async fn load_window_state(&self) -> Result<WindowStateSnapshot, StoreError> {
|
||||
match self.fs.read(&self.path()).await {
|
||||
Ok(bytes) => {
|
||||
serde_json::from_slice(&bytes).map_err(|e| StoreError::Serialization(e.to_string()))
|
||||
}
|
||||
Err(domain::ports::FsError::NotFound(_)) => Ok(WindowStateSnapshot::default()),
|
||||
Err(e) => Err(StoreError::Io(e.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user