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:
@ -3,8 +3,9 @@
|
||||
//! window is dropped; an active moved tab hands activity back to a sibling.
|
||||
|
||||
use domain::{
|
||||
LayoutError, LayoutNode, LayoutTree, LeafCell, NodeId, ProjectId, Tab, TabId, Window, WindowId,
|
||||
Workspace,
|
||||
LayoutError, LayoutNode, LayoutTree, LeafCell, NodeId, PersistedMonitorState,
|
||||
PersistedWindowKind, PersistedWindowPosition, PersistedWindowSize, PersistedWindowState,
|
||||
ProjectId, Tab, TabId, Window, WindowId, WindowStateSnapshot, Workspace,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -94,3 +95,43 @@ fn move_non_active_tab_leaves_source_active_unchanged() {
|
||||
assert_eq!(source.active_tab, tid(1), "active tab unchanged");
|
||||
assert_eq!(source.tabs.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_state_snapshot_round_trips_with_camel_case_schema() {
|
||||
let snapshot = WindowStateSnapshot::new(vec![PersistedWindowState {
|
||||
label: "view-tickets-0000000000000000000000000000002a".to_owned(),
|
||||
kind: PersistedWindowKind::View,
|
||||
panel: Some("tickets".to_owned()),
|
||||
project_id: Some(ProjectId::from_uuid(Uuid::from_u128(42))),
|
||||
url: Some(
|
||||
"index.html?panel=tickets&project=00000000-0000-0000-0000-00000000002a".to_owned(),
|
||||
),
|
||||
visible: true,
|
||||
maximized: false,
|
||||
fullscreen: false,
|
||||
outer_position: Some(PersistedWindowPosition { x: 12, y: 34 }),
|
||||
outer_size: Some(PersistedWindowSize {
|
||||
width: 1200,
|
||||
height: 800,
|
||||
}),
|
||||
monitor: Some(PersistedMonitorState {
|
||||
name: Some("HDMI-1".to_owned()),
|
||||
scale_factor: Some(1.25),
|
||||
position: Some(PersistedWindowPosition { x: 0, y: 0 }),
|
||||
size: Some(PersistedWindowSize {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
}),
|
||||
}),
|
||||
last_focused_at: Some(123),
|
||||
}]);
|
||||
|
||||
let json = serde_json::to_string(&snapshot).unwrap();
|
||||
assert!(json.contains("\"projectId\""), "json was {json}");
|
||||
assert!(json.contains("\"outerPosition\""), "json was {json}");
|
||||
assert!(json.contains("\"scaleFactor\""), "json was {json}");
|
||||
assert!(!json.contains("project_id"), "no snake_case leak: {json}");
|
||||
|
||||
let decoded: WindowStateSnapshot = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(decoded, snapshot);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user