Skip to main content

mxr_tui/
desktop_manifest.rs

1use crate::action::Action;
2use crate::keybindings::{
3    action_from_name, all_bindings_for_context, default_keybindings, format_keybinding, ViewContext,
4};
5use crate::ui::command_palette::default_commands;
6use crossterm::event::KeyCode;
7use serde::Serialize;
8
9#[derive(Debug, Clone, Serialize)]
10#[serde(rename_all = "camelCase")]
11pub struct DesktopManifest {
12    pub bindings: DesktopBindings,
13    pub commands: Vec<DesktopCommand>,
14}
15
16#[derive(Debug, Clone, Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct DesktopBindings {
19    pub mail_list: Vec<DesktopBinding>,
20    pub message_view: Vec<DesktopBinding>,
21    pub thread_view: Vec<DesktopBinding>,
22}
23
24#[derive(Debug, Clone, Serialize)]
25#[serde(rename_all = "camelCase")]
26pub struct DesktopBinding {
27    pub action: String,
28    pub label: String,
29    pub display: String,
30    pub tokens: Vec<String>,
31}
32
33#[derive(Debug, Clone, Serialize)]
34#[serde(rename_all = "camelCase")]
35pub struct DesktopCommand {
36    pub label: String,
37    pub shortcut: String,
38    pub action: String,
39    pub category: String,
40}
41
42pub fn desktop_manifest() -> DesktopManifest {
43    let config = default_keybindings();
44
45    DesktopManifest {
46        bindings: DesktopBindings {
47            mail_list: bindings_for_context(&config.mail_list, ViewContext::MailList),
48            message_view: bindings_for_context(&config.message_view, ViewContext::MessageView),
49            thread_view: bindings_for_context(&config.thread_view, ViewContext::ThreadView),
50        },
51        commands: default_commands()
52            .into_iter()
53            .filter_map(|command| {
54                action_name(&command.action).map(|action| DesktopCommand {
55                    label: command.label,
56                    shortcut: command.shortcut,
57                    action: action.to_string(),
58                    category: command.category,
59                })
60            })
61            .collect(),
62    }
63}
64
65fn bindings_for_context(
66    bindings: &std::collections::HashMap<crate::keybindings::KeyBinding, String>,
67    context: ViewContext,
68) -> Vec<DesktopBinding> {
69    let display_names = all_bindings_for_context(context)
70        .into_iter()
71        .collect::<std::collections::HashMap<_, _>>();
72
73    let mut entries = bindings
74        .iter()
75        .filter_map(|(binding, action)| {
76            if action_from_name(action).is_none() {
77                return None;
78            }
79            Some(DesktopBinding {
80                action: action.clone(),
81                label: display_names
82                    .get(&format_keybinding(binding))
83                    .cloned()
84                    .unwrap_or_else(|| action.clone()),
85                display: format_keybinding(binding),
86                tokens: binding.keys.iter().map(key_press_token).collect(),
87            })
88        })
89        .collect::<Vec<_>>();
90
91    entries.sort_by(|left, right| {
92        left.display
93            .cmp(&right.display)
94            .then_with(|| left.action.cmp(&right.action))
95    });
96    entries
97}
98
99fn key_press_token(key: &crate::keybindings::KeyPress) -> String {
100    if key
101        .modifiers
102        .contains(crossterm::event::KeyModifiers::CONTROL)
103    {
104        if let KeyCode::Char(c) = key.code {
105            return format!("Ctrl-{}", c.to_ascii_lowercase());
106        }
107    }
108
109    match key.code {
110        KeyCode::Enter => "Enter".to_string(),
111        KeyCode::Esc => "Esc".to_string(),
112        KeyCode::Tab => "Tab".to_string(),
113        KeyCode::Char(c) => c.to_string(),
114        _ => "?".to_string(),
115    }
116}
117
118fn action_name(action: &Action) -> Option<&'static str> {
119    match action {
120        Action::MoveDown => Some("move_down"),
121        Action::MoveUp => Some("move_up"),
122        Action::JumpTop => Some("jump_top"),
123        Action::JumpBottom => Some("jump_bottom"),
124        Action::PageDown => Some("page_down"),
125        Action::PageUp => Some("page_up"),
126        Action::ViewportTop => Some("visible_top"),
127        Action::ViewportMiddle => Some("visible_middle"),
128        Action::ViewportBottom => Some("visible_bottom"),
129        Action::CenterCurrent => Some("center_current"),
130        Action::SwitchPane => Some("switch_panes"),
131        Action::OpenSelected => Some("open"),
132        Action::Back => Some("back"),
133        Action::QuitView => Some("quit_view"),
134        Action::ClearSelection => Some("clear_selection"),
135        Action::OpenMailboxScreen => Some("open_mailbox_screen"),
136        Action::OpenSearchScreen => Some("open_search_screen"),
137        Action::OpenRulesScreen => Some("open_rules_screen"),
138        Action::OpenDiagnosticsScreen => Some("open_diagnostics_screen"),
139        Action::OpenAccountsScreen => Some("open_accounts_screen"),
140        Action::OpenTab1 => Some("open_tab_1"),
141        Action::OpenTab2 => Some("open_tab_2"),
142        Action::OpenTab3 => Some("open_tab_3"),
143        Action::OpenTab4 => Some("open_tab_4"),
144        Action::OpenTab5 => Some("open_tab_5"),
145        Action::OpenSearch => Some("search"),
146        Action::SubmitSearch => Some("submit_search"),
147        Action::CloseSearch => Some("close_search"),
148        Action::CycleSearchMode => Some("cycle_search_mode"),
149        Action::NextSearchResult => Some("next_search_result"),
150        Action::PrevSearchResult => Some("prev_search_result"),
151        Action::GoToInbox => Some("go_inbox"),
152        Action::GoToStarred => Some("go_starred"),
153        Action::GoToSent => Some("go_sent"),
154        Action::GoToDrafts => Some("go_drafts"),
155        Action::GoToAllMail => Some("go_all_mail"),
156        Action::OpenSubscriptions => Some("open_subscriptions"),
157        Action::GoToLabel => Some("go_label"),
158        Action::OpenCommandPalette => Some("command_palette"),
159        Action::CloseCommandPalette => Some("close_command_palette"),
160        Action::SyncNow => Some("sync"),
161        Action::Compose => Some("compose"),
162        Action::Reply => Some("reply"),
163        Action::ReplyAll => Some("reply_all"),
164        Action::Forward => Some("forward"),
165        Action::Archive => Some("archive"),
166        Action::MarkReadAndArchive => Some("mark_read_archive"),
167        Action::Trash => Some("trash"),
168        Action::Spam => Some("spam"),
169        Action::Star => Some("star"),
170        Action::MarkRead => Some("mark_read"),
171        Action::MarkUnread => Some("mark_unread"),
172        Action::ApplyLabel => Some("apply_label"),
173        Action::MoveToLabel => Some("move_to_label"),
174        Action::Unsubscribe => Some("unsubscribe"),
175        Action::Snooze => Some("snooze"),
176        Action::OpenInBrowser => Some("open_in_browser"),
177        Action::ToggleReaderMode => Some("toggle_reader_mode"),
178        Action::ToggleSignature => Some("toggle_signature"),
179        Action::ToggleSelect => Some("toggle_select"),
180        Action::VisualLineMode => Some("visual_line_mode"),
181        Action::AttachmentList => Some("attachment_list"),
182        Action::OpenLinks => Some("open_links"),
183        Action::ToggleFullscreen => Some("toggle_fullscreen"),
184        Action::ExportThread => Some("export_thread"),
185        Action::Help => Some("help"),
186        Action::ToggleMailListMode => Some("toggle_mail_list_mode"),
187        Action::RefreshRules => Some("refresh_rules"),
188        Action::RefreshDiagnostics => Some("refresh_diagnostics"),
189        Action::RefreshAccounts => Some("refresh_accounts"),
190        Action::GenerateBugReport => Some("generate_bug_report"),
191        Action::OpenRuleFormNew => Some("open_rule_form_new"),
192        Action::OpenRuleFormEdit => Some("open_rule_form_edit"),
193        Action::ToggleRuleEnabled => Some("toggle_rule_enabled"),
194        Action::ShowRuleDryRun => Some("show_rule_dry_run"),
195        Action::ShowRuleHistory => Some("show_rule_history"),
196        Action::DeleteRule => Some("delete_rule"),
197        Action::OpenAccountFormNew => Some("open_account_form_new"),
198        Action::TestAccountForm => Some("test_account_form"),
199        Action::SetDefaultAccount => Some("set_default_account"),
200        Action::Noop => Some("noop"),
201        Action::OpenMessageView
202        | Action::CloseMessageView
203        | Action::SelectLabel(_)
204        | Action::SelectSavedSearch(_, _)
205        | Action::ClearFilter
206        | Action::SaveRuleForm
207        | Action::SaveAccountForm
208        | Action::ReauthorizeAccountForm
209        | Action::ConfirmUnsubscribeOnly
210        | Action::ConfirmUnsubscribeAndArchiveSender
211        | Action::CancelUnsubscribe
212        | Action::PatternSelect(_) => None,
213    }
214}