hermes - 💡(How to fix) Fix Desktop app: Send button doesn't switch from voice button when typing Chinese (IME composition)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

The hasComposerPayload variable in apps/desktop/src/app/chat/composer/index.tsx only checks the draft state:

const hasComposerPayload = draft.trim().length > 0 || attachments.length > 0

However, during IME composition, the handleEditorInput function returns early and doesn't update draft:

const handleEditorInput = (event: FormEvent<HTMLDivElement>) => {
    if (composingRef.current) {
      return  // Skip during IME composition
    }
    // ...
}

Fix Action

Solution

Modify hasComposerPayload to also check if the editor has content directly:

const hasComposerPayload = draft.trim().length > 0 || 
                          attachments.length > 0 || 
                          (editorRef.current && composerPlainText(editorRef.current).trim().length > 0)

Additionally, trigger re-renders when IME composition state changes:

onCompositionStart={() => {
  composingRef.current = true
  setComposingVersion(v => v + 1)  // Force re-render
}}
onCompositionEnd={() => {
  composingRef.current = false
  setComposingVersion(v => v + 1)  // Force re-render
}}

Code Example

const hasComposerPayload = draft.trim().length > 0 || attachments.length > 0

---

const handleEditorInput = (event: FormEvent<HTMLDivElement>) => {
    if (composingRef.current) {
      return  // Skip during IME composition
    }
    // ...
}

---

const hasComposerPayload = draft.trim().length > 0 || 
                          attachments.length > 0 || 
                          (editorRef.current && composerPlainText(editorRef.current).trim().length > 0)

---

onCompositionStart={() => {
  composingRef.current = true
  setComposingVersion(v => v + 1)  // Force re-render
}}
onCompositionEnd={() => {
  composingRef.current = false
  setComposingVersion(v => v + 1)  // Force re-render
}}
RAW_BUFFERClick to expand / collapse

Bug Description

When typing Chinese using an IME (Input Method Editor) on the Windows desktop app, the voice button doesn't switch to the send button until the text is committed (e.g., by pressing space). This creates a poor user experience for CJK input users.

Steps to Reproduce

  1. Open Hermes desktop app on Windows
  2. Focus on the composer input
  3. Switch to a Chinese IME (e.g., Microsoft Pinyin)
  4. Start typing pinyin (e.g., "nihao")
  5. Observe that the voice button remains as-is during composition
  6. Press space to commit the Chinese characters
  7. The button only changes to send after pressing delete or space

Expected Behavior

The voice button should switch to the send button as soon as the user starts typing, regardless of whether they're using an IME or not.

Root Cause

The hasComposerPayload variable in apps/desktop/src/app/chat/composer/index.tsx only checks the draft state:

const hasComposerPayload = draft.trim().length > 0 || attachments.length > 0

However, during IME composition, the handleEditorInput function returns early and doesn't update draft:

const handleEditorInput = (event: FormEvent<HTMLDivElement>) => {
    if (composingRef.current) {
      return  // Skip during IME composition
    }
    // ...
}

Solution

Modify hasComposerPayload to also check if the editor has content directly:

const hasComposerPayload = draft.trim().length > 0 || 
                          attachments.length > 0 || 
                          (editorRef.current && composerPlainText(editorRef.current).trim().length > 0)

Additionally, trigger re-renders when IME composition state changes:

onCompositionStart={() => {
  composingRef.current = true
  setComposingVersion(v => v + 1)  // Force re-render
}}
onCompositionEnd={() => {
  composingRef.current = false
  setComposingVersion(v => v + 1)  // Force re-render
}}

Environment

  • OS: Windows 10
  • Hermes Version: v0.15.1 (2026.5.29)
  • Input Method: Microsoft Pinyin (or any CJK IME)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING