IME composition text partially not displayed until the next key input

Resolved 💬 2 comments Opened Aug 26, 2025 by toda-bps Closed Oct 18, 2025

What version of Codex is running?

commit: 8192cf147e9db47c62289ce3e0332d063b59c89a (at least)

Which model were you using?

any

What platform is your computer?

macOS 15.6.1, Ghostty 1.1.3 (build 9438), Google 日本語入力 2.30.5590.1

What steps can reproduce the bug?

  1. Focus a text input/editor with a Japanese IME enabled.
  2. Start composing text (e.g., type “konnichiha” and convert).
  3. Observe the preedit/composition string while typing.
  4. Notice that some characters are missing until you press another key (e.g., space/arrow/any key), after which the missing characters suddenly appear.

What is the expected behavior?

The composition/preedit text updates and renders accurately on every composition event without requiring an extra keypress.

What do you see instead?

The composition text is partially missing or appears one keystroke late; it only updates after the next key input.

Additional information

When typing with a Japanese IME (composition/preedit), parts of the composition string are not rendered until an additional key is pressed. The
composition appears to “lag” by one input, making IME input unreliable.

For reference, applying this patch resolves the issue for me.

diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs
index fa320f9b..ea7be812 100644
--- a/codex-rs/tui/src/bottom_pane/chat_composer.rs
+++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs
@@ -808,6 +808,21 @@ impl ChatComposer {
             let has_ctrl_or_alt =
                 modifiers.contains(KeyModifiers::CONTROL) || modifiers.contains(KeyModifiers::ALT);
             if !has_ctrl_or_alt {
+                // Do not treat IME-finalized non-ASCII characters (e.g., Japanese)
+                // as part of the unbracketed paste "burst" detection.
+                // This ensures they render immediately upon confirmation without
+                // waiting for the next key event.
+                if !ch.is_ascii() {
+                    self.textarea.input(input);
+                    let text_after = self.textarea.text();
+                    self.pending_pastes
+                        .retain(|(placeholder, _)| text_after.contains(placeholder));
+                    // Reset burst-related state to avoid false positives while typing.
+                    self.last_plain_char_time = None;
+                    self.consecutive_plain_char_burst = 0;
+                    self.paste_burst_until = None;
+                    return (InputResult::None, true);
+                }
                 // Update burst heuristics.
                 match self.last_plain_char_time {
                     Some(prev) if now.duration_since(prev) <= PASTE_BURST_CHAR_INTERVAL => {

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