From a27d529fa7b0c9846e046c47bb5a5eee1f9dc663 Mon Sep 17 00:00:00 2001 From: "J. Champagne" Date: Sat, 14 Mar 2026 11:47:27 -0400 Subject: [PATCH] feat(frontend): Add generations counter to inform frontends when a list should be re-rendered. --- crates/pikl-core/src/model/event.rs | 4 ++++ crates/pikl-tui/src/lib.rs | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/pikl-core/src/model/event.rs b/crates/pikl-core/src/model/event.rs index 3fcf2c2..4028945 100644 --- a/crates/pikl-core/src/model/event.rs +++ b/crates/pikl-core/src/model/event.rs @@ -72,6 +72,10 @@ pub struct ViewState { pub total_items: usize, pub total_filtered: usize, pub mode: Mode, + /// Monotonically increasing counter. Each call to + /// `build_view_state()` bumps this, so frontends can + /// detect duplicate broadcasts and skip redundant redraws. + pub generation: u64, } /// A single item in the current viewport window. Has the diff --git a/crates/pikl-tui/src/lib.rs b/crates/pikl-tui/src/lib.rs index 8c2fe8b..9c17f96 100644 --- a/crates/pikl-tui/src/lib.rs +++ b/crates/pikl-tui/src/lib.rs @@ -82,6 +82,7 @@ async fn run_inner( let mut event_stream = EventStream::new(); let mut mode = Mode::Insert; let mut pending = PendingKey::None; + let mut last_generation: u64 = 0; loop { if let Some(ref vs) = view_state { @@ -118,6 +119,11 @@ async fn run_inner( menu_event = event_rx.recv() => { match menu_event { Ok(MenuEvent::StateChanged(vs)) => { + // Skip duplicate broadcasts + if vs.generation == last_generation { + continue; + } + last_generation = vs.generation; // Sync filter text from core. Local keystrokes // update filter_text immediately for responsiveness, // but if core pushes a different value (e.g. IPC @@ -135,7 +141,9 @@ async fn run_inner( Ok(MenuEvent::Selected(_) | MenuEvent::Quicklist(_) | MenuEvent::Cancelled) => { break; } - Err(broadcast::error::RecvError::Lagged(_)) => {} + Err(broadcast::error::RecvError::Lagged(n)) => { + tracing::warn!(skipped = n, "TUI fell behind on state broadcasts"); + } Err(broadcast::error::RecvError::Closed) => { break; } @@ -340,6 +348,7 @@ mod tests { total_items: 5, total_filtered: 3, mode: Mode::Insert, + generation: 1, } } @@ -811,6 +820,7 @@ mod tests { total_items: 0, total_filtered: 0, mode: Mode::Insert, + generation: 1, }; let backend = render_to_backend(30, 4, &vs, ""); let prompt = line_text(&backend, 0);