Skip to content

Conversation

@seijikohara
Copy link

@seijikohara seijikohara commented Jan 28, 2026

Summary

This PR fixes a bug where pressing Enter to confirm IME (Input Method Editor) conversion unintentionally triggers message submission in chat interfaces.

Closes #19
Supersedes #278 (more comprehensive fix applied across all input components)

Problem

When users type with IME for East Asian languages, they press Enter to confirm character conversion (e.g., hiragana → kanji). The application incorrectly interpreted this Enter keypress as a submit action.

Root Cause

The justEndedRef flag reset logic was flawed:

// Before (incorrect)
if (hasCompositionJustEndedRef.current && keyCode !== 229) {
  hasCompositionJustEndedRef.current = false;
}

Since Enter has keyCode === 13 (not 229), the condition keyCode !== 229 evaluated to true, resetting the flag before the Enter key check could use it.

Event Sequence Diagram

sequenceDiagram
    participant User
    participant IME
    participant Browser
    participant App

    User->>IME: Type "nihon"
    IME->>Browser: compositionstart
    Browser->>App: Set isComposingRef = true

    User->>IME: Press Enter (confirm)
    IME->>Browser: compositionend
    Browser->>App: Set isComposingRef = false
    Browser->>App: Set justEndedRef = true
    Browser->>App: keydown (Enter)

    Note over App: Check justEndedRef → true
    App->>App: Skip submission ✓
    App->>App: Reset justEndedRef = false

    User->>IME: Press Enter (submit)
    Browser->>App: keydown (Enter)
    Note over App: Check justEndedRef → false
    App->>App: Submit message ✓
Loading

Solution

1. Fix Flag Reset Condition

Changed the reset condition to only reset on non-Enter keys:

// After (correct)
if (justEndedRef.current && e.key !== "Enter") {
  justEndedRef.current = false;
}

2. Multi-Layered IME Detection

Implemented a detection strategy for cross-browser compatibility:

flowchart TD
    A[keydown event] --> B{e.isComposing?}
    B -->|true| Z[Skip: IME active]
    B -->|false| C{native.isComposing?}
    C -->|true| Z
    C -->|false| D{isComposingRef?}
    D -->|true| Z
    D -->|false| E{keyCode === 229?}
    E -->|true| Z
    E -->|false| F{justEndedRef?}
    F -->|true| Y[Skip: Just confirmed]
    F -->|false| G[Process Enter]
Loading
Layer Method Purpose
Primary isComposing property Standard API (React/Native)
Secondary Composition event tracking Manual fallback
Tertiary keyCode === 229 Safari/WKWebView edge cases

3. Shared Utilities (DRY/SSOT)

Created centralized utilities to eliminate code duplication:

src/
├── utils/
│   └── ime.ts              # Single source of truth
└── hooks/
    └── useIMESafeEnterSubmit.ts  # Reusable hook

Comparison with #278

Aspect #278 This PR
Components fixed FloatingPromptInput only All 5 input components
Flag reset bug fix No Yes (e.key !== "Enter")
Shared utilities No Yes (DRY/SSOT)
Custom hook No Yes (useIMESafeEnterSubmit)
onBlur cleanup No Yes

Changes

File Change
src/utils/ime.ts New: Shared IME detection utilities
src/hooks/useIMESafeEnterSubmit.ts New: Custom hook for IME-safe Enter
src/components/FloatingPromptInput.tsx Modified: Use shared utilities
src/components/ClaudeCodeSession.tsx Modified: Use shared utilities
src/components/TimelineNavigator.tsx Modified: Use shared utilities
src/components/WebviewPreview.tsx Modified: Use shared utilities
src/components/AgentExecution.tsx Modified: Use shared utilities

Supported Languages

This implementation is language-agnostic and supports any IME using composition events:

  • Japanese (Hiragana/Katakana/Kanji)
  • Chinese (Pinyin, Wubi, Zhuyin, Cangjie)
  • Korean (Hangul)
  • Vietnamese (Telex, VNI)
  • Other composition-based input methods

Testing

  • TypeScript type check (tsc --noEmit)
  • Rust tests (9/9 passed)
  • Manual testing with Japanese IME on macOS
  • Verified: IME confirmation Enter does not submit
  • Verified: Subsequent Enter correctly submits

References

…bmission

- Add shared IME utilities (src/utils/ime.ts) as single source of truth
- Add useIMESafeEnterSubmit custom hook for reusable IME handling
- Fix justEndedRef reset condition from keyCode !== 229 to e.key !== "Enter"
- Implement multi-layered IME detection: isComposing, composition events, keyCode 229

Supports all IME languages: Japanese, Chinese, Korean, Vietnamese, etc.
@seijikohara seijikohara force-pushed the fix/ime-enter-handling branch from 63d5855 to ba4e6d2 Compare January 28, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Japanese strings sent before confirming

1 participant