From f32ba5d99e23f13269118d0f1feb86d5bb222d09 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Mon, 17 Aug 2026 16:04:48 +0200 Subject: [PATCH] fix(terminal): preserve fullscreen under modals --- crates/okena-workspace/src/focus.rs | 40 ++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/crates/okena-workspace/src/focus.rs b/crates/okena-workspace/src/focus.rs index 7d73272b0..400d7db33 100644 --- a/crates/okena-workspace/src/focus.rs +++ b/crates/okena-workspace/src/focus.rs @@ -183,7 +183,7 @@ impl FocusManager { /// Get fullscreen state as (project_id, terminal_id) if in fullscreen pub fn fullscreen_state(&self) -> Option<(&str, &str)> { - if self.context != FocusContext::Fullscreen { + if !self.has_fullscreen_context() { return None; } self.current_focus.as_ref().and_then(|f| { @@ -201,11 +201,7 @@ impl FocusManager { /// Check if any terminal is in fullscreen mode pub fn has_fullscreen(&self) -> bool { - self.context == FocusContext::Fullscreen - && self - .current_focus - .as_ref() - .is_some_and(|f| f.terminal_id.is_some()) + self.fullscreen_state().is_some() } /// Get the project ID of the fullscreened terminal (if any) @@ -213,6 +209,20 @@ impl FocusManager { self.fullscreen_state().map(|(pid, _)| pid) } + /// Modals own keyboard focus without changing the presentation beneath them. + fn has_fullscreen_context(&self) -> bool { + match self.context { + FocusContext::Fullscreen => true, + FocusContext::Modal => self + .focus_stack + .iter() + .rev() + .find(|entry| entry.context != FocusContext::Modal) + .is_some_and(|entry| entry.context == FocusContext::Fullscreen), + FocusContext::Terminal => false, + } + } + // --- Focus actions --- /// Focus a terminal pane. @@ -515,6 +525,24 @@ mod tests { assert_eq!(target.project_id, "proj1"); } + #[test] + fn modal_preserves_fullscreen_presentation() { + let mut fm = FocusManager::new(); + fm.focus_terminal("proj1".to_string(), vec![0]); + fm.enter_fullscreen("proj1".to_string(), vec![0], "term1".to_string()); + + fm.enter_modal(); + + assert!(fm.is_modal()); + assert!(fm.has_fullscreen()); + assert_eq!(fm.fullscreen_project_id(), Some("proj1")); + assert!(fm.is_terminal_fullscreened("proj1", "term1")); + + fm.exit_modal(); + assert_eq!(*fm.context(), FocusContext::Fullscreen); + assert!(fm.has_fullscreen()); + } + #[test] fn redirect_modal_focus_keeps_modal_and_rewrites_restore() { let mut fm = FocusManager::new();