Skip to main content

ai_agent/services/
notifier.rs

1// Source: /data/home/swei/claudecode/openclaudecode/src/services/notifier.ts
2//! Notifier service - sends system notifications.
3//!
4////! Translates notifier.ts from claude code.
5
6pub const DEFAULT_TITLE: &str = "Claude Code";
7
8#[derive(Debug, Clone)]
9pub struct NotificationOptions {
10    pub message: String,
11    pub title: Option<String>,
12    pub notification_type: String,
13}
14
15impl NotificationOptions {
16    pub fn new(message: impl Into<String>, notification_type: impl Into<String>) -> Self {
17        Self {
18            message: message.into(),
19            title: None,
20            notification_type: notification_type.into(),
21        }
22    }
23
24    pub fn with_title(mut self, title: impl Into<String>) -> Self {
25        self.title = Some(title.into());
26        self
27    }
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum NotificationChannel {
32    Auto,
33    Iterm2,
34    Iterm2WithBell,
35    Kitty,
36    Ghostty,
37    TerminalBell,
38    NotificationsDisabled,
39    None,
40    Error,
41    NoMethodAvailable,
42}
43
44impl NotificationChannel {
45    pub fn from_str(s: &str) -> Self {
46        match s {
47            "auto" => NotificationChannel::Auto,
48            "iterm2" => NotificationChannel::Iterm2,
49            "iterm2_with_bell" => NotificationChannel::Iterm2WithBell,
50            "kitty" => NotificationChannel::Kitty,
51            "ghostty" => NotificationChannel::Ghostty,
52            "terminal_bell" => NotificationChannel::TerminalBell,
53            "notifications_disabled" => NotificationChannel::NotificationsDisabled,
54            _ => NotificationChannel::None,
55        }
56    }
57
58    pub fn as_str(&self) -> &'static str {
59        match self {
60            NotificationChannel::Auto => "auto",
61            NotificationChannel::Iterm2 => "iterm2",
62            NotificationChannel::Iterm2WithBell => "iterm2_with_bell",
63            NotificationChannel::Kitty => "kitty",
64            NotificationChannel::Ghostty => "ghostty",
65            NotificationChannel::TerminalBell => "terminal_bell",
66            NotificationChannel::NotificationsDisabled => "disabled",
67            NotificationChannel::None => "none",
68            NotificationChannel::Error => "error",
69            NotificationChannel::NoMethodAvailable => "no_method_available",
70        }
71    }
72}
73
74pub fn generate_kitty_id() -> u32 {
75    use std::time::{SystemTime, UNIX_EPOCH};
76
77    let nanos = SystemTime::now()
78        .duration_since(UNIX_EPOCH)
79        .unwrap_or_default()
80        .subsec_nanos();
81
82    (nanos % 10000) as u32
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_notification_options() {
91        let opts = NotificationOptions::new("Test message", "test");
92        assert_eq!(opts.message, "Test message");
93        assert_eq!(opts.notification_type, "test");
94        assert!(opts.title.is_none());
95    }
96
97    #[test]
98    fn test_notification_options_with_title() {
99        let opts = NotificationOptions::new("Test message", "test").with_title("Test Title");
100        assert_eq!(opts.title, Some("Test Title".to_string()));
101    }
102
103    #[test]
104    fn test_notification_channel_from_str() {
105        assert_eq!(
106            NotificationChannel::from_str("auto"),
107            NotificationChannel::Auto
108        );
109        assert_eq!(
110            NotificationChannel::from_str("iterm2"),
111            NotificationChannel::Iterm2
112        );
113        assert_eq!(
114            NotificationChannel::from_str("kitty"),
115            NotificationChannel::Kitty
116        );
117        assert_eq!(
118            NotificationChannel::from_str("unknown"),
119            NotificationChannel::None
120        );
121    }
122
123    #[test]
124    fn test_generate_kitty_id() {
125        let id = generate_kitty_id();
126        assert!(id < 10000);
127    }
128}