Skip to content

tui: stop the hidden filter input from swallowing every letter key - #74

Merged
binlecode merged 1 commit into
mainfrom
fix-letter-key-autofocus
Aug 23, 2026
Merged

binlecode merged 1 commit into
mainfrom
fix-letter-key-autofocus

Conversation

@binlecode

Copy link
Copy Markdown
Owner

Behavior change

Every single-letter keybinding was dead on startup — l, g, c, p, s, t, and q. Pressing l typed an l into an invisible regex box instead of cycling the layout preset. A user in that state could not even quit with q.

Root cause

Not the layout code — the key never reached it. Two facts combined:

  • Textual's default screen AUTO_FOCUS = "*" focuses the first focusable widget (screen.py:1493 falls back to app.AUTO_FOCUS).
  • display = False does not make a widget unfocusable — visible tracks the visibility style, not display.

So the hidden #filter-input was a legitimate focus candidate, won the focus on mount, and consumed every printable key.

Reproduced by pressing l g c p s on a mounted app and reading the filter box back: value == 'lgcps'.

Fix

  • AUTO_FOCUS = None on ActopApp.
  • New _focus_default_widget(): focuses the process table when shown (its cursor wants the arrow keys), else the non-focusable dashboard. Called from on_mount, from action_toggle_processes (so p hands over the arrow keys), and from _close_filter_input (which previously duplicated the branch).
  • DataTable.BINDINGS claims only navigation keys — no letters, no space — so the app bindings stay live while it holds focus.

Tests

Five TUI tests had been working around this with app.set_focus(None), blaming "the headless harness" for what was a real production bug — which is why the suite stayed green while l was dead for users. All the workarounds are removed, so test_pressing_l_cycles_the_dashboard_layout_preset now genuinely guards this.

Validation

Tested on macOS Darwin 25.5.0 + Apple M4 Max (4E+12P+40GPU).

.venv/bin/ruff check --fix . && .venv/bin/ruff format .   # clean
.venv/bin/python -m actop.actop --help                    # OK
.venv/bin/pytest -q                                       # 153 passed

An interactive sweep over the real app (34/34) covered all seven letter bindings, space, /, ?, escape, enter, arrow-key table navigation, --layout grid|stack, --show-processes, help open/close, and quit. Confirmed the fix does not invert the problem: typing in an open filter still inserts text and does not fire bindings.

Real-TTY pass (tmux, 200 cols): grid on startup → l gives a single full-width column → c shows per-core panels → / accepts python and the table filters to Python processes → escape closes → ? opens help → q quits cleanly.

Non-TUI surfaces unaffected (they never construct the App): --json and --json-processes NDJSON export verified.

Sample of the failure, before/after, on a mounted app:

before:  press 'l' -> preset=grid  filter box value='l'
after:   press 'l' -> preset=stack filter box value=''

🤖 Generated with Claude Code

Textual's default screen AUTO_FOCUS ("*") focuses the first focusable
widget, and `display = False` does not make a widget unfocusable
(`visible` tracks the `visibility` style, not `display`). The hidden
#filter-input therefore won the focus on mount and consumed every
printable key: pressing `l` typed an `l` into an invisible regex box
instead of cycling the layout preset. `g`, `c`, `p`, `s`, `t` and `q`
were dead the same way -- a user could not even quit.

Set AUTO_FOCUS = None and assign focus explicitly via a new
_focus_default_widget(): the process table when it is shown (its cursor
wants the arrow keys), else the non-focusable dashboard. Call it on
mount, when `p` toggles the table, and when the filter closes.
DataTable claims only navigation keys, so the letter bindings stay live
while it holds focus.

The TUI tests had been papering over this with `app.set_focus(None)`,
blaming the headless harness for what was a real production bug; those
workarounds are removed so the tests now fail if focus regresses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@binlecode
binlecode merged commit ab4564d into main Aug 23, 2026
5 checks passed
@binlecode
binlecode deleted the fix-letter-key-autofocus branch August 23, 2026 17:15
@binlecode binlecode mentioned this pull request Aug 23, 2026
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.

1 participant