""" 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' elif key == ord('v') and self.current_view == 'main': # Visualize Chapter 10 signals return 'visualize' elif key == ord('\t') and self.current_view == 'main': # Tab key to switch detail panel tabs return 'switch_tab' 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 [Tab]switch tabs [PgUp/PgDn]scroll [t]imeline:{timeline_status} [v]isualize CH10 [d]issection [q]uit" elif self.current_view == 'dissection': return "[m]ain view [q]uit" else: return "[m]ain [d]issection [q]uit" def has_chapter10_data(self, flow: FlowStats) -> bool: """Check if a flow contains Chapter 10 data""" # Check if any frame types in the flow are Chapter 10 related for frame_type in flow.frame_types.keys(): if 'CH10' in frame_type.upper() or 'TMATS' in frame_type.upper(): return True # Check detected protocol types if 'CHAPTER10' in flow.detected_protocol_types or 'CH10' in flow.detected_protocol_types: return True return False