From 7b2008f57adb75230cb0f50d8938b73249e0fdfa Mon Sep 17 00:00:00 2001 From: Genki Takiuchi Date: Sun, 28 Sep 2025 01:45:48 +0900 Subject: [PATCH 1/2] Fix IME submissions dropping leading digits --- 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 a7793205f5a..3fa8684f6b2 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -2043,6 +2043,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 559744a4623..227262f1519 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; + } + let mut out = std::mem::take(&mut self.buffer); + if let Some((ch, _)) = self.pending_first_char.take() { + out.push(ch); } + self.clear_after_explicit_paste(); + if out.is_empty() { None } else { Some(out) } } /// Clear only the timing window and any pending first-char. From 593336dde75aec087d5bd385d0a2667a891b8ecf Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Mon, 20 Oct 2025 18:16:42 -0700 Subject: [PATCH 2/2] Keep the active false and Some(out) --- codex-rs/tui/src/bottom_pane/paste_burst.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codex-rs/tui/src/bottom_pane/paste_burst.rs b/codex-rs/tui/src/bottom_pane/paste_burst.rs index 227262f1519..33a02bdefa6 100644 --- a/codex-rs/tui/src/bottom_pane/paste_burst.rs +++ b/codex-rs/tui/src/bottom_pane/paste_burst.rs @@ -201,12 +201,12 @@ impl PasteBurst { if !self.is_active() { return None; } + self.active = false; let mut out = std::mem::take(&mut self.buffer); - if let Some((ch, _)) = self.pending_first_char.take() { + if let Some((ch, _at)) = self.pending_first_char.take() { out.push(ch); } - self.clear_after_explicit_paste(); - if out.is_empty() { None } else { Some(out) } + Some(out) } /// Clear only the timing window and any pending first-char.