diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-22 17:01:25 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-22 17:01:25 +0300 |
| commit | 4b482f37a1e233b9aca6bf99b12e8b4da3e5c05e (patch) | |
| tree | 69d1a56ad6335547eec9727691310cf96ebe9623 /src/app | |
| parent | 5bd939fbfc92a8073d0eac11dfb02a31ea638b15 (diff) | |
feat: add terminal-delivered client notifications
Diffstat (limited to 'src/app')
| -rw-r--r-- | src/app/actions.rs | 38 | ||||
| -rw-r--r-- | src/app/api.rs | 57 | ||||
| -rw-r--r-- | src/app/config_io.rs | 17 | ||||
| -rw-r--r-- | src/app/input/mod.rs | 37 | ||||
| -rw-r--r-- | src/app/input/modal.rs | 12 | ||||
| -rw-r--r-- | src/app/input/overlays.rs | 81 | ||||
| -rw-r--r-- | src/app/input/settings.rs | 68 | ||||
| -rw-r--r-- | src/app/mod.rs | 35 | ||||
| -rw-r--r-- | src/app/state.rs | 10 |
9 files changed, 185 insertions, 170 deletions
diff --git a/src/app/actions.rs b/src/app/actions.rs index f9136b9c..33952b9f 100644 --- a/src/app/actions.rs +++ b/src/app/actions.rs @@ -35,7 +35,7 @@ pub fn notification_sound_for_state_change( } } -fn notification_toast_for_state_change( +pub fn notification_toast_for_state_change( is_active_tab: bool, prev_state: AgentState, new_state: AgentState, @@ -57,7 +57,7 @@ fn toast_agent_label(agent_label: &str) -> &str { agent_label } -fn notification_context( +pub fn notification_context( ws: &crate::workspace::Workspace, ws_idx: usize, pane_id: PaneId, @@ -469,11 +469,16 @@ impl AppState { self.update_available = Some(version.clone()); self.latest_release_notes_available = true; self.update_dismissed = true; - self.toast = Some(ToastNotification { - kind: ToastKind::UpdateInstalled, - title: format!("v{version} available"), - context: "detach, then run `herdr update`".to_string(), - }); + if matches!( + self.toast_config.delivery, + crate::config::ToastDelivery::Herdr + ) { + self.toast = Some(ToastNotification { + kind: ToastKind::UpdateInstalled, + title: format!("v{version} available"), + context: "detach, then run `herdr update`".to_string(), + }); + } Vec::new() } AppEvent::StateChanged { @@ -575,7 +580,10 @@ impl AppState { } } - if self.toast_config.enabled { + if matches!( + self.toast_config.delivery, + crate::config::ToastDelivery::Herdr + ) { if let (Some(agent_label), Some(kind)) = ( change.agent_label.as_deref(), notification_toast_for_state_change( @@ -665,6 +673,7 @@ mod tests { #[test] fn update_ready_sets_explicit_upgrade_toast() { let mut state = AppState::test_new(); + state.toast_config.delivery = crate::config::ToastDelivery::Herdr; let updates = state.handle_app_event(crate::events::AppEvent::UpdateReady { version: "0.5.0".into(), @@ -921,7 +930,7 @@ mod tests { fn background_waiting_sets_attention_toast() { let mut state = app_with_workspaces(&["active", "background"]); state.active = Some(0); - state.toast_config.enabled = true; + state.toast_config.delivery = crate::config::ToastDelivery::Herdr; let bg_pane_id = *state.workspaces[1].panes.keys().next().unwrap(); state.handle_app_event(AppEvent::StateChanged { @@ -940,7 +949,7 @@ mod tests { fn hook_reported_unknown_agent_sets_toast_title_from_label() { let mut state = app_with_workspaces(&["active", "background"]); state.active = Some(0); - state.toast_config.enabled = true; + state.toast_config.delivery = crate::config::ToastDelivery::Herdr; let bg_pane_id = *state.workspaces[1].panes.keys().next().unwrap(); state.handle_app_event(AppEvent::HookStateReported { @@ -961,7 +970,7 @@ mod tests { fn background_idle_sets_finished_toast() { let mut state = app_with_workspaces(&["active", "background"]); state.active = Some(0); - state.toast_config.enabled = true; + state.toast_config.delivery = crate::config::ToastDelivery::Herdr; let bg_pane_id = *state.workspaces[1].panes.keys().next().unwrap(); state.workspaces[1] .panes @@ -985,7 +994,7 @@ mod tests { fn background_toast_includes_tab_name_when_workspace_has_multiple_tabs() { let mut state = app_with_workspaces(&["active", "background"]); state.active = Some(0); - state.toast_config.enabled = true; + state.toast_config.delivery = crate::config::ToastDelivery::Herdr; state.workspaces[1].tabs[0].set_custom_name("main".into()); let second_tab = state.workspaces[1].test_add_tab(Some("logs")); let bg_pane_id = state.workspaces[1].tabs[second_tab].root_pane; @@ -1006,7 +1015,7 @@ mod tests { fn background_tab_in_active_workspace_still_sets_toast() { let mut state = app_with_workspaces(&["active"]); state.active = Some(0); - state.toast_config.enabled = true; + state.toast_config.delivery = crate::config::ToastDelivery::Herdr; state.workspaces[0].tabs[0].set_custom_name("main".into()); let second_tab = state.workspaces[0].test_add_tab(Some("logs")); let bg_pane_id = state.workspaces[0].tabs[second_tab].root_pane; @@ -1027,7 +1036,7 @@ mod tests { fn active_workspace_active_tab_does_not_set_toast() { let mut state = app_with_workspaces(&["active"]); state.active = Some(0); - state.toast_config.enabled = true; + state.toast_config.delivery = crate::config::ToastDelivery::Herdr; let pane_id = *state.workspaces[0].panes.keys().next().unwrap(); state.handle_app_event(AppEvent::StateChanged { @@ -1042,6 +1051,7 @@ mod tests { #[test] fn update_ready_sets_manual_update_toast() { let mut state = AppState::test_new(); + state.toast_config.delivery = crate::config::ToastDelivery::Herdr; let updates = state.handle_app_event(AppEvent::UpdateReady { version: "0.5.0".into(), diff --git a/src/app/api.rs b/src/app/api.rs index e47eb452..b07c1267 100644 --- a/src/app/api.rs +++ b/src/app/api.rs @@ -47,6 +47,11 @@ impl App { None }; + let update_ready_version = if let AppEvent::UpdateReady { version } = &ev { + Some(version.clone()) + } else { + None + }; let previous_toast = self.state.toast.clone(); let pane_updates = self.state.handle_app_event(ev); for update in &pane_updates { @@ -64,6 +69,58 @@ impl App { if let Some(overlay) = overlay_state { self.restore_overlay_after_exit(overlay); } + + if matches!( + self.state.toast_config.delivery, + crate::config::ToastDelivery::Terminal + ) { + if let Some(version) = update_ready_version { + let _ = crate::terminal_notify::show_notification( + &format!("v{version} available"), + Some("detach, then run `herdr update`"), + ); + } else { + for update in &pane_updates { + let is_active_tab = self + .state + .pane_is_in_active_tab(update.ws_idx, update.pane_id); + let Some(kind) = crate::app::actions::notification_toast_for_state_change( + is_active_tab, + update.previous_state, + update.state, + ) else { + continue; + }; + let Some(ws) = self.state.workspaces.get(update.ws_idx) else { + continue; + }; + let Some(pane) = ws + .tabs + .iter() + .find_map(|tab| tab.panes.get(&update.pane_id)) + else { + continue; + }; + let Some(agent_label) = pane.effective_agent_label() else { + continue; + }; + let event_text = match kind { + ToastKind::NeedsAttention => "needs attention", + ToastKind::Finished => "finished", + ToastKind::UpdateInstalled => "updated", + }; + let _ = crate::terminal_notify::show_notification( + &format!("{} {}", agent_label, event_text), + Some(&crate::app::actions::notification_context( + ws, + update.ws_idx, + update.pane_id, + )), + ); + } + } + } + self.sync_toast_deadline(previous_toast); } diff --git a/src/app/config_io.rs b/src/app/config_io.rs index 21d4365b..5d4b2b13 100644 --- a/src/app/config_io.rs +++ b/src/app/config_io.rs @@ -25,6 +25,12 @@ impl App { } } + pub(super) fn mark_onboarding_complete(&mut self) { + self.update_config_file("onboarding setting", |content| { + crate::config::upsert_top_level_bool(content, "onboarding", false) + }); + } + pub(super) fn save_theme(&mut self, name: &str) { self.update_config_file("theme", |content| { crate::config::upsert_section_value(content, "theme", "name", &format!("\"{name}\"")) @@ -37,9 +43,16 @@ impl App { }); } - pub(super) fn save_toast(&mut self, enabled: bool) { + pub(super) fn save_toast_delivery(&mut self, delivery: crate::config::ToastDelivery) { + let value = match delivery { + crate::config::ToastDelivery::Off => "\"off\"", + crate::config::ToastDelivery::Herdr => "\"herdr\"", + crate::config::ToastDelivery::Terminal => "\"terminal\"", + }; self.update_config_file("toast setting", |content| { - crate::config::upsert_section_bool(content, "ui.toast", "enabled", enabled) + let content = + crate::config::upsert_section_value(content, "ui.toast", "delivery", value); + crate::config::remove_section_key(&content, "ui.toast", "enabled") }); } } diff --git a/src/app/input/mod.rs b/src/app/input/mod.rs index 2f069fea..9676da62 100644 --- a/src/app/input/mod.rs +++ b/src/app/input/mod.rs @@ -37,11 +37,11 @@ pub(crate) use self::{ handle_keybind_help_key, handle_rename_key, handle_resize_key, }, navigate::{handle_navigate_key, terminal_direct_navigation_action}, + settings::open_settings, }; use self::{ modal::{ - modal_action_from_key, ModalAction, ONBOARDING_NOTIFICATION_ACTIONS, - ONBOARDING_WELCOME_ACTIONS, RELEASE_NOTES_ACTIONS, + modal_action_from_key, ModalAction, ONBOARDING_WELCOME_ACTIONS, RELEASE_NOTES_ACTIONS, }, settings::SettingsAction, }; @@ -89,32 +89,11 @@ impl App { } pub(crate) fn handle_onboarding_key(&mut self, key: KeyEvent) { - match self.state.onboarding_step { - 0 => match key.code { - KeyCode::Right | KeyCode::Char('l') => { - self.state.onboarding_step = 1; - } - _ => match modal_action_from_key(&key, ONBOARDING_WELCOME_ACTIONS) { - Some(ModalAction::Continue) => self.state.onboarding_step = 1, - _ => {} - }, - }, - _ => match key.code { - KeyCode::Up | KeyCode::Char('k') => self.state.onboarding_list.move_prev(), - KeyCode::Down | KeyCode::Char('j') => self.state.onboarding_list.move_next(4), - KeyCode::Left | KeyCode::Char('h') => { - self.state.onboarding_step = 0; - } - KeyCode::Char(c) if ('1'..='4').contains(&c) => { - self.state - .onboarding_list - .select((c as usize) - ('1' as usize)); - } - _ => match modal_action_from_key(&key, ONBOARDING_NOTIFICATION_ACTIONS) { - Some(ModalAction::Back) => self.state.onboarding_step = 0, - Some(ModalAction::Save) => self.complete_onboarding(), - _ => {} - }, + match key.code { + KeyCode::Right | KeyCode::Char('l') => self.open_settings_from_onboarding(), + _ => match modal_action_from_key(&key, ONBOARDING_WELCOME_ACTIONS) { + Some(ModalAction::Continue) => self.open_settings_from_onboarding(), + _ => {} }, } } @@ -170,7 +149,7 @@ impl App { match action { SettingsAction::SaveTheme(name) => self.save_theme(&name), SettingsAction::SaveSound(enabled) => self.save_sound(enabled), - SettingsAction::SaveToast(enabled) => self.save_toast(enabled), + SettingsAction::SaveToastDelivery(delivery) => self.save_toast_delivery(delivery), } } diff --git a/src/app/input/modal.rs b/src/app/input/modal.rs index fe909326..5c3108f2 100644 --- a/src/app/input/modal.rs +++ b/src/app/input/modal.rs @@ -9,7 +9,6 @@ use crate::{ #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum ModalAction { Continue, - Back, Save, Clear, Cancel, @@ -211,17 +210,6 @@ pub(super) const ONBOARDING_WELCOME_ACTIONS: &[ModalActionSpec<ModalAction>] = & bindings: &[ModalKeyBinding::Enter], }]; -pub(super) const ONBOARDING_NOTIFICATION_ACTIONS: &[ModalActionSpec<ModalAction>] = &[ - ModalActionSpec { - action: ModalAction::Back, - bindings: &[ModalKeyBinding::Esc], - }, - ModalActionSpec { - action: ModalAction::Save, - bindings: &[ModalKeyBinding::Enter], - }, -]; - pub(super) const RELEASE_NOTES_ACTIONS: &[ModalActionSpec<ModalAction>] = &[ModalActionSpec { action: ModalAction::Close, bindings: &[ModalKeyBinding::Enter, ModalKeyBinding::Esc], diff --git a/src/app/input/overlays.rs b/src/app/input/overlays.rs index 6475895e..b283847e 100644 --- a/src/app/input/overlays.rs +++ b/src/app/input/overlays.rs @@ -272,51 +272,17 @@ impl AppState { return; } - match self.onboarding_step { - 0 => { - let Some(inner) = self.onboarding_modal_inner(64, 16) else { - return; - }; - let actions = crate::ui::modal_stack_areas(inner, 2, 0, 1, 1) - .actions - .unwrap_or_default(); - let button = crate::ui::onboarding_welcome_continue_rect(actions); - if matches!(mouse.kind, MouseEventKind::Down(MouseButton::Left)) - && modal_action_from_buttons( - mouse.column, - mouse.row, - &[(button, ModalAction::Continue)], - ) == Some(ModalAction::Continue) - { - self.onboarding_step = 1; - } - } - _ => { - let Some(inner) = self.onboarding_modal_inner(56, 14) else { - return; - }; - let stack = crate::ui::modal_stack_areas(inner, 3, 0, 1, 1); - if mouse.row >= stack.content.y && mouse.row < stack.content.y + 4 { - self.onboarding_list - .select((mouse.row - stack.content.y) as usize); - return; - } - - let (back, save) = crate::ui::onboarding_notification_button_rects( - stack.actions.unwrap_or_default(), - ); - if matches!(mouse.kind, MouseEventKind::Down(MouseButton::Left)) { - match modal_action_from_buttons( - mouse.column, - mouse.row, - &[(back, ModalAction::Back), (save, ModalAction::Save)], - ) { - Some(ModalAction::Back) => self.onboarding_step = 0, - Some(ModalAction::Save) => self.request_complete_onboarding = true, - _ => {} - } - } - } + let Some(inner) = self.onboarding_modal_inner(64, 16) else { + return; + }; + let actions = crate::ui::modal_stack_areas(inner, 2, 0, 1, 1) + .actions + .unwrap_or_default(); + let button = crate::ui::onboarding_welcome_continue_rect(actions); + if modal_action_from_buttons(mouse.column, mouse.row, &[(button, ModalAction::Continue)]) + == Some(ModalAction::Continue) + { + self.request_complete_onboarding = true; } } @@ -453,31 +419,30 @@ mod tests { fn onboarding_hover_does_not_change_selection() { let mut app = app_for_mouse_test(); app.state.mode = Mode::Onboarding; - app.state.onboarding_step = 1; - app.state.onboarding_list.select(1); - let inner = app.state.onboarding_modal_inner(56, 14).unwrap(); - let content = crate::ui::modal_stack_areas(inner, 3, 0, 1, 1).content; + let inner = app.state.onboarding_modal_inner(64, 16).unwrap(); + let content = crate::ui::modal_stack_areas(inner, 2, 0, 1, 1).content; app.handle_mouse(mouse(MouseEventKind::Moved, content.x + 2, content.y)); - assert_eq!(app.state.onboarding_list.selected, 1); + assert!(!app.state.request_complete_onboarding); } #[test] - fn onboarding_click_selects_notification_option() { + fn onboarding_click_continue_requests_completion() { let mut app = app_for_mouse_test(); app.state.mode = Mode::Onboarding; - app.state.onboarding_step = 1; - app.state.onboarding_list.select(0); - let inner = app.state.onboarding_modal_inner(56, 14).unwrap(); - let content = crate::ui::modal_stack_areas(inner, 3, 0, 1, 1).content; + let inner = app.state.onboarding_modal_inner(64, 16).unwrap(); + let actions = crate::ui::modal_stack_areas(inner, 2, 0, 1, 1) + .actions + .unwrap(); + let continue_rect = crate::ui::onboarding_welcome_continue_rect(actions); app.handle_mouse(mouse( MouseEventKind::Down(MouseButton::Left), - content.x + 2, - content.y + 2, + continue_rect.x, + continue_rect.y, )); - assert_eq!(app.state.onboarding_list.selected, 2); + assert!(app.state.request_complete_onboarding); } } diff --git a/src/app/input/settings.rs b/src/app/input/settings.rs index 9433b7d8..8bf06dc6 100644 --- a/src/app/input/settings.rs +++ b/src/app/input/settings.rs @@ -1,16 +1,19 @@ use crossterm::event::{KeyCode, KeyEvent, MouseButton, MouseEvent, MouseEventKind}; use ratatui::layout::Rect; -use crate::app::{ - state::{AppState, SettingsSection, THEME_NAMES}, - App, Mode, +use crate::{ + app::{ + state::{AppState, SettingsSection, THEME_NAMES}, + App, Mode, + }, + config::ToastDelivery, }; #[derive(Debug, Clone, PartialEq, Eq)] pub(super) enum SettingsAction { SaveTheme(String), SaveSound(bool), - SaveToast(bool), + SaveToastDelivery(ToastDelivery), } impl App { @@ -19,7 +22,7 @@ impl App { match action { SettingsAction::SaveTheme(name) => self.save_theme(&name), SettingsAction::SaveSound(enabled) => self.save_sound(enabled), - SettingsAction::SaveToast(enabled) => self.save_toast(enabled), + SettingsAction::SaveToastDelivery(delivery) => self.save_toast_delivery(delivery), } } } @@ -37,6 +40,22 @@ fn current_theme_index(theme_name: &str) -> usize { .unwrap_or(0) } +fn toast_delivery_index(delivery: ToastDelivery) -> usize { + match delivery { + ToastDelivery::Off => 0, + ToastDelivery::Herdr => 1, + ToastDelivery::Terminal => 2, + } +} + +fn toast_delivery_for_index(idx: usize) -> ToastDelivery { + match idx { + 0 => ToastDelivery::Off, + 1 => ToastDelivery::Herdr, + _ => ToastDelivery::Terminal, + } +} + fn preview_selected_theme(state: &mut AppState) { use crate::app::state::Palette; @@ -111,7 +130,7 @@ pub(super) fn update_settings_state(state: &mut AppState, key: KeyEvent) -> Opti } KeyCode::Tab | KeyCode::Right | KeyCode::Char('l') => { state.settings.section = SettingsSection::Toast; - state.settings.list.selected = usize::from(!state.toast_config.enabled); + state.settings.list.selected = toast_delivery_index(state.toast_delivery()); } KeyCode::BackTab | KeyCode::Left | KeyCode::Char('h') => { state.settings.section = SettingsSection::Theme; @@ -123,13 +142,13 @@ pub(super) fn update_settings_state(state: &mut AppState, key: KeyEvent) -> Opti }, }, SettingsSection::Toast => 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::Up | KeyCode::Char('k') => state.settings.list.move_prev(), + KeyCode::Down | KeyCode::Char('j') => state.settings.list.move_next(3), KeyCode::Enter | KeyCode::Char(' ') => { - let enabled = state.settings.list.selected == 0; - state.toast_config.enabled = enabled; - return Some(SettingsAction::SaveToast(enabled)); + let delivery = toast_delivery_for_index(state.settings.list.selected); + state.toast_config.delivery = delivery; + state.toast = None; + return Some(SettingsAction::SaveToastDelivery(delivery)); } KeyCode::BackTab | KeyCode::Left | KeyCode::Char('h') => { state.settings.section = SettingsSection::Sound; @@ -149,7 +168,7 @@ pub(super) fn update_settings_state(state: &mut AppState, key: KeyEvent) -> Opti None } -pub(super) fn open_settings(state: &mut AppState) { +pub(crate) fn open_settings(state: &mut AppState) { state.settings.original_palette = Some(state.palette.clone()); state.settings.original_theme = Some(state.theme_name.clone()); state.settings.section = SettingsSection::Theme; @@ -159,7 +178,7 @@ pub(super) fn open_settings(state: &mut AppState) { impl AppState { fn settings_popup_rect(&self) -> Rect { - crate::ui::centered_popup_rect(self.screen_rect(), 56, 20).unwrap_or_default() + crate::ui::centered_popup_rect(self.screen_rect(), 76, 22).unwrap_or_default() } fn settings_inner_rect(&self) -> Rect { @@ -212,14 +231,22 @@ impl AppState { let idx = scroll + (row - area.y) as usize; (idx < THEME_NAMES.len()).then_some(idx) } - SettingsSection::Sound | SettingsSection::Toast => { - let list_y = area.y + 2; + SettingsSection::Sound => { + let list_y = area.y + 3; if row >= list_y && row < list_y + 2 { Some((row - list_y) as usize) } else { None } } + SettingsSection::Toast => { + let list_y = area.y + 3; + if row >= list_y && row < list_y + 6 { + Some(((row - list_y) / 2) as usize) + } else { + None + } + } } } @@ -231,7 +258,7 @@ impl AppState { self.settings.list.select(match section { SettingsSection::Theme => current_theme_index(&self.theme_name), SettingsSection::Sound => usize::from(!self.sound_enabled()), - SettingsSection::Toast => usize::from(!self.toast_config.enabled), + SettingsSection::Toast => toast_delivery_index(self.toast_delivery()), }); return None; } @@ -248,9 +275,10 @@ impl AppState { Some(SettingsAction::SaveSound(enabled)) } SettingsSection::Toast => { - let enabled = idx == 0; - self.toast_config.enabled = enabled; - Some(SettingsAction::SaveToast(enabled)) + let delivery = toast_delivery_for_index(idx); + self.toast_config.delivery = delivery; + self.toast = None; + Some(SettingsAction::SaveToastDelivery(delivery)) } }; } diff --git a/src/app/mod.rs b/src/app/mod.rs index ff6dc7ac..78681e06 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -254,8 +254,6 @@ impl App { request_complete_onboarding: false, name_input: String::new(), name_input_replace_on_type: false, - onboarding_step: 0, - onboarding_list: state::SelectionListState::new(1), release_notes: startup_release_notes.map(|notes| state::ReleaseNotesState { version: notes.version, body: notes.body, @@ -401,7 +399,7 @@ impl App { if self.state.request_complete_onboarding { self.state.request_complete_onboarding = false; - self.complete_onboarding(); + self.open_settings_from_onboarding(); needs_render = true; } @@ -528,30 +526,9 @@ impl App { } } - pub(crate) fn complete_onboarding(&mut self) { - let (sound_enabled, toast_enabled) = match self.state.onboarding_list.selected { - 0 => (false, false), - 1 => (false, true), - 2 => (true, false), - _ => (true, true), - }; - - match crate::config::save_onboarding_choices(sound_enabled, toast_enabled) { - Ok(()) => { - self.state.sound.enabled = sound_enabled; - self.state.toast_config.enabled = toast_enabled; - self.state.mode = if self.state.active.is_some() { - Mode::Terminal - } else { - Mode::Navigate - }; - } - Err(err) => { - self.state.config_diagnostic = - Some(format!("failed to save onboarding config: {err}")); - self.config_diagnostic_deadline = Some(Instant::now() + Duration::from_secs(8)); - } - } + pub(crate) fn open_settings_from_onboarding(&mut self) { + self.mark_onboarding_complete(); + crate::app::input::open_settings(&mut self.state); } pub(crate) fn reload_keybinds(&mut self) { @@ -1395,12 +1372,10 @@ mod tests { fn route_client_input_advances_onboarding_modal() { let mut app = test_app(); app.state.mode = Mode::Onboarding; - app.state.onboarding_step = 0; app.route_client_input(b"\r".to_vec()); - assert_eq!(app.state.onboarding_step, 1); - assert_eq!(app.state.mode, Mode::Onboarding); + assert_eq!(app.state.mode, Mode::Settings); } #[test] diff --git a/src/app/state.rs b/src/app/state.rs index 67f89606..e54b431a 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -1,4 +1,4 @@ -use crate::config::{Keybinds, SoundConfig, ToastConfig}; +use crate::config::{Keybinds, SoundConfig, ToastConfig, ToastDelivery}; use crossterm::event::{KeyCode, KeyModifiers}; use ratatui::layout::{Direction, Rect}; use ratatui::style::Color; @@ -602,8 +602,6 @@ pub struct AppState { pub request_complete_onboarding: bool, pub name_input: String, pub name_input_replace_on_type: bool, - pub onboarding_step: usize, - pub onboarding_list: SelectionListState, pub release_notes: Option<ReleaseNotesState>, pub keybind_help: KeybindHelpState, pub workspace_scroll: usize, @@ -665,6 +663,10 @@ impl AppState { self.sound.enabled } + pub fn toast_delivery(&self) -> ToastDelivery { + self.toast_config.delivery + } + pub fn is_prefix(&self, key: &crossterm::event::KeyEvent) -> bool { key_matches(key, self.prefix_code, self.prefix_mods) } @@ -722,8 +724,6 @@ impl AppState { request_complete_onboarding: false, name_input: String::new(), name_input_replace_on_type: false, - onboarding_step: 0, - onboarding_list: SelectionListState::new(1), release_notes: None, keybind_help: KeybindHelpState { scroll: 0 }, workspace_scroll: 0, |
