Skip to main content

coding_agent_search/ui/
shortcuts.rs

1//! Keyboard shortcut constants for consistent documentation.
2
3pub const HELP: &str = "F1";
4pub const THEME: &str = "F2";
5pub const FILTER_AGENT: &str = "F3";
6pub const FILTER_WORKSPACE: &str = "F4";
7pub const FILTER_DATE_FROM: &str = "F5";
8pub const FILTER_DATE_TO: &str = "F6";
9pub const CONTEXT_WINDOW: &str = "F7";
10pub const EDITOR: &str = "F8";
11pub const MATCH_MODE: &str = "F9";
12pub const SEARCH_MODE: &str = "Alt+S";
13pub const QUIT: &str = "Esc/F10";
14pub const CLEAR_FILTERS: &str = "Ctrl+Del";
15pub const RESET_STATE: &str = "Ctrl+Shift+Del";
16pub const RANKING: &str = "F12";
17pub const REFRESH: &str = "Ctrl+Shift+R";
18pub const DETAIL_OPEN: &str = "Enter";
19pub const DETAIL_CLOSE: &str = "Esc";
20pub const FOCUS_QUERY: &str = "Tab/Shift+Tab";
21pub const HISTORY_NEXT: &str = "Ctrl+n";
22pub const HISTORY_PREV: &str = "Ctrl+p";
23pub const HISTORY_CYCLE: &str = "Ctrl+R";
24
25// Filter scopes
26pub const SCOPE_AGENT: &str = "Shift+F3";
27pub const SCOPE_WORKSPACE: &str = "Shift+F4";
28pub const CYCLE_TIME_PRESETS: &str = "Shift+F5";
29
30// Command palette
31pub const PALETTE: &str = "Ctrl+P";
32pub const DENSITY: &str = "Ctrl+D";
33pub const BORDERS: &str = "Ctrl+B";
34pub const STATS_BAR: &str = "Ctrl+S";
35
36// Actions
37pub const COPY: &str = "Alt+Y";
38pub const COPY_PATH: &str = "Ctrl+Y";
39pub const COPY_CONTENT: &str = "Ctrl+Shift+C";
40pub const BULK_MENU: &str = "Alt+B";
41pub const JSON_VIEW: &str = "Alt+Shift+J";
42pub const TOGGLE_SELECT: &str = "Ctrl+X";
43pub const PANE_FILTER: &str = "Alt+/";
44pub const EXPORT_HTML: &str = "Ctrl+E";
45pub const EXPORT_MARKDOWN: &str = "Ctrl+Shift+E";
46
47// Find in detail
48pub const DETAIL_FIND: &str = "/";
49pub const DETAIL_FIND_NEXT: &str = "n";
50pub const DETAIL_FIND_PREV: &str = "N";
51
52// Theme cycling
53pub const THEME_PREV: &str = "Shift+F2";
54
55// Sources management
56pub const SOURCES: &str = "Ctrl+Shift+S";
57
58// Inspector
59pub const INSPECTOR: &str = "Ctrl+Shift+I";
60
61// Macro recording
62pub const MACRO_TOGGLE: &str = "Alt+M";
63
64// Surface navigation
65pub const SURFACE_ANALYTICS: &str = "Alt+A";
66pub const SURFACE_SWARM: &str = "Alt+W";
67
68// Navigation
69pub const TAB_FOCUS: &str = "Tab";
70pub const VIM_NAV: &str = "Alt+h/j/k/l";
71pub const JUMP_TOP: &str = "Home";
72pub const JUMP_BOTTOM: &str = "End";
73
74// =============================================================================
75// Unit Tests
76// =============================================================================
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use std::collections::HashSet;
82
83    // =========================================================================
84    // Function Key Constants Tests
85    // =========================================================================
86
87    #[test]
88    fn function_key_shortcuts_are_not_empty() {
89        assert!(!HELP.is_empty());
90        assert!(!THEME.is_empty());
91        assert!(!FILTER_AGENT.is_empty());
92        assert!(!FILTER_WORKSPACE.is_empty());
93        assert!(!FILTER_DATE_FROM.is_empty());
94        assert!(!FILTER_DATE_TO.is_empty());
95        assert!(!CONTEXT_WINDOW.is_empty());
96        assert!(!EDITOR.is_empty());
97        assert!(!MATCH_MODE.is_empty());
98        assert!(!RANKING.is_empty());
99    }
100
101    #[test]
102    fn function_key_shortcuts_have_expected_values() {
103        assert_eq!(HELP, "F1");
104        assert_eq!(THEME, "F2");
105        assert_eq!(FILTER_AGENT, "F3");
106        assert_eq!(FILTER_WORKSPACE, "F4");
107        assert_eq!(FILTER_DATE_FROM, "F5");
108        assert_eq!(FILTER_DATE_TO, "F6");
109        assert_eq!(CONTEXT_WINDOW, "F7");
110        assert_eq!(EDITOR, "F8");
111        assert_eq!(MATCH_MODE, "F9");
112        assert_eq!(RANKING, "F12");
113    }
114
115    // =========================================================================
116    // Modifier Key Constants Tests
117    // =========================================================================
118
119    #[test]
120    fn modifier_shortcuts_are_not_empty() {
121        assert!(!SEARCH_MODE.is_empty());
122        assert!(!SURFACE_ANALYTICS.is_empty());
123        assert!(!CLEAR_FILTERS.is_empty());
124        assert!(!RESET_STATE.is_empty());
125        assert!(!REFRESH.is_empty());
126        assert!(!HISTORY_NEXT.is_empty());
127        assert!(!HISTORY_PREV.is_empty());
128        assert!(!HISTORY_CYCLE.is_empty());
129        assert!(!STATS_BAR.is_empty());
130        assert!(!TOGGLE_SELECT.is_empty());
131    }
132
133    #[test]
134    fn modifier_shortcuts_have_expected_values() {
135        assert_eq!(SEARCH_MODE, "Alt+S");
136        assert_eq!(SURFACE_ANALYTICS, "Alt+A");
137        assert_eq!(CLEAR_FILTERS, "Ctrl+Del");
138        assert_eq!(RESET_STATE, "Ctrl+Shift+Del");
139        assert_eq!(REFRESH, "Ctrl+Shift+R");
140        assert_eq!(HISTORY_NEXT, "Ctrl+n");
141        assert_eq!(HISTORY_PREV, "Ctrl+p");
142        assert_eq!(HISTORY_CYCLE, "Ctrl+R");
143        assert_eq!(STATS_BAR, "Ctrl+S");
144        assert_eq!(TOGGLE_SELECT, "Ctrl+X");
145    }
146
147    // =========================================================================
148    // Scope Constants Tests
149    // =========================================================================
150
151    #[test]
152    fn scope_shortcuts_are_not_empty() {
153        assert!(!SCOPE_AGENT.is_empty());
154        assert!(!SCOPE_WORKSPACE.is_empty());
155        assert!(!CYCLE_TIME_PRESETS.is_empty());
156    }
157
158    #[test]
159    fn scope_shortcuts_have_expected_values() {
160        assert_eq!(SCOPE_AGENT, "Shift+F3");
161        assert_eq!(SCOPE_WORKSPACE, "Shift+F4");
162        assert_eq!(CYCLE_TIME_PRESETS, "Shift+F5");
163    }
164
165    // =========================================================================
166    // Action Constants Tests
167    // =========================================================================
168
169    #[test]
170    fn action_shortcuts_are_not_empty() {
171        assert!(!COPY.is_empty());
172        assert!(!BULK_MENU.is_empty());
173        assert!(!PANE_FILTER.is_empty());
174        assert!(!EXPORT_HTML.is_empty());
175        assert!(!EXPORT_MARKDOWN.is_empty());
176    }
177
178    #[test]
179    fn action_shortcuts_have_expected_values() {
180        assert_eq!(COPY, "Alt+Y");
181        assert_eq!(BULK_MENU, "Alt+B");
182        assert_eq!(PANE_FILTER, "Alt+/");
183        assert_eq!(EXPORT_HTML, "Ctrl+E");
184        assert_eq!(EXPORT_MARKDOWN, "Ctrl+Shift+E");
185    }
186
187    // =========================================================================
188    // Navigation Constants Tests
189    // =========================================================================
190
191    #[test]
192    fn navigation_shortcuts_are_not_empty() {
193        assert!(!TAB_FOCUS.is_empty());
194        assert!(!VIM_NAV.is_empty());
195        assert!(!JUMP_TOP.is_empty());
196        assert!(!JUMP_BOTTOM.is_empty());
197    }
198
199    #[test]
200    fn navigation_shortcuts_have_expected_values() {
201        assert_eq!(TAB_FOCUS, "Tab");
202        assert_eq!(VIM_NAV, "Alt+h/j/k/l");
203        assert_eq!(JUMP_TOP, "Home");
204        assert_eq!(JUMP_BOTTOM, "End");
205    }
206
207    // =========================================================================
208    // Detail View Constants Tests
209    // =========================================================================
210
211    #[test]
212    fn detail_shortcuts_are_not_empty() {
213        assert!(!DETAIL_OPEN.is_empty());
214        assert!(!DETAIL_CLOSE.is_empty());
215        assert!(!FOCUS_QUERY.is_empty());
216    }
217
218    #[test]
219    fn detail_shortcuts_have_expected_values() {
220        assert_eq!(DETAIL_OPEN, "Enter");
221        assert_eq!(DETAIL_CLOSE, "Esc");
222        assert_eq!(FOCUS_QUERY, "Tab/Shift+Tab");
223    }
224
225    // =========================================================================
226    // Quit Constants Tests
227    // =========================================================================
228
229    #[test]
230    fn quit_shortcut_is_not_empty() {
231        assert!(!QUIT.is_empty());
232    }
233
234    #[test]
235    fn quit_shortcut_has_expected_value() {
236        assert_eq!(QUIT, "Esc/F10");
237    }
238
239    // =========================================================================
240    // Uniqueness Tests (Primary shortcuts should not conflict)
241    // =========================================================================
242
243    #[test]
244    fn primary_function_keys_are_unique() {
245        let mut seen = HashSet::new();
246        let function_keys = [
247            HELP,
248            THEME,
249            FILTER_AGENT,
250            FILTER_WORKSPACE,
251            FILTER_DATE_FROM,
252            FILTER_DATE_TO,
253            CONTEXT_WINDOW,
254            EDITOR,
255            MATCH_MODE,
256            RANKING,
257        ];
258
259        for key in &function_keys {
260            assert!(seen.insert(*key), "Duplicate function key found: {}", key);
261        }
262    }
263
264    #[test]
265    fn shift_function_keys_are_unique() {
266        let mut seen = HashSet::new();
267        let shift_keys = [THEME_PREV, SCOPE_AGENT, SCOPE_WORKSPACE, CYCLE_TIME_PRESETS];
268
269        for key in &shift_keys {
270            assert!(
271                seen.insert(*key),
272                "Duplicate shift+function key found: {}",
273                key
274            );
275        }
276    }
277
278    #[test]
279    fn ctrl_shortcuts_are_unique() {
280        let mut seen = HashSet::new();
281        let ctrl_keys = [
282            CLEAR_FILTERS,
283            RESET_STATE,
284            REFRESH,
285            HISTORY_NEXT,
286            HISTORY_PREV,
287            HISTORY_CYCLE,
288            STATS_BAR,
289            TOGGLE_SELECT,
290        ];
291
292        for key in &ctrl_keys {
293            assert!(seen.insert(*key), "Duplicate ctrl shortcut found: {}", key);
294        }
295    }
296
297    // =========================================================================
298    // Format Validation Tests
299    // =========================================================================
300
301    #[test]
302    fn function_key_format_is_valid() {
303        // Function keys should start with "F" followed by a number
304        let function_keys = [
305            HELP,
306            THEME,
307            FILTER_AGENT,
308            FILTER_WORKSPACE,
309            FILTER_DATE_FROM,
310            FILTER_DATE_TO,
311            CONTEXT_WINDOW,
312            EDITOR,
313            MATCH_MODE,
314            RANKING,
315        ];
316
317        for key in &function_keys {
318            assert!(
319                key.starts_with('F') && key[1..].chars().all(|c| c.is_ascii_digit()),
320                "Invalid function key format: {}",
321                key
322            );
323        }
324    }
325
326    #[test]
327    fn shift_function_key_format_is_valid() {
328        let shift_keys = [THEME_PREV, SCOPE_AGENT, SCOPE_WORKSPACE, CYCLE_TIME_PRESETS];
329
330        for key in &shift_keys {
331            assert!(
332                key.starts_with("Shift+F"),
333                "Shift key should start with 'Shift+F': {}",
334                key
335            );
336        }
337    }
338
339    #[test]
340    fn modifier_shortcuts_contain_plus_separator() {
341        let modifier_keys = [
342            SEARCH_MODE,
343            SURFACE_ANALYTICS,
344            CLEAR_FILTERS,
345            RESET_STATE,
346            REFRESH,
347            HISTORY_NEXT,
348            HISTORY_PREV,
349            HISTORY_CYCLE,
350            STATS_BAR,
351            TOGGLE_SELECT,
352        ];
353
354        for key in &modifier_keys {
355            assert!(
356                key.contains('+'),
357                "Modifier shortcut should contain '+': {}",
358                key
359            );
360        }
361    }
362}