Skip to content

fix: stop terminal border flashing from cursor blink and emoji spinners - #470

Closed
ygd58 wants to merge 1 commit into
NousResearch:mainfrom
ygd58:fix/terminal-cursor-blink-flashing
Closed

ygd58 wants to merge 1 commit into
NousResearch:mainfrom
ygd58:fix/terminal-cursor-blink-flashing

Conversation

@ygd58

@ygd58 ygd58 commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #464

Problem

On Ubuntu 24.04 with ghostty + tmux, the prompt input box border lines
flash repeatedly due to two causes:

  1. Terminal cursor blink triggering full border redraws
  2. Emoji-based spinner animations (brain/moon/sparkle) causing rapid redraws

Changes

  • cli.py: Add CursorShape.STEADY_BLOCK to Application() to disable
    blinking cursor
  • run_agent.py: Replace random emoji spinner types with stable dots
    spinner during thinking phase
  • agent/display.py: Increase spinner frame interval from 0.12s to
    0.25s to reduce terminal redraw frequency

Tested

Ubuntu 24.04, ghostty + tmux, local llama.cpp server

@ygd58

ygd58 commented Mar 6, 2026

Copy link
Copy Markdown
Contributor Author

Update: after extensive testing with @q4220, we've confirmed the root cause.

The spinner writes directly to the terminal using \r inside prompt_toolkit's
patch_stdout context, which triggers a full layout redraw (including borders)
on every frame — causing the flashing.

Current fixes in this PR:

  • CursorShape.STEADY_BLOCK for prompt_toolkit cursor
  • dots spinner instead of emoji spinners
  • Slower frame rate (0.25s)
  • patch_stdout-aware stdout writes

HERMES_SPINNER_PAUSE=1 env var can be used as a workaround to disable
the spinner entirely.

Proper long-term fix would be integrating the thinking spinner as a
prompt_toolkit Window component instead of raw \r writes — happy to
work on that as a follow-up PR if this one gets merged.

@ygd58

ygd58 commented Mar 6, 2026

Copy link
Copy Markdown
Contributor Author

@q4220 great to hear! Glad it's working now 🙏

For anyone following along — the fix was moving the spinner from raw \r
terminal writes into prompt_toolkit's layout system via a thinking_callback.
This eliminated the patch_stdout conflict that was causing the border flashing.

@q4220

q4220 commented Mar 7, 2026

Copy link
Copy Markdown

well done, awesome job. So often the little things are hard hey.

So just to be clear, the spinner is not appearing but there is no flashing and the thinking text is there.

However, it seems as though a line of thinking text is persisting after the agent turn is complete as shown in the image

fix branch:
image

main branch:
image

@ygd58

ygd58 commented Mar 7, 2026

Copy link
Copy Markdown
Contributor Author

@q4220 pushed a fix for the persisting thinking text — spinner now clears
when agent finishes.

git fetch ygd58
git pull ygd58 fix/terminal-cursor-blink-flashing

Let us know! 🙏

@ygd58
ygd58 force-pushed the fix/terminal-cursor-blink-flashing branch from 4bfec38 to 72c7305 Compare March 9, 2026 06:04
teknium1 added a commit that referenced this pull request Mar 10, 2026
… widget

Cherry-picked and improved from PR #470 (fixes #464).

Problem: On Ubuntu 24.04 with ghostty + tmux, the prompt input box
border lines flash due to cursor blink and raw spinner terminal writes
conflicting with prompt_toolkit's rendering.

Changes:
- cli.py: Add CursorShape.BLOCK to Application() to disable cursor blink
- cli.py: Add thinking_callback + spinner_widget in TUI layout so
  thinking status displays as a proper prompt_toolkit widget instead of
  raw terminal writes that conflict with the TUI renderer
- run_agent.py: Add thinking_callback parameter to AIAgent; when set,
  uses the callback instead of KawaiiSpinner for thinking display

What was NOT changed (preserving existing behavior):
- agent/display.py: Untouched. KawaiiSpinner _write() stdout capture,
  _animate() logic, and 0.12s frame interval all preserved. This
  protects subagent stdout redirection and keeps smooth animations
  for non-CLI contexts (gateway, batch runner).
