From 4001fd2ec8ff05a982da18744b8cbde9df3ab530 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Sat, 6 Jun 2026 17:41:44 +0300 Subject: feat: improve toast ergonomics refs #486 --- src/config/model.rs | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) (limited to 'src/config/model.rs') diff --git a/src/config/model.rs b/src/config/model.rs index 3b8b2cb7..c36150f6 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -8,6 +8,8 @@ use super::{ DEFAULT_MOUSE_SCROLL_LINES, DEFAULT_SCROLLBACK_LIMIT_BYTES, }; +pub const MAX_TOAST_DELAY_SECONDS: u64 = 3600; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)] #[serde(rename_all = "lowercase")] pub enum UpdateChannelConfig { @@ -49,6 +51,28 @@ pub enum ToastDelivery { System, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)] +#[serde(rename_all = "kebab-case")] +pub enum ToastHerdrPosition { + TopLeft, + TopRight, + BottomLeft, + #[default] + BottomRight, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)] +#[serde(rename_all = "kebab-case")] +pub enum ToastClipboardPosition { + TopLeft, + TopCenter, + TopRight, + BottomLeft, + #[default] + BottomCenter, + BottomRight, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)] #[serde(rename_all = "lowercase")] pub enum AgentPanelScopeConfig { @@ -122,6 +146,22 @@ fn parse_right_click_passthrough_modifier(value: &str) -> Option Self { Self { delivery: ToastDelivery::Off, + delay_seconds: 1, + herdr: HerdrToastConfig::default(), + clipboard: ClipboardToastConfig::default(), + } + } +} + +impl Default for HerdrToastConfig { + fn default() -> Self { + Self { + position: ToastHerdrPosition::BottomRight, + } + } +} + +impl Default for ClipboardToastConfig { + fn default() -> Self { + Self { + enabled: true, + position: ToastClipboardPosition::BottomCenter, } } } @@ -614,6 +674,9 @@ impl<'de> Deserialize<'de> for ToastConfig { struct RawToastConfig { delivery: Option, enabled: Option, + delay_seconds: Option, + herdr: HerdrToastConfig, + clipboard: ClipboardToastConfig, } let raw = RawToastConfig::deserialize(deserializer)?; @@ -622,7 +685,19 @@ impl<'de> Deserialize<'de> for ToastConfig { Some(false) | None => ToastDelivery::Off, }; let delivery = raw.delivery.unwrap_or(legacy_delivery); - Ok(Self { delivery }) + let default = Self::default(); + let delay_seconds = raw.delay_seconds.unwrap_or(default.delay_seconds); + if delay_seconds > MAX_TOAST_DELAY_SECONDS { + return Err(de::Error::custom(format!( + "ui.toast.delay_seconds must be between 0 and {MAX_TOAST_DELAY_SECONDS}" + ))); + } + Ok(Self { + delivery, + delay_seconds, + herdr: raw.herdr, + clipboard: raw.clipboard, + }) } } @@ -984,9 +1059,40 @@ mouse_scroll_lines = 0 let toml = r#" [ui.toast] delivery = "terminal" +delay_seconds = 2 + +[ui.toast.herdr] +position = "top-left" + +[ui.toast.clipboard] +enabled = false +position = "top-center" "#; let config: Config = toml::from_str(toml).unwrap(); assert_eq!(config.ui.toast.delivery, ToastDelivery::Terminal); + assert_eq!(config.ui.toast.delay_seconds, 2); + assert_eq!(config.ui.toast.herdr.position, ToastHerdrPosition::TopLeft); + assert!(!config.ui.toast.clipboard.enabled); + assert_eq!( + config.ui.toast.clipboard.position, + ToastClipboardPosition::TopCenter + ); + } + + #[test] + fn toast_config_defaults_preserve_existing_behavior_with_delay() { + let config = Config::default(); + assert_eq!(config.ui.toast.delivery, ToastDelivery::Off); + assert_eq!(config.ui.toast.delay_seconds, 1); + assert_eq!( + config.ui.toast.herdr.position, + ToastHerdrPosition::BottomRight + ); + assert!(config.ui.toast.clipboard.enabled); + assert_eq!( + config.ui.toast.clipboard.position, + ToastClipboardPosition::BottomCenter + ); } #[test] @@ -1030,6 +1136,21 @@ delivery = "terminal" assert_eq!(config.ui.toast.delivery, ToastDelivery::Terminal); } + #[test] + fn toast_config_rejects_unbounded_delay() { + let toml = format!( + r#" +[ui.toast] +delay_seconds = {} +"#, + MAX_TOAST_DELAY_SECONDS + 1 + ); + + let error = toml::from_str::(&toml).unwrap_err().to_string(); + + assert!(error.contains("ui.toast.delay_seconds must be between 0 and 3600")); + } + #[test] fn missing_onboarding_shows_setup() { let config = Config::default(); -- cgit v1.2.3