Originally created by: sosidudku1
Companion to [#67], which raises the catalogs to 337 and 305 models. Based on that branch; merge [#67] first and this retargets to main.
Typing in the picker filters the list by substring, case-insensitively. Backspace trims the query, arrows and Enter operate on the filtered rows, and the cursor resets to the top on every keystroke so it can never point at a row that was just filtered away. Backspace with an empty query is a no-op, so it cannot reset the cursor. Ctrl and Meta combos are not typed into the filter; the modal swallows them like every other key, and Ctrl+C keeps working only because handleAppKey runs before the modal handler ever sees the key.
The counter reads 3/12 of 337 when a filter is active, and no match on an empty result set.
Both were visible in a screenshot of the panel before this change: model rows rendering as toolss and toolsls, and the header reading Press ←/→ to switch modeternal llama.cpp.
The box changed height between renders. Ink repaints a frame by overwriting the previous one line for line, so a shorter frame leaves the tail of the taller one on screen. The window is now fixed height and pads with blank rows when the result set is shorter.
Rows were keyed by model id. Ids repeat across a re-filter, and duplicate keys made Ink reuse the previous row's text, which left half-erased names behind (reported as "the last letter of the model sticks"). Rows are now keyed by slot.
The window-start computation is also clamped defensively for lists shorter than the window. The previous expression already produced correct slices in that case; the clamp just makes the invariant explicit instead of implicit.
12 new tests: query narrows the rows and resets the cursor, case-insensitive substring matching, empty query shows everything, no-match yields an empty list, printable keys dispatch a query update, backspace trims, backspace on an empty query is a no-op that keeps the cursor put, arrows wrap within the filtered list, Enter selects from the filtered list, Enter on an empty result does nothing, Esc closes with a query active, Ctrl combos are not typed.
src/tui/providers: 39 passing. tsc clean. Full suite shows the same pre-existing failures as main.
Originally posted by: sosidudku1
Thanks for the careful read, all four points addressed in the latest commit.
shouldClearTerminalForFramein ink.js switches to a full clear-terminal rewrite once a frame outgrows the viewport, sinceeraseLinescannot reach rows that scrolled past the top. The artifact showed up exactly when the unwindowed 337-row catalog pushed frames into that regime. The code comment now states what we verified and no more.[6~input), arrows and control bytes. Also added a test pinning the trim behavior: whitespace stays visible in the filter line but is ignored when matching.Ticket changed by: Ooooze