- Original emoji spinner types (brain/sparkle/pulse/moon/star) preserved
  for all non-CLI contexts.

Fixes from original PR #470:
- CursorShape.STEADY_BLOCK -> CursorShape.BLOCK (STEADY_BLOCK doesn't
  exist in prompt_toolkit 3.0.52)
- Removed duplicate self._spinner_text = '' line
- Removed redundant nested if-checks

Tested: 2706 tests pass, interactive CLI verified via tmux.
…der flashing

Root cause: spinner was writing \r directly to terminal inside patch_stdout
context, triggering full prompt_toolkit layout redraws on every frame.

Changes:
- cli.py: Add _on_thinking() callback, spinner_widget as FormattedTextControl
  Window in HSplit layout, CursorShape.STEADY_BLOCK, clear spinner on finish
- run_agent.py: Pass thinking_callback to agent, remove KawaiiSpinner calls
- agent/display.py: Remove cursor escape codes that leaked as raw text,
  use patch_stdout-aware stdout writes, slower frame rate (0.25s)

Tested with @q4220 on Ubuntu 24.04 ghostty+tmux — no flashing, thinking
text visible, clears correctly after agent finishes.

Fixes NousResearch#464
@ygd58
ygd58 force-pushed the fix/terminal-cursor-blink-flashing branch from 72c7305 to 76af49b Compare March 10, 2026 07:04
@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for submitting this fix, @ygd58! The core idea was solid — we cherry-picked and improved your approach in commit ee40084, which addresses the flashing issue by routing CLI thinking through a proper prompt_toolkit widget (avoiding the raw spinner vs TUI conflict entirely). We also added spinner flush rate-limiting in e4adb67 on top of that.

