use crossterm::event::{KeyCode, KeyModifiers};
#[derive(Debug, Clone)]
pub struct Keybinding {
pub key: KeyCode,
pub modifiers: KeyModifiers,
pub description: &'static str,
pub context: KeyContext,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyContext {
Global,
Sidebar,
MainPanel,
Register,
Budget,
Dialog,
}
pub static KEYBINDINGS: &[Keybinding] = &[
Keybinding {
key: KeyCode::Char('q'),
modifiers: KeyModifiers::NONE,
description: "Quit",
context: KeyContext::Global,
},
Keybinding {
key: KeyCode::Char('?'),
modifiers: KeyModifiers::NONE,
description: "Help",
context: KeyContext::Global,
},
Keybinding {
key: KeyCode::Char(':'),
modifiers: KeyModifiers::NONE,
description: "Command palette",
context: KeyContext::Global,
},
Keybinding {
key: KeyCode::Tab,
modifiers: KeyModifiers::NONE,
description: "Switch panel",
context: KeyContext::Global,
},
Keybinding {
key: KeyCode::Char('h'),
modifiers: KeyModifiers::NONE,
description: "Move left/Focus sidebar",
context: KeyContext::Global,
},
Keybinding {
key: KeyCode::Char('l'),
modifiers: KeyModifiers::NONE,
description: "Move right/Focus main",
context: KeyContext::Global,
},
Keybinding {
key: KeyCode::Char('j'),
modifiers: KeyModifiers::NONE,
description: "Move down",
context: KeyContext::Global,
},
Keybinding {
key: KeyCode::Char('k'),
modifiers: KeyModifiers::NONE,
description: "Move up",
context: KeyContext::Global,
},
Keybinding {
key: KeyCode::Char('a'),
modifiers: KeyModifiers::NONE,
description: "Add account",
context: KeyContext::Sidebar,
},
Keybinding {
key: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
description: "Select account",
context: KeyContext::Sidebar,
},
Keybinding {
key: KeyCode::Char('1'),
modifiers: KeyModifiers::NONE,
description: "Accounts view",
context: KeyContext::Sidebar,
},
Keybinding {
key: KeyCode::Char('2'),
modifiers: KeyModifiers::NONE,
description: "Budget view",
context: KeyContext::Sidebar,
},
Keybinding {
key: KeyCode::Char('3'),
modifiers: KeyModifiers::NONE,
description: "Reports view",
context: KeyContext::Sidebar,
},
Keybinding {
key: KeyCode::Char('A'),
modifiers: KeyModifiers::SHIFT,
description: "Toggle archived",
context: KeyContext::Sidebar,
},
Keybinding {
key: KeyCode::Char('a'),
modifiers: KeyModifiers::NONE,
description: "Add transaction",
context: KeyContext::Register,
},
Keybinding {
key: KeyCode::Char('e'),
modifiers: KeyModifiers::NONE,
description: "Edit transaction",
context: KeyContext::Register,
},
Keybinding {
key: KeyCode::Char('c'),
modifiers: KeyModifiers::NONE,
description: "Clear transaction",
context: KeyContext::Register,
},
Keybinding {
key: KeyCode::Char('d'),
modifiers: KeyModifiers::CONTROL,
description: "Delete transaction",
context: KeyContext::Register,
},
Keybinding {
key: KeyCode::Char('v'),
modifiers: KeyModifiers::NONE,
description: "Multi-select mode",
context: KeyContext::Register,
},
Keybinding {
key: KeyCode::Char(' '),
modifiers: KeyModifiers::NONE,
description: "Toggle selection",
context: KeyContext::Register,
},
Keybinding {
key: KeyCode::Char('g'),
modifiers: KeyModifiers::NONE,
description: "Go to top (gg)",
context: KeyContext::Register,
},
Keybinding {
key: KeyCode::Char('G'),
modifiers: KeyModifiers::SHIFT,
description: "Go to bottom",
context: KeyContext::Register,
},
Keybinding {
key: KeyCode::Char('g'),
modifiers: KeyModifiers::NONE,
description: "Go to top (gg)",
context: KeyContext::Budget,
},
Keybinding {
key: KeyCode::Char('G'),
modifiers: KeyModifiers::SHIFT,
description: "Go to bottom",
context: KeyContext::Budget,
},
Keybinding {
key: KeyCode::Char('['),
modifiers: KeyModifiers::NONE,
description: "Previous period",
context: KeyContext::Budget,
},
Keybinding {
key: KeyCode::Char(']'),
modifiers: KeyModifiers::NONE,
description: "Next period",
context: KeyContext::Budget,
},
Keybinding {
key: KeyCode::Char('m'),
modifiers: KeyModifiers::NONE,
description: "Move funds",
context: KeyContext::Budget,
},
Keybinding {
key: KeyCode::Char('a'),
modifiers: KeyModifiers::NONE,
description: "Add category",
context: KeyContext::Budget,
},
Keybinding {
key: KeyCode::Char('A'),
modifiers: KeyModifiers::SHIFT,
description: "Add category group",
context: KeyContext::Budget,
},
Keybinding {
key: KeyCode::Esc,
modifiers: KeyModifiers::NONE,
description: "Close dialog",
context: KeyContext::Dialog,
},
Keybinding {
key: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
description: "Confirm",
context: KeyContext::Dialog,
},
];
pub fn get_keybindings(context: KeyContext) -> Vec<&'static Keybinding> {
KEYBINDINGS
.iter()
.filter(|kb| kb.context == context || kb.context == KeyContext::Global)
.collect()
}
pub fn format_keybinding(kb: &Keybinding) -> String {
let mut parts = Vec::new();
if kb.modifiers.contains(KeyModifiers::CONTROL) {
parts.push("Ctrl");
}
if kb.modifiers.contains(KeyModifiers::ALT) {
parts.push("Alt");
}
if kb.modifiers.contains(KeyModifiers::SHIFT) {
if !matches!(kb.key, KeyCode::Char(_)) {
parts.push("Shift");
}
}
let key_str = match kb.key {
KeyCode::Char(c) => c.to_string(),
KeyCode::Enter => "Enter".to_string(),
KeyCode::Tab => "Tab".to_string(),
KeyCode::Esc => "Esc".to_string(),
KeyCode::Backspace => "Backspace".to_string(),
KeyCode::Delete => "Delete".to_string(),
KeyCode::Up => "↑".to_string(),
KeyCode::Down => "↓".to_string(),
KeyCode::Left => "←".to_string(),
KeyCode::Right => "→".to_string(),
_ => format!("{:?}", kb.key),
};
parts.push(&key_str);
parts.join("+")
}