diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-19 23:58:17 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-20 00:00:16 +0300 |
| commit | fb93e3747544afc3288d7b5e831d5ca3dd5c3768 (patch) | |
| tree | 95ed3208395ebe68dc49fbd7d8edb7e16597ba49 /src/app/input | |
| parent | 2bebac461b1d5d6a1ec42d70c2338cf27eee8c56 (diff) | |
feat: surface agent integrations in onboarding
Diffstat (limited to 'src/app/input')
| -rw-r--r-- | src/app/input/mod.rs | 11 | ||||
| -rw-r--r-- | src/app/input/settings.rs | 95 |
2 files changed, 92 insertions, 14 deletions
diff --git a/src/app/input/mod.rs b/src/app/input/mod.rs index 18dd8856..ae8fb12b 100644 --- a/src/app/input/mod.rs +++ b/src/app/input/mod.rs @@ -37,7 +37,7 @@ pub(crate) use self::{ handle_keybind_help_key, handle_rename_key, handle_resize_key, }, navigate::terminal_direct_navigation_action, - settings::open_settings, + settings::open_settings_at, }; use self::{ modal::{ @@ -181,6 +181,7 @@ impl App { } let previous_agent_panel_scope = self.state.agent_panel_scope; + let previous_settings_section = self.state.settings.section; if let Some(action) = self.state.handle_mouse(mouse) { match action { SettingsAction::SaveTheme(name) => self.save_theme(&name), @@ -189,8 +190,16 @@ impl App { SettingsAction::SaveAgentBorderLabels(enabled) => { self.save_agent_border_labels(enabled) } + SettingsAction::InstallRecommendedIntegrations => { + self.install_recommended_integrations() + } } } + if previous_settings_section != crate::app::state::SettingsSection::Integrations + && self.state.settings.section == crate::app::state::SettingsSection::Integrations + { + self.refresh_integration_recommendations(); + } if self.state.agent_panel_scope != previous_agent_panel_scope { self.save_agent_panel_scope(self.state.agent_panel_scope); } diff --git a/src/app/input/settings.rs b/src/app/input/settings.rs index 79fdf5d8..400b2796 100644 --- a/src/app/input/settings.rs +++ b/src/app/input/settings.rs @@ -17,10 +17,12 @@ pub(super) enum SettingsAction { SaveSound(bool), SaveToastDelivery(ToastDelivery), SaveAgentBorderLabels(bool), + InstallRecommendedIntegrations, } impl App { pub(crate) fn handle_settings_key(&mut self, key: KeyEvent) { + let previous_section = self.state.settings.section; if let Some(action) = update_settings_state(&mut self.state, key) { match action { SettingsAction::SaveTheme(name) => self.save_theme(&name), @@ -29,8 +31,16 @@ impl App { SettingsAction::SaveAgentBorderLabels(enabled) => { self.save_agent_border_labels(enabled) } + SettingsAction::InstallRecommendedIntegrations => { + self.install_recommended_integrations() + } } } + if previous_section != SettingsSection::Integrations + && self.state.settings.section == SettingsSection::Integrations + { + self.refresh_integration_recommendations(); + } } } @@ -84,6 +94,13 @@ fn cancel_settings(state: &mut AppState) { super::modal::leave_modal(state); } +fn integrations_need_install(state: &AppState) -> bool { + state + .integration_recommendations + .iter() + .any(crate::integration::IntegrationRecommendation::needs_install) +} + fn apply_settings(state: &mut AppState) -> Option<SettingsAction> { match state.settings.section { SettingsSection::Theme => { @@ -93,6 +110,10 @@ fn apply_settings(state: &mut AppState) -> Option<SettingsAction> { super::modal::leave_modal(state); Some(SettingsAction::SaveTheme(theme_name)) } + SettingsSection::Integrations if integrations_need_install(state) => { + Some(SettingsAction::InstallRecommendedIntegrations) + } + SettingsSection::Integrations => None, _ => { super::modal::leave_modal(state); None @@ -187,8 +208,8 @@ pub(super) fn update_settings_state(state: &mut AppState, key: KeyEvent) -> Opti state.settings.list.selected = toast_delivery_index(state.toast_delivery()); } KeyCode::Tab | KeyCode::Right | KeyCode::Char('l') => { - state.settings.section = SettingsSection::Theme; - state.settings.list.selected = current_theme_index(&state.theme_name); + state.settings.section = SettingsSection::Integrations; + state.settings.list.selected = 0; } _ => { if let Some(super::modal::ModalAction::Close) = @@ -198,16 +219,44 @@ pub(super) fn update_settings_state(state: &mut AppState, key: KeyEvent) -> Opti } } }, + SettingsSection::Integrations => match key.code { + KeyCode::Enter | KeyCode::Char(' ') if integrations_need_install(state) => { + return Some(SettingsAction::InstallRecommendedIntegrations); + } + KeyCode::BackTab | KeyCode::Left | KeyCode::Char('h') => { + state.settings.section = SettingsSection::PaneLabels; + state.settings.list.selected = usize::from(!state.agent_border_labels_enabled()); + } + KeyCode::Tab | KeyCode::Right | KeyCode::Char('l') => { + state.settings.section = SettingsSection::Theme; + state.settings.list.selected = current_theme_index(&state.theme_name); + } + _ => match super::modal::modal_action_from_key(&key, super::modal::SETTINGS_ACTIONS) { + Some(super::modal::ModalAction::Apply) => return apply_settings(state), + Some(super::modal::ModalAction::Close) => cancel_settings(state), + _ => {} + }, + }, } None } pub(crate) fn open_settings(state: &mut AppState) { + open_settings_at(state, SettingsSection::Theme); +} + +pub(crate) fn open_settings_at(state: &mut AppState, section: SettingsSection) { state.settings.original_palette = Some(state.palette.clone()); state.settings.original_theme = Some(state.theme_name.clone()); - state.settings.section = SettingsSection::Theme; - state.settings.list.selected = current_theme_index(&state.theme_name); + state.settings.section = section; + state.settings.list.selected = match section { + SettingsSection::Theme => current_theme_index(&state.theme_name), + SettingsSection::Sound => usize::from(!state.sound_enabled()), + SettingsSection::Toast => toast_delivery_index(state.toast_delivery()), + SettingsSection::PaneLabels => usize::from(!state.agent_border_labels_enabled()), + SettingsSection::Integrations => 0, + }; state.mode = Mode::Settings; } @@ -290,6 +339,7 @@ impl AppState { None } } + SettingsSection::Integrations => None, } } @@ -305,6 +355,7 @@ impl AppState { SettingsSection::PaneLabels => { usize::from(!self.agent_border_labels_enabled()) } + SettingsSection::Integrations => 0, }); return None; } @@ -327,19 +378,19 @@ impl AppState { let enabled = idx == 0; Some(SettingsAction::SaveAgentBorderLabels(enabled)) } + SettingsSection::Integrations => None, }; } let inner = self.settings_inner_rect(); - let (apply, close) = crate::ui::settings_button_rects(inner); - match super::modal::modal_action_from_buttons( - mouse.column, - mouse.row, - &[ - (apply, super::modal::ModalAction::Apply), - (close, super::modal::ModalAction::Close), - ], - ) { + let show_primary = crate::ui::settings_show_primary_action(self); + let (apply, close) = + crate::ui::settings_button_rects(inner, self.settings.section, show_primary); + let mut buttons = vec![(close, super::modal::ModalAction::Close)]; + if let Some(apply) = apply { + buttons.insert(0, (apply, super::modal::ModalAction::Apply)); + } + match super::modal::modal_action_from_buttons(mouse.column, mouse.row, &buttons) { Some(super::modal::ModalAction::Apply) => apply_settings(self), Some(super::modal::ModalAction::Close) => { cancel_settings(self); @@ -414,6 +465,24 @@ mod tests { } #[test] + fn integrations_enter_does_nothing_when_nothing_needs_install() { + let mut state = state_with_workspaces(&["test"]); + open_settings_at(&mut state, SettingsSection::Integrations); + + let enter_action = update_settings_state( + &mut state, + KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()), + ); + assert_eq!(enter_action, None); + + let space_action = update_settings_state( + &mut state, + KeyEvent::new(KeyCode::Char(' '), KeyModifiers::empty()), + ); + assert_eq!(space_action, None); + } + + #[test] fn settings_hover_does_not_change_selection() { let mut app = app_for_mouse_test(); open_settings(&mut app.state); |
