From 5deebbbf98dd706bbf5feed065e5168dfe2a6767 Mon Sep 17 00:00:00 2001 From: Bendt Date: Fri, 19 Dec 2025 15:10:50 -0500 Subject: [PATCH] Fix search stability and improve Escape key behavior - Add bounds check in _mark_message_as_read to prevent IndexError - Clear metadata_by_id when search returns no results - Escape now focuses search input when in search mode instead of exiting - Add focus_input() method to SearchPanel --- src/mail/app.py | 35 +++++++++++++-------------------- src/mail/screens/SearchPanel.py | 5 +++++ 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/mail/app.py b/src/mail/app.py index 9168876..363a522 100644 --- a/src/mail/app.py +++ b/src/mail/app.py @@ -273,6 +273,12 @@ class EmailViewerApp(App): async def _mark_message_as_read(self, message_id: int, index: int) -> None: """Mark a message as read and update the UI.""" + # Skip if message_id is invalid or index is out of bounds + if message_id <= 0: + return + if index < 0 or index >= len(self.message_store.envelopes): + return + # Check if already read envelope_data = self.message_store.envelopes[index] if envelope_data and envelope_data.get("type") != "header": @@ -885,32 +891,18 @@ class EmailViewerApp(App): self._update_list_view_subtitle() def action_clear_selection(self) -> None: - """Clear all selected messages and exit search mode.""" + """Clear all selected messages or focus search input if in search mode.""" + # If in search mode, focus the search input instead of exiting + if self.search_mode: + search_panel = self.query_one("#search_panel", SearchPanel) + search_panel.focus_input() + return + if self.selected_messages: self.selected_messages.clear() self.refresh_list_view_items() # Refresh all items to uncheck checkboxes self._update_list_view_subtitle() - # Exit search mode if active - if self.search_mode: - search_panel = self.query_one("#search_panel", SearchPanel) - search_panel.hide() - self.search_mode = False - self.search_query = "" - - # Restore cached envelopes - if self._cached_envelopes: - self.message_store.envelopes = self._cached_envelopes - self._cached_envelopes = [] - self._populate_list_view() - - # Restore envelope list title - sort_indicator = "↑" if self.sort_order_ascending else "↓" - self.query_one( - "#envelopes_list" - ).border_title = f"1️⃣ Emails {sort_indicator}" - self._update_list_view_subtitle() - def action_oldest(self) -> None: self.fetch_envelopes() if self.reload_needed else None self.show_message(self.message_store.get_oldest_id()) @@ -1017,6 +1009,7 @@ class EmailViewerApp(App): content_container = self.query_one(ContentContainer) content_container.clear_content() self.message_store.envelopes = [] + self.message_store.metadata_by_id = {} self.total_messages = 0 self.current_message_id = 0 return diff --git a/src/mail/screens/SearchPanel.py b/src/mail/screens/SearchPanel.py index f8729c0..aae4428 100644 --- a/src/mail/screens/SearchPanel.py +++ b/src/mail/screens/SearchPanel.py @@ -216,6 +216,11 @@ class SearchPanel(Widget): self._cancel_debounce() self.result_count = -1 + def focus_input(self) -> None: + """Focus the search input field.""" + input_widget = self.query_one("#search-input", Input) + input_widget.focus() + @property def is_visible(self) -> bool: """Check if the panel is visible."""