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