aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-21 21:27:20 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-21 21:27:20 +0300
commit1f2f939862a399e8655d9ce99ab644faf6a32d17 (patch)
treeaf2046db96c194462e9d47f3b8d651cf4efb35b3 /src
parent866aaf740e10fcfce169aaf81380ea0257fbe272 (diff)
fix: keep notifications below floating overlays
refs #228
Diffstat (limited to 'src')
-rw-r--r--src/app/input/mouse.rs40
-rw-r--r--src/ui.rs47
2 files changed, 84 insertions, 3 deletions
diff --git a/src/app/input/mouse.rs b/src/app/input/mouse.rs
index 4d49a917..9e54fce1 100644
--- a/src/app/input/mouse.rs
+++ b/src/app/input/mouse.rs
@@ -62,14 +62,16 @@ impl AppState {
return None;
}
- if self.clickable_toast_at(mouse.column, mouse.row)
+ if self.mode == Mode::Terminal
+ && self.clickable_toast_at(mouse.column, mouse.row)
&& matches!(mouse.kind, MouseEventKind::Down(MouseButton::Left))
{
self.focus_toast_target();
return None;
}
- if self.clickable_toast_at(mouse.column, mouse.row)
+ if self.mode == Mode::Terminal
+ && self.clickable_toast_at(mouse.column, mouse.row)
&& matches!(mouse.kind, MouseEventKind::Up(MouseButton::Left))
{
return None;
@@ -1457,6 +1459,40 @@ mod tests {
}
#[test]
+ fn toast_click_does_not_steal_mouse_from_settings_overlay() {
+ let mut app = app_for_mouse_test();
+ let active = Workspace::test_new("active");
+ let background = Workspace::test_new("background");
+ let target_pane = background.tabs[0].root_pane;
+ let workspace_id = background.id.clone();
+
+ app.state.workspaces = vec![active, background];
+ app.state.active = Some(0);
+ app.state.selected = 0;
+ app.state.toast = Some(crate::app::state::ToastNotification {
+ kind: crate::app::state::ToastKind::Finished,
+ title: "pi finished".into(),
+ context: "background ยท 2".into(),
+ target: Some(crate::app::state::ToastTarget {
+ workspace_id,
+ pane_id: target_pane,
+ }),
+ });
+ app.state.mode = Mode::Settings;
+ crate::ui::compute_view(&mut app.state, Rect::new(0, 0, 106, 20));
+
+ let hit = app.state.view.toast_hit_area;
+ app.handle_mouse(mouse(
+ MouseEventKind::Down(MouseButton::Left),
+ hit.x + 1,
+ hit.y + 1,
+ ));
+
+ assert_eq!(app.state.active, Some(0));
+ assert!(app.state.toast.is_some());
+ }
+
+ #[test]
fn clicking_confirm_close_accepts_workspace_close() {
let mut app = app_for_mouse_test();
app.state.workspaces = vec![Workspace::test_new("a"), Workspace::test_new("b")];
diff --git a/src/ui.rs b/src/ui.rs
index 7e5187b0..9082ce00 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -355,6 +355,9 @@ pub fn render_with_runtime_registry(
}
render_panes(app, terminal_runtimes, frame, terminal_area);
+ // Ambient notifications sit above panes, but below interactive overlays.
+ render_notifications(app, frame, terminal_area);
+
match app.mode {
Mode::Onboarding => render_onboarding_overlay(app, frame, frame.area()),
Mode::ReleaseNotes => render_release_notes_overlay(app, frame, frame.area()),
@@ -377,8 +380,9 @@ pub fn render_with_runtime_registry(
Mode::KeybindHelp => render_keybind_help_overlay(app, frame),
Mode::Terminal => {}
}
+}
- // Notifications (rendered on top of everything)
+fn render_notifications(app: &AppState, frame: &mut Frame, terminal_area: Rect) {
let has_config_diagnostic = app.config_diagnostic.is_some();
if let Some(message) = &app.config_diagnostic {
render_config_diagnostic(frame, terminal_area, message, &app.palette);
@@ -504,6 +508,47 @@ mod tests {
}
#[test]
+ fn product_announcement_renders_above_config_diagnostic() {
+ let mut app = crate::app::state::AppState::test_new();
+ app.workspaces = vec![Workspace::test_new("one")];
+ app.active = Some(0);
+ app.selected = 0;
+ app.mode = Mode::ProductAnnouncement;
+ app.product_announcement = Some(crate::app::state::ProductAnnouncementState {
+ version: "0.6.0".into(),
+ id: "keybinding-v2".into(),
+ title: "Keybinding syntax changed".into(),
+ body: "### Update\n- Body".into(),
+ scroll: 0,
+ preview: false,
+ });
+ app.config_diagnostic = Some(
+ "unsafe direct keybinding: keys.new_workspace = \"n\"\nunsafe direct keybinding: keys.new_tab = \"c\""
+ .into(),
+ );
+
+ let area = Rect::new(0, 0, 44, 20);
+ compute_view(&mut app, area);
+
+ let backend = TestBackend::new(area.width, area.height);
+ let mut terminal = Terminal::new(backend).unwrap();
+ terminal.draw(|frame| render(&app, frame)).unwrap();
+ let buffer = terminal.backend().buffer();
+
+ let popup = centered_popup_rect(
+ area,
+ PRODUCT_ANNOUNCEMENT_MODAL_SIZE.0,
+ PRODUCT_ANNOUNCEMENT_MODAL_SIZE.1,
+ )
+ .expect("announcement popup");
+ let title_row = popup.y + 1;
+ let row = buffer_row_text(buffer, Rect::new(0, title_row, area.width, 1), title_row);
+
+ assert!(row.contains("Keybinding syntax changed"));
+ assert!(!row.contains("config warning"));
+ }
+
+ #[test]
fn compute_view_clamps_sidebar_width_to_configured_max() {
let mut app = crate::app::state::AppState::test_new();
app.workspaces = vec![Workspace::test_new("one")];