From d4b09e53386a7683a6e7db60bc780fd8e01e4cb0 Mon Sep 17 00:00:00 2001 From: Bendt Date: Fri, 19 Dec 2025 15:38:57 -0500 Subject: [PATCH] Improve search autocomplete UX - Tab key accepts autocomplete suggestion (like right arrow) - Prevent search from firing while autocomplete suggestion is visible --- src/mail/screens/SearchPanel.py | 34 +++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/mail/screens/SearchPanel.py b/src/mail/screens/SearchPanel.py index 880688f..7ee5cb7 100644 --- a/src/mail/screens/SearchPanel.py +++ b/src/mail/screens/SearchPanel.py @@ -160,7 +160,6 @@ class SearchPanel(Widget): width: auto; padding: 0 1; color: $primary; - height: 1; } SearchPanel Input { @@ -178,7 +177,6 @@ class SearchPanel(Widget): width: auto; padding: 0 1; color: $text-muted; - height: 1; } """ @@ -231,6 +229,34 @@ class SearchPanel(Widget): yield Button("?", variant="default", id="help-btn") yield Button("Cancel", variant="warning", id="cancel-btn") + def _has_suggestion(self) -> bool: + """Check if the search input currently has an autocomplete suggestion.""" + try: + input_widget = self.query_one("#search-input", Input) + return bool(input_widget._suggestion and input_widget._cursor_at_end) + except Exception: + return False + + def _accept_suggestion(self) -> bool: + """Accept the current autocomplete suggestion if present. Returns True if accepted.""" + try: + input_widget = self.query_one("#search-input", Input) + if input_widget._suggestion and input_widget._cursor_at_end: + input_widget.value = input_widget._suggestion + input_widget.cursor_position = len(input_widget.value) + return True + except Exception: + pass + return False + + def on_key(self, event) -> None: + """Handle key events to intercept Tab for autocomplete.""" + if event.key == "tab": + # Try to accept suggestion; if successful, prevent default tab behavior + if self._accept_suggestion(): + event.prevent_default() + event.stop() + def show(self, initial_query: str = "") -> None: """Show the search panel and focus the input.""" self.add_class("visible") @@ -268,6 +294,10 @@ class SearchPanel(Widget): def _trigger_search(self) -> None: """Trigger the actual search after debounce.""" + # Don't search if an autocomplete suggestion is visible + if self._has_suggestion(): + return + query = self.query_one("#search-input", Input).value.strip() if query and query != self._last_query: self._last_query = query