mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
fix(cli): clamp scrollback box widths + suppress status bar after resize (#25975)
When the terminal shrinks, already-printed box-drawing rules (response, reasoning, streaming TTS, background-task Panels) reflow into multiple narrower rows — visible as duplicated horizontal separators / ghost lines in scrollback. Similarly, prompt_toolkit redraws a fresh status bar on SIGWINCH on top of one the terminal just reflowed, producing double-bar artifacts on column shrink. Two surgical changes: 1. Decorative scrollback boxes now use a new `HermesCLI._scrollback_box_width()` helper that clamps to `max(32, min(width, 56))`. The live TUI footer is unaffected and still uses the full width. Covers: streaming response box (open + close), reasoning box (open + close, both streaming and post-stream paths), streaming-TTS box close, final-response Rich Panel, and the background-task Rich Panel. 2. `_recover_after_resize()` now also sets a new `_status_bar_suppressed_after_resize` flag so the dynamic status bar and both input separator rules stay hidden until the next user input. The flag is cleared in the process loop the moment the user submits their next prompt, restoring chrome cleanly. Tests: - New `test_input_rules_hide_after_resize_until_next_input` covers the flag's effect on rule heights. - New `test_scrollback_box_width_caps_to_resize_safe_value` covers the helper at floor / cap / mid-range / overflow. - Existing resize-recovery test extended to assert the flag flips. Refs: #18449 #19280 #22976 Salvage of #24403. Co-authored-by: Szymonclawd <szymonclawd@mac.home>
This commit is contained in:
parent
f491b07cb2
commit
2844c888f1
4 changed files with 98 additions and 8 deletions
|
|
@ -79,6 +79,10 @@ class TestForceFullRedraw:
|
|||
SIGWINCH removes it and ``_replay_output_history`` cannot
|
||||
reconstruct it. The fix is to only reset the renderer cache and
|
||||
let ``original_on_resize`` recalculate layout.
|
||||
|
||||
Additionally, ``_status_bar_suppressed_after_resize`` must be set
|
||||
so the input rules and status bar hide until the next user input,
|
||||
preventing duplicated-bar artifacts on column shrink (#19280).
|
||||
"""
|
||||
app = MagicMock()
|
||||
events = []
|
||||
|
|
@ -86,6 +90,8 @@ class TestForceFullRedraw:
|
|||
app.invalidate.side_effect = lambda: events.append("invalidate")
|
||||
original_on_resize = lambda: events.append("original_resize")
|
||||
|
||||
# bare_cli skips __init__, so seed the attribute the way __init__ would.
|
||||
bare_cli._status_bar_suppressed_after_resize = False
|
||||
bare_cli._recover_after_resize(app, original_on_resize)
|
||||
|
||||
assert events == [
|
||||
|
|
@ -97,6 +103,8 @@ class TestForceFullRedraw:
|
|||
app.renderer.output.erase_screen.assert_not_called()
|
||||
app.renderer.output.write_raw.assert_not_called()
|
||||
app.renderer.output.cursor_goto.assert_not_called()
|
||||
# Status bar / input rules must be suppressed until the next prompt.
|
||||
assert bare_cli._status_bar_suppressed_after_resize is True
|
||||
|
||||
def test_force_redraw_uses_full_screen_clear_without_scrollback_clear(self, bare_cli):
|
||||
app = MagicMock()
|
||||
|
|
|
|||
|
|
@ -332,6 +332,38 @@ class TestCLIStatusBar:
|
|||
assert cli_obj._tui_input_rule_height("bottom", width=50) == 0
|
||||
assert cli_obj._tui_input_rule_height("bottom", width=90) == 1
|
||||
|
||||
def test_input_rules_hide_after_resize_until_next_input(self):
|
||||
"""When _status_bar_suppressed_after_resize is set, both rules hide.
|
||||
|
||||
See _recover_after_resize — column shrink reflows already-rendered
|
||||
bars into scrollback, so we hide the separators until the user
|
||||
submits the next input, at which point the flag is cleared.
|
||||
"""
|
||||
cli_obj = _make_cli()
|
||||
cli_obj._status_bar_suppressed_after_resize = True
|
||||
|
||||
assert cli_obj._tui_input_rule_height("top", width=90) == 0
|
||||
assert cli_obj._tui_input_rule_height("bottom", width=90) == 0
|
||||
|
||||
cli_obj._status_bar_suppressed_after_resize = False
|
||||
assert cli_obj._tui_input_rule_height("top", width=90) == 1
|
||||
assert cli_obj._tui_input_rule_height("bottom", width=90) == 1
|
||||
|
||||
def test_scrollback_box_width_caps_to_resize_safe_value(self):
|
||||
"""Decorative scrollback boxes clamp to a width small enough that
|
||||
moderate terminal shrinks don't cause reflow into scrollback."""
|
||||
from cli import HermesCLI
|
||||
|
||||
# Floor at 32 — narrow terminals still get something usable.
|
||||
assert HermesCLI._scrollback_box_width(20) == 32
|
||||
assert HermesCLI._scrollback_box_width(32) == 32
|
||||
# Cap at 56 — wide terminals don't get full-width boxes.
|
||||
assert HermesCLI._scrollback_box_width(80) == 56
|
||||
assert HermesCLI._scrollback_box_width(120) == 56
|
||||
assert HermesCLI._scrollback_box_width(200) == 56
|
||||
# Mid-range passes through up to the cap.
|
||||
assert HermesCLI._scrollback_box_width(48) == 48
|
||||
|
||||
def test_agent_spacer_reclaimed_on_narrow_terminals(self):
|
||||
cli_obj = _make_cli()
|
||||
cli_obj._agent_running = True
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue