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 = CargoCommandBuilder::new(
532            &target.name,
533            &manifest_path,
534            &cli.subcommand,
535            cli.filter,
536            cli.cached,
537            cli.default_binary_is_runner,
538        )
539        .with_target(target)
540        .with_cli(cli);
541        let cmd = builder.build_command();
542
543        // Set current directory appropriately.
544        // if target.kind == TargetKind::ManifestTauri {
545        //     let manifest_dir = manifest_path.parent().expect("Expected parent directory");
546        //     cmd.current_dir(manifest_dir);
547        // } else if target.extended {
548        //     if let Some(dir) = manifest_path.parent() {
549        //         cmd.current_dir(dir);
550        //     }
551        // }
552
553        println!("Running command: {:?}", cmd);
554        // If the target is extended, we want to run it from its directory.
555        if target.extended {
556            Path::new(&manifest_path).parent().map(|p| p.to_owned())
557        } else {
558            None
559        };
560
561        // Before spawning, patch the manifest if needed.
562        let manifest_path_obj = Path::new(&manifest_path);
563        let backup = maybe_patch_manifest_for_run(manifest_path_obj)?;
564
565        // // // Build the command.
566        // // let mut cmd = Command::new("cargo");
567        // // cmd.args(&args);
568        // // if let Some(ref dir) = current_dir {
569        // //     cmd.current_dir(dir);
570        // // }
571        // // Convert command args into &str slices for spawn_cargo_process.
572        // // (Assuming spawn_cargo_process accepts a slice of &str.)
573        // let owned_args: Vec<String> = cmd
574        //     .get_args()
575        //     .map(|arg| arg.to_string_lossy().to_string())
576        //     .collect();
577        // // Now create a vector of &str references valid as long as `owned_args` is in scope:
578        // let args_ref: Vec<&str> = owned_args.iter().map(|s| s.as_str()).collect();
579
580        // // let args_ref: Vec<&str> = args.iter().map(|s| &**s).collect();
581        // let mut child = crate::e_runner::spawn_cargo_process(&args_ref)?;
582
583        let pid = Arc::new(builder).run(|_pid, handle| {
584            manager.register(handle);
585        })?;
586        // let ret=manager.wait(pid, None)?;
587        let handle = manager.get(pid).unwrap().clone();
588        let start_time = handle.lock().unwrap().start_time;
589        // let mut child = cmd.spawn()?;
590        // if cli.print_instruction {
591        //     println!("Process started. Press Ctrl+C to terminate or 'd' to detach...");
592        // }
593        let mut update_history = true;
594        let mut status_code: i32 = -255;
595        let mut detached = false;
596        let shared_child = handle.clone();
597        let system = Arc::new(Mutex::new(System::new_all()));
598        std::thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
599        // Refresh CPU usage to get actual value.
600        let mut system_guard = system.lock().unwrap();
601        system_guard.refresh_processes_specifics(
602            ProcessesToUpdate::All,
603            true,
604            ProcessRefreshKind::nothing().with_cpu(),
605        );
606        drop(system_guard);
607        // Now we enter an event loop, periodically checking if the child has exited
608        // and polling for keyboard input.
609        {
610            let mut stdout = std::io::stdout();
611            crossterm::execute!(stdout, crossterm::cursor::Hide)?;
612        }
613        let _cursor_guard = crate::e_processmanager::CursorGuard;
614
615        const SAMPLE_INTERVAL: usize = 10; // Update status every 10 iterations
616
617        let mut sample_count: usize = 0;
618        loop {
619            sample_count += 1;
620
621            // Check if the process has finished.
622            {
623                let mut child_guard = shared_child.lock().unwrap();
624                if let Some(status) = child_guard.child.try_wait()? {
625                    let status_code = status.code().unwrap_or(1);
626                    println!("Process exited with status: {}", status_code);
627                    break;
628                }
629            }
630            // // Check if the child process has finished.
631            if let Some(status) = handle.clone().lock().unwrap().child.try_wait()? {
632                status_code = status.code().unwrap_or(1);
633
634                println!("Process exited with status: {}", status_code);
635                // println!("HERE IS THE RESULT!{} {:?}",pid,manager.get(pid));
636                // manager.print_shortened_output();
637                manager.print_compact();
638                break;
639            }
640            // Poll for input events with a 100ms timeout.
641            if event::poll(Duration::from_millis(100))? {
642                if let Event::Key(key_event) = event::read()? {
643                    if key_event.code == KeyCode::Char('c')
644                        && key_event.modifiers.contains(event::KeyModifiers::CONTROL)
645                    {
646                        if cli.print_instruction {
647                            println!("Ctrl+C detected in event loop, killing process...");
648                        }
649                        shared_child.lock().expect("expected child").child.kill()?;
650                        update_history = false; // do not update history if cancelled
651                                                // Optionally, you can also wait for the child after killing.
652                        let status = shared_child
653                            .lock()
654                            .expect("shared child can't lock")
655                            .child
656                            .wait()?;
657                        status_code = status.code().unwrap_or(1);
658                        break;
659                    } else if key_event.code == KeyCode::Char('d') && key_event.modifiers.is_empty()
660                    {
661                        if cli.print_instruction {
662                            println!(
663                                "'d' pressed; detaching process. Process will continue running."
664                            );
665                        }
666                        detached = true;
667                        update_history = false;
668                        // Do not kill or wait on the child.
669                        // Break out of the loop immediately.
670                        // We can optionally leave the process running.
671                        status_code = 0;
672                        break;
673                    }
674                }
675            }
676
677            // Only update the status display every SAMPLE_INTERVAL iterations.
678            if sample_count % SAMPLE_INTERVAL == 0 && !cli.no_status_lines {
679                // system.refresh_all();
680                let mut system_guard = system.lock().unwrap();
681                system_guard.refresh_processes_specifics(
682                    ProcessesToUpdate::All,
683                    true,
684                    ProcessRefreshKind::nothing().with_cpu(),
685                );
686                drop(system_guard);
687                // Refresh CPU usage to get actual value.
688                let status_display = ProcessManager::format_process_status(
689                    pid,
690                    Some(start_time),
691                    &target,
692                    (index + 1, examples.len()),
693                );
694                ProcessManager::update_status_line(&status_display, true).ok();
695            }
696        }
697        // Restore the manifest if it was patched.
698        if let Some(original) = backup {
699            fs::write(manifest_path_obj, original)?;
700        }
701        // Wrap the child process so that we can share it with our Ctrl+C handler.
702        // let child_arc = Arc::new(Mutex::new(child));
703        // let child_for_handler = Arc::clone(&child_arc);
704
705        // Set up a Ctrl+C handler to kill the spawned process.
706        // ctrlc::set_handler(move || {
707        // eprintln!("Ctrl+C pressed, terminating process...");
708        // if let Ok(mut child) = child_for_handler.lock() {
709        // let _ = child.kill();
710        // }
711        // })?;
712
713        // Wait for the process to finish.
714        // let status = child_arc.lock().unwrap().wait()?;
715        // println!("Process exited with status: {:?}", status.code());
716
717        if !detached {
718            // Only update run history if update_history is true and exit code is zero.
719            if update_history && status_code == 0 {
720                use std::io::Write;
721                let mut file = fs::OpenOptions::new()
722                    .create(true)
723                    .append(true)
724                    .open(history_path)?;
725                // Append a newline-separated entry.
726                writeln!(file, "{}", target.name)?;
727            }
728            // let message = if cli.print_exit_code {
729            //     format!("Exitcode {:?}. Press any key to continue...", status_code)
730            // } else {
731            //     "".to_string()
732            // };
733            // PROMPT let _ = crate::e_prompts::prompt(&message, cli.wait)?;
734        }
735
736        reinit_terminal(terminal)?; // Reinitialize the terminal after running the target.
737
738        // // Flush stray input events.
739        // while event::poll(std::time::Duration::from_millis(0))? {
740        //     let _ = event::read()?;
741        // }
742        // std::thread::sleep(std::time::Duration::from_millis(50));
743
744        // // // Reinitialize the terminal.
745        // enable_raw_mode()?;
746        // let mut stdout = io::stdout();
747        // execute!(
748        //     stdout,
749        //     EnterAlternateScreen,
750        //     crossterm::event::EnableMouseCapture,
751        //     Clear(ClearType::All)
752        // )?;
753        // *terminal = Terminal::new(CrosstermBackend::new(stdout))?;
754        Ok(())
755    }
756}