Since the fix is already on main (and issue #464 is closed), closing this PR. Appreciate you identifying the problem and putting together a fix! 🙏

@teknium1 teknium1 closed this Mar 10, 2026
angelburgosrosado pushed a commit to angelburgosrosado/hermes-agent that referenced this pull request Apr 27, 2026
… widget

Cherry-picked and improved from PR NousResearch#470 (fixes NousResearch#464).

Problem: On Ubuntu 24.04 with ghostty + tmux, the prompt input box
border lines flash due to cursor blink and raw spinner terminal writes
conflicting with prompt_toolkit's rendering.

Changes:
- cli.py: Add CursorShape.BLOCK to Application() to disable cursor blink
- cli.py: Add thinking_callback + spinner_widget in TUI layout so
  thinking status displays as a proper prompt_toolkit widget instead of
  raw terminal writes that conflict with the TUI renderer
- run_agent.py: Add thinking_callback parameter to AIAgent; when set,
  uses the callback instead of KawaiiSpinner for thinking display

What was NOT changed (preserving existing behavior):
- agent/display.py: Untouched. KawaiiSpinner _write() stdout capture,
  _animate() logic, and 0.12s frame interval all preserved. This
  protects subagent stdout redirection and keeps smooth animations
  for non-CLI contexts (gateway, batch runner).
- Original emoji spinner types (brain/sparkle/pulse/moon/star) preserved
  for all non-CLI contexts.

Fixes from original PR NousResearch#470:
- CursorShape.STEADY_BLOCK -> CursorShape.BLOCK (STEADY_BLOCK doesn't
  exist in prompt_toolkit 3.0.52)
- Removed duplicate self._spinner_text = '' line
- Removed redundant nested if-checks

Tested: 2706 tests pass, interactive CLI verified via tmux.
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
… widget

Cherry-picked and improved from PR NousResearch#470 (fixes NousResearch#464).

Problem: On Ubuntu 24.04 with ghostty + tmux, the prompt input box
border lines flash due to cursor blink and raw spinner terminal writes
conflicting with prompt_toolkit's rendering.

Changes:
- cli.py: Add CursorShape.BLOCK to Application() to disable cursor blink
- cli.py: Add thinking_callback + spinner_widget in TUI layout so
  thinking status displays as a proper prompt_toolkit widget instead of
  raw terminal writes that conflict with the TUI renderer
- run_agent.py: Add thinking_callback parameter to AIAgent; when set,
  uses the callback instead of KawaiiSpinner for thinking display

What was NOT changed (preserving existing behavior):
- agent/display.py: Untouched. KawaiiSpinner _write() stdout capture,
  _animate() logic, and 0.12s frame interval all preserved. This
  protects subagent stdout redirection and keeps smooth animations
  for non-CLI contexts (gateway, batch runner).
- Original emoji spinner types (brain/sparkle/pulse/moon/star) preserved
  for all non-CLI contexts.

Fixes from original PR NousResearch#470:
- CursorShape.STEADY_BLOCK -> CursorShape.BLOCK (STEADY_BLOCK doesn't
  exist in prompt_toolkit 3.0.52)
- Removed duplicate self._spinner_text = '' line
- Removed redundant nested if-checks

Tested: 2706 tests pass, interactive CLI verified via tmux.
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
… widget

Cherry-picked and improved from PR NousResearch#470 (fixes NousResearch#464).

Problem: On Ubuntu 24.04 with ghostty + tmux, the prompt input box
border lines flash due to cursor blink and raw spinner terminal writes
conflicting with prompt_toolkit's rendering.

Changes:
- cli.py: Add CursorShape.BLOCK to Application() to disable cursor blink
- cli.py: Add thinking_callback + spinner_widget in TUI layout so
  thinking status displays as a proper prompt_toolkit widget instead of
  raw terminal writes that conflict with the TUI renderer
- run_agent.py: Add thinking_callback parameter to AIAgent; when set,
  uses the callback instead of KawaiiSpinner for thinking display

What was NOT changed (preserving existing behavior):
- agent/display.py: Untouched. KawaiiSpinner _write() stdout capture,
  _animate() logic, and 0.12s frame interval all preserved. This
  protects subagent stdout redirection and keeps smooth animations
  for non-CLI contexts (gateway, batch runner).
- Original emoji spinner types (brain/sparkle/pulse/moon/star) preserved
  for all non-CLI contexts.

Fixes from original PR NousResearch#470:
- CursorShape.STEADY_BLOCK -> CursorShape.BLOCK (STEADY_BLOCK doesn't
  exist in prompt_toolkit 3.0.52)
- Removed duplicate self._spinner_text = '' line
- Removed redundant nested if-checks

Tested: 2706 tests pass, interactive CLI verified via tmux.
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
… widget

Cherry-picked and improved from PR NousResearch#470 (fixes NousResearch#464).

Problem: On Ubuntu 24.04 with ghostty + tmux, the prompt input box
border lines flash due to cursor blink and raw spinner terminal writes
conflicting with prompt_toolkit's rendering.

Changes:
- cli.py: Add CursorShape.BLOCK to Application() to disable cursor blink
- cli.py: Add thinking_callback + spinner_widget in TUI layout so
  thinking status displays as a proper prompt_toolkit widget instead of
  raw terminal writes that conflict with the TUI renderer
- run_agent.py: Add thinking_callback parameter to AIAgent; when set,
  uses the callback instead of KawaiiSpinner for thinking display

What was NOT changed (preserving existing behavior):
- agent/display.py: Untouched. KawaiiSpinner _write() stdout capture,
  _animate() logic, and 0.12s frame interval all preserved. This
  protects subagent stdout redirection and keeps smooth animations
  for non-CLI contexts (gateway, batch runner).
- Original emoji spinner types (brain/sparkle/pulse/moon/star) preserved
  for all non-CLI contexts.

Fixes from original PR NousResearch#470:
- CursorShape.STEADY_BLOCK -> CursorShape.BLOCK (STEADY_BLOCK doesn't
  exist in prompt_toolkit 3.0.52)
- Removed duplicate self._spinner_text = '' line
- Removed redundant nested if-checks

Tested: 2706 tests pass, interactive CLI verified via tmux.
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.

[Bug]: Blinking cursor or switching emojis causing prompt lines to flash repeatedly in terminal

3 participants