""" Main StreamLens Textual Application Modern TUI interface using Textual framework """ from textual.app import App, ComposeResult from textual.containers import Container, Horizontal, Vertical from textual.widgets import Header, Footer, TabbedContent, TabPane, Static, DataTable from textual.reactive import reactive from typing import TYPE_CHECKING from .widgets.flow_table import FlowAnalysisWidget from .widgets.packet_viewer import PacketDecoderWidget from .widgets.metrics_dashboard import StatisticalAnalysisWidget if TYPE_CHECKING: from ...analysis.core import EthernetAnalyzer class StreamLensApp(App): """ StreamLens Textual TUI Application Modern interface with three main tabs: - Flow Analysis: Interactive flow table with hierarchical protocol breakdown - Packet Decoder: 3-panel packet inspection interface - Statistical Analysis: Real-time metrics and performance analysis """ CSS_PATH = "styles/streamlens.tcss" BINDINGS = [ ("1", "show_tab('flows')", "Flow Analysis"), ("2", "show_tab('decoder')", "Packet Decoder"), ("3", "show_tab('stats')", "Statistics"), ("q", "quit", "Quit"), ("ctrl+c", "quit", "Quit"), ("?", "toggle_help", "Help"), ] # Reactive attributes for live data updates total_flows = reactive(0) total_packets = reactive(0) live_status = reactive("Stopped") def __init__(self, analyzer: 'EthernetAnalyzer'): super().__init__() self.analyzer = analyzer self.title = "StreamLens - Ethernet Traffic Analyzer" self.sub_title = "Modern Network Flow Analysis" def compose(self) -> ComposeResult: """Create the application layout""" yield Header() with Container(id="main-container"): # Status summary bar yield Static( f"Flows: {self.total_flows} | Packets: {self.total_packets} | Status: {self.live_status}", id="status-summary" ) # Main tabbed interface with TabbedContent(initial="flows"): # Flow Analysis Tab with TabPane("Flow Analysis", id="flows"): yield FlowAnalysisWidget(self.analyzer, id="flow-analysis") # Packet Decoder Tab with TabPane("Packet Decoder", id="decoder"): yield PacketDecoderWidget(self.analyzer, id="packet-decoder") # Statistical Analysis Tab with TabPane("Statistics", id="stats"): yield StatisticalAnalysisWidget(self.analyzer, id="statistics") yield Footer() def on_mount(self) -> None: """Initialize the application""" self.update_status() # Set up periodic updates for live analysis if self.analyzer.is_live: self.set_interval(1.0, self.update_status) def update_status(self) -> None: """Update reactive status attributes""" summary = self.analyzer.get_summary() self.total_flows = summary.get('unique_flows', 0) self.total_packets = summary.get('total_packets', 0) self.live_status = "Live" if self.analyzer.is_live else "Offline" # Update status summary display status_widget = self.query_one("#status-summary", Static) status_widget.update( f"Flows: {self.total_flows} | Packets: {self.total_packets} | Status: {self.live_status}" ) def action_show_tab(self, tab_id: str) -> None: """Switch to specified tab""" tabs = self.query_one(TabbedContent) tabs.active = tab_id def action_toggle_help(self) -> None: """Toggle help information""" # TODO: Implement help modal pass def watch_total_flows(self, new_value: int) -> None: """React to flow count changes""" # Trigger updates in child widgets flow_widget = self.query_one("#flow-analysis", FlowAnalysisWidget) flow_widget.refresh_data() def watch_total_packets(self, new_value: int) -> None: """React to packet count changes""" # Trigger updates in decoder widget decoder_widget = self.query_one("#packet-decoder", PacketDecoderWidget) decoder_widget.refresh_data() def get_selected_flow(self): """Get currently selected flow from flow analysis widget""" flow_widget = self.query_one("#flow-analysis", FlowAnalysisWidget) return flow_widget.get_selected_flow() def action_quit(self) -> None: """Clean exit""" self.exit()