Skip to content

fix(desktop): guard IME composition Enter key on macOS Chromium edge case - #59319

Open
aotian16 wants to merge 1 commit into
NousResearch:mainfrom
aotian16:fix/ime-composition-recent-guard
Open

fix(desktop): guard IME composition Enter key on macOS Chromium edge case#59319
aotian16 wants to merge 1 commit into
NousResearch:mainfrom
aotian16:fix/ime-composition-recent-guard

Conversation

@aotian16

@aotian16 aotian16 commented Jul 6, 2026

Copy link
Copy Markdown

Problem

When using a Chinese IME on macOS (e.g. macOS Pinyin), pressing Enter to confirm an IME candidate sometimes submits the draft message instead of committing the composed text.

Root Cause

The existing IME guards check composingRef.current (set by compositionstart/compositionend) and event.nativeEvent.isComposing. However, macOS Chinese IMEs trigger a known Chromium edge case where the Enter keydown event is dispatched after compositionend without isComposing=true. Both guards miss this window, so Enter reaches submitDraft().

Fix

Introduce recentCompositionRef, set to true on compositionend and cleared after a 100ms timeout. The keydown handler now checks composingRef.current || event.nativeEvent.isComposing || recentCompositionRef.current, which covers all IME timing scenarios.

Testing

  1. Open Hermes Desktop
  2. Switch to Chinese Pinyin IME
  3. Type English (or Pinyin) characters
  4. Press Enter to confirm the IME candidate
  5. Press Enter again (empty draft to submit) — should submit, not compose

…onend edge case

On macOS, some Chinese IMEs trigger a Chromium bug where the Enter
keydown event is dispatched *after* compositionend without setting
nativeEvent.isComposing=true.  The existing composingRef + isComposing
guards both miss this window, so pressing Enter to confirm an IME
candidate fires submitDraft() instead.

Fix: introduce recentCompositionRef, set true on compositionend and
cleared after 100ms.  The keydown handler checks this flag as a third
guard alongside composingRef and isComposing, covering all known IME
timing scenarios.
@alt-glitch alt-glitch added type/bug Something isn't working comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have labels Jul 6, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #37483 (canonical issue — Enter sends message during IME composition). Competing/alternate fix to #37487 (adds !isComposing guard to the Enter handlers). This PR instead adds a recentCompositionRef 100ms window to catch the macOS Chromium case where the Enter keydown arrives after compositionend without isComposing=true. Maintainer to pick between the two mechanisms.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for targeting the post-compositionend Chromium sequence; current main still lacks that guard at apps/desktop/src/app/chat/composer/index.tsx:370.

Problems

  • apps/desktop/src/app/chat/composer/index.tsx:736 schedules an independent clear on every composition end. If two compositions finish within 100 ms, the first callback can clear recentCompositionRef before the second window expires, re-opening the reported submit path.
  • Commit c4751749d0ba13cb3deaa1c94404f2900899cb7f adds no regression test. The existing apps/desktop/src/app/chat/composer/ime-composition-dom-repro.test.tsx:61 covers draft synchronization, not an Enter dispatched after compositionend with isComposing=false.

Suggested changes

  • Cancel or supersede the prior timer so only the latest composition-end window can clear the guard, and clean it up on unmount.
  • Add a fake-timer regression test for the stated event ordering, including consecutive compositions and a normal Enter after expiration.

Automated hermes-sweeper review.


// Chromium on macOS sometimes dispatches the Enter keydown event
// *after* compositionend, without setting isComposing=true on the
// native event. The brief guard window prevents that Enter from

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each composition end creates an independent timer. If another composition ends before this callback runs, this older callback can clear the shared ref during the newer composition's 100 ms window. Store and cancel the prior timer (or use a generation token) before scheduling this callback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants