diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-08 14:53:45 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-08 14:53:45 +0300 |
| commit | c08e51e666f6df9a940393b568943b67940ceedc (patch) | |
| tree | 6a6c4cd3c8ea5ddf9e97a4695c6b0d31eb0d0717 /src/app/input | |
| parent | b0aa7b2cddc57a55a1c0285a1ea82b59ac9783bd (diff) | |
feat: make agent toasts focus panes
Diffstat (limited to 'src/app/input')
| -rw-r--r-- | src/app/input/mouse.rs | 81 | ||||
| -rw-r--r-- | src/app/input/navigate.rs | 1 |
2 files changed, 82 insertions, 0 deletions
diff --git a/src/app/input/mouse.rs b/src/app/input/mouse.rs index 185393f0..d0c33138 100644 --- a/src/app/input/mouse.rs +++ b/src/app/input/mouse.rs @@ -31,6 +31,19 @@ impl AppState { return None; } + if 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) + && matches!(mouse.kind, MouseEventKind::Up(MouseButton::Left)) + { + return None; + } + if self.mode == Mode::Settings { return self.handle_settings_mouse(mouse); } @@ -977,6 +990,34 @@ impl AppState { } } + fn clickable_toast_at(&self, col: u16, row: u16) -> bool { + self.toast + .as_ref() + .is_some_and(|toast| toast.target.is_some()) + && rect_contains(self.view.toast_hit_area, col, row) + } + + fn focus_toast_target(&mut self) { + let Some(target) = self.toast.as_ref().and_then(|toast| toast.target.clone()) else { + return; + }; + let Some(ws_idx) = self + .workspaces + .iter() + .position(|workspace| workspace.id == target.workspace_id) + else { + return; + }; + let Some(tab_idx) = self.workspaces[ws_idx].find_tab_index_for_pane(target.pane_id) else { + return; + }; + + self.switch_workspace(ws_idx); + self.switch_tab(tab_idx); + self.focus_pane(target.pane_id); + self.mode = Mode::Terminal; + } + pub(super) fn scroll_pane_up(&self, pane_id: crate::layout::PaneId, lines: usize) { if let Some(ws) = self.active.and_then(|i| self.workspaces.get(i)) { if let Some(rt) = ws.runtimes.get(&pane_id) { @@ -1205,6 +1246,7 @@ mod tests { use super::*; use crate::{ app::state::{ContextMenuKind, ContextMenuState, MenuListState, Mode, ViewLayout}, + detect::{Agent, AgentState}, workspace::Workspace, }; @@ -1226,6 +1268,45 @@ mod tests { } #[test] + fn clicking_agent_toast_focuses_target_pane() { + let mut app = app_for_mouse_test(); + let active = Workspace::test_new("active"); + let mut background = Workspace::test_new("background"); + let first_pane = background.tabs[0].root_pane; + let target_pane = background.test_split(Direction::Horizontal); + background.tabs[0].layout.focus_pane(first_pane); + + app.state.workspaces = vec![active, background]; + app.state.active = Some(0); + app.state.selected = 0; + app.state.toast_config.delivery = crate::config::ToastDelivery::Herdr; + app.state.workspaces[1] + .panes + .get_mut(&target_pane) + .unwrap() + .state = AgentState::Working; + + app.state + .handle_app_event(crate::events::AppEvent::StateChanged { + pane_id: target_pane, + agent: Some(Agent::Pi), + state: AgentState::Idle, + }); + 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(1)); + assert_eq!(app.state.workspaces[1].focused_pane_id(), Some(target_pane)); + assert_eq!(app.state.mode, Mode::Terminal); + } + + #[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/app/input/navigate.rs b/src/app/input/navigate.rs index 546228cc..3bc8da78 100644 --- a/src/app/input/navigate.rs +++ b/src/app/input/navigate.rs @@ -130,6 +130,7 @@ impl App { kind: crate::app::state::ToastKind::NeedsAttention, title: "custom command failed".to_string(), context: err.to_string(), + target: None, }); self.sync_toast_deadline(previous_toast); } |
