""" Navigation and input handling for the TUI """ import curses from typing import List from ..models import FlowStats class NavigationHandler: """Handles navigation and input for the TUI""" def __init__(self): self.current_view = 'main' # main, dissection self.selected_flow = 0 self.scroll_offset = 0 self.show_timeline = True # Toggle for bottom timeline plot def handle_input(self, key: int, flows_list: List[FlowStats]) -> str: """ Handle keyboard input and return action Returns: Action string: 'quit', 'view_change', 'selection_change', 'none' """ if key == ord('q'): return 'quit' elif key == ord('d'): self.current_view = 'dissection' return 'view_change' elif key == ord('m') or key == 27: # 'm' or ESC to return to main self.current_view = 'main' return 'view_change' elif key == curses.KEY_UP and self.current_view == 'main': self.selected_flow = max(0, self.selected_flow - 1) return 'selection_change' elif key == curses.KEY_DOWN and self.current_view == 'main': max_items = self._get_total_display_items(flows_list) self.selected_flow = min(max_items - 1, self.selected_flow + 1) return 'selection_change' elif key == ord('t'): # Toggle timeline plot self.show_timeline = not self.show_timeline return 'view_change' elif key == curses.KEY_PPAGE and self.current_view == 'main': # Page Up self.selected_flow = max(0, self.selected_flow - 10) return 'selection_change' elif key == curses.KEY_NPAGE and self.current_view == 'main': # Page Down max_items = self._get_total_display_items(flows_list) self.selected_flow = min(max_items - 1, self.selected_flow + 10) return 'selection_change' elif key == curses.KEY_HOME and self.current_view == 'main': # Home self.selected_flow = 0 return 'selection_change' elif key == curses.KEY_END and self.current_view == 'main': # End max_items = self._get_total_display_items(flows_list) self.selected_flow = max_items - 1 return 'selection_change' return 'none' def _get_total_display_items(self, flows_list: List[FlowStats]) -> int: """Calculate total number of selectable items (flows + frame types)""" total = 0 for flow in flows_list: total += 1 # Flow itself total += len(flow.frame_types) # Frame types under this flow return total def get_status_bar_text(self) -> str: """Get status bar text based on current view""" if self.current_view == 'main': timeline_status = "ON" if self.show_timeline else "OFF" return f"[↑↓]navigate [PgUp/PgDn]scroll [t]imeline:{timeline_status} [d]issection [q]uit" elif self.current_view == 'dissection': return "[m]ain view [q]uit" else: return "[m]ain [d]issection [q]uit"