diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-20 22:08:04 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-20 22:08:04 +0300 |
| commit | c584037c77c63f6a08f8837f357c89432b8043e7 (patch) | |
| tree | 16647d6628a11dbbf5e576fb86f2a87b507e202e /src/app | |
| parent | f7b26a911772dab16a49d982002d2bbe58e0956b (diff) | |
feat: badge outdated integration updates
Diffstat (limited to 'src/app')
| -rw-r--r-- | src/app/input/settings.rs | 73 | ||||
| -rw-r--r-- | src/app/input/sidebar.rs | 10 | ||||
| -rw-r--r-- | src/app/state.rs | 19 |
3 files changed, 98 insertions, 4 deletions
diff --git a/src/app/input/settings.rs b/src/app/input/settings.rs index 400b2796..6caccb0e 100644 --- a/src/app/input/settings.rs +++ b/src/app/input/settings.rs @@ -283,7 +283,12 @@ impl AppState { } let mut x = inner.x; for section in SettingsSection::ALL { - let width = section.label().len() as u16 + 2; + let badge_width = if self.settings_section_has_badge(*section) { + 2 + } else { + 0 + }; + let width = section.label().len() as u16 + 2 + badge_width; if col >= x && col < x + width { return Some(*section); } @@ -493,4 +498,70 @@ mod tests { assert_eq!(app.state.settings.list.selected, 0); } + + #[test] + fn integration_update_badge_only_tracks_outdated_recommendations() { + let mut state = state_with_workspaces(&["test"]); + state.integration_recommendations = vec![integration_recommendation( + crate::integration::IntegrationStatusKind::NotInstalled, + true, + )]; + assert!(!state.integration_updates_available()); + + state.integration_recommendations = vec![integration_recommendation( + crate::integration::IntegrationStatusKind::NotInstalled, + false, + )]; + assert!(!state.integration_updates_available()); + + state.integration_recommendations = vec![integration_recommendation( + crate::integration::IntegrationStatusKind::Current, + true, + )]; + assert!(!state.integration_updates_available()); + + state.integration_recommendations = vec![integration_recommendation( + crate::integration::IntegrationStatusKind::Outdated, + true, + )]; + assert!(state.integration_updates_available()); + } + + #[test] + fn settings_tab_hit_area_includes_integration_update_badge() { + let mut state = state_with_workspaces(&["test"]); + state.integration_recommendations = vec![integration_recommendation( + crate::integration::IntegrationStatusKind::Outdated, + true, + )]; + open_settings(&mut state); + + let inner = state.settings_inner_rect(); + let tab_y = inner.y + 1; + let integrations_x = inner.x + + SettingsSection::ALL[..SettingsSection::ALL.len() - 1] + .iter() + .map(|section| section.label().len() as u16 + 3) + .sum::<u16>(); + let dotted_width = SettingsSection::Integrations.label().len() as u16 + 4; + + assert_eq!( + state.settings_tab_at(integrations_x + dotted_width - 1, tab_y), + Some(SettingsSection::Integrations) + ); + } + + fn integration_recommendation( + state: crate::integration::IntegrationStatusKind, + available: bool, + ) -> crate::integration::IntegrationRecommendation { + crate::integration::IntegrationRecommendation { + target: crate::api::schema::IntegrationTarget::Claude, + label: "claude", + command: "claude", + available, + path: std::path::PathBuf::from("/tmp/herdr-test-integration"), + state, + } + } } diff --git a/src/app/input/sidebar.rs b/src/app/input/sidebar.rs index 94feaf93..7491c9c1 100644 --- a/src/app/input/sidebar.rs +++ b/src/app/input/sidebar.rs @@ -175,7 +175,7 @@ impl AppState { } let footer = self.sidebar_footer_rect(); - let width = if self.update_available.is_some() { + let width = if self.global_menu_attention_badge_visible() { 8 } else { 6 @@ -203,8 +203,12 @@ impl AppState { let content_width = labels .iter() .map(|label| { - let extra = if *label == "update ready" { 2 } else { 0 }; - label.chars().count() as u16 + extra + let badge_width = if self.global_menu_item_has_badge(label) { + 2 + } else { + 0 + }; + label.chars().count() as u16 + badge_width }) .max() .unwrap_or(8) diff --git a/src/app/state.rs b/src/app/state.rs index 2b078caf..921a82c9 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -1011,6 +1011,25 @@ impl AppState { self.show_agent_labels_on_pane_borders } + pub(crate) fn integration_updates_available(&self) -> bool { + self.integration_recommendations + .iter() + .any(|item| item.state == crate::integration::IntegrationStatusKind::Outdated) + } + + pub(crate) fn global_menu_attention_badge_visible(&self) -> bool { + self.update_available.is_some() || self.integration_updates_available() + } + + pub(crate) fn global_menu_item_has_badge(&self, item: &str) -> bool { + (item == "update ready" && self.update_available.is_some()) + || (item == "settings" && self.integration_updates_available()) + } + + pub(crate) fn settings_section_has_badge(&self, section: SettingsSection) -> bool { + section == SettingsSection::Integrations && self.integration_updates_available() + } + pub fn focused_pane_requests_mouse_capture(&self) -> bool { self.mode == Mode::Terminal && self |
