refactor(cli): align model picker viewport with PR #11260 vocabulary

Match the row-budget naming introduced in PR #11260 for the approval and
clarify panels: rename chrome_reserve=14 into reserved_below=6 (input
chrome below the panel) + panel_chrome=6 (this panel's borders, blanks,
and hint row) + min_visible=3 (floor on visible items). Same arithmetic
as before, but a reviewer reading both files now sees the same handle.

Compact-chrome mode is intentionally not adopted — that pattern fits the
"fixed mandatory content might overflow" shape of approval/clarify
(solved by truncating with a marker), whereas the picker's overflow is
already handled by the scrolling viewport.
This commit is contained in:
Jorge 2026-04-17 21:45:50 +09:30 committed by Teknium
parent 5fbe16635b
commit 86f02d8d71
2 changed files with 19 additions and 18 deletions

18
cli.py
View file

@ -4520,16 +4520,18 @@ class HermesCLI:
scroll_offset: int,
n: int,
term_rows: int,
chrome_reserve: int = 14,
reserved_below: int = 6,
panel_chrome: int = 6,
min_visible: int = 3,
) -> tuple[int, int]:
"""Resolve (scroll_offset, visible_count) for the /model picker panel.
"""Resolve (scroll_offset, visible) for the /model picker viewport.
``term_rows - chrome_reserve`` caps how many rows the panel may use for
items; when the list overflows we slide the offset to keep ``selected``
on screen. The position counter sits in the bottom border, so no extra
row is reserved for it.
``reserved_below`` matches the approval / clarify panels input area,
status bar, and separators below the panel. ``panel_chrome`` covers
this panel's own borders + blanks + hint row. The remaining rows hold
the scrollable list, with the offset slid to keep ``selected`` on screen.
"""
max_visible = max(3, term_rows - chrome_reserve)
max_visible = max(min_visible, term_rows - reserved_below - panel_chrome)
if n <= max_visible:
return 0, n
visible = max_visible
@ -9537,7 +9539,7 @@ class HermesCLI:
from prompt_toolkit.application import get_app
term_rows = get_app().output.get_size().rows
except Exception:
term_rows = shutil.get_terminal_size((80, 24)).lines
term_rows = shutil.get_terminal_size((100, 24)).lines
scroll_offset, visible = HermesCLI._compute_model_picker_viewport(
selected, state.get("_scroll_offset", 0), len(choices), term_rows,
)

View file

@ -19,16 +19,15 @@ class TestPickerViewport:
assert visible == 5
def test_long_list_caps_visible_to_chrome_budget(self):
# 36 models, 30 terminal rows, chrome_reserve=14 → max_visible=16.
# Position counter lives in the bottom border, no row reserved.
# 30 rows minus reserved_below=6 minus panel_chrome=6 → max_visible=18.
offset, visible = _compute(selected=0, scroll_offset=0, n=36, term_rows=30)
assert visible == 16
assert visible == 18
assert offset == 0
def test_cursor_past_window_scrolls_down(self):
offset, visible = _compute(selected=20, scroll_offset=0, n=36, term_rows=30)
assert visible == 16
assert 20 in range(offset, offset + visible)
offset, visible = _compute(selected=22, scroll_offset=0, n=36, term_rows=30)
assert visible == 18
assert 22 in range(offset, offset + visible)
def test_cursor_above_window_scrolls_up(self):
offset, visible = _compute(selected=3, scroll_offset=15, n=36, term_rows=30)
@ -36,16 +35,16 @@ class TestPickerViewport:
assert 3 in range(offset, offset + visible)
def test_offset_clamped_to_bottom(self):
# Selected on last item — offset must keep visible window full, not
# walk past the end of the list.
# Selected on the last item — offset must keep the visible window
# full, not walk past the end of the list.
offset, visible = _compute(selected=35, scroll_offset=0, n=36, term_rows=30)
assert offset + visible == 36
assert 35 in range(offset, offset + visible)
def test_tiny_terminal_uses_minimum_visible(self):
# term_rows below chrome_reserve falls back to the floor of 3 rows.
# term_rows below the chrome budget falls back to the floor of 3 rows.
_, visible = _compute(selected=0, scroll_offset=0, n=20, term_rows=10)
assert visible == 3 # max(3, 10 - 14) == 3
assert visible == 3
def test_offset_recovers_after_stage_switch(self):
# When the user backs out of the model stage and re-enters with