From ed32da04d7baaf0e61c0beb4c15b91c497115d6b Mon Sep 17 00:00:00 2001 From: Genki Takiuchi Date: Thu, 23 Oct 2025 07:18:17 +0900 Subject: [PATCH] Fix IME submissions dropping leading digits (#4359) - ensure paste burst flush preserves ASCII characters before IME commits - add regression test covering digit followed by Japanese text submission Fixes openai/codex#4356 Co-authored-by: Josh McKinney --- codex-rs/tui/src/bottom_pane/chat_composer.rs | 29 +++++++++++++++++++ codex-rs/tui/src/bottom_pane/paste_burst.rs | 13 +++++---- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index 51329eb1..7b4d2f7e 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -2069,6 +2069,35 @@ mod tests { } } + #[test] + fn ascii_prefix_survives_non_ascii_followup() { + use crossterm::event::KeyCode; + use crossterm::event::KeyEvent; + use crossterm::event::KeyModifiers; + + let (tx, _rx) = unbounded_channel::(); + let sender = AppEventSender::new(tx); + let mut composer = ChatComposer::new( + true, + sender, + false, + "Ask Codex to do anything".to_string(), + false, + ); + + let _ = composer.handle_key_event(KeyEvent::new(KeyCode::Char('1'), KeyModifiers::NONE)); + assert!(composer.is_in_paste_burst()); + + let _ = composer.handle_key_event(KeyEvent::new(KeyCode::Char('あ'), KeyModifiers::NONE)); + + let (result, _) = + composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); + match result { + InputResult::Submitted(text) => assert_eq!(text, "1あ"), + _ => panic!("expected Submitted"), + } + } + #[test] fn handle_paste_small_inserts_text() { use crossterm::event::KeyCode; diff --git a/codex-rs/tui/src/bottom_pane/paste_burst.rs b/codex-rs/tui/src/bottom_pane/paste_burst.rs index 559744a4..33a02bde 100644 --- a/codex-rs/tui/src/bottom_pane/paste_burst.rs +++ b/codex-rs/tui/src/bottom_pane/paste_burst.rs @@ -198,12 +198,15 @@ impl PasteBurst { /// Before applying modified/non-char input: flush buffered burst immediately. pub fn flush_before_modified_input(&mut self) -> Option { - if self.is_active() { - self.active = false; - Some(std::mem::take(&mut self.buffer)) - } else { - None + if !self.is_active() { + return None; } + self.active = false; + let mut out = std::mem::take(&mut self.buffer); + if let Some((ch, _at)) = self.pending_first_char.take() { + out.push(ch); + } + Some(out) } /// Clear only the timing window and any pending first-char.