envelope_cli/tui/
keybindings.rs

1//! Keybinding definitions
2//!
3//! Defines all keyboard shortcuts for different contexts
4
5use crossterm::event::{KeyCode, KeyModifiers};
6
7/// A keybinding definition
8#[derive(Debug, Clone)]
9pub struct Keybinding {
10    /// The key code
11    pub key: KeyCode,
12    /// Required modifiers
13    pub modifiers: KeyModifiers,
14    /// Description of what the key does
15    pub description: &'static str,
16    /// Context where this keybinding is active
17    pub context: KeyContext,
18}
19
20/// Context in which a keybinding is active
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum KeyContext {
23    /// Active everywhere
24    Global,
25    /// Active in the sidebar
26    Sidebar,
27    /// Active in the main panel
28    MainPanel,
29    /// Active in the register view
30    Register,
31    /// Active in the budget view
32    Budget,
33    /// Active in dialogs
34    Dialog,
35}
36
37/// All keybindings
38pub static KEYBINDINGS: &[Keybinding] = &[
39    // Global
40    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    // Sidebar
89    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    // Register
126    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    // Budget
175    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    // Dialog
218    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
232/// Get keybindings for a specific context
233pub 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
240/// Format a keybinding for display
241pub 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        // Only show Shift for non-character keys
252        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}