1use crossterm::event::{KeyCode, KeyModifiers};
6
7#[derive(Debug, Clone)]
9pub struct Keybinding {
10 pub key: KeyCode,
12 pub modifiers: KeyModifiers,
14 pub description: &'static str,
16 pub context: KeyContext,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum KeyContext {
23 Global,
25 Sidebar,
27 MainPanel,
29 Register,
31 Budget,
33 Dialog,
35}
36
37pub static KEYBINDINGS: &[Keybinding] = &[
39 Keybinding {
41 key: KeyCode::Char('q'),
42 modifiers: KeyModifiers::NONE,
43 description: "Quit",
44 context: KeyContext::Global,
45 },
46 Keybinding {
47 key: KeyCode::Char('?'),
48 modifiers: KeyModifiers::NONE,
49 description: "Help",
50 context: KeyContext::Global,
51 },
52 Keybinding {
53 key: KeyCode::Char(':'),
54 modifiers: KeyModifiers::NONE,
55 description: "Command palette",
56 context: KeyContext::Global,
57 },
58 Keybinding {
59 key: KeyCode::Tab,
60 modifiers: KeyModifiers::NONE,
61 description: "Switch panel",
62 context: KeyContext::Global,
63 },
64 Keybinding {
65 key: KeyCode::Char('h'),
66 modifiers: KeyModifiers::NONE,
67 description: "Move left/Focus sidebar",
68 context: KeyContext::Global,
69 },
70 Keybinding {
71 key: KeyCode::Char('l'),
72 modifiers: KeyModifiers::NONE,
73 description: "Move right/Focus main",
74 context: KeyContext::Global,
75 },
76 Keybinding {
77 key: KeyCode::Char('j'),
78 modifiers: KeyModifiers::NONE,
79 description: "Move down",
80 context: KeyContext::Global,
81 },
82 Keybinding {
83 key: KeyCode::Char('k'),
84 modifiers: KeyModifiers::NONE,
85 description: "Move up",
86 context: KeyContext::Global,
87 },
88 Keybinding {
90 key: KeyCode::Char('a'),
91 modifiers: KeyModifiers::NONE,
92 description: "Add account",
93 context: KeyContext::Sidebar,
94 },
95 Keybinding {
96 key: KeyCode::Enter,
97 modifiers: KeyModifiers::NONE,
98 description: "Select account",
99 context: KeyContext::Sidebar,
100 },
101 Keybinding {
102 key: KeyCode::Char('1'),
103 modifiers: KeyModifiers::NONE,
104 description: "Accounts view",
105 context: KeyContext::Sidebar,
106 },
107 Keybinding {
108 key: KeyCode::Char('2'),
109 modifiers: KeyModifiers::NONE,
110 description: "Budget view",
111 context: KeyContext::Sidebar,
112 },
113 Keybinding {
114 key: KeyCode::Char('3'),
115 modifiers: KeyModifiers::NONE,
116 description: "Reports view",
117 context: KeyContext::Sidebar,
118 },
119 Keybinding {
120 key: KeyCode::Char('A'),
121 modifiers: KeyModifiers::SHIFT,
122 description: "Toggle archived",
123 context: KeyContext::Sidebar,
124 },
125 Keybinding {
127 key: KeyCode::Char('a'),
128 modifiers: KeyModifiers::NONE,
129 description: "Add transaction",
130 context: KeyContext::Register,
131 },
132 Keybinding {
133 key: KeyCode::Char('e'),
134 modifiers: KeyModifiers::NONE,
135 description: "Edit transaction",
136 context: KeyContext::Register,
137 },
138 Keybinding {
139 key: KeyCode::Char('c'),
140 modifiers: KeyModifiers::NONE,
141 description: "Clear transaction",
142 context: KeyContext::Register,
143 },
144 Keybinding {
145 key: KeyCode::Char('d'),
146 modifiers: KeyModifiers::CONTROL,
147 description: "Delete transaction",
148 context: KeyContext::Register,
149 },
150 Keybinding {
151 key: KeyCode::Char('v'),
152 modifiers: KeyModifiers::NONE,
153 description: "Multi-select mode",
154 context: KeyContext::Register,
155 },
156 Keybinding {
157 key: KeyCode::Char(' '),
158 modifiers: KeyModifiers::NONE,
159 description: "Toggle selection",
160 context: KeyContext::Register,
161 },
162 Keybinding {
163 key: KeyCode::Char('g'),
164 modifiers: KeyModifiers::NONE,
165 description: "Go to top (gg)",
166 context: KeyContext::Register,
167 },
168 Keybinding {
169 key: KeyCode::Char('G'),
170 modifiers: KeyModifiers::SHIFT,
171 description: "Go to bottom",
172 context: KeyContext::Register,
173 },
174 Keybinding {
176 key: KeyCode::Char('g'),
177 modifiers: KeyModifiers::NONE,
178 description: "Go to top (gg)",
179 context: KeyContext::Budget,
180 },
181 Keybinding {
182 key: KeyCode::Char('G'),
183 modifiers: KeyModifiers::SHIFT,
184 description: "Go to bottom",
185 context: KeyContext::Budget,
186 },
187 Keybinding {
188 key: KeyCode::Char('['),
189 modifiers: KeyModifiers::NONE,
190 description: "Previous period",
191 context: KeyContext::Budget,
192 },
193 Keybinding {
194 key: KeyCode::Char(']'),
195 modifiers: KeyModifiers::NONE,
196 description: "Next period",
197 context: KeyContext::Budget,
198 },
199 Keybinding {
200 key: KeyCode::Char('m'),
201 modifiers: KeyModifiers::NONE,
202 description: "Move funds",
203 context: KeyContext::Budget,
204 },
205 Keybinding {
206 key: KeyCode::Char('a'),
207 modifiers: KeyModifiers::NONE,
208 description: "Add category",
209 context: KeyContext::Budget,
210 },
211 Keybinding {
212 key: KeyCode::Char('A'),
213 modifiers: KeyModifiers::SHIFT,
214 description: "Add category group",
215 context: KeyContext::Budget,
216 },
217 Keybinding {
219 key: KeyCode::Esc,
220 modifiers: KeyModifiers::NONE,
221 description: "Close dialog",
222 context: KeyContext::Dialog,
223 },
224 Keybinding {
225 key: KeyCode::Enter,
226 modifiers: KeyModifiers::NONE,
227 description: "Confirm",
228 context: KeyContext::Dialog,
229 },
230];
231
232pub fn get_keybindings(context: KeyContext) -> Vec<&'static Keybinding> {
234 KEYBINDINGS
235 .iter()
236 .filter(|kb| kb.context == context || kb.context == KeyContext::Global)
237 .collect()
238}
239
240pub fn format_keybinding(kb: &Keybinding) -> String {
242 let mut parts = Vec::new();
243
244 if kb.modifiers.contains(KeyModifiers::CONTROL) {
245 parts.push("Ctrl");
246 }
247 if kb.modifiers.contains(KeyModifiers::ALT) {
248 parts.push("Alt");
249 }
250 if kb.modifiers.contains(KeyModifiers::SHIFT) {
251 if !matches!(kb.key, KeyCode::Char(_)) {
253 parts.push("Shift");
254 }
255 }
256
257 let key_str = match kb.key {
258 KeyCode::Char(c) => c.to_string(),
259 KeyCode::Enter => "Enter".to_string(),
260 KeyCode::Tab => "Tab".to_string(),
261 KeyCode::Esc => "Esc".to_string(),
262 KeyCode::Backspace => "Backspace".to_string(),
263 KeyCode::Delete => "Delete".to_string(),
264 KeyCode::Up => "↑".to_string(),
265 KeyCode::Down => "↓".to_string(),
266 KeyCode::Left => "←".to_string(),
267 KeyCode::Right => "→".to_string(),
268 _ => format!("{:?}", kb.key),
269 };
270
271 parts.push(&key_str);
272 parts.join("+")
273}