Repository navigation
tui: stop the hidden filter input from swallowing every letter key - #74
Merged
Merged
Conversation
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>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Behavior change
Every single-letter keybinding was dead on startup —
l,g,c,p,s,t, andq. Pressingltyped anlinto an invisible regex box instead of cycling the layout preset. A user in that state could not even quit withq.Root cause
Not the layout code — the key never reached it. Two facts combined:
AUTO_FOCUS = "*"focuses the first focusable widget (screen.py:1493falls back toapp.AUTO_FOCUS).display = Falsedoes not make a widget unfocusable —visibletracks thevisibilitystyle, notdisplay.So the hidden
#filter-inputwas a legitimate focus candidate, won the focus on mount, and consumed every printable key.Reproduced by pressing
l g c p son a mounted app and reading the filter box back:value == 'lgcps'.Fix
AUTO_FOCUS = NoneonActopApp._focus_default_widget(): focuses the process table when shown (its cursor wants the arrow keys), else the non-focusable dashboard. Called fromon_mount, fromaction_toggle_processes(sophands over the arrow keys), and from_close_filter_input(which previously duplicated the branch).DataTable.BINDINGSclaims 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 whilelwas dead for users. All the workarounds are removed, sotest_pressing_l_cycles_the_dashboard_layout_presetnow genuinely guards this.Validation
Tested on macOS Darwin 25.5.0 + Apple M4 Max (4E+12P+40GPU).
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 →
lgives a single full-width column →cshows per-core panels →/acceptspythonand the table filters to Python processes →escapecloses →?opens help →qquits cleanly.Non-TUI surfaces unaffected (they never construct the App):
--jsonand--json-processesNDJSON export verified.Sample of the failure, before/after, on a mounted app:
🤖 Generated with Claude Code