diff --git a/src/ui/enhanced_tui.rs b/src/ui/enhanced_tui.rs index afe7408b..25295d71 100644 --- a/src/ui/enhanced_tui.rs +++ b/src/ui/enhanced_tui.rs @@ -1879,7 +1879,10 @@ impl EnhancedTuiApp { Ok(false) } ChordResult::SingleKey(single_key) => { - // Not a chord, process normally + // Not a chord (or a chord that lapsed and was reset on this key). + // Clear any lingering chord hint so the indicator reflects reality. + self.key_sequence_renderer.clear_chord_mode(); + // Process normally self.handle_results_input(single_key) } } @@ -1963,6 +1966,16 @@ impl EnhancedTuiApp { // Ignore other events (mouse, resize, etc.) to reduce CPU } } else { + // No key event. If a pending chord has lapsed, cancel it and refresh + // so the "Yank mode" hint visibly disappears (chord reset indicator). + if self.key_chord_handler.clear_if_timed_out() { + self.key_sequence_renderer.clear_chord_mode(); + self.state_container + .set_status_message("Chord cancelled (timeout)".to_string()); + terminal.draw(|f| self.ui(f))?; + return Ok(false); + } + // No event available, but still redraw if we have pending debounced actions or table needs render if self.search_modes_widget.is_active() || self.table_widget_manager.borrow().needs_render() diff --git a/src/ui/key_handling/chord_handler.rs b/src/ui/key_handling/chord_handler.rs index e253f76f..ca72b707 100644 --- a/src/ui/key_handling/chord_handler.rs +++ b/src/ui/key_handling/chord_handler.rs @@ -263,7 +263,7 @@ impl KeyChordHandler { let description = if self.current_chord.len() == 1 && self.current_chord[0].code == KeyCode::Char('y') { - "Yank mode: y=row, c=column, a=all, ESC=cancel".to_string() + "Yank mode: y/r=row, c=column, a=all, v=cell, q=query, ESC=cancel".to_string() } else { format!("Waiting for: {}", possible.join(", ")) }; @@ -333,6 +333,22 @@ impl KeyChordHandler { self.chord_mode_active } + /// Cancel a pending chord if it has exceeded the timeout. + /// + /// The normal timeout check only runs when the next key arrives, so a chord + /// left dangling (e.g. `y` with no follow-up key) never resets on its own. + /// Calling this from the idle loop lets the UI clear the chord hint once the + /// window lapses. Returns true if a pending chord was cleared. + pub fn clear_if_timed_out(&mut self) -> bool { + if let Some(start) = self.chord_start { + if start.elapsed() > self.chord_timeout { + self.cancel_chord(); + return true; + } + } + false + } + /// Get chord mode description #[must_use] pub fn get_chord_mode_description(&self) -> Option<&str> {