1use crossterm::event::{KeyCode, KeyEvent, MouseEvent, MouseEventKind};
2use log::info;
3
4use super::popups::run_script::RunScriptPopup;
5use super::state::{App, AppMode, FocusedPanel};
6
7impl<'a> App<'a> {
8 pub fn handle_search_input(&mut self, key: KeyEvent) {
9 match key.code {
10 KeyCode::Esc => self.toggle_search_mode(),
11 KeyCode::Enter if !self.search.results.is_empty() => {
12 let selected_item = self.search.results[self.search.selected_idx].clone();
13 if self.log_mode {
14 info!(
15 "Selected script from search: {}/{}",
16 selected_item.item.category, selected_item.item.name
17 );
18 }
19
20 if let Some(category_idx) =
21 self.categories.items.iter().position(|c| *c == selected_item.item.category)
22 {
23 self.categories.state.select(Some(category_idx));
24 self.update_script_list();
25
26 if let Some(script_idx) =
27 self.scripts.items.iter().position(|s| s.name == selected_item.item.name)
28 {
29 self.scripts.state.select(Some(script_idx));
30 }
31 }
32
33 self.update_preview();
34 self.toggle_search_mode();
35 self.focused_panel = FocusedPanel::Scripts;
36 self.mode = AppMode::Normal;
37 }
38 KeyCode::Down if !self.search.results.is_empty() => {
39 self.search.selected_idx =
40 (self.search.selected_idx + 1) % self.search.results.len();
41 }
42 KeyCode::Up if !self.search.results.is_empty() => {
43 self.search.selected_idx = if self.search.selected_idx > 0 {
44 self.search.selected_idx - 1
45 } else {
46 self.search.results.len() - 1
47 };
48 }
49 KeyCode::Tab => {
50 if let Some(autocomplete) = self.search.autocomplete.take() {
51 self.search.input = autocomplete;
52 self.search.cursor_position = self.search.input.len();
53 self.perform_search();
54 self.update_autocomplete();
55 }
56 }
57 KeyCode::Char(c) => {
58 self.search.input.push(c);
59 self.search.cursor_position += 1;
60 self.perform_search();
61 self.update_autocomplete();
62 self.search.selected_idx = 0;
63 }
64 KeyCode::Backspace if self.search.cursor_position > 0 => {
65 self.search.input.remove(self.search.cursor_position - 1);
66 self.search.cursor_position -= 1;
67 self.perform_search();
68 self.update_autocomplete();
69 self.search.selected_idx = 0;
70 }
71 KeyCode::Left => {
72 if self.search.cursor_position > 0 {
73 self.search.cursor_position -= 1;
74 }
75 self.search.autocomplete = None;
76 }
77 KeyCode::Right => {
78 if self.search.cursor_position < self.search.input.len() {
79 self.search.cursor_position += 1;
80 }
81 if self.search.cursor_position == self.search.input.len()
82 && self.search.autocomplete.is_some()
83 {
84 self.search.input = self.search.autocomplete.take().unwrap();
85 self.search.cursor_position = self.search.input.len();
86 self.perform_search();
87 }
88 }
89 _ => {}
90 }
91 }
92
93 pub fn handle_key_normal_mode(&mut self, key: KeyEvent) {
94 match key.code {
95 KeyCode::Char('q') => {
96 if self.multi_select.enabled {
97 self.toggle_multi_select_mode();
98 } else {
99 self.quit = true;
100 }
101 }
102 KeyCode::Esc if self.multi_select.enabled => {
103 self.toggle_multi_select_mode();
104 }
105 KeyCode::Char('j') | KeyCode::Down => {
106 self.next();
107 }
108 KeyCode::Char('k') | KeyCode::Up => {
109 self.previous();
110 }
111 KeyCode::Char('h') | KeyCode::Left => {
112 self.focused_panel = FocusedPanel::Categories;
113 self.scripts.state.select(None);
114 }
115 KeyCode::Char('l') | KeyCode::Right => {
116 if self.focused_panel == FocusedPanel::Scripts {
117 if self.scripts.state.selected().is_some()
118 && !(self.multi_select.enabled && self.multi_select.scripts.is_empty())
119 {
120 self.mode = AppMode::Confirm;
121 }
122 } else {
123 self.focused_panel = FocusedPanel::Scripts;
124 if !self.scripts.items.is_empty() {
125 self.scripts.state.select(Some(0));
126 }
127 }
128 }
129 KeyCode::Home => {
130 self.top();
131 }
132 KeyCode::End => {
133 self.bottom();
134 }
135 KeyCode::Char('/') => {
136 self.toggle_search_mode();
137 }
138 KeyCode::Char('p') => {
139 self.toggle_preview_mode();
140 }
141 KeyCode::Char('m') => {
142 self.toggle_multi_select_mode();
143 }
144 KeyCode::Char('?') => {
145 self.toggle_help_mode();
146 }
147 KeyCode::Char('d') => {
148 self.toggle_description_popup();
149 }
150 KeyCode::Char('t') if !self.theme_locked => {
151 self.cycle_theme();
152 }
153 KeyCode::Enter
154 if self.focused_panel == FocusedPanel::Scripts
155 && self.scripts.state.selected().is_some()
156 && !(self.multi_select.enabled && self.multi_select.scripts.is_empty()) =>
157 {
158 self.mode = AppMode::Confirm;
159 }
160 KeyCode::Char(' ') if self.multi_select.enabled => {
161 self.toggle_script_selection();
162 }
163 _ => {}
164 }
165 }
166
167 pub fn handle_key_preview_mode(&mut self, key: KeyEvent) {
168 match key.code {
169 KeyCode::Char('q') | KeyCode::Esc | KeyCode::Char('p') | KeyCode::Char('h') => {
170 self.mode = AppMode::Normal;
171 }
172 KeyCode::Char('j') | KeyCode::Down => {
173 self.scroll_preview_down();
174 }
175 KeyCode::Char('k') | KeyCode::Up => {
176 self.scroll_preview_up();
177 }
178 KeyCode::PageDown => {
179 self.scroll_preview_page_down();
180 }
181 KeyCode::PageUp => {
182 self.scroll_preview_page_up();
183 }
184 KeyCode::Home => {
185 self.preview.scroll = 0;
186 }
187 KeyCode::End => {
188 self.preview.scroll = self.preview.max_scroll;
189 }
190 _ => {}
191 }
192 }
193
194 pub fn handle_mouse(&mut self, event: MouseEvent) {
195 match event.kind {
196 MouseEventKind::ScrollDown => match self.mode {
197 AppMode::Normal => self.next(),
198 AppMode::Preview => {
199 for _ in 0..2 {
200 self.scroll_preview_down();
201 }
202 }
203 AppMode::Search => {
204 if !self.search.results.is_empty() {
205 self.search.selected_idx =
206 (self.search.selected_idx + 1) % self.search.results.len();
207 }
208 }
209 AppMode::Confirm => {}
210 AppMode::Help => {
211 self.help.scroll = self.help.scroll.saturating_add(2).min(self.help.max_scroll);
212 }
213 AppMode::Description => {}
214 AppMode::RunScript => {}
215 AppMode::RootWarning => {}
216 },
217 MouseEventKind::ScrollUp => match self.mode {
218 AppMode::Normal => self.previous(),
219 AppMode::Preview => {
220 for _ in 0..2 {
221 self.scroll_preview_up();
222 }
223 }
224 AppMode::Search => {
225 if !self.search.results.is_empty() {
226 self.search.selected_idx = if self.search.selected_idx > 0 {
227 self.search.selected_idx - 1
228 } else {
229 self.search.results.len() - 1
230 };
231 }
232 }
233 AppMode::Confirm => {}
234 AppMode::Help => {
235 self.help.scroll = self.help.scroll.saturating_sub(2);
236 }
237 AppMode::Description => {}
238 AppMode::RunScript => {}
239 AppMode::RootWarning => {}
240 },
241 _ => {}
242 }
243 }
244
245 pub fn handle_key_confirmation_mode(&mut self, key: KeyEvent) {
246 match key.code {
247 KeyCode::Char('y') | KeyCode::Char('Y') | KeyCode::Char('l') | KeyCode::Right => {
248 if self.log_mode {
249 if self.multi_select.enabled && !self.multi_select.scripts.is_empty() {
250 info!(
251 "Confirmed execution of {} scripts.",
252 self.multi_select.scripts.len()
253 );
254 } else if let Some(script_path) = self.get_script_path() {
255 info!("Confirmed execution of script: {}", script_path.display());
256 }
257 }
258
259 if self.multi_select.enabled && !self.multi_select.scripts.is_empty() {
260 self.script_execution_queue = self.multi_select.scripts.clone();
261 } else if let Some(script_path) = self.get_script_path() {
262 self.script_execution_queue.push(script_path);
263 }
264
265 if !self.script_execution_queue.is_empty() {
266 let script_path = self.script_execution_queue.remove(0);
267 let popup = RunScriptPopup::new(script_path, self.log_mode, self.theme.clone());
268 self.run_script_popup = Some(popup);
269 self.mode = AppMode::RunScript;
270 } else {
271 self.mode = AppMode::Normal;
272 }
273 }
274 KeyCode::Char('n')
275 | KeyCode::Char('N')
276 | KeyCode::Esc
277 | KeyCode::Char('h')
278 | KeyCode::Char('q')
279 | KeyCode::Left => {
280 if self.log_mode {
281 info!("Cancelled script execution.");
282 }
283 self.mode = AppMode::Normal;
284 }
285 _ => {}
286 }
287 }
288
289 pub fn handle_key_help_mode(&mut self, key: KeyEvent) {
290 match key.code {
291 KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('?') => {
292 self.mode = AppMode::Normal;
293 self.help.scroll = 0;
294 }
295 KeyCode::Char('j') | KeyCode::Down => {
296 self.help.scroll = (self.help.scroll + 1).min(self.help.max_scroll);
297 }
298 KeyCode::Char('k') | KeyCode::Up => {
299 self.help.scroll = self.help.scroll.saturating_sub(1);
300 }
301 KeyCode::Home => {
302 self.help.scroll = 0;
303 }
304 KeyCode::End => {
305 self.help.scroll = self.help.max_scroll;
306 }
307 KeyCode::PageDown => {
308 self.help.scroll = (self.help.scroll + 10).min(self.help.max_scroll);
309 }
310 KeyCode::PageUp => {
311 self.help.scroll = self.help.scroll.saturating_sub(10);
312 }
313 _ => {}
314 }
315 }
316
317 pub fn handle_key_description_mode(&mut self, key: KeyEvent) {
318 match key.code {
319 KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('d') => {
320 self.mode = AppMode::Normal;
321 self.description.content = None;
322 self.description.scroll = 0;
323 }
324 KeyCode::Char('j') | KeyCode::Down => {
325 self.description.scroll =
326 (self.description.scroll + 1).min(self.description.max_scroll);
327 }
328 KeyCode::Char('k') | KeyCode::Up => {
329 self.description.scroll = self.description.scroll.saturating_sub(1);
330 }
331 _ => {}
332 }
333 }
334}