mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
notifs
This commit is contained in:
@@ -51,6 +51,9 @@ pub(crate) struct App {
|
||||
|
||||
// Esc-backtracking state grouped
|
||||
pub(crate) backtrack: crate::app_backtrack::BacktrackState,
|
||||
|
||||
// Whether the terminal has focus (tracked via TuiEvent::FocusChanged)
|
||||
pub(crate) app_focused: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -93,8 +96,23 @@ impl App {
|
||||
deferred_history_lines: Vec::new(),
|
||||
commit_anim_running: Arc::new(AtomicBool::new(false)),
|
||||
backtrack: BacktrackState::default(),
|
||||
app_focused: Arc::new(AtomicBool::new(true)),
|
||||
};
|
||||
|
||||
// Periodic notification task: every 5 seconds, if unfocused, send a reminder.
|
||||
{
|
||||
let focused = app.app_focused.clone();
|
||||
tokio::spawn(async move {
|
||||
use tokio::time::{sleep, Duration};
|
||||
loop {
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
if !focused.load(Ordering::Relaxed) {
|
||||
crate::notifications::send_os_notification("Codex is running in the background");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let tui_events = tui.event_stream();
|
||||
tokio::pin!(tui_events);
|
||||
|
||||
@@ -126,6 +144,7 @@ impl App {
|
||||
}
|
||||
TuiEvent::FocusChanged(focused) => {
|
||||
self.chat_widget.set_input_focus(focused);
|
||||
self.app_focused.store(focused, Ordering::Relaxed);
|
||||
}
|
||||
TuiEvent::Paste(pasted) => {
|
||||
// Many terminals convert newlines to \r when pasting (e.g., iTerm2),
|
||||
|
||||
@@ -48,6 +48,7 @@ mod markdown_stream;
|
||||
pub mod onboarding;
|
||||
mod pager_overlay;
|
||||
mod render;
|
||||
mod notifications;
|
||||
mod session_log;
|
||||
mod shimmer;
|
||||
mod slash_command;
|
||||
|
||||
62
codex-rs/tui/src/notifications.rs
Normal file
62
codex-rs/tui/src/notifications.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use std::process::Command;
|
||||
|
||||
/// Send a simple OS notification with a fixed app title.
|
||||
/// Best-effort and silently ignores errors if the platform/tooling is unavailable.
|
||||
pub fn send_os_notification(message: &str) {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
fn detect_bundle_id() -> Option<&'static str> {
|
||||
use std::env;
|
||||
// Common terminal mappings.
|
||||
let term_program = env::var("TERM_PROGRAM").unwrap_or_default();
|
||||
match term_program.as_str() {
|
||||
"Apple_Terminal" => Some("com.apple.Terminal"),
|
||||
"iTerm.app" | "iTerm2" | "iTerm2.app" => Some("com.googlecode.iterm2"),
|
||||
"WezTerm" => Some("com.github.wez.wezterm"),
|
||||
"Alacritty" => Some("io.alacritty"),
|
||||
other => {
|
||||
// Fallback heuristics.
|
||||
let term = env::var("TERM").unwrap_or_default();
|
||||
if other.to_lowercase().contains("kitty") || term.contains("xterm-kitty") {
|
||||
Some("net.kovidgoyal.kitty")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer terminal-notifier on macOS and attempt to activate the current terminal on click.
|
||||
let mut cmd = Command::new("terminal-notifier");
|
||||
cmd.arg("-title").arg("Codex").arg("-message").arg(message);
|
||||
if let Some(bundle) = detect_bundle_id() {
|
||||
cmd.arg("-activate").arg(bundle);
|
||||
}
|
||||
let _ = cmd.spawn();
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
{
|
||||
// Use notify-send if available (Linux/BSD). Title first, then body.
|
||||
let _ = Command::new("notify-send")
|
||||
.arg("Codex")
|
||||
.arg(message)
|
||||
.spawn();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Best-effort: try a lightweight Toast via PowerShell if available.
|
||||
// Fall back to no-op if this fails.
|
||||
let ps = r#"
|
||||
Add-Type -AssemblyName System.Windows.Forms | Out-Null
|
||||
[System.Windows.Forms.MessageBox]::Show($args[0], 'Codex') | Out-Null
|
||||
"#;
|
||||
let _ = Command::new("powershell")
|
||||
.arg("-NoProfile")
|
||||
.arg("-Command")
|
||||
.arg(ps)
|
||||
.arg(message)
|
||||
.spawn();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user