Conversation
WalkthroughThis change modifies the IME (Input Method Editor) event handling in the terminal wrapper component. It replaces a blanket suppression approach for composition events with a time-based deduplication window of 20 milliseconds. The modification introduces conditional logic to suppress data during composition only when it occurs outside this window, extends the composition state reset to clear additional tracking fields, wraps the keydown handler with composition bypass logic, and adds logging for suppressed IME events. The public API signatures remain unchanged. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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 |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (1 file)
Review NotesThis PR refactors the IME (Input Method Editor) composition handling logic: Changes:
Logic: Verification:
No bugs or issues detected. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/app/view/term/termwrap.ts`:
- Around line 366-373: The dlog call inside the IME suppression branch is
logging raw IME payloads via data; change it to avoid exposing user text by
logging only metadata (e.g., data length, timeSinceCompositionEnd,
this.isComposing state, and IMEDedupWindowMs) or a masked representation instead
of the verbatim data. Update the logging call in the block where isComposing is
checked (referencing isComposing, lastCompositionEnd, IMEDedupWindowMs, and the
dlog invocation) to include those safe fields and remove the raw data value.
| // IME fix: suppress isComposing=true events unless they immediately follow | ||
| // a compositionend (within 20ms). This handles CapsLock input method switching | ||
| // where the composition buffer gets flushed as a spurious isComposing=true event | ||
| if (this.isComposing) { | ||
| dlog("Blocked data during composition:", data); | ||
| return; | ||
| } | ||
|
|
||
| // IME Deduplication (for Capslock input method switching) | ||
| // When switching input methods with Capslock during composition, some systems send the | ||
| // composed text twice. We allow the first send and block subsequent duplicates. | ||
| const IMEDedupWindowMs = 50; | ||
| const now = Date.now(); | ||
| const timeSinceCompositionEnd = now - this.lastCompositionEnd; | ||
| if (timeSinceCompositionEnd < IMEDedupWindowMs && data === this.lastComposedText && this.lastComposedText) { | ||
| if (!this.firstDataAfterCompositionSent) { | ||
| // First send after composition - allow it but mark as sent | ||
| this.firstDataAfterCompositionSent = true; | ||
| dlog("First data after composition, allowing:", data); | ||
| } else { | ||
| // Second send of the same data - this is a duplicate from Capslock switching, block it | ||
| dlog("Blocked duplicate IME data:", data); | ||
| this.lastComposedText = ""; // Clear to allow same text to be typed again later | ||
| this.firstDataAfterCompositionSent = false; | ||
| const timeSinceCompositionEnd = Date.now() - this.lastCompositionEnd; | ||
| if (timeSinceCompositionEnd > IMEDedupWindowMs) { | ||
| dlog("Suppressed IME data (composing, not near compositionend):", data); | ||
| return; |
There was a problem hiding this comment.
Avoid logging raw IME input payloads.
Line 372 logs user-typed text verbatim. Even under debug logging, this can expose sensitive data/PII. Log metadata only (length/timing/state).
🔒 Suggested safe logging change
- dlog("Suppressed IME data (composing, not near compositionend):", data);
+ dlog("Suppressed IME data (composing, not near compositionend)", {
+ length: data.length,
+ timeSinceCompositionEnd,
+ });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/app/view/term/termwrap.ts` around lines 366 - 373, The dlog call
inside the IME suppression branch is logging raw IME payloads via data; change
it to avoid exposing user text by logging only metadata (e.g., data length,
timeSinceCompositionEnd, this.isComposing state, and IMEDedupWindowMs) or a
masked representation instead of the verbatim data. Update the logging call in
the block where isComposing is checked (referencing isComposing,
lastCompositionEnd, IMEDedupWindowMs, and the dlog invocation) to include those
safe fields and remove the raw data value.
…v#3164) xterm.js v6.x defers compositionend's final text via setTimeout(0). On fast IME input — an issue that can affect any CJK IME — a next-keydown can reach the PTY before the deferred IME text, producing reordered output. Two-pronged fix: 1. term-model.ts: drop keydown events while event.isComposing or keyCode === 229, so xterm doesn't forward the composition trigger key to the PTY during composition. 2. termwrap.ts: on compositionend, immediately send e.data via sendDataHandler and queue it in pendingImeDedup. handleTermData drops the matching string when xterm's deferred setTimeout(0) onData fires it again. As a best-effort additional guard, reset xterm's internal _isSendingComposition flag so the deferred callback becomes a no-op when the field name isn't minified. The dedup queue (string[]) supports overlapping composition cycles for very fast input. History: This is the same class of bug fixed in wavetermdev#2938, but the patches added there were intentionally removed in wavetermdev#3095 ("upgrade xterm.js to v6.0.0") under the assumption that xterm v6 would handle composition correctly. v6.0.0's CompositionHelper still defers final-text onData via setTimeout(0), so the race resurfaced and was reported as wavetermdev#3164 against v0.14.4. This PR re-fixes it in a way compatible with the v6 codebase, using a dedup queue rather than the v5-era patches. Closes wavetermdev#3164
…v#3164) xterm.js v6.x defers compositionend's final text via setTimeout(0). On fast IME input — an issue that can affect any CJK IME — a next-keydown can reach the PTY before the deferred IME text, producing reordered output. Two-pronged fix: 1. term-model.ts: drop keydown events while event.isComposing or keyCode === 229, so xterm doesn't forward the composition trigger key to the PTY during composition. 2. termwrap.ts: on compositionend, immediately send e.data via sendDataHandler and queue it in pendingImeDedup. handleTermData drops the matching string when xterm's deferred setTimeout(0) onData fires it again. As a best-effort additional guard, reset xterm's internal _isSendingComposition flag so the deferred callback becomes a no-op when the field name isn't minified. The dedup queue (string[]) supports overlapping composition cycles for very fast input. History: This is the same class of bug fixed in wavetermdev#2938, but the patches added there were intentionally removed in wavetermdev#3095 ("upgrade xterm.js to v6.0.0") under the assumption that xterm v6 would handle composition correctly. v6.0.0's CompositionHelper still defers final-text onData via setTimeout(0), so the race resurfaced and was reported as wavetermdev#3164 against v0.14.4. This PR re-fixes it in a way compatible with the v6 codebase, using a dedup queue rather than the v5-era patches. Closes wavetermdev#3164
…v#3164) xterm.js v6.x defers compositionend's final text via setTimeout(0). On fast IME input — an issue that can affect any CJK IME — a next-keydown can reach the PTY before the deferred IME text, producing reordered output. Two-pronged fix: 1. term-model.ts: drop keydown events while event.isComposing or keyCode === 229, so xterm doesn't forward the composition trigger key to the PTY during composition. 2. termwrap.ts: on compositionend, immediately send e.data via sendDataHandler and queue it in pendingImeDedup. handleTermData drops the matching string when xterm's deferred setTimeout(0) onData fires it again. As a best-effort additional guard, reset xterm's internal _isSendingComposition flag so the deferred callback becomes a no-op when the field name isn't minified. The dedup queue (string[]) supports overlapping composition cycles for very fast input. History: This is the same class of bug fixed in wavetermdev#2938, but the patches added there were intentionally removed in wavetermdev#3095 ("upgrade xterm.js to v6.0.0") under the assumption that xterm v6 would handle composition correctly. v6.0.0's CompositionHelper still defers final-text onData via setTimeout(0), so the race resurfaced and was reported as wavetermdev#3164 against v0.14.4. This PR re-fixes it in a way compatible with the v6 codebase, using a dedup queue rather than the v5-era patches. Closes wavetermdev#3164
No description provided.