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",
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('['),
177        modifiers: KeyModifiers::NONE,
178        description: "Previous period",
179        context: KeyContext::Budget,
180    },
181    Keybinding {
182        key: KeyCode::Char(']'),
183        modifiers: KeyModifiers::NONE,
184        description: "Next period",
185        context: KeyContext::Budget,
186    },
187    Keybinding {
188        key: KeyCode::Char('m'),
189        modifiers: KeyModifiers::NONE,
190        description: "Move funds",
191        context: KeyContext::Budget,
192    },
193    Keybinding {
194        key: KeyCode::Char('a'),
195        modifiers: KeyModifiers::NONE,
196        description: "Add category",
197        context: KeyContext::Budget,
198    },
199    Keybinding {
200        key: KeyCode::Char('A'),
201        modifiers: KeyModifiers::SHIFT,
202        description: "Add category group",
203        context: KeyContext::Budget,
204    },
205    // Dialog
206    Keybinding {
207        key: KeyCode::Esc,
208        modifiers: KeyModifiers::NONE,
209        description: "Close dialog",
210        context: KeyContext::Dialog,
211    },
212    Keybinding {
213        key: KeyCode::Enter,
214        modifiers: KeyModifiers::NONE,
215        description: "Confirm",
216        context: KeyContext::Dialog,
217    },
218];
219
220/// Get keybindings for a specific context
221pub fn get_keybindings(context: KeyContext) -> Vec<&'static Keybinding> {
222    KEYBINDINGS
223        .iter()
224        .filter(|kb| kb.context == context || kb.context == KeyContext::Global)
225        .collect()
226}
227
228/// Format a keybinding for display
229pub fn format_keybinding(kb: &Keybinding) -> String {
230    let mut parts = Vec::new();
231
232    if kb.modifiers.contains(KeyModifiers::CONTROL) {
233        parts.push("Ctrl");
234    }
235    if kb.modifiers.contains(KeyModifiers::ALT) {
236        parts.push("Alt");
237    }
238    if kb.modifiers.contains(KeyModifiers::SHIFT) {
239        // Only show Shift for non-character keys
240        if !matches!(kb.key, KeyCode::Char(_)) {
241            parts.push("Shift");
242        }
243    }
244
245    let key_str = match kb.key {
246        KeyCode::Char(c) => c.to_string(),
247        KeyCode::Enter => "Enter".to_string(),
248        KeyCode::Tab => "Tab".to_string(),
249        KeyCode::Esc => "Esc".to_string(),
250        KeyCode::Backspace => "Backspace".to_string(),
251        KeyCode::Delete => "Delete".to_string(),
252        KeyCode::Up => "↑".to_string(),
253        KeyCode::Down => "↓".to_string(),
254        KeyCode::Left => "←".to_string(),
255        KeyCode::Right => "→".to_string(),
256        _ => format!("{:?}", kb.key),
257    };
258
259    parts.push(&key_str);
260    parts.join("+")
261}