Add search autocomplete and fix search state restoration
- Add SuggestFromList with Himalaya keywords for search input autocomplete - Cache and restore metadata_by_id when cancelling search (fixes navigation) - Set search_mode=True when opening panel for consistent Escape behavior - Fix SearchPanel CSS vertical alignment with explicit heights
This commit is contained in:
@@ -77,6 +77,7 @@ class EmailViewerApp(App):
|
||||
search_query: Reactive[str] = reactive("") # Current search filter
|
||||
search_mode: Reactive[bool] = reactive(False) # True when showing search results
|
||||
_cached_envelopes: List[Dict[str, Any]] = [] # Cached envelopes before search
|
||||
_cached_metadata: Dict[int, Dict[str, Any]] = {} # Cached metadata before search
|
||||
|
||||
def get_system_commands(self, screen: Screen) -> Iterable[SystemCommand]:
|
||||
yield from super().get_system_commands(screen)
|
||||
@@ -917,6 +918,8 @@ class EmailViewerApp(App):
|
||||
if not search_panel.is_visible:
|
||||
# Cache current envelopes before searching
|
||||
self._cached_envelopes = list(self.message_store.envelopes)
|
||||
self._cached_metadata = dict(self.message_store.metadata_by_id)
|
||||
self.search_mode = True
|
||||
search_panel.show(self.search_query)
|
||||
|
||||
def on_search_panel_search_requested(
|
||||
@@ -938,11 +941,14 @@ class EmailViewerApp(App):
|
||||
self.search_mode = False
|
||||
self.search_query = ""
|
||||
|
||||
# Restore cached envelopes
|
||||
# Restore cached envelopes and metadata
|
||||
if self._cached_envelopes:
|
||||
self.message_store.envelopes = self._cached_envelopes
|
||||
self._cached_envelopes = []
|
||||
self._populate_list_view()
|
||||
if self._cached_metadata:
|
||||
self.message_store.metadata_by_id = self._cached_metadata
|
||||
self._cached_metadata = {}
|
||||
self._populate_list_view()
|
||||
|
||||
# Restore envelope list title
|
||||
sort_indicator = "↑" if self.sort_order_ascending else "↓"
|
||||
|
||||
@@ -17,6 +17,32 @@ from textual.screen import ModalScreen
|
||||
from textual.timer import Timer
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Button, Input, Label, Static
|
||||
from textual.suggester import SuggestFromList
|
||||
|
||||
# Himalaya search keywords for autocomplete
|
||||
HIMALAYA_KEYWORDS = [
|
||||
"from ",
|
||||
"to ",
|
||||
"subject ",
|
||||
"body ",
|
||||
"date ",
|
||||
"before ",
|
||||
"after ",
|
||||
"flag ",
|
||||
"not ",
|
||||
"and ",
|
||||
"or ",
|
||||
"order by ",
|
||||
"order by date ",
|
||||
"order by date asc",
|
||||
"order by date desc",
|
||||
"order by from ",
|
||||
"order by to ",
|
||||
"order by subject ",
|
||||
"flag seen",
|
||||
"flag flagged",
|
||||
"not flag seen",
|
||||
]
|
||||
|
||||
HIMALAYA_SEARCH_HELP = """
|
||||
## Himalaya Search Query Syntax
|
||||
@@ -125,7 +151,7 @@ class SearchPanel(Widget):
|
||||
}
|
||||
|
||||
SearchPanel > Horizontal {
|
||||
height: auto;
|
||||
height: 3;
|
||||
width: 100%;
|
||||
align: left middle;
|
||||
}
|
||||
@@ -134,6 +160,7 @@ class SearchPanel(Widget):
|
||||
width: auto;
|
||||
padding: 0 1;
|
||||
color: $primary;
|
||||
height: 1;
|
||||
}
|
||||
|
||||
SearchPanel Input {
|
||||
@@ -151,6 +178,7 @@ class SearchPanel(Widget):
|
||||
width: auto;
|
||||
padding: 0 1;
|
||||
color: $text-muted;
|
||||
height: 1;
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -197,6 +225,7 @@ class SearchPanel(Widget):
|
||||
yield Input(
|
||||
placeholder="from <name> or subject <text> or body <text>...",
|
||||
id="search-input",
|
||||
suggester=SuggestFromList(HIMALAYA_KEYWORDS, case_sensitive=False),
|
||||
)
|
||||
yield Label("", classes="search-status", id="search-status")
|
||||
yield Button("?", variant="default", id="help-btn")
|
||||
|
||||
Reference in New Issue
Block a user