feat(tui): run the highlighted search again with S

The search list survives a restart, but a restored query was only ever a
reminder: seeing what the network has now meant typing it out again. In
the Searches pane, `S` now re-runs the highlighted query in its place — no
duplicate row, no reordering — cancelling a run still collecting first and
starting the Results display over for what comes back.

The key is listed in the shortcut bar, the `?` overlay, the README and the
website. `start_search` is split into launching a search and showing it,
so both paths share one implementation.

A new `tui_e2e` suite drives the real window over a real session against
soulfind: keys in, a screen out, a sharing peer on the other end. It
restores a query from a previous run's state directory, presses `S`, and
checks the peer's file lands on screen — then again once the query has
been on the wire, proving a repeat is answered and the display does not
stack. To make that possible `MainTui` is exported with `handle_key_event`,
`render`, `poll_session` and a `state()` accessor, and CI runs the suite
with `SOULSEEK_E2E_REQUIRED=1`.

Closes #85
This commit is contained in:
Michel de Graaf
2026-09-08 13:11:40 +02:00
parent af1f9241e4
commit 76f4854169
10 changed files with 550 additions and 63 deletions
+11 -7
View File
@@ -14,7 +14,8 @@ use ratatui::widgets::TableState;
const NAME_SCROLL_STEP: isize = 8;
impl MainTui {
pub(super) fn handle_key_event(&mut self, key: KeyEvent) {
/// One key press, routed to whatever owns the keyboard right now.
pub fn handle_key_event(&mut self, key: KeyEvent) {
// Command bar takes priority
if self.state.command_bar_active {
return self.handle_command_bar_input(key);
@@ -400,11 +401,10 @@ impl MainTui {
if self.navigate_focused_list(key) || self.scroll_focused_name(key) {
return;
}
let selected = self.state.searches_table_state.selected();
match key.code {
KeyCode::Enter => {
if let Some(selected) =
self.state.searches_table_state.selected()
{
if let Some(selected) = selected {
self.state.selected_search_index = Some(selected);
if let Some(search) = self.state.searches.get(selected) {
self.state.results_items = search.results.clone();
@@ -420,12 +420,16 @@ impl MainTui {
}
}
KeyCode::Char('d') => {
if let Some(selected) =
self.state.searches_table_state.selected()
{
if let Some(selected) = selected {
self.remove_search_at_index(selected);
}
}
// `s` asks for a query; `S` reuses the highlighted one.
KeyCode::Char('S') => {
if let Some(selected) = selected {
self.rerun_search_at_index(selected);
}
}
KeyCode::Char('C') => {
self.clear_all_searches();
}