aboutsummaryrefslogtreecommitdiff
path: root/src/app/input
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-08-04 17:26:30 +0300
committerGitHub <noreply@github.com>2026-08-04 17:26:30 +0300
commitcc9fa47540d8525f24fe46a10f759ca40090a6e0 (patch)
treeef6979a7d5168952dc2de8d7a68e42b3ad69c45b /src/app/input
parent4473e391962c68d59bd980fa98a44f3397d9931d (diff)
feat(ui): add distinct status indicators (#2282)
refs #2260
Diffstat (limited to 'src/app/input')
-rw-r--r--src/app/input/mod.rs3
-rw-r--r--src/app/input/settings.rs83
2 files changed, 79 insertions, 7 deletions
diff --git a/src/app/input/mod.rs b/src/app/input/mod.rs
index 7eca40b6..f77821c9 100644
--- a/src/app/input/mod.rs
+++ b/src/app/input/mod.rs
@@ -399,6 +399,9 @@ impl App {
}
MouseAction::Settings(action) => match action {
SettingsAction::SaveTheme(name) => self.save_theme(&name),
+ SettingsAction::SaveStatusIndicators(style) => {
+ self.save_status_indicators(style)
+ }
SettingsAction::SaveSound(enabled) => self.save_sound(enabled),
SettingsAction::SaveToastDelivery(delivery) => {
self.save_toast_delivery(delivery)
diff --git a/src/app/input/settings.rs b/src/app/input/settings.rs
index 326d841b..fb1a7d75 100644
--- a/src/app/input/settings.rs
+++ b/src/app/input/settings.rs
@@ -6,7 +6,7 @@ use crate::{
state::{AppState, SettingsSection, THEME_NAMES},
App, Mode,
},
- config::ToastDelivery,
+ config::{StatusIndicatorStyle, ToastDelivery},
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -14,6 +14,7 @@ use crate::{
#[allow(clippy::enum_variant_names)]
pub(super) enum SettingsAction {
SaveTheme(String),
+ SaveStatusIndicators(StatusIndicatorStyle),
SaveSound(bool),
SaveToastDelivery(ToastDelivery),
SaveAgentBorderLabels(bool),
@@ -26,6 +27,7 @@ impl App {
if let Some(action) = update_settings_state(&mut self.state, key) {
match action {
SettingsAction::SaveTheme(name) => self.save_theme(&name),
+ SettingsAction::SaveStatusIndicators(style) => self.save_status_indicators(style),
SettingsAction::SaveSound(enabled) => self.save_sound(enabled),
SettingsAction::SaveToastDelivery(delivery) => self.save_toast_delivery(delivery),
SettingsAction::SaveAgentBorderLabels(enabled) => {
@@ -56,6 +58,21 @@ fn current_theme_index(theme_name: &str) -> usize {
.unwrap_or(0)
}
+fn status_indicator_index(style: StatusIndicatorStyle) -> usize {
+ match style {
+ StatusIndicatorStyle::Dots => 0,
+ StatusIndicatorStyle::Symbols => 1,
+ }
+}
+
+fn status_indicator_for_index(idx: usize) -> StatusIndicatorStyle {
+ if idx == 0 {
+ StatusIndicatorStyle::Dots
+ } else {
+ StatusIndicatorStyle::Symbols
+ }
+}
+
fn toast_delivery_index(delivery: ToastDelivery) -> usize {
match delivery {
ToastDelivery::Off => 0,
@@ -145,8 +162,8 @@ pub(super) fn update_settings_state(state: &mut AppState, key: KeyEvent) -> Opti
}
}
KeyCode::Tab | KeyCode::Right | KeyCode::Char('l') => {
- state.settings.section = SettingsSection::Sound;
- state.settings.list.selected = usize::from(!state.sound_enabled());
+ state.settings.section = SettingsSection::Indicators;
+ state.settings.list.selected = status_indicator_index(state.status_indicators);
}
KeyCode::BackTab | KeyCode::Left | KeyCode::Char('h') => {
state.settings.section = SettingsSection::Integrations;
@@ -158,6 +175,30 @@ pub(super) fn update_settings_state(state: &mut AppState, key: KeyEvent) -> Opti
_ => {}
},
},
+ SettingsSection::Indicators => match key.code {
+ KeyCode::Up | KeyCode::Char('k') | KeyCode::Down | KeyCode::Char('j') => {
+ state.settings.list.selected = 1 - state.settings.list.selected.min(1);
+ }
+ KeyCode::Enter | KeyCode::Char(' ') => {
+ let style = status_indicator_for_index(state.settings.list.selected);
+ return Some(SettingsAction::SaveStatusIndicators(style));
+ }
+ KeyCode::BackTab | KeyCode::Left | KeyCode::Char('h') => {
+ state.settings.section = SettingsSection::Theme;
+ state.settings.list.selected = current_theme_index(&state.theme_name);
+ }
+ KeyCode::Tab | KeyCode::Right | KeyCode::Char('l') => {
+ state.settings.section = SettingsSection::Sound;
+ state.settings.list.selected = usize::from(!state.sound_enabled());
+ }
+ _ => {
+ if let Some(super::modal::ModalAction::Close) =
+ super::modal::modal_action_from_key(&key, super::modal::SETTINGS_ACTIONS)
+ {
+ cancel_settings(state);
+ }
+ }
+ },
SettingsSection::Sound => match key.code {
KeyCode::Up | KeyCode::Char('k') | KeyCode::Down | KeyCode::Char('j') => {
state.settings.list.selected = 1 - state.settings.list.selected.min(1);
@@ -171,8 +212,8 @@ pub(super) fn update_settings_state(state: &mut AppState, key: KeyEvent) -> Opti
state.settings.list.selected = toast_delivery_index(state.toast_delivery());
}
KeyCode::BackTab | KeyCode::Left | KeyCode::Char('h') => {
- state.settings.section = SettingsSection::Theme;
- state.settings.list.selected = current_theme_index(&state.theme_name);
+ state.settings.section = SettingsSection::Indicators;
+ state.settings.list.selected = status_indicator_index(state.status_indicators);
}
_ => {
if let Some(super::modal::ModalAction::Close) =
@@ -263,6 +304,7 @@ pub(crate) fn open_settings_at(state: &mut AppState, section: SettingsSection) {
state.settings.section = section;
state.settings.list.selected = match section {
SettingsSection::Theme => current_theme_index(&state.theme_name),
+ SettingsSection::Indicators => status_indicator_index(state.status_indicators),
SettingsSection::Sound => usize::from(!state.sound_enabled()),
SettingsSection::Toast => toast_delivery_index(state.toast_delivery()),
SettingsSection::PaneLabels => usize::from(!state.agent_border_labels_enabled()),
@@ -336,7 +378,7 @@ impl AppState {
let idx = scroll + (row - area.y) as usize;
(idx < THEME_NAMES.len()).then_some(idx)
}
- SettingsSection::Sound => {
+ SettingsSection::Indicators | SettingsSection::Sound => {
let list_y = area.y + 3;
if row >= list_y && row < list_y + 2 {
Some((row - list_y) as usize)
@@ -371,6 +413,9 @@ impl AppState {
self.settings.section = section;
self.settings.list.select(match section {
SettingsSection::Theme => current_theme_index(&self.theme_name),
+ SettingsSection::Indicators => {
+ status_indicator_index(self.status_indicators)
+ }
SettingsSection::Sound => usize::from(!self.sound_enabled()),
SettingsSection::Toast => toast_delivery_index(self.toast_delivery()),
SettingsSection::PaneLabels => {
@@ -387,6 +432,9 @@ impl AppState {
preview_selected_theme(self);
None
}
+ SettingsSection::Indicators => Some(SettingsAction::SaveStatusIndicators(
+ status_indicator_for_index(idx),
+ )),
SettingsSection::Sound => {
let enabled = idx == 0;
Some(SettingsAction::SaveSound(enabled))
@@ -454,7 +502,7 @@ mod tests {
);
assert_eq!(
state.settings.section,
- crate::app::state::SettingsSection::Sound
+ crate::app::state::SettingsSection::Indicators
);
update_settings_state(
@@ -469,6 +517,27 @@ mod tests {
}
#[test]
+ fn settings_indicator_choice_returns_save_action() {
+ let mut state = state_with_workspaces(&["test"]);
+ open_settings_at(&mut state, SettingsSection::Indicators);
+ state.settings.list.selected = 1;
+
+ let action = update_settings_state(
+ &mut state,
+ KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
+ );
+
+ assert_eq!(
+ action,
+ Some(SettingsAction::SaveStatusIndicators(
+ StatusIndicatorStyle::Symbols
+ ))
+ );
+ assert_eq!(state.status_indicators, StatusIndicatorStyle::Dots);
+ assert_eq!(state.mode, Mode::Settings);
+ }
+
+ #[test]
fn settings_sound_toggle_returns_save_action() {
let mut state = state_with_workspaces(&["test"]);
open_settings(&mut state);