@@ -19,6 +19,34 @@ use crate::{
1919 } ,
2020} ;
2121
22+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
23+ pub enum Panel {
24+ SystemMonitor = 0 ,
25+ SystemStatus = 1 ,
26+ ProcessManager = 2 ,
27+ FileExplorer = 3 ,
28+ NetworkGraph = 4 ,
29+ }
30+
31+ impl Panel {
32+ pub const COUNT : usize = 5 ;
33+
34+ pub fn from_index ( index : usize ) -> Option < Panel > {
35+ match index {
36+ 0 => Some ( Panel :: SystemMonitor ) ,
37+ 1 => Some ( Panel :: SystemStatus ) ,
38+ 2 => Some ( Panel :: ProcessManager ) ,
39+ 3 => Some ( Panel :: FileExplorer ) ,
40+ 4 => Some ( Panel :: NetworkGraph ) ,
41+ _ => None ,
42+ }
43+ }
44+
45+ pub fn as_index ( self ) -> usize {
46+ self as usize
47+ }
48+ }
49+
2250// Cached data structures
2351#[ derive( Clone ) ]
2452pub struct CachedProcess {
@@ -41,9 +69,10 @@ pub struct App {
4169 pub networks : Networks ,
4270 pub last_update : Instant ,
4371 pub last_manual_refresh : Instant ,
44- pub selected_panel : usize ,
72+ pub selected_panel : Panel ,
4573 pub current_dir : PathBuf ,
4674 pub dir_entries : Vec < String > ,
75+ pub dir_entry_paths : Vec < PathBuf > , // Store original paths for navigation
4776 pub selected_process : usize ,
4877 pub selected_file : usize ,
4978 pub selected_network : usize ,
@@ -68,7 +97,7 @@ impl App {
6897 warn ! ( "Failed to get current directory: {}, using '.'" , e) ;
6998 PathBuf :: from ( "." )
7099 } ) ;
71- let dir_entries = Self :: read_directory ( & current_dir) ;
100+ let ( dir_entries, dir_entry_paths ) = Self :: read_directory ( & current_dir) ;
72101
73102 let mut process_list_state = ListState :: default ( ) ;
74103 process_list_state. select ( Some ( 0 ) ) ;
@@ -84,9 +113,10 @@ impl App {
84113 networks,
85114 last_update : Instant :: now ( ) ,
86115 last_manual_refresh : Instant :: now ( ) ,
87- selected_panel : 0 ,
116+ selected_panel : Panel :: SystemMonitor ,
88117 current_dir,
89118 dir_entries,
119+ dir_entry_paths,
90120 selected_process : 0 ,
91121 selected_file : 0 ,
92122 selected_network : 0 ,
@@ -104,33 +134,52 @@ impl App {
104134 app
105135 }
106136
107- fn read_directory ( path : & PathBuf ) -> Vec < String > {
137+ fn read_directory ( path : & PathBuf ) -> ( Vec < String > , Vec < PathBuf > ) {
108138 match fs:: read_dir ( path) {
109139 Ok ( entries) => {
110140 let mut items = vec ! [ ".." . to_string( ) ] ;
141+ let mut paths = vec ! [ path. parent( ) . unwrap_or( path) . to_path_buf( ) ] ; // Parent path for ".."
111142 let mut dirs = Vec :: new ( ) ;
143+ let mut dir_paths = Vec :: new ( ) ;
112144 let mut files = Vec :: new ( ) ;
145+ let mut file_paths = Vec :: new ( ) ;
113146
114- for entry in entries. flatten ( ) . take ( MAX_FILES ) { // Limit to 10000 entries
147+ for entry in entries. flatten ( ) . take ( MAX_FILES ) {
148+ let entry_path = entry. path ( ) ;
115149 let name = entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
116- // Truncate very long file/directory names to prevent layout issues
117150 let truncated_name = truncate_string ( & name, FILE_NAME_MAX_LEN ) ;
118- if entry. path ( ) . is_dir ( ) {
151+
152+ if entry_path. is_dir ( ) {
119153 dirs. push ( format ! ( "📁 {}" , truncated_name) ) ;
154+ dir_paths. push ( entry_path) ;
120155 } else {
121156 files. push ( format ! ( "📄 {}" , truncated_name) ) ;
157+ file_paths. push ( entry_path) ;
122158 }
123159 }
124160
125- dirs. sort ( ) ;
126- files. sort ( ) ;
127- items. extend ( dirs) ;
128- items. extend ( files) ;
129- items
161+ // Sort directories and files together with their paths
162+ let mut combined: Vec < _ > = dirs. into_iter ( ) . zip ( dir_paths. into_iter ( ) ) . collect ( ) ;
163+ combined. sort_by ( |a, b| a. 0 . cmp ( & b. 0 ) ) ;
164+
165+ let mut file_combined: Vec < _ > = files. into_iter ( ) . zip ( file_paths. into_iter ( ) ) . collect ( ) ;
166+ file_combined. sort_by ( |a, b| a. 0 . cmp ( & b. 0 ) ) ;
167+
168+ // Extract sorted items and paths
169+ for ( item, path) in combined {
170+ items. push ( item) ;
171+ paths. push ( path) ;
172+ }
173+ for ( item, path) in file_combined {
174+ items. push ( item) ;
175+ paths. push ( path) ;
176+ }
177+
178+ ( items, paths)
130179 }
131180 Err ( e) => {
132181 error ! ( "Failed to read directory {:?}: {}" , path, e) ;
133- vec ! [ format!( "<Error: {}>" , e) ]
182+ ( vec ! [ format!( "<Error: {}>" , e) ] , vec ! [ path . clone ( ) ] )
134183 } ,
135184 }
136185 }
@@ -207,19 +256,19 @@ impl App {
207256 }
208257 KeyCode :: Up | KeyCode :: Char ( 'k' ) => {
209258 match self . selected_panel {
210- 2 => { // Process manager
259+ Panel :: ProcessManager => { // Process manager
211260 if self . selected_process > 0 {
212261 self . selected_process -= 1 ;
213262 self . process_list_state . select ( Some ( self . selected_process ) ) ;
214263 }
215264 }
216- 3 => { // File browser
265+ Panel :: FileExplorer => { // File browser
217266 if self . selected_file > 0 {
218267 self . selected_file -= 1 ;
219268 self . file_list_state . select ( Some ( self . selected_file ) ) ;
220269 }
221270 }
222- 4 => { // Network panel - cycle to previous interface
271+ Panel :: NetworkGraph => { // Network panel - cycle to previous interface
223272 let network_count = self . cached_networks . len ( ) ;
224273 if network_count > 0 {
225274 self . selected_network = if self . selected_network == 0 {
@@ -236,20 +285,20 @@ impl App {
236285 }
237286 KeyCode :: Down | KeyCode :: Char ( 'j' ) => {
238287 match self . selected_panel {
239- 2 => { // Process manager
288+ Panel :: ProcessManager => { // Process manager
240289 let max_processes = self . cached_processes . len ( ) ;
241290 if self . selected_process < max_processes. saturating_sub ( 1 ) {
242291 self . selected_process += 1 ;
243292 self . process_list_state . select ( Some ( self . selected_process ) ) ;
244293 }
245294 }
246- 3 => { // File browser
295+ Panel :: FileExplorer => { // File browser
247296 if self . selected_file < self . dir_entries . len ( ) - 1 {
248297 self . selected_file += 1 ;
249298 self . file_list_state . select ( Some ( self . selected_file ) ) ;
250299 }
251300 }
252- 4 => { // Network panel - cycle to next interface
301+ Panel :: NetworkGraph => { // Network panel - cycle to next interface
253302 let network_count = self . cached_networks . len ( ) ;
254303 if network_count > 0 {
255304 self . selected_network = ( self . selected_network + 1 ) % network_count;
@@ -262,12 +311,12 @@ impl App {
262311 }
263312 KeyCode :: PageUp => {
264313 match self . selected_panel {
265- 2 => { // Process manager
314+ Panel :: ProcessManager => { // Process manager
266315 let page_size = PAGE_SIZE ; // Approximate visible items per page
267316 self . selected_process = self . selected_process . saturating_sub ( page_size) ;
268317 self . process_list_state . select ( Some ( self . selected_process ) ) ;
269318 }
270- 3 => { // File browser
319+ Panel :: FileExplorer => { // File browser
271320 let page_size = PAGE_SIZE ;
272321 self . selected_file = self . selected_file . saturating_sub ( page_size) ;
273322 self . file_list_state . select ( Some ( self . selected_file ) ) ;
@@ -277,13 +326,13 @@ impl App {
277326 }
278327 KeyCode :: PageDown => {
279328 match self . selected_panel {
280- 2 => { // Process manager
329+ Panel :: ProcessManager => { // Process manager
281330 let page_size = PAGE_SIZE ;
282331 let max_processes = self . cached_processes . len ( ) ;
283332 self . selected_process = ( self . selected_process + page_size) . min ( max_processes. saturating_sub ( 1 ) ) ;
284333 self . process_list_state . select ( Some ( self . selected_process ) ) ;
285334 }
286- 3 => { // File browser
335+ Panel :: FileExplorer => { // File browser
287336 let page_size = PAGE_SIZE ;
288337 let max_files = self . dir_entries . len ( ) ;
289338 self . selected_file = ( self . selected_file + page_size) . min ( max_files. saturating_sub ( 1 ) ) ;
@@ -294,11 +343,11 @@ impl App {
294343 }
295344 KeyCode :: Home => {
296345 match self . selected_panel {
297- 2 => { // Process manager
346+ Panel :: ProcessManager => { // Process manager
298347 self . selected_process = 0 ;
299348 self . process_list_state . select ( Some ( 0 ) ) ;
300349 }
301- 3 => { // File browser
350+ Panel :: FileExplorer => { // File browser
302351 self . selected_file = 0 ;
303352 self . file_list_state . select ( Some ( 0 ) ) ;
304353 }
@@ -307,14 +356,14 @@ impl App {
307356 }
308357 KeyCode :: End => {
309358 match self . selected_panel {
310- 2 => { // Process manager
359+ Panel :: ProcessManager => { // Process manager
311360 let max_processes = self . cached_processes . len ( ) ;
312361 if max_processes > 0 {
313362 self . selected_process = max_processes. saturating_sub ( 1 ) ;
314363 self . process_list_state . select ( Some ( self . selected_process ) ) ;
315364 }
316365 }
317- 3 => { // File browser
366+ Panel :: FileExplorer => { // File browser
318367 let max_files = self . dir_entries . len ( ) ;
319368 if max_files > 0 {
320369 self . selected_file = max_files - 1 ;
@@ -325,15 +374,17 @@ impl App {
325374 }
326375 }
327376 KeyCode :: Enter => {
328- if self . selected_panel == 3 {
377+ if self . selected_panel == Panel :: FileExplorer {
329378 self . navigate_into_selected ( ) ;
330379 }
331380 }
332381 KeyCode :: Char ( 'r' ) => {
333382 // Force refresh with rate limiting
334383 if self . last_manual_refresh . elapsed ( ) >= MANUAL_REFRESH_COOLDOWN {
335384 self . system . refresh_all ( ) ;
336- self . dir_entries = Self :: read_directory ( & self . current_dir ) ;
385+ let ( dir_entries, dir_entry_paths) = Self :: read_directory ( & self . current_dir ) ;
386+ self . dir_entries = dir_entries;
387+ self . dir_entry_paths = dir_entry_paths;
337388 self . last_manual_refresh = Instant :: now ( ) ;
338389 }
339390 }
@@ -342,10 +393,12 @@ impl App {
342393 }
343394 KeyCode :: Backspace => {
344395 // Go up one directory (same as selecting "..")
345- if self . selected_panel == 3 { // File browser panel
396+ if self . selected_panel == Panel :: FileExplorer { // File browser panel
346397 if let Some ( parent) = self . current_dir . parent ( ) {
347398 self . current_dir = parent. to_path_buf ( ) ;
348- self . dir_entries = Self :: read_directory ( & self . current_dir ) ;
399+ let ( dir_entries, dir_entry_paths) = Self :: read_directory ( & self . current_dir ) ;
400+ self . dir_entries = dir_entries;
401+ self . dir_entry_paths = dir_entry_paths;
349402 self . selected_file = 0 ;
350403 self . file_list_state . select ( Some ( 0 ) ) ;
351404 }
@@ -356,27 +409,28 @@ impl App {
356409 }
357410
358411 fn navigate_into_selected ( & mut self ) {
359- if self . selected_file >= self . dir_entries . len ( ) {
412+ if self . selected_file >= self . dir_entries . len ( ) || self . selected_file >= self . dir_entry_paths . len ( ) {
360413 return ;
361414 }
362415
363416 let selected_item = & self . dir_entries [ self . selected_file ] ;
417+ let selected_path = & self . dir_entry_paths [ self . selected_file ] ;
364418
365419 if selected_item == ".." {
366- // Go up one directory
367- if let Some ( parent ) = self . current_dir . parent ( ) {
368- self . current_dir = parent . to_path_buf ( ) ;
369- self . dir_entries = Self :: read_directory ( & self . current_dir ) ;
370- self . selected_file = 0 ;
371- self . file_list_state . select ( Some ( 0 ) ) ;
372- }
420+ // Go up one directory using the stored parent path
421+ self . current_dir = selected_path . clone ( ) ;
422+ let ( dir_entries , dir_entry_paths ) = Self :: read_directory ( & self . current_dir ) ;
423+ self . dir_entries = dir_entries ;
424+ self . dir_entry_paths = dir_entry_paths ;
425+ self . selected_file = 0 ;
426+ self . file_list_state . select ( Some ( 0 ) ) ;
373427 } else if selected_item. starts_with ( "📁" ) {
374- // Enter directory - handle truncated names properly
375- let dir_name = selected_item . trim_start_matches ( "📁 " ) . trim_end_matches ( "..." ) ;
376- let new_path = self . current_dir . join ( dir_name ) ;
377- if new_path . is_dir ( ) {
378- self . current_dir = new_path ;
379- self . dir_entries = Self :: read_directory ( & self . current_dir ) ;
428+ // Enter directory using the stored original path
429+ if selected_path . is_dir ( ) {
430+ self . current_dir = selected_path . clone ( ) ;
431+ let ( dir_entries , dir_entry_paths ) = Self :: read_directory ( & self . current_dir ) ;
432+ self . dir_entries = dir_entries ;
433+ self . dir_entry_paths = dir_entry_paths ;
380434 self . selected_file = 0 ;
381435 self . file_list_state . select ( Some ( 0 ) ) ;
382436 }
@@ -385,15 +439,19 @@ impl App {
385439 }
386440
387441 fn select_next_panel ( & mut self ) {
388- self . selected_panel = ( self . selected_panel + 1 ) % 5 ; // 5 panels total
442+ let current_index = self . selected_panel . as_index ( ) ;
443+ let next_index = ( current_index + 1 ) % Panel :: COUNT ;
444+ self . selected_panel = Panel :: from_index ( next_index) . unwrap_or ( Panel :: SystemMonitor ) ;
389445 }
390446
391447 fn select_previous_panel ( & mut self ) {
392- self . selected_panel = if self . selected_panel == 0 {
393- 4 // 5 panels total (0-4)
448+ let current_index = self . selected_panel . as_index ( ) ;
449+ let prev_index = if current_index == 0 {
450+ Panel :: COUNT - 1
394451 } else {
395- self . selected_panel - 1
452+ current_index - 1
396453 } ;
454+ self . selected_panel = Panel :: from_index ( prev_index) . unwrap_or ( Panel :: SystemMonitor ) ;
397455 }
398456
399457 pub fn render_header ( & self , frame : & mut Frame , area : Rect ) {
0 commit comments