fix: IME Shift+punctuation requires double-press on macOS - #983
Conversation
📝 WalkthroughWalkthroughChangesIME keyboard handling
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/modules/terminal/lib/rendererPool.ts`:
- Around line 255-264: Restrict the keyCode 229 IME fallback in the renderer’s
key handler to macOS by adding the existing IS_MAC guard to the condition before
resolving the leaf and forwarding event.key; preserve the current character and
modifier checks for the macOS path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 43d21249-0f96-4541-803c-dc5b7c32fea8
📒 Files selected for processing (2)
src/modules/shortcuts/lib/useGlobalShortcuts.tssrc/modules/terminal/lib/rendererPool.ts
| if (event.keyCode === 229 && event.type === "keydown") { | ||
| const leafId = slot.currentLeafId; | ||
| const bridge = leafId !== null ? adapter?.resolveLeaf(leafId) : null; | ||
| if (bridge && event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) { | ||
| bridge.writeToPty(event.key); | ||
| event.preventDefault(); | ||
| return false; | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Chrome keydown keyCode 229 preventDefault effect on IME text insertion Windows Linux
💡 Result:
The keyCode 229 is the Windows Virtual-Key code (VK_PROCESSKEY) used by browsers to indicate that an Input Method Editor (IME) is processing a keystroke [1][2][3]. When a user types using an IME, the browser fires keydown events with this keyCode to signal that the IME has intercepted the key event [1][4]. Using event.preventDefault on a keydown event with keyCode 229 does not reliably prevent text insertion in Chrome on Windows or Linux [5][6][2]. Because the IME has already processed the keystroke before or simultaneously with the browser's event handling, the browser's attempt to cancel the default action typically fails to override the IME's operation [3][7]. Consequently, text will often still be committed to the input field regardless of the preventDefault call [6][2][7]. To manage text input effectively while an IME is active, you should avoid relying on keydown/keyup events with keyCode 229 [2][4]. Instead, use the modern Composition Events API: 1. compositionstart: Fired when an IME composition session begins. 2. compositionupdate: Fired when the composition value changes. 3. compositionend: Fired when the composition session is finished and the text is committed [7]. For scenarios where you need to restrict or sanitize user input (e.g., allowing only numeric characters), combining these composition events with the input event is the recommended approach, as the input event captures the final committed text [6][4]. Attempting to filter text via keydown is generally considered an anti-pattern when IME support is required [2][4].
Citations:
- 1: https://www.w3.org/2008/webapps/wiki/Legacy_key_events
- 2: https://stackoverflow.com/questions/51624624/keyboard-code-229-not-working-with-preventdefault
- 3: preventDefault() of keydown event should not kill composition start caused by the keydown w3c/uievents#180
- 4: https://stackoverflow.com/questions/36753548/keycode-on-android-is-always-229
- 5: https://issues.chromium.org/41368867
- 6: https://stackoverflow.com/questions/59212595/event-preventdefault-doesnt-work-with-keycode-229
- 7: https://stackoverflow.com/questions/58177953/javascript-event-preventdefault-does-not-work-for-japanese-ime
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="src/modules/terminal/lib/rendererPool.ts"
echo "## Around the reviewed block"
sed -n '220,310p' "$file" | cat -n
echo
echo "## IS_MAC references in file"
rg -n "IS_MAC|keyCode === 229|composition|preventDefault" "$file"
echo
echo "## File outline"
ast-grep outline "$file" --view expandedRepository: crynta/terax-ai
Length of output: 8848
Gate the 229 IME fallback to macOS src/modules/terminal/lib/rendererPool.ts:255-264
On Windows/Linux, keyCode === 229 also routes through IME processing, and forwarding event.key here can double-insert characters outside the macOS Chinese IME case. IS_MAC is already in scope, so scope this handler to macOS.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/modules/terminal/lib/rendererPool.ts` around lines 255 - 264, Restrict
the keyCode 229 IME fallback in the renderer’s key handler to macOS by adding
the existing IS_MAC guard to the condition before resolving the leaf and
forwarding event.key; preserve the current character and modifier checks for the
macOS path.
Source: Path instructions
Problem
On macOS with Chinese IME active, Shift+digit keys (producing
@#¥etc.) require pressing twice — the first press is silently consumed.Root Cause
xterm.js
attachCustomKeyEventHandlerreturnedfalsefor allkeyCode=229events. This skips xterm's_keyDownentirely, falling through toCompositionHelper._handleAnyTextareaChanges()which uses a textarea value-diff to detect input. However, the first Shift press triggers an IME mode-switch on macOS that doesn't update the textarea value, so the diff is empty and the character is lost.Fix
When
keyCode=229butisComposing=falseandevent.keyis a single printable character, write it directly to the PTY viawriteToPty(), bypassing the textarea-diff path entirely.Also adds an IME guard to the global shortcut listener to prevent interference during composition.
Testing
@), Shift+3 (#), Shift+4 (¥)Summary by CodeRabbit