aboutsummaryrefslogtreecommitdiff
path: root/src/config/model.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-06 17:41:44 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-06 17:42:38 +0300
commit4001fd2ec8ff05a982da18744b8cbde9df3ab530 (patch)
treef8dbf984cff9a4b025e86481d3f70c89322bd79f /src/config/model.rs
parent048d8a29195d580014b24ee3fc7e89ac6bdfee8a (diff)
feat: improve toast ergonomics
refs #486
Diffstat (limited to 'src/config/model.rs')
-rw-r--r--src/config/model.rs123
1 files changed, 122 insertions, 1 deletions
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 {
@@ -50,6 +52,28 @@ pub enum ToastDelivery {
}
#[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 {
Current,
@@ -122,6 +146,22 @@ fn parse_right_click_passthrough_modifier(value: &str) -> Option<Option<KeyModif
#[derive(Debug, Clone)]
pub struct ToastConfig {
pub delivery: ToastDelivery,
+ pub delay_seconds: u64,
+ pub herdr: HerdrToastConfig,
+ pub clipboard: ClipboardToastConfig,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
+#[serde(default)]
+pub struct HerdrToastConfig {
+ pub position: ToastHerdrPosition,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
+#[serde(default)]
+pub struct ClipboardToastConfig {
+ pub enabled: bool,
+ pub position: ToastClipboardPosition,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
@@ -600,6 +640,26 @@ impl Default for ToastConfig {
fn default() -> 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<ToastDelivery>,
enabled: Option<bool>,
+ delay_seconds: Option<u64>,
+ 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]
@@ -1031,6 +1137,21 @@ delivery = "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::<Config>(&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();
assert!(config.should_show_onboarding());