Compare commits

...

8 Commits

Author SHA1 Message Date
pap-openai
9ea6958472 Merge branch 'main' into kpp_check_for_alt_keys 2025-08-01 01:15:32 +01:00
easong-openai
041c18a3c9 Merge branch 'main' into kpp_check_for_alt_keys 2025-07-31 15:48:32 -07:00
pap
05f6e56aba fmt 2025-07-31 22:49:34 +01:00
pap
d2875d47ac fmt 2025-07-31 22:40:50 +01:00
pap
58dbea787f fmt 2025-07-31 22:27:16 +01:00
pap-openai
d1c18fae50 Merge branch 'main' into kpp_check_for_alt_keys 2025-07-31 22:05:35 +01:00
pap
a527223a59 changing newline hint + unit test if kpp enabled/disabled 2025-07-31 22:02:18 +01:00
pap
6efe3474d4 add kpp check 2025-07-31 14:47:59 +01:00
5 changed files with 98 additions and 1 deletions

1
codex-rs/Cargo.lock generated
View File

@@ -854,6 +854,7 @@ dependencies = [
"image",
"insta",
"lazy_static",
"libc",
"mcp-types",
"path-clean",
"pretty_assertions",

View File

@@ -32,6 +32,7 @@ color-eyre = "0.6.3"
crossterm = { version = "0.28.1", features = ["bracketed-paste"] }
image = { version = "^0.25.6", default-features = false, features = ["jpeg"] }
lazy_static = "1"
libc = "0.2"
mcp-types = { path = "../mcp-types" }
path-clean = "1.0.1"
ratatui = { version = "0.29.0", features = [

View File

@@ -712,11 +712,16 @@ impl WidgetRef for &ChatComposer<'_> {
Span::from(" to quit"),
]
} else {
let newline_hint = if crate::tui::is_kkp_enabled() {
"Shift+⏎"
} else {
"Ctrl+J"
};
vec![
Span::from(" "),
"".set_style(key_hint_style),
Span::from(" send "),
"Shift+⏎".set_style(key_hint_style),
newline_hint.set_style(key_hint_style),
Span::from(" newline "),
"Ctrl+C".set_style(key_hint_style),
Span::from(" quit"),
@@ -961,6 +966,9 @@ mod tests {
use ratatui::Terminal;
use ratatui::backend::TestBackend;
// First, run snapshots with KKP enabled so hints show Shift+⏎.
crate::tui::set_kkp_for_tests(true);
let (tx, _rx) = std::sync::mpsc::channel();
let sender = AppEventSender::new(tx);
let mut terminal = match Terminal::new(TestBackend::new(100, 10)) {
@@ -1005,6 +1013,18 @@ mod tests {
assert_snapshot!(name, terminal.backend());
}
// Also add one snapshot with KKP disabled so we still see Ctrl+J.
crate::tui::set_kkp_for_tests(false);
let mut terminal_ctrlj = match Terminal::new(TestBackend::new(100, 10)) {
Ok(t) => t,
Err(e) => panic!("Failed to create terminal: {e}"),
};
let composer = ChatComposer::new(true, sender.clone());
terminal_ctrlj
.draw(|f| f.render_widget_ref(&composer, f.area()))
.unwrap_or_else(|e| panic!("Failed to draw empty_ctrlj composer: {e}"));
assert_snapshot!("empty_ctrlj", terminal_ctrlj.backend());
}
#[test]

View File

@@ -0,0 +1,14 @@
---
source: tui/src/bottom_pane/chat_composer.rs
expression: terminal_ctrlj.backend()
---
"▌ ... "
"▌ "
"▌ "
"▌ "
"▌ "
"▌ "
"▌ "
"▌ "
"▌ "
" ⏎ send Ctrl+J newline Ctrl+C quit "

View File

@@ -1,6 +1,8 @@
use std::io::Result;
use std::io::Stdout;
use std::io::stdout;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use codex_core::config::Config;
use crossterm::event::DisableBracketedPaste;
@@ -18,6 +20,60 @@ use crate::custom_terminal::Terminal;
/// A type alias for the terminal type used in this application
pub type Tui = Terminal<CrosstermBackend<Stdout>>;
// Global flag indicating whether Kitty Keyboard Protocol (KKP) appears enabled.
static KKP_ENABLED: AtomicBool = AtomicBool::new(false);
/// Return whether KKP (alternate key reporting) appears enabled.
pub(crate) fn is_kkp_enabled() -> bool {
KKP_ENABLED.load(Ordering::Relaxed)
}
#[cfg(test)]
pub(crate) fn set_kkp_for_tests(value: bool) {
KKP_ENABLED.store(value, Ordering::Relaxed);
}
/// Try to detect Kitty Keyboard Protocol support by issuing a progressive
/// enhancement query and waiting briefly for a response.
#[cfg(unix)]
fn detect_kitty_protocol() -> std::io::Result<bool> {
use std::io::Read;
use std::io::Write;
use std::io::{self};
use std::os::unix::io::AsRawFd;
let mut stdout = io::stdout();
let mut stdin = io::stdin();
// Send query for progressive enhancement + DA1
write!(stdout, "\x1b[?u\x1b[c")?;
stdout.flush()?;
// Wait up to ~200ms for a response
let fd = stdin.as_raw_fd();
let mut pfd = libc::pollfd {
fd,
events: libc::POLLIN,
revents: 0,
};
let rc = unsafe { libc::poll(&mut pfd as *mut libc::pollfd, 1, 200) };
if rc > 0 && (pfd.revents & libc::POLLIN) != 0 {
let mut buf = [0u8; 256];
if let Ok(n) = stdin.read(&mut buf) {
let response = String::from_utf8_lossy(&buf[..n]);
if response.contains("[?") && response.contains('u') {
return Ok(true);
}
}
}
Ok(false)
}
#[cfg(not(unix))]
fn detect_kitty_protocol() -> std::io::Result<bool> {
Ok(false)
}
/// Initialize the terminal (inline viewport; history stays in normal scrollback)
pub fn init(_config: &Config) -> Result<Tui> {
execute!(stdout(), EnableBracketedPaste)?;
@@ -34,6 +90,11 @@ pub fn init(_config: &Config) -> Result<Tui> {
| KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
)
)?;
// Detect KKP availability; used to adjust UI hints in the composer.
let kkp = detect_kitty_protocol().unwrap_or(false);
KKP_ENABLED.store(kkp, Ordering::Relaxed);
set_panic_hook();
let backend = CrosstermBackend::new(stdout());