mirror of
https://github.com/openai/codex.git
synced 2026-02-02 06:57:03 +00:00
Compare commits
2 Commits
queue/stee
...
jif/codex-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38a34d8888 | ||
|
|
479d125a35 |
19
codex-rs/Cargo.lock
generated
19
codex-rs/Cargo.lock
generated
@@ -1460,6 +1460,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"mcp-types",
|
||||
"objc",
|
||||
"opentelemetry-appender-tracing",
|
||||
"pathdiff",
|
||||
"pretty_assertions",
|
||||
@@ -3634,6 +3635,15 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
@@ -4021,6 +4031,15 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||
dependencies = [
|
||||
"malloc_buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.2"
|
||||
|
||||
@@ -93,6 +93,9 @@ codex-windows-sandbox = { workspace = true }
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { workspace = true }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2"
|
||||
|
||||
# Clipboard support via `arboard` is not available on Android/Termux.
|
||||
# Only include it for non-Android targets so the crate builds on Android.
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
|
||||
@@ -32,6 +32,8 @@ use ratatui::text::Line;
|
||||
use ratatui::text::Span;
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::Wrap;
|
||||
use crate::notifications::post_notification;
|
||||
use crate::tui::TuiEvent::Paste;
|
||||
|
||||
/// Request coming from the agent that needs user approval.
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -74,7 +76,9 @@ impl ApprovalOverlay {
|
||||
current_complete: false,
|
||||
done: false,
|
||||
};
|
||||
view.set_current(request);
|
||||
if cfg!(not(test)) {
|
||||
post_notification("Hey, codex needs you");view.set_current(request);
|
||||
}
|
||||
view
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ mod markdown;
|
||||
mod markdown_render;
|
||||
mod markdown_stream;
|
||||
mod model_migration;
|
||||
mod notifications;
|
||||
pub mod onboarding;
|
||||
mod pager_overlay;
|
||||
pub mod public_widgets;
|
||||
|
||||
154
codex-rs/tui/src/notifications.rs
Normal file
154
codex-rs/tui/src/notifications.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use std::fmt;
|
||||
use std::io::stdout;
|
||||
|
||||
use ratatui::crossterm::Command;
|
||||
use ratatui::crossterm::execute;
|
||||
|
||||
pub(crate) fn post_notification(message: &str) -> bool {
|
||||
#[cfg(target_os = "macos")]
|
||||
if post_macos_notification(message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
post_ansi_notification(message)
|
||||
}
|
||||
|
||||
fn post_ansi_notification(message: &str) -> bool {
|
||||
let _ = execute!(stdout(), PostNotification(message.to_string()));
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn post_macos_notification(message: &str) -> bool {
|
||||
macos::post_notification(message)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn post_macos_notification(_: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Command that emits an OSC 9 desktop notification with a message.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PostNotification(pub String);
|
||||
|
||||
impl Command for PostNotification {
|
||||
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
||||
write!(f, "\x1b]9;{}\x07", self.0)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn execute_winapi(&self) -> std::io::Result<()> {
|
||||
Err(std::io::Error::other(
|
||||
"tried to execute PostNotification using WinAPI; use ANSI instead",
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn is_ansi_code_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "macos", test))]
|
||||
mod tests {
|
||||
use super::post_notification;
|
||||
|
||||
#[test]
|
||||
#[ignore = "triggers a real macOS notification; run manually when testing"]
|
||||
fn smoke_test_macos_notification() {
|
||||
assert!(
|
||||
post_notification("Codex macOS notification smoke test"),
|
||||
"expected post_notification to report success"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[allow(unexpected_cfgs)]
|
||||
mod macos {
|
||||
use objc::class;
|
||||
use objc::msg_send;
|
||||
use objc::rc::autoreleasepool;
|
||||
use objc::runtime::Object;
|
||||
use objc::sel;
|
||||
use objc::sel_impl;
|
||||
|
||||
#[link(name = "AppKit", kind = "framework")]
|
||||
unsafe extern "C" {}
|
||||
|
||||
#[link(name = "Foundation", kind = "framework")]
|
||||
unsafe extern "C" {}
|
||||
|
||||
pub(super) fn post_notification(message: &str) -> bool {
|
||||
autoreleasepool(|| deliver_notification(message))
|
||||
}
|
||||
|
||||
fn deliver_notification(message: &str) -> bool {
|
||||
unsafe {
|
||||
let notification = match create_notification() {
|
||||
Some(notification) => notification,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if !set_text(notification, "Codex", message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let center_class = class!(NSUserNotificationCenter);
|
||||
let center: *mut Object = msg_send![center_class, defaultUserNotificationCenter];
|
||||
if center.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let _: () = msg_send![center, deliverNotification: notification];
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_notification() -> Option<*mut Object> {
|
||||
let class = class!(NSUserNotification);
|
||||
let notification: *mut Object = msg_send![class, alloc];
|
||||
if notification.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let notification: *mut Object = msg_send![notification, init];
|
||||
if notification.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(msg_send![notification, autorelease])
|
||||
}
|
||||
|
||||
unsafe fn set_text(notification: *mut Object, title: &str, body: &str) -> bool {
|
||||
let Some(title) = nsstring(title) else {
|
||||
return false;
|
||||
};
|
||||
let Some(body) = nsstring(body) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let _: () = msg_send![notification, setTitle: title];
|
||||
let _: () = msg_send![notification, setInformativeText: body];
|
||||
true
|
||||
}
|
||||
|
||||
fn nsstring(value: &str) -> Option<*mut Object> {
|
||||
let utf16: Vec<u16> = value.encode_utf16().collect();
|
||||
unsafe {
|
||||
let string: *mut Object = msg_send![class!(NSString), alloc];
|
||||
if string.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let string: *mut Object =
|
||||
msg_send![string, initWithCharacters:utf16.as_ptr() length:utf16.len()];
|
||||
if string.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(msg_send![string, autorelease])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ use tokio_stream::Stream;
|
||||
|
||||
use crate::custom_terminal;
|
||||
use crate::custom_terminal::Terminal as CustomTerminal;
|
||||
use crate::notifications::PostNotification;
|
||||
#[cfg(unix)]
|
||||
use crate::tui::job_control::SUSPEND_KEY;
|
||||
#[cfg(unix)]
|
||||
@@ -482,25 +483,3 @@ fn spawn_frame_scheduler(
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Command that emits an OSC 9 desktop notification with a message.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PostNotification(pub String);
|
||||
|
||||
impl Command for PostNotification {
|
||||
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
||||
write!(f, "\x1b]9;{}\x07", self.0)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn execute_winapi(&self) -> Result<()> {
|
||||
Err(std::io::Error::other(
|
||||
"tried to execute PostNotification using WinAPI; use ANSI instead",
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn is_ansi_code_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user