fix: prevent Enter from sending during IME composition - #965
Conversation
WalkthroughIME composition detection is centralized in a shared helper and applied across desktop/mobile inputs, task controls, find handling, resizing, and application shortcuts to prevent keyboard actions from firing during composition. ChangesIME keyboard handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR addresses an IME/WebKit composition edge case where Enter (and other key handlers) can fire while the user is still composing text, by centralizing composition detection in a shared helper and using it to early-return in keydown handlers across desktop and mobile UI.
Changes:
- Add
isKeyboardEventComposinghelper to consistently detect IME composition (including a WebKit workaround). - Guard global shortcuts and multiple component keydown handlers to avoid acting on keys during composition.
- Replace direct
event.isComposingchecks with the shared helper in affected flows (find, chat, command palette, inputs, etc.).
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| apps/desktop/src/routing/app-shortcuts.ts | Skip app-level shortcut handling while IME composition is active. |
| apps/desktop/src/providers/note-find-provider.tsx | Prevent find-related key handlers (Escape/mod+F) from firing during composition. |
| apps/desktop/src/mobile/task-row.tsx | Prevent Enter/Space “edit” activation while composing text. |
| apps/desktop/src/mobile/search-input.tsx | Prevent Enter-to-blur behavior during composition. |
| apps/desktop/src/mobile/onboarding-icloud-section.tsx | Prevent Enter-to-create actions while composing in the input. |
| apps/desktop/src/mobile/new-graph-drawer.tsx | Prevent Enter-to-create actions while composing in the input. |
| apps/desktop/src/mobile/connect-github-drawer.tsx | Prevent Enter-to-continue actions while composing in wizard inputs. |
| apps/desktop/src/lib/keyboard.ts | Introduce shared IME composition detection helper with WebKit workaround. |
| apps/desktop/src/hooks/use-sidebar-resize.ts | Prevent keyboard-resize handling while IME composition is active. |
| apps/desktop/src/components/tasks/task-row.tsx | Prevent Enter/Space task selection behavior from firing during composition. |
| apps/desktop/src/components/settings/model-combobox.tsx | Prevent Enter-to-commit behavior while composing. |
| apps/desktop/src/components/settings/destructive-section.tsx | Prevent Enter-to-confirm destructive action while composing. |
| apps/desktop/src/components/note-find-bar.tsx | Prevent Escape/mod key handling during composition in the find UI. |
| apps/desktop/src/components/graph-chooser.tsx | Prevent Enter-to-create behavior while composing in graph name inputs. |
| apps/desktop/src/components/context-sidebar/attendee-combobox.tsx | Prevent Enter behavior during composition in attendee combobox. |
| apps/desktop/src/components/command-palette/command-palette.tsx | Prevent Enter/pointer-intent handling during composition in cmdk. |
| apps/desktop/src/components/chat/chat-input.tsx | Prevent Enter-to-send while composing (core motivation of the PR). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let isComposing = false | ||
| let timer : ReturnType<typeof setTimeout> | undefined | ||
|
|
||
| // Workaround for a bug in WebKit where the isComposing property is reset to false event when the IME is still composing. | ||
| // https://bugs.webkit.org/show_bug.cgi?id=311717 | ||
| export function isKeyboardEventComposing(event: KeyboardEvent): boolean { | ||
| if (event.isComposing) { | ||
| if (timer) { | ||
| clearTimeout(timer) | ||
| timer = undefined | ||
| } | ||
| isComposing = true | ||
| } else if (isComposing && !timer) { | ||
| timer = setTimeout(() => { | ||
| isComposing = false | ||
| timer = undefined | ||
| }, 40) | ||
| } | ||
|
|
||
| return isComposing | ||
| } |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@apps/desktop/src/components/context-sidebar/attendee-combobox.tsx`:
- Around line 154-156: Add stopPropagation() to the isKeyboardEventComposing
guard in the attendee combobox keydown handler before returning, while
preserving the existing default behavior by not preventing the event.
In `@apps/desktop/src/routing/app-shortcuts.ts`:
- Around line 318-320: Update onHistoryKeyDownCapture to call
isKeyboardEventComposing(event) at the start and return immediately when
composition is active, before any triggerCommand() logic. Keep the existing
bubbling onKeyDown guard unchanged.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3a2c8648-559f-4941-bf14-b8eb996831f0
📒 Files selected for processing (17)
apps/desktop/src/components/chat/chat-input.tsxapps/desktop/src/components/command-palette/command-palette.tsxapps/desktop/src/components/context-sidebar/attendee-combobox.tsxapps/desktop/src/components/graph-chooser.tsxapps/desktop/src/components/note-find-bar.tsxapps/desktop/src/components/settings/destructive-section.tsxapps/desktop/src/components/settings/model-combobox.tsxapps/desktop/src/components/tasks/task-row.tsxapps/desktop/src/hooks/use-sidebar-resize.tsapps/desktop/src/lib/keyboard.tsapps/desktop/src/mobile/connect-github-drawer.tsxapps/desktop/src/mobile/new-graph-drawer.tsxapps/desktop/src/mobile/onboarding-icloud-section.tsxapps/desktop/src/mobile/search-input.tsxapps/desktop/src/mobile/task-row.tsxapps/desktop/src/providers/note-find-provider.tsxapps/desktop/src/routing/app-shortcuts.ts
| if (isKeyboardEventComposing(keyEvent.nativeEvent)) { | ||
| return | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target file around the reported lines.
FILE="apps/desktop/src/components/context-sidebar/attendee-combobox.tsx"
wc -l "$FILE"
sed -n '120,190p' "$FILE"
# Find composition and cmdk-related handling in the same file.
rg -n "isKeyboardEventComposing|stopPropagation|cmdk|Enter|composition" "$FILE"
# Locate any local references to cmdk or the combobox component.
rg -n "attendee-combobox|Command|cmdk" apps/desktop/src -g '!**/dist/**' -g '!**/build/**'Repository: team-reflect/reflect-open
Length of output: 37550
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="apps/desktop/src/components/context-sidebar/attendee-combobox.tsx"
TEST="apps/desktop/src/components/context-sidebar/attendee-combobox.test.tsx"
# Show the component's root structure and event wiring.
sed -n '180,260p' "$FILE"
# Show the test coverage around keyboard/composition behavior.
wc -l "$TEST"
sed -n '1,220p' "$TEST"
# Look for any explicit IME/composition tests across the desktop app.
rg -n "composition|isKeyboardEventComposing|Composing|IME" apps/desktop/src -g '!**/dist/**' -g '!**/build/**'Repository: team-reflect/reflect-open
Length of output: 17268
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the composition helper and nearby explanation.
sed -n '1,120p' apps/desktop/src/lib/keyboard.ts
# Inspect sibling handlers that use the same composition guard.
sed -n '140,190p' apps/desktop/src/components/command-palette/command-palette.tsx
sed -n '30,70p' apps/desktop/src/components/note-find-bar.tsx
sed -n '90,130p' apps/desktop/src/components/tasks/task-row.tsx
sed -n '75,110p' apps/desktop/src/components/chat/chat-input.tsxRepository: team-reflect/reflect-open
Length of output: 6686
Stop propagation during composition.
This early return lets the IME keydown bubble to cmdk’s root Enter handler during the WebKit composition window, so an IME commit can still select an attendee. Call stopPropagation() before returning and leave default behavior alone.
🤖 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 `@apps/desktop/src/components/context-sidebar/attendee-combobox.tsx` around
lines 154 - 156, Add stopPropagation() to the isKeyboardEventComposing guard in
the attendee combobox keydown handler before returning, while preserving the
existing default behavior by not preventing the event.
Ignore the keydown that commits an IME composition (WebKit fires it after compositionend with isComposing false) so committing Japanese text can't send the chat message, select a cmdk item, or trigger app shortcuts.
Summary by CodeRabbit