1use std::collections::HashMap;
2use std::path::PathBuf;
3
4use fuzzy_matcher::skim::SkimMatcherV2;
5use ratatui::layout::Rect;
6use ratatui::text::Text;
7
8use super::popups::run_script::RunScriptPopup;
9use super::theme::Theme;
10
11#[derive(PartialEq, Debug, Clone, Copy)]
12pub enum AppMode {
13 Normal,
14 Search,
15 Confirm,
16 Help,
17 Preview,
18 RunScript,
19 Description,
20 RootWarning,
21}
22
23#[derive(PartialEq, Debug, Clone, Copy)]
24pub enum FocusedPanel {
25 Categories,
26 Scripts,
27}
28
29pub struct StatefulList<T> {
30 pub state: ratatui::widgets::ListState,
31 pub items: Vec<T>,
32}
33
34impl<T> Default for StatefulList<T> {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl<T> StatefulList<T> {
41 pub fn new() -> StatefulList<T> {
42 StatefulList { state: ratatui::widgets::ListState::default(), items: Vec::new() }
43 }
44
45 pub fn with_items(items: Vec<T>) -> StatefulList<T> {
46 let mut list = StatefulList::new();
47 list.items = items;
48 if !list.items.is_empty() {
49 list.state.select(Some(0));
50 }
51 list
52 }
53
54 pub fn next(&mut self) {
55 let i = match self.state.selected() {
56 Some(i) => {
57 if i >= self.items.len() - 1 {
58 0
59 } else {
60 i + 1
61 }
62 }
63 None => 0,
64 };
65 self.state.select(Some(i));
66 }
67
68 pub fn previous(&mut self) {
69 let i = match self.state.selected() {
70 Some(i) => {
71 if i == 0 {
72 self.items.len() - 1
73 } else {
74 i - 1
75 }
76 }
77 None => 0,
78 };
79 self.state.select(Some(i));
80 }
81}
82
83#[derive(Clone, Debug)]
84pub struct ScriptItem {
85 pub category: String,
86 pub name: String,
87 pub path: PathBuf,
88}
89
90#[derive(Default, Clone)]
91pub struct UiOptions {
92 pub log_mode: bool,
93 pub theme: String,
94 pub theme_locked: bool,
95 pub is_root: bool,
96}
97
98#[derive(Default)]
99pub struct PreviewState<'a> {
100 pub content: String,
101 pub scroll: u16,
102 pub max_scroll: u16,
103 pub cache: HashMap<PathBuf, Text<'a>>,
104}
105
106#[derive(Clone, Debug)]
107pub struct SearchResult {
108 pub item: ScriptItem,
109 pub score: i64,
110 pub indices: Vec<usize>,
111}
112
113#[derive(Default)]
114pub struct SearchState {
115 pub input: String,
116 pub results: Vec<SearchResult>,
117 pub cursor_position: usize,
118 pub selected_idx: usize,
119 pub autocomplete: Option<String>,
120 pub matcher: SkimMatcherV2,
121}
122
123#[derive(Default)]
124pub struct MultiSelectState {
125 pub enabled: bool,
126 pub scripts: Vec<PathBuf>,
127}
128
129#[derive(Default)]
130pub struct HelpState {
131 pub scroll: u16,
132 pub max_scroll: u16,
133}
134
135#[derive(Default)]
136pub struct DescriptionState {
137 pub content: Option<String>,
138 pub scroll: u16,
139 pub max_scroll: u16,
140}
141
142pub struct App<'a> {
143 pub mode: AppMode,
144 pub quit: bool,
145 pub focused_panel: FocusedPanel,
146 pub log_mode: bool,
147 pub modules_dir: PathBuf,
148 pub theme: Theme,
149 pub theme_locked: bool,
150
151 pub scripts: StatefulList<ScriptItem>,
152 pub categories: StatefulList<String>,
153 pub all_scripts: HashMap<String, Vec<ScriptItem>>,
154
155 pub script_panel_area: Rect,
156 pub preview: PreviewState<'a>,
157 pub search: SearchState,
158 pub multi_select: MultiSelectState,
159 pub help: HelpState,
160 pub description: DescriptionState,
161 pub run_script_popup: Option<RunScriptPopup>,
162 pub script_execution_queue: Vec<PathBuf>,
163}