cargo_e/
e_tui.rs

1#[cfg(feature = "tui")]
2pub mod tui_interactive {
3    use crate::e_command_builder::CargoCommandBuilder;
4    use crate::e_manifest::maybe_patch_manifest_for_run;
5    use crate::e_processmanager::ProcessManager;
6    use crate::e_prompts::prompt_line;
7    use crate::e_target::CargoTarget;
8    use crate::prelude::*;
9    use crate::{e_bacon, e_findmain, Cli};
10    use crossterm::event::KeyEventKind;
11    use crossterm::event::{poll, read};
12    use crossterm::{
13        event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, MouseEventKind},
14        execute,
15        terminal::{
16            disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen,
17            LeaveAlternateScreen,
18        },
19    };
20    use ratatui::{
21        backend::CrosstermBackend,
22        layout::{Constraint, Direction, Layout, Rect},
23        style::{Color, Style},
24        text::{Line, Span},
25        widgets::{Block, Borders, List, ListItem, ListState},
26        Terminal,
27    };
28    use std::{collections::HashSet, thread, time::Duration};
29    use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, System};
30
31    /// Flushes the input event queue, ignoring any stray Enter key events.
32    pub fn flush_input() -> Result<(), Box<dyn std::error::Error>> {
33        while poll(Duration::from_millis(0))? {
34            if let Event::Key(key_event) = read()? {
35                // Optionally, log or ignore specific keys.
36                if key_event.code == KeyCode::Enter {
37                    // Filtering out stray Return keys.
38                    continue;
39                }
40                // You can also choose to ignore all events:
41                continue;
42            }
43        }
44        Ok(())
45    }
46
47    /// Try to collect an escape sequence if the first event is Esc.
48    /// Returns Some(arrow) if the sequence matches an arrow key, otherwise None.
49    fn try_collect_arrow_sequence() -> Result<Option<KeyCode>, Box<dyn std::error::Error>> {
50        // Buffer to hold the sequence. We already know the first event is Esc.
51        let mut sequence = vec![];
52        let start = Instant::now();
53        // Give a short window (e.g. 50 ms) to collect additional events.
54        while start.elapsed() < Duration::from_millis(50) {
55            if poll(Duration::from_millis(0))? {
56                if let Event::Key(key) = read()? {
57                    // Only consider Press events.
58                    if key.kind == KeyEventKind::Press {
59                        sequence.push(key);
60                    }
61                }
62            }
63        }
64        // Now, an arrow key should have a sequence like: Esc, '[' and then 'A' (or 'B', 'C', 'D').
65        if sequence.len() >= 2 && sequence[0].code == KeyCode::Char('[') {
66            // Check the third element if available.
67            if let Some(third) = sequence.get(1) {
68                // Compare the character case-insensitively (to handle unexpected modifiers).
69                if let KeyCode::Char(ch) = third.code {
70                    let ch = ch.to_ascii_uppercase();
71                    return Ok(match ch {
72                        'A' => Some(KeyCode::Up),
73                        'B' => Some(KeyCode::Down),
74                        'C' => Some(KeyCode::Right),
75                        'D' => Some(KeyCode::Left),
76                        _ => None,
77                    });
78                }
79            }
80        }
81        Ok(None)
82    }
83
84    /// Launches an interactive terminal UI for selecting an example.
85    pub fn launch_tui(
86        manager: Arc<ProcessManager>,
87        cli: &Cli,
88        examples: &[CargoTarget],
89    ) -> Result<(), Box<dyn std::error::Error>> {
90        flush_input()?; // Clear any buffered input (like stray Return keys)
91        let mut exs = examples.to_vec();
92        if exs.is_empty() {
93            println!("No examples found!");
94            return Ok(());
95        }
96        exs.sort_by(|a, b| a.display_name.cmp(&b.display_name));
97        // Determine the directory containing the Cargo.toml at runtime.
98        let manifest_dir = crate::e_manifest::find_manifest_dir()?;
99        let history_path = manifest_dir.join("run_history.txt");
100        let mut run_history: HashSet<String> = HashSet::new();
101        if let Ok(contents) = fs::read_to_string(&history_path) {
102            for line in contents.lines() {
103                if !line.trim().is_empty() {
104                    run_history.insert(line.trim().to_string());
105                }
106            }
107        }
108
109        enable_raw_mode()?;
110        let mut stdout = io::stdout();
111        execute!(
112            stdout,
113            EnterAlternateScreen,
114            EnableMouseCapture,
115            Clear(ClearType::All)
116        )?;
117        let backend = CrosstermBackend::new(stdout);
118        let mut terminal = Terminal::new(backend)?;
119
120        let mut list_state = ListState::default();
121        list_state.select(Some(0));
122        let mut exit_hover = false;
123        let mut run_history_map = crate::e_parser::read_run_history(&history_path);
124        'main_loop: loop {
125            terminal.draw(|f| {
126                let size = f.area();
127                let area = Rect::new(0, 0, size.width, size.height);
128                let chunks = Layout::default()
129                    .direction(Direction::Vertical)
130                    .margin(2)
131                    .constraints([Constraint::Min(0)].as_ref())
132                    .split(area);
133                let list_area = chunks[0];
134
135                let left_text = format!("Select target ({} found)", exs.len());
136                let separator = " ┃ ";
137                let right_text = "q to EXIT";
138                let title_line = if exit_hover {
139                    Line::from(vec![
140                        Span::raw(left_text),
141                        Span::raw(separator),
142                        Span::styled(right_text, Style::default().fg(Color::Yellow)),
143                    ])
144                } else {
145                    Line::from(vec![
146                        Span::raw(left_text),
147                        Span::raw(separator),
148                        Span::styled("q to ", Style::default().fg(Color::White)),
149                        Span::styled("EXIT", Style::default().fg(Color::Red)),
150                    ])
151                };
152                let pad_width = exs.len().to_string().len();
153                // Compute the maximum width for the ex.kind values.
154                let max_kind_width = exs
155                    .iter()
156                    .map(|ex| format!("{:?}", ex.kind).len())
157                    .max()
158                    .unwrap_or(0);
159                let mut line_number = 0;
160                let block = Block::default().borders(Borders::ALL).title(title_line);
161                let items: Vec<ListItem> = exs
162                    .iter()
163                    .map(|ex| {
164                        let kind_str = format!("{:?}", ex.kind);
165                        let mut display = format!(
166                            "{:>width$}: [{:>max_kind_width$}] {}",
167                            line_number,
168                            kind_str,
169                            &ex.display_name,
170                            width = pad_width,
171                            max_kind_width = max_kind_width
172                        );
173                        if let Some(count) = run_history_map.get(&ex.name) {
174                            display.push_str(&format!(
175                                "({} run{})",
176                                count,
177                                if *count == 1 { "" } else { "s" }
178                            ));
179                        }
180                        line_number = line_number + 1;
181                        let mut item = ListItem::new(display);
182                        if run_history_map.get(&ex.name).is_some() {
183                            item = item.style(Style::default().fg(Color::Blue));
184                        }
185                        item
186                    })
187                    .collect();
188                let list = List::new(items)
189                    .block(block)
190                    .highlight_style(Style::default().fg(Color::Yellow))
191                    .highlight_symbol(">> ");
192                f.render_stateful_widget(list, list_area, &mut list_state);
193            })?;
194
195            {
196                match event::read()? {
197                    Event::Key(key) => {
198                        // Only process key-press events.
199                        if key.kind == KeyEventKind::Press {
200                            if key.code == KeyCode::Char('c')
201                                && key.modifiers.contains(event::KeyModifiers::CONTROL)
202                            {
203                                eprintln!(
204                                    "CTRL-C detected within TUI event loop. Terminating processes."
205                                );
206                                manager.kill_all();
207                                // Optionally, exit the TUI or perform additional cleanup.
208                            }
209                            // Check if we might be starting an escape sequence for an arrow key.
210                            if key.code == KeyCode::Esc {
211                                // Try to collect the rest of the sequence.
212                                if let Some(arrow_code) = try_collect_arrow_sequence()? {
213                                    match arrow_code {
214                                        KeyCode::Up => {
215                                            let new_index = match list_state.selected() {
216                                                Some(0) | None => 0,
217                                                Some(i) => i.saturating_sub(1),
218                                            };
219                                            list_state.select(Some(new_index));
220                                        }
221                                        KeyCode::Down => {
222                                            let new_index = match list_state.selected() {
223                                                Some(i) if i >= exs.len() - 1 => i,
224                                                Some(i) => i + 1,
225                                                None => 0,
226                                            };
227                                            list_state.select(Some(new_index));
228                                        }
229                                        KeyCode::Left => {
230                                            // Handle left arrow if needed.
231                                        }
232                                        KeyCode::Right => {
233                                            // Handle right arrow if needed.
234                                        }
235                                        _ => {}
236                                    }
237                                    // We've handled the arrow, so skip further processing.
238                                    continue;
239                                } else {
240                                    // No follow-up sequence—treat it as a standalone Esc if needed.
241                                    // For example, you might decide not to exit on Esc now.
242                                    // println!("Standalone Esc detected (ignoring).");
243                                    continue;
244                                }
245                            }
246                            match key.code {
247                                KeyCode::Char('q') => {
248                                    // Exit the TUI mode when 'q' is pressed.
249                                    println!("Exiting TUI mode...");
250                                    break 'main_loop;
251                                }
252                                KeyCode::Down => {
253                                    let i = match list_state.selected() {
254                                        Some(i) if i >= exs.len() - 1 => i,
255                                        Some(i) => i + 1,
256                                        None => 0,
257                                    };
258                                    list_state.select(Some(i));
259                                    thread::sleep(Duration::from_millis(50));
260                                }
261                                KeyCode::Up => {
262                                    let i = match list_state.selected() {
263                                        Some(0) | None => 0,
264                                        Some(i) => i - 1,
265                                    };
266                                    list_state.select(Some(i));
267                                    thread::sleep(Duration::from_millis(50));
268                                }
269                                KeyCode::PageDown => {
270                                    // Compute page size based on the terminal's current height.
271                                    let page = terminal
272                                        .size()
273                                        .map(|r| r.height.saturating_sub(4)) // subtract borders/margins; adjust as needed
274                                        .unwrap_or(5)
275                                        as usize;
276                                    let current = list_state.selected().unwrap_or(0);
277                                    let new = std::cmp::min(current + page, exs.len() - 1);
278                                    list_state.select(Some(new));
279                                }
280                                KeyCode::PageUp => {
281                                    let page = terminal
282                                        .size()
283                                        .map(|r| r.height.saturating_sub(4))
284                                        .unwrap_or(5)
285                                        as usize;
286                                    let current = list_state.selected().unwrap_or(0);
287                                    let new = current.saturating_sub(page);
288                                    list_state.select(Some(new));
289                                }
290                                KeyCode::Char('b') => {
291                                    if let Some(selected) = list_state.selected() {
292                                        let sample = &examples[selected];
293                                        // Run bacon in detached mode. Extra arguments can be added if needed.
294                                        if let Err(e) = e_bacon::run_bacon(sample, &Vec::new()) {
295                                            eprintln!("Error running bacon: {}", e);
296                                        } else {
297                                            println!("Bacon launched for sample: {}", sample.name);
298                                        }
299                                        reinit_terminal(&mut terminal)?;
300                                    }
301                                }
302                                KeyCode::Char('e') => {
303                                    if let Some(selected) = list_state.selected() {
304                                        // Disable raw mode for debug printing.
305                                        crossterm::terminal::disable_raw_mode()?;
306                                        crossterm::execute!(
307                                            std::io::stdout(),
308                                            crossterm::terminal::LeaveAlternateScreen
309                                        )?;
310                                        // When 'e' is pressed, attempt to open the sample in VSCode.
311                                        let sample = &exs[selected];
312                                        println!(
313                                            "Opening VSCode for path: {}",
314                                            sample
315                                                .manifest_path
316                                                .to_str()
317                                                .unwrap_or_default()
318                                                .to_owned()
319                                        );
320                                        // Here we block on the asynchronous open_vscode call.
321                                        // futures::executor::block_on(open_vscode(Path::new(&sample.manifest_path)));
322                                        futures::executor::block_on(
323                                            e_findmain::open_vscode_for_sample(sample),
324                                        );
325                                        std::thread::sleep(std::time::Duration::from_secs(5));
326                                        reinit_terminal(&mut terminal)?;
327                                    }
328                                }
329                                KeyCode::Char('i') => {
330                                    if let Some(selected) = list_state.selected() {
331                                        // Disable raw mode for debug printing.
332                                        crossterm::terminal::disable_raw_mode()?;
333                                        crossterm::execute!(
334                                            std::io::stdout(),
335                                            crossterm::terminal::LeaveAlternateScreen
336                                        )?;
337                                        let target = &exs[selected];
338                                        println!("Target: {:?}", target);
339                                        futures::executor::block_on(
340                                            crate::e_runner::open_ai_summarize_for_target(target),
341                                        );
342                                        prompt_line("", 120).ok();
343                                        reinit_terminal(&mut terminal)?;
344                                    }
345                                }
346                                // KeyCode::Char('v') => {
347                                //     if let Some(selected) = list_state.selected() {
348                                //         // Disable raw mode for debug printing.
349                                //         crossterm::terminal::disable_raw_mode()?;
350                                //         crossterm::execute!(
351                                //             std::io::stdout(),
352                                //             crossterm::terminal::LeaveAlternateScreen
353                                //         )?;
354                                //         // When 'e' is pressed, attempt to open the sample in VSCode.
355                                //         let sample = &examples[selected];
356                                //         println!("Opening VIM for path: {}", sample.manifest_path);
357                                //         // Here we block on the asynchronous open_vscode call.
358                                //         // futures::executor::block_on(open_vscode(Path::new(&sample.manifest_path)));
359                                //         e_findmain::open_vim_for_sample(sample);
360                                //         std::thread::sleep(std::time::Duration::from_secs(5));
361                                //         reinit_terminal(&mut terminal)?;
362                                //     }
363                                // }
364                                KeyCode::Enter => {
365                                    if let Some(selected) = list_state.selected() {
366                                        run_piece(
367                                            manager.clone(),
368                                            &exs,
369                                            selected,
370                                            &history_path,
371                                            &mut run_history,
372                                            &mut terminal,
373                                            cli,
374                                        )?;
375                                        run_history_map =
376                                            crate::e_parser::read_run_history(&history_path);
377                                        reinit_terminal(&mut terminal)?;
378                                    }
379                                }
380                                _ => {
381                                    //println!("Unhandled key event: {:?}", key.code);
382                                }
383                            }
384                        }
385                    }
386                    Event::Mouse(mouse_event) => {
387                        let size = terminal.size()?;
388                        let area = Rect::new(0, 0, size.width, size.height);
389                        let chunks = Layout::default()
390                            .direction(Direction::Vertical)
391                            .margin(2)
392                            .constraints([Constraint::Min(0)].as_ref())
393                            .split(area);
394                        let list_area = chunks[0];
395                        let title_row = list_area.y;
396                        let title_start = list_area.x + 2;
397                        let left_text = format!("Select target ({} found)", exs.len());
398                        let separator = " ┃ ";
399                        let right_text = "q to EXIT";
400                        let offset = (left_text.len() + separator.len()) as u16;
401                        let right_region_start = title_start + offset;
402                        let right_region_end = right_region_start + (right_text.len() as u16);
403
404                        match mouse_event.kind {
405                            MouseEventKind::ScrollDown => {
406                                let current = list_state.selected().unwrap_or(0);
407                                let new = std::cmp::min(current + 1, exs.len() - 1);
408                                list_state.select(Some(new));
409                            }
410                            MouseEventKind::ScrollUp => {
411                                let current = list_state.selected().unwrap_or(0);
412                                let new = if current == 0 { 0 } else { current - 1 };
413                                list_state.select(Some(new));
414                            }
415
416                            MouseEventKind::Moved => {
417                                if mouse_event.row == title_row {
418                                    exit_hover = mouse_event.column >= right_region_start
419                                        && mouse_event.column < right_region_end;
420                                } else {
421                                    exit_hover = false;
422                                    let inner_y = list_area.y + 1;
423                                    let inner_height = list_area.height.saturating_sub(2);
424                                    if mouse_event.column > list_area.x + 1
425                                        && mouse_event.column < list_area.x + list_area.width - 1
426                                        && mouse_event.row >= inner_y
427                                        && mouse_event.row < inner_y + inner_height
428                                    {
429                                        let index = (mouse_event.row - inner_y) as usize;
430                                        if index < exs.len() {
431                                            list_state.select(Some(index));
432                                        }
433                                    }
434                                }
435                            }
436                            MouseEventKind::Down(_) => {
437                                if mouse_event.row == title_row
438                                    && mouse_event.column >= right_region_start
439                                    && mouse_event.column < right_region_end
440                                {
441                                    println!("Exiting TUI mode...");
442                                    break 'main_loop;
443                                }
444                                let inner_y = list_area.y + 1;
445                                let inner_height = list_area.height.saturating_sub(2);
446                                if mouse_event.column > list_area.x + 1
447                                    && mouse_event.column < list_area.x + list_area.width - 1
448                                    && mouse_event.row >= inner_y
449                                    && mouse_event.row < inner_y + inner_height
450                                {
451                                    let index = (mouse_event.row - inner_y) as usize;
452                                    if index < exs.len() {
453                                        list_state.select(Some(index));
454                                        run_piece(
455                                            manager.clone(),
456                                            &exs.clone(),
457                                            index,
458                                            &history_path,
459                                            &mut run_history,
460                                            &mut terminal,
461                                            cli,
462                                        )?;
463                                        run_history_map =
464                                            crate::e_parser::read_run_history(&history_path);
465                                    }
466                                }
467                            }
468                            _ => {}
469                        }
470                    }
471                    _ => {}
472                }
473            }
474        }
475
476        disable_raw_mode()?;
477        let mut stdout = io::stdout();
478        execute!(
479            stdout,
480            LeaveAlternateScreen,
481            DisableMouseCapture,
482            Clear(ClearType::All)
483        )?;
484        terminal.show_cursor()?;
485        manager.generate_report(cli.gist);
486        Ok(())
487    }
488
489    /// Reinitializes the terminal: enables raw mode, enters the alternate screen,
490    /// enables mouse capture, clears the screen, and creates a new Terminal instance.
491    /// This function updates the provided terminal reference.
492    pub fn reinit_terminal(
493        terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
494    ) -> Result<(), Box<dyn Error>> {
495        enable_raw_mode()?;
496        let mut stdout = io::stdout();
497        execute!(
498            stdout,
499            EnterAlternateScreen,
500            EnableMouseCapture,
501            Clear(ClearType::All)
502        )?;
503        *terminal = Terminal::new(CrosstermBackend::new(stdout))?;
504        flush_input()?; // Clear any buffered input after reinitializing the terminal.
505        Ok(())
506    }
507
508    /// Runs the given example (or binary) target. It leaves TUI mode, spawns a cargo process,
509    /// installs a Ctrl+C handler to kill the process, waits for it to finish, updates history,
510    /// flushes stray input, and then reinitializes the terminal.
511    pub fn run_piece(
512        manager: Arc<crate::e_processmanager::ProcessManager>,
513        examples: &[CargoTarget],
514        index: usize,
515        history_path: &Path,
516        _run_history: &mut HashSet<String>,
517        terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
518        cli: &Cli,
519    ) -> Result<(), Box<dyn Error>> {
520        let target = &examples[index];
521        // Leave TUI mode before running the target.
522        disable_raw_mode()?;
523        execute!(
524            terminal.backend_mut(),
525            LeaveAlternateScreen,
526            crossterm::event::DisableMouseCapture
527        )?;
528        terminal.show_cursor()?;
529
530        let manifest_path = PathBuf::from(target.manifest_path.clone());
531        let builder =
532            CargoCommandBuilder::new(&target.name, &manifest_path, &cli.subcommand, cli.filter)
533                .with_target(target)
534                .with_cli(cli);
535        let cmd = builder.build_command();
536
537        // Set current directory appropriately.
538        // if target.kind == TargetKind::ManifestTauri {
539        //     let manifest_dir = manifest_path.parent().expect("Expected parent directory");
540        //     cmd.current_dir(manifest_dir);
541        // } else if target.extended {
542        //     if let Some(dir) = manifest_path.parent() {
543        //         cmd.current_dir(dir);
544        //     }
545        // }
546
547        println!("Running command: {:?}", cmd);
548        // If the target is extended, we want to run it from its directory.
549        if target.extended {
550            Path::new(&manifest_path).parent().map(|p| p.to_owned())
551        } else {
552            None
553        };
554
555        // Before spawning, patch the manifest if needed.
556        let manifest_path_obj = Path::new(&manifest_path);
557        let backup = maybe_patch_manifest_for_run(manifest_path_obj)?;
558
559        // // // Build the command.
560        // // let mut cmd = Command::new("cargo");
561        // // cmd.args(&args);
562        // // if let Some(ref dir) = current_dir {
563        // //     cmd.current_dir(dir);
564        // // }
565        // // Convert command args into &str slices for spawn_cargo_process.
566        // // (Assuming spawn_cargo_process accepts a slice of &str.)
567        // let owned_args: Vec<String> = cmd
568        //     .get_args()
569        //     .map(|arg| arg.to_string_lossy().to_string())
570        //     .collect();
571        // // Now create a vector of &str references valid as long as `owned_args` is in scope:
572        // let args_ref: Vec<&str> = owned_args.iter().map(|s| s.as_str()).collect();
573
574        // // let args_ref: Vec<&str> = args.iter().map(|s| &**s).collect();
575        // let mut child = crate::e_runner::spawn_cargo_process(&args_ref)?;
576
577        let pid = Arc::new(builder).run(|_pid, handle| {
578            manager.register(handle);
579        })?;
580        // let ret=manager.wait(pid, None)?;
581        let handle = manager.get(pid).unwrap().clone();
582        let start_time = handle.lock().unwrap().start_time;
583        // let mut child = cmd.spawn()?;
584        // if cli.print_instruction {
585        //     println!("Process started. Press Ctrl+C to terminate or 'd' to detach...");
586        // }
587        let mut update_history = true;
588        let mut status_code: i32 = -255;
589        let mut detached = false;
590        let shared_child = handle.clone();
591        let system = Arc::new(Mutex::new(System::new_all()));
592        std::thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
593        // Refresh CPU usage to get actual value.
594        let mut system_guard = system.lock().unwrap();
595        system_guard.refresh_processes_specifics(
596            ProcessesToUpdate::All,
597            true,
598            ProcessRefreshKind::nothing().with_cpu(),
599        );
600        drop(system_guard);
601        // Now we enter an event loop, periodically checking if the child has exited
602        // and polling for keyboard input.
603        {
604            let mut stdout = std::io::stdout();
605            crossterm::execute!(stdout, crossterm::cursor::Hide)?;
606        }
607        let _cursor_guard = crate::e_processmanager::CursorGuard;
608
609        const SAMPLE_INTERVAL: usize = 10; // Update status every 10 iterations
610
611        let mut sample_count: usize = 0;
612        loop {
613            sample_count += 1;
614
615            // Check if the process has finished.
616            {
617                let mut child_guard = shared_child.lock().unwrap();
618                if let Some(status) = child_guard.child.try_wait()? {
619                    let status_code = status.code().unwrap_or(1);
620                    println!("Process exited with status: {}", status_code);
621                    break;
622                }
623            }
624            // // Check if the child process has finished.
625            if let Some(status) = handle.clone().lock().unwrap().child.try_wait()? {
626                status_code = status.code().unwrap_or(1);
627
628                println!("Process exited with status: {}", status_code);
629                // println!("HERE IS THE RESULT!{} {:?}",pid,manager.get(pid));
630                // manager.print_shortened_output();
631                manager.print_compact();
632                break;
633            }
634            // Poll for input events with a 100ms timeout.
635            if event::poll(Duration::from_millis(100))? {
636                if let Event::Key(key_event) = event::read()? {
637                    if key_event.code == KeyCode::Char('c')
638                        && key_event.modifiers.contains(event::KeyModifiers::CONTROL)
639                    {
640                        if cli.print_instruction {
641                            println!("Ctrl+C detected in event loop, killing process...");
642                        }
643                        shared_child.lock().expect("expected child").child.kill()?;
644                        update_history = false; // do not update history if cancelled
645                                                // Optionally, you can also wait for the child after killing.
646                        let status = shared_child
647                            .lock()
648                            .expect("shared child can't lock")
649                            .child
650                            .wait()?;
651                        status_code = status.code().unwrap_or(1);
652                        break;
653                    } else if key_event.code == KeyCode::Char('d') && key_event.modifiers.is_empty()
654                    {
655                        if cli.print_instruction {
656                            println!(
657                                "'d' pressed; detaching process. Process will continue running."
658                            );
659                        }
660                        detached = true;
661                        update_history = false;
662                        // Do not kill or wait on the child.
663                        // Break out of the loop immediately.
664                        // We can optionally leave the process running.
665                        status_code = 0;
666                        break;
667                    }
668                }
669            }
670
671            // Only update the status display every SAMPLE_INTERVAL iterations.
672            if sample_count % SAMPLE_INTERVAL == 0 {
673                // system.refresh_all();
674                let mut system_guard = system.lock().unwrap();
675                system_guard.refresh_processes_specifics(
676                    ProcessesToUpdate::All,
677                    true,
678                    ProcessRefreshKind::nothing().with_cpu(),
679                );
680                drop(system_guard);
681                // Refresh CPU usage to get actual value.
682                let status_display = ProcessManager::format_process_status(
683                    pid,
684                    Some(start_time),
685                    system.clone(),
686                    &target,
687                    (index + 1, examples.len()),
688                );
689                ProcessManager::update_status_line(&status_display, true).ok();
690            }
691        }
692        // Restore the manifest if it was patched.
693        if let Some(original) = backup {
694            fs::write(manifest_path_obj, original)?;
695        }
696        // Wrap the child process so that we can share it with our Ctrl+C handler.
697        // let child_arc = Arc::new(Mutex::new(child));
698        // let child_for_handler = Arc::clone(&child_arc);
699
700        // Set up a Ctrl+C handler to kill the spawned process.
701        // ctrlc::set_handler(move || {
702        // eprintln!("Ctrl+C pressed, terminating process...");
703        // if let Ok(mut child) = child_for_handler.lock() {
704        // let _ = child.kill();
705        // }
706        // })?;
707
708        // Wait for the process to finish.
709        // let status = child_arc.lock().unwrap().wait()?;
710        // println!("Process exited with status: {:?}", status.code());
711
712        if !detached {
713            // Only update run history if update_history is true and exit code is zero.
714            if update_history && status_code == 0 {
715                use std::io::Write;
716                let mut file = fs::OpenOptions::new()
717                    .create(true)
718                    .append(true)
719                    .open(history_path)?;
720                // Append a newline-separated entry.
721                writeln!(file, "{}", target.name)?;
722            }
723            // let message = if cli.print_exit_code {
724            //     format!("Exitcode {:?}. Press any key to continue...", status_code)
725            // } else {
726            //     "".to_string()
727            // };
728            // PROMPT let _ = crate::e_prompts::prompt(&message, cli.wait)?;
729        }
730
731        reinit_terminal(terminal)?; // Reinitialize the terminal after running the target.
732
733        // // Flush stray input events.
734        // while event::poll(std::time::Duration::from_millis(0))? {
735        //     let _ = event::read()?;
736        // }
737        // std::thread::sleep(std::time::Duration::from_millis(50));
738
739        // // // Reinitialize the terminal.
740        // enable_raw_mode()?;
741        // let mut stdout = io::stdout();
742        // execute!(
743        //     stdout,
744        //     EnterAlternateScreen,
745        //     crossterm::event::EnableMouseCapture,
746        //     Clear(ClearType::All)
747        // )?;
748        // *terminal = Terminal::new(CrosstermBackend::new(stdout))?;
749        Ok(())
750    }
751}