carch_core/ui/
app.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3
4use log::info;
5use ratatui::layout::Rect;
6
7use super::state::{
8    App, AppMode, DescriptionState, FocusedPanel, HelpState, MultiSelectState, PreviewState,
9    SearchState, StatefulList,
10};
11use crate::ui::state::{ScriptItem, UiOptions};
12use crate::ui::theme::Theme;
13
14impl<'a> App<'a> {
15    pub fn new(options: &UiOptions) -> App<'a> {
16        let theme = match options.theme.as_str() {
17            "catppuccin-mocha" => Theme::catppuccin_mocha(),
18            "dracula" => Theme::dracula(),
19            "gruvbox" => Theme::gruvbox(),
20            "nord" => Theme::nord(),
21            "rose-pine" => Theme::rose_pine(),
22            _ => Theme::catppuccin_mocha(),
23        };
24
25        App {
26            mode: if options.is_root { AppMode::RootWarning } else { AppMode::Normal },
27            quit: false,
28            focused_panel: FocusedPanel::Categories,
29            log_mode: false,
30            modules_dir: PathBuf::new(),
31            theme,
32            theme_locked: options.theme_locked,
33
34            scripts: StatefulList::new(),
35            categories: StatefulList::new(),
36            all_scripts: HashMap::new(),
37
38            script_panel_area: Rect::default(),
39            preview: PreviewState::default(),
40            search: SearchState::default(),
41            multi_select: MultiSelectState::default(),
42            help: HelpState::default(),
43            description: DescriptionState::default(),
44            run_script_popup: None,
45            script_execution_queue: Vec::new(),
46        }
47    }
48
49    pub fn cycle_theme(&mut self) {
50        self.theme = match self.theme.name.as_str() {
51            "Catppuccin Mocha" => Theme::dracula(),
52            "Dracula" => Theme::gruvbox(),
53            "Gruvbox" => Theme::nord(),
54            "Nord" => Theme::rose_pine(),
55            "Rosé Pine" => Theme::catppuccin_mocha(),
56            _ => Theme::catppuccin_mocha(),
57        }
58    }
59
60    pub fn toggle_description_popup(&mut self) {
61        if self.mode == AppMode::Description {
62            self.mode = AppMode::Normal;
63            self.description.content = None;
64            if self.log_mode {
65                info!("Closed description popup");
66            }
67        } else if let Some(selected_script) = self.get_selected_script() {
68            let desc_path = self.modules_dir.join(&selected_script.category).join("desc.toml");
69
70            if self.log_mode {
71                info!(
72                    "Attempting to show description for script: {}/{}",
73                    selected_script.category, selected_script.name
74                );
75                info!("Description file path: {}", desc_path.display());
76            }
77
78            if desc_path.exists() {
79                if let Ok(content) = std::fs::read_to_string(&desc_path) {
80                    if let Ok(table) = content.parse::<toml::Table>() {
81                        let script_path = PathBuf::from(&selected_script.name);
82                        let script_name_without_ext =
83                            script_path.file_stem().and_then(|s| s.to_str());
84
85                        if let Some(name) = script_name_without_ext {
86                            if let Some(desc) = table
87                                .get(name)
88                                .and_then(|v| v.as_table())
89                                .and_then(|t| t.get("description"))
90                                .and_then(|v| v.as_str())
91                            {
92                                self.description.content = Some(desc.to_string());
93                                self.mode = AppMode::Description;
94                                if self.log_mode {
95                                    info!(
96                                        "Successfully loaded description and entered description mode."
97                                    );
98                                }
99                            } else if self.log_mode {
100                                info!(
101                                    "No description found for script '{}' in desc.toml",
102                                    selected_script.name
103                                );
104                            }
105                        }
106                    } else if self.log_mode {
107                        info!("Failed to parse desc.toml at {}", desc_path.display());
108                    }
109                } else if self.log_mode {
110                    info!("Failed to read desc.toml at {}", desc_path.display());
111                }
112            } else if self.log_mode {
113                info!("desc.toml not found at {}", desc_path.display());
114            }
115        }
116    }
117
118    pub fn get_selected_script(&self) -> Option<&ScriptItem> {
119        self.scripts.state.selected().map(|i| &self.scripts.items[i])
120    }
121}