@@ -25,6 +25,7 @@ struct App {
2525 dir_entries : Vec < String > ,
2626 selected_process : usize ,
2727 selected_file : usize ,
28+ selected_network : usize ,
2829 show_help : bool ,
2930 process_list_state : ListState ,
3031 file_list_state : ListState ,
@@ -39,6 +40,7 @@ struct NetworkHistory {
3940 last_rx_bytes : u64 ,
4041 last_tx_bytes : u64 ,
4142 max_history : usize ,
43+ current_interface : String ,
4244}
4345
4446impl NetworkHistory {
@@ -51,14 +53,31 @@ impl NetworkHistory {
5153 last_rx_bytes : 0 ,
5254 last_tx_bytes : 0 ,
5355 max_history : 60 , // Keep 60 data points (2 minutes at 2-second intervals)
56+ current_interface : String :: new ( ) ,
5457 }
5558 }
5659
57- fn update ( & mut self , networks : & Networks ) {
58- let ( total_rx, total_tx) = networks. list ( ) . iter ( )
59- . fold ( ( 0 , 0 ) , |( rx_acc, tx_acc) , ( _, network) | {
60- ( rx_acc + network. total_received ( ) , tx_acc + network. total_transmitted ( ) )
61- } ) ;
60+ fn update ( & mut self , networks : & Networks , selected_interface : & str ) {
61+ // Find the selected network interface or use the first available one
62+ let network_list: Vec < _ > = networks. list ( ) . iter ( ) . collect ( ) ;
63+ let ( interface_name, network_data) = if let Some ( item) = network_list. get ( 0 ) {
64+ // If we have a specific interface selected, try to find it
65+ if !selected_interface. is_empty ( ) {
66+ network_list. iter ( )
67+ . find ( |( name, _) | * name == selected_interface)
68+ . unwrap_or ( item)
69+ } else {
70+ item
71+ }
72+ } else {
73+ return ; // No network interfaces available
74+ } ;
75+
76+ // Update current interface name
77+ self . current_interface = interface_name. to_string ( ) ;
78+
79+ let total_rx = network_data. total_received ( ) ;
80+ let total_tx = network_data. total_transmitted ( ) ;
6281
6382 if self . last_rx_bytes > 0 && self . last_tx_bytes > 0 {
6483 // Calculate rate (bytes per 2 seconds)
@@ -132,6 +151,7 @@ impl App {
132151 dir_entries,
133152 selected_process : 0 ,
134153 selected_file : 0 ,
154+ selected_network : 0 ,
135155 show_help : false ,
136156 process_list_state,
137157 file_list_state,
@@ -171,7 +191,16 @@ impl App {
171191 self . system . refresh_all ( ) ;
172192 self . disks . refresh ( true ) ;
173193 self . networks . refresh ( true ) ;
174- self . network_history . update ( & self . networks ) ;
194+
195+ // Get the selected network interface name
196+ let network_list: Vec < _ > = self . networks . list ( ) . iter ( ) . collect ( ) ;
197+ let selected_interface_name = if let Some ( ( name, _) ) = network_list. get ( self . selected_network ) {
198+ name. to_string ( )
199+ } else {
200+ String :: new ( )
201+ } ;
202+
203+ self . network_history . update ( & self . networks , & selected_interface_name) ;
175204 self . last_update = Instant :: now ( ) ;
176205 }
177206 }
@@ -211,13 +240,25 @@ impl App {
211240 self . file_list_state . select ( Some ( self . selected_file ) ) ;
212241 }
213242 }
243+ 4 => { // Network panel - cycle to previous interface
244+ let network_count = self . networks . list ( ) . len ( ) ;
245+ if network_count > 0 {
246+ self . selected_network = if self . selected_network == 0 {
247+ network_count - 1
248+ } else {
249+ self . selected_network - 1
250+ } ;
251+ // Reset network history when switching interfaces
252+ self . network_history = NetworkHistory :: new ( ) ;
253+ }
254+ }
214255 _ => { }
215256 }
216257 }
217258 KeyCode :: Down | KeyCode :: Char ( 'j' ) => {
218259 match self . selected_panel {
219260 2 => { // Process manager
220- let max_processes = self . system . processes ( ) . len ( ) . min ( 10 ) ;
261+ let max_processes = self . system . processes ( ) . len ( ) ;
221262 if self . selected_process < max_processes - 1 {
222263 self . selected_process += 1 ;
223264 self . process_list_state . select ( Some ( self . selected_process ) ) ;
@@ -229,6 +270,78 @@ impl App {
229270 self . file_list_state . select ( Some ( self . selected_file ) ) ;
230271 }
231272 }
273+ 4 => { // Network panel - cycle to next interface
274+ let network_count = self . networks . list ( ) . len ( ) ;
275+ if network_count > 0 {
276+ self . selected_network = ( self . selected_network + 1 ) % network_count;
277+ // Reset network history when switching interfaces
278+ self . network_history = NetworkHistory :: new ( ) ;
279+ }
280+ }
281+ _ => { }
282+ }
283+ }
284+ KeyCode :: PageUp => {
285+ match self . selected_panel {
286+ 2 => { // Process manager
287+ let page_size = 10 ; // Approximate visible items per page
288+ self . selected_process = self . selected_process . saturating_sub ( page_size) ;
289+ self . process_list_state . select ( Some ( self . selected_process ) ) ;
290+ }
291+ 3 => { // File browser
292+ let page_size = 10 ;
293+ self . selected_file = self . selected_file . saturating_sub ( page_size) ;
294+ self . file_list_state . select ( Some ( self . selected_file ) ) ;
295+ }
296+ _ => { }
297+ }
298+ }
299+ KeyCode :: PageDown => {
300+ match self . selected_panel {
301+ 2 => { // Process manager
302+ let page_size = 10 ;
303+ let max_processes = self . system . processes ( ) . len ( ) ;
304+ self . selected_process = ( self . selected_process + page_size) . min ( max_processes. saturating_sub ( 1 ) ) ;
305+ self . process_list_state . select ( Some ( self . selected_process ) ) ;
306+ }
307+ 3 => { // File browser
308+ let page_size = 10 ;
309+ let max_files = self . dir_entries . len ( ) ;
310+ self . selected_file = ( self . selected_file + page_size) . min ( max_files. saturating_sub ( 1 ) ) ;
311+ self . file_list_state . select ( Some ( self . selected_file ) ) ;
312+ }
313+ _ => { }
314+ }
315+ }
316+ KeyCode :: Home => {
317+ match self . selected_panel {
318+ 2 => { // Process manager
319+ self . selected_process = 0 ;
320+ self . process_list_state . select ( Some ( 0 ) ) ;
321+ }
322+ 3 => { // File browser
323+ self . selected_file = 0 ;
324+ self . file_list_state . select ( Some ( 0 ) ) ;
325+ }
326+ _ => { }
327+ }
328+ }
329+ KeyCode :: End => {
330+ match self . selected_panel {
331+ 2 => { // Process manager
332+ let max_processes = self . system . processes ( ) . len ( ) ;
333+ if max_processes > 0 {
334+ self . selected_process = max_processes - 1 ;
335+ self . process_list_state . select ( Some ( self . selected_process ) ) ;
336+ }
337+ }
338+ 3 => { // File browser
339+ let max_files = self . dir_entries . len ( ) ;
340+ if max_files > 0 {
341+ self . selected_file = max_files - 1 ;
342+ self . file_list_state . select ( Some ( self . selected_file ) ) ;
343+ }
344+ }
232345 _ => { }
233346 }
234347 }
@@ -381,7 +494,7 @@ fn render(app: &App, frame: &mut Frame) {
381494 let help_text = if app. show_help {
382495 "ESC or ? to close • System Monitor v1.0"
383496 } else {
384- "Navigation: ←→hl | ↑↓jk (navigate lists ) | Enter (open directory ) | r (refresh) | ? (help) | q (quit) • Live Updates "
497+ "Navigation: ←→hl | ↑↓jk/PgUp/PgDn/Home/End (navigate/cycle ) | Enter (open dir ) | r (refresh) | ? (help) | q (quit)"
385498 } ;
386499 let footer = Paragraph :: new ( help_text)
387500 . style ( Style :: default ( ) . fg ( Color :: DarkGray ) )
@@ -395,7 +508,9 @@ SYSTEM MONITOR - HELP
395508
396509NAVIGATION:
397510 ← → h l - Switch between panels
398- ↑ ↓ j k - Navigate within Task Manager
511+ ↑ ↓ j k - Navigate within lists/cycle network interfaces
512+ PgUp/PgDn- Jump by page in lists
513+ Home/End - Jump to first/last item in lists
399514 Enter - Navigate directories (File Browser)
400515 r - Refresh all data
401516 ? - Show/hide this help
@@ -404,9 +519,9 @@ NAVIGATION:
404519PANELS:
405520 1. System Monitor - CPU, Memory, Uptime, Architecture
406521 2. System Status - Time, Disk usage, Network stats
407- 3. Process Manager- Top processes (navigable with j/k)
408- 4. File Explorer - Navigate directories (j/k + Enter)
409- 5. Network Graph - Real-time network traffic visualization
522+ 3. Process Manager- Top processes (j/k/PgUp/PgDn/Home/End )
523+ 4. File Explorer - Navigate directories (j/k/PgUp/PgDn/Home/End + Enter)
524+ 5. Network Graph - Real-time network traffic (↑↓ to cycle interfaces)
410525
411526FEATURES:
412527 • Real-time system monitoring
@@ -510,16 +625,16 @@ fn render_clock(app: &App, frame: &mut Frame, area: Rect, is_selected: bool) {
510625 ( 0 , 0 )
511626 } ;
512627
513- // Get network info
514- let network_info = app. networks . list ( ) . iter ( )
515- . map ( | ( name, network) | {
516- format ! ( "{}: ↓{} MB ↑{} MB" ,
517- name,
518- network. total_received( ) / 1024 / 1024 ,
519- network. total_transmitted( ) / 1024 / 1024 )
520- } )
521- . next ( )
522- . unwrap_or_else ( || "No network data" . to_string ( ) ) ;
628+ // Get network info for the selected interface
629+ let network_list : Vec < _ > = app. networks . list ( ) . iter ( ) . collect ( ) ;
630+ let network_info = if let Some ( ( name, network) ) = network_list . get ( app . selected_network ) {
631+ format ! ( "{}: ↓{} MB ↑{} MB" ,
632+ name,
633+ network. total_received( ) / 1024 / 1024 ,
634+ network. total_transmitted( ) / 1024 / 1024 )
635+ } else {
636+ "No network data" . to_string ( )
637+ } ;
523638
524639 let content = format ! ( "▶ Time: {}\n ▶ Date: {}\n ▶ Boot disk: {} GB / {} GB\n ▶ Disk usage: {:.1}%\n \n ▶ Network: \n {}\n \n ▶ Load avg: {:.2}" ,
525640 time_str,
@@ -627,8 +742,17 @@ fn render_network_graph(app: &App, frame: &mut Frame, area: Rect, is_selected: b
627742 Style :: default ( ) . fg ( Color :: White )
628743 } ;
629744
745+ let interface_name = & app. network_history . current_interface ;
746+ let network_count = app. networks . list ( ) . len ( ) ;
747+ let title = if network_count > 1 {
748+ format ! ( "📡 Network Traffic Monitor - {} ({}/{}) [↑↓ to cycle]" ,
749+ interface_name, app. selected_network + 1 , network_count)
750+ } else {
751+ format ! ( "📡 Network Traffic Monitor - {}" , interface_name)
752+ } ;
753+
630754 let main_block = Block :: default ( )
631- . title ( "📡 Network Traffic Monitor" )
755+ . title ( title )
632756 . borders ( Borders :: ALL )
633757 . border_style ( border_style) ;
634758
0 commit comments