Codex Handbook
tui/src/notifications/mod.rs 135 lines
mod bel;mod osc9;use std::io;use bel::BelBackend;use codex_config::types::NotificationMethod;use codex_terminal_detection::TerminalInfo;use codex_terminal_detection::TerminalName;use codex_terminal_detection::terminal_info;use osc9::Osc9Backend;#[derive(Debug)]pub enum DesktopNotificationBackend {    Osc9(Osc9Backend),    Bel(BelBackend),}impl DesktopNotificationBackend {    pub fn for_method(method: NotificationMethod) -> Self {        match method {            NotificationMethod::Auto => {                if supports_osc9(&terminal_info()) {                    Self::Osc9(Osc9Backend::new())                } else {                    Self::Bel(BelBackend)                }            }            NotificationMethod::Osc9 => Self::Osc9(Osc9Backend::new()),            NotificationMethod::Bel => Self::Bel(BelBackend),        }    }    pub fn method(&self) -> NotificationMethod {        match self {            DesktopNotificationBackend::Osc9(_) => NotificationMethod::Osc9,            DesktopNotificationBackend::Bel(_) => NotificationMethod::Bel,        }    }    pub fn notify(&mut self, message: &str) -> io::Result<()> {        match self {            DesktopNotificationBackend::Osc9(backend) => backend.notify(message),            DesktopNotificationBackend::Bel(backend) => backend.notify(message),        }    }}pub fn detect_backend(method: NotificationMethod) -> DesktopNotificationBackend {    DesktopNotificationBackend::for_method(method)}fn supports_osc9(terminal: &TerminalInfo) -> bool {    matches!(        terminal.name,        TerminalName::Ghostty            | TerminalName::Iterm2            | TerminalName::Kitty            | TerminalName::WarpTerminal            | TerminalName::WezTerm    )}#[cfg(test)]mod tests {    use super::detect_backend;    use super::supports_osc9;    use codex_config::types::NotificationMethod;    use codex_terminal_detection::TerminalInfo;    use codex_terminal_detection::TerminalName;    use pretty_assertions::assert_eq;    fn test_terminal(name: TerminalName) -> TerminalInfo {        TerminalInfo {            name,            term_program: None,            version: None,            term: None,            multiplexer: None,        }    }    #[test]    fn selects_osc9_method() {        assert!(matches!(            detect_backend(NotificationMethod::Osc9),            super::DesktopNotificationBackend::Osc9(_)        ));    }    #[test]    fn selects_bel_method() {        assert!(matches!(            detect_backend(NotificationMethod::Bel),            super::DesktopNotificationBackend::Bel(_)        ));    }    #[test]    fn supports_osc9_for_supported_terminals() {        for name in [            TerminalName::Ghostty,            TerminalName::Iterm2,            TerminalName::Kitty,            TerminalName::WarpTerminal,            TerminalName::WezTerm,        ] {            assert!(                supports_osc9(&test_terminal(name)),                "{name:?} should support OSC 9"            );        }    }    #[test]    fn supports_osc9_for_unsupported_terminals() {        for name in [            TerminalName::AppleTerminal,            TerminalName::Alacritty,            TerminalName::Dumb,            TerminalName::GnomeTerminal,            TerminalName::Konsole,            TerminalName::Unknown,            TerminalName::VsCode,            TerminalName::Vte,            TerminalName::WindowsTerminal,        ] {            assert_eq!(                supports_osc9(&test_terminal(name)),                false,                "{name:?} should not support OSC 9"            );        }    }}