hermes - 💡(How to fix) Fix Desktop] Enter key doesn't send message — must add trailing space

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…

On the Hermes Desktop app (Electron), pressing Enter after typing a message frequently has no reaction. The message does not send. Adding a trailing space before pressing Enter works around the issue. This affects Chinese/IME users disproportionately due to more complex input event timing.

Root Cause

The chat input uses a custom contentEditable div (not a plain <textarea>). The input handling library (@assistant-ui/react) manages a rich text editor with a hidden ComposerPrimitive.Input binding.

The key issue is a race condition between the DOM and React state:

  1. User types → DOM updates instantly (visible on screen)
  2. onInput handler fires → calls aui.composer().setText(nextDraft) to sync to React/AUI state
  3. User presses Enter → handleEditorKeyDown fires → submitDraft() reads draft from React state (useAuiState(s => s.composer.text))
  4. If React state hasn't caught up to the latest keystroke (which happens ~90% of the time with IME or fast typing), draft is empty → submitDraft() does nothing

Adding a trailing space triggers an extra onInput event, forcing a state sync, which is why it works around the bug.

Fix Action

Fix / Workaround

Workaround: Type a space at the end of the message, then press Enter → works 100%.

Patch

RAW_BUFFERClick to expand / collapse

title: "[Desktop] Enter key doesn't send message — must add trailing space (React state vs DOM race condition)"

Description

On the Hermes Desktop app (Electron), pressing Enter after typing a message frequently has no reaction. The message does not send. Adding a trailing space before pressing Enter works around the issue. This affects Chinese/IME users disproportionately due to more complex input event timing.

Steps to Reproduce

  1. Open Hermes Desktop app
  2. Type any text in the chat input (e.g. "你好")
  3. Press Enter directly

Expected: Message is sent immediately.

Actual: Nothing happens (~90% of attempts). Rarely, the message sends but is missing the last few characters.

Workaround: Type a space at the end of the message, then press Enter → works 100%.

Root Cause

The chat input uses a custom contentEditable div (not a plain <textarea>). The input handling library (@assistant-ui/react) manages a rich text editor with a hidden ComposerPrimitive.Input binding.

The key issue is a race condition between the DOM and React state:

  1. User types → DOM updates instantly (visible on screen)
  2. onInput handler fires → calls aui.composer().setText(nextDraft) to sync to React/AUI state
  3. User presses Enter → handleEditorKeyDown fires → submitDraft() reads draft from React state (useAuiState(s => s.composer.text))
  4. If React state hasn't caught up to the latest keystroke (which happens ~90% of the time with IME or fast typing), draft is empty → submitDraft() does nothing

Adding a trailing space triggers an extra onInput event, forcing a state sync, which is why it works around the bug.

Technical Details

Three components in apps/desktop/src/app/chat/composer/index.tsx need fixing:

1. handleEditorKeyDown (the Enter handler)

Before the queue-drain check (!busy && !hasComposerPayload && queuedPrompts.length > 0), read the text directly from the DOM editor node using composerPlainText(editorRef.current). If the DOM has content, bypass the stale hasComposerPayload check and call submitDraft() immediately.

2. submitDraft() (the submission function)

Replace all reads of the React state variable draft (derived from useAuiState) with reads from draftRef.current, which is synced from the DOM at the top of the function. This affects:

  • Slash command detection (SLASH_COMMAND_RE.test(draft.trim()))
  • Send payload (onSubmit(draft, ...))
  • Busy-mode queue/cancel decision

3. Busy-mode routing

In the busy path, use the DOM text to decide whether to queue (if DOM has content) vs cancel (if truly empty), rather than the stale React state variable Ee (hasComposerPayload = draft.trim().length > 0 || attachments.length > 0).

Patch

The bundled JS is in apps/desktop/release/win-unpacked/resources/app.asar.

Minified variable mapping for the fix:

  • x = React state draft (useAuiState(s => s.composer.text)) ← stale
  • te = draftRef (useRef) ← always current after DOM sync
  • O = editorRef (ref to contentEditable div)
  • $k = composerPlainText() (reads DOM text)
  • b = aui (assistant-ui context)
  • Ee = hasComposerPayload (uses x.trim())
  • xt = submitDraft() (submission function)
  • ot = handleEditorKeyDown (keyboard handler)

The fix replaces all x references inside xt() with te.current, and adds a DOM read guard in ot() before the queue-drain check.

Environment

  • Hermes version: 0.15.1
  • OS: Windows 10
  • Desktop app (Electron), not CLI

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