Skip to main content

binocular/ui/
shortcuts.rs

1use crate::app::{InputMode, Mode};
2use ratatui::{
3    style::{Color, Style},
4    text::{Line, Span},
5};
6
7pub struct ShortcutHint {
8    pub keys: &'static str,
9    pub description: &'static str,
10}
11
12const SEARCH_RESULTS_NORMAL_HINTS: &[ShortcutHint] = &[
13    ShortcutHint {
14        keys: "j/k",
15        description: "move",
16    },
17    ShortcutHint {
18        keys: "enter",
19        description: "select",
20    },
21    ShortcutHint {
22        keys: "tab",
23        description: "mark",
24    },
25    ShortcutHint {
26        keys: "ctrl+w",
27        description: "focus preview",
28    },
29];
30
31const SEARCH_RESULTS_INSERT_HINTS: &[ShortcutHint] = &[
32    ShortcutHint {
33        keys: "↑/↓",
34        description: "move",
35    },
36    ShortcutHint {
37        keys: "enter",
38        description: "select",
39    },
40    ShortcutHint {
41        keys: "tab",
42        description: "mark",
43    },
44    ShortcutHint {
45        keys: "ctrl+w",
46        description: "focus preview",
47    },
48];
49
50const SEARCH_BAR_NORMAL_HINTS: &[ShortcutHint] = &[
51    ShortcutHint {
52        keys: "i",
53        description: "insert",
54    },
55    ShortcutHint {
56        keys: "h/l",
57        description: "move",
58    },
59    ShortcutHint {
60        keys: "w/b",
61        description: "word",
62    },
63    ShortcutHint {
64        keys: "j/k",
65        description: "list",
66    },
67];
68
69const SEARCH_BAR_INSERT_HINTS: &[ShortcutHint] = &[ShortcutHint {
70    keys: "esc",
71    description: "normal",
72}];
73
74const PREVIEW_NORMAL_HINTS: &[ShortcutHint] = &[
75    ShortcutHint {
76        keys: "h/j/k/l",
77        description: "move",
78    },
79    ShortcutHint {
80        keys: "i",
81        description: "insert",
82    },
83    ShortcutHint {
84        keys: "/",
85        description: "find",
86    },
87    ShortcutHint {
88        keys: "ctrl+w",
89        description: "focus search",
90    },
91];
92
93const PREVIEW_INSERT_HINTS: &[ShortcutHint] = &[
94    ShortcutHint {
95        keys: "esc",
96        description: "normal",
97    },
98    ShortcutHint {
99        keys: "ctrl+s",
100        description: "save",
101    },
102    ShortcutHint {
103        keys: "enter",
104        description: "newline",
105    },
106];
107
108pub fn search_results_hints(
109    app_mode: Mode,
110    input_mode: InputMode,
111    show_preview: bool,
112) -> &'static [ShortcutHint] {
113    if app_mode != Mode::Search {
114        return &[];
115    }
116
117    if !show_preview {
118        if input_mode == InputMode::Insert {
119            return &SEARCH_RESULTS_INSERT_HINTS[..3];
120        }
121        return &SEARCH_RESULTS_NORMAL_HINTS[..3];
122    }
123
124    if input_mode == InputMode::Insert {
125        SEARCH_RESULTS_INSERT_HINTS
126    } else {
127        SEARCH_RESULTS_NORMAL_HINTS
128    }
129}
130
131pub fn search_bar_hints(app_mode: Mode, input_mode: InputMode) -> &'static [ShortcutHint] {
132    if app_mode != Mode::Search {
133        return &[];
134    }
135
136    if input_mode == InputMode::Insert {
137        SEARCH_BAR_INSERT_HINTS
138    } else {
139        SEARCH_BAR_NORMAL_HINTS
140    }
141}
142
143pub fn preview_hints(app_mode: Mode, input_mode: InputMode) -> &'static [ShortcutHint] {
144    if app_mode != Mode::Preview {
145        return &[];
146    }
147
148    if input_mode == InputMode::Insert {
149        PREVIEW_INSERT_HINTS
150    } else {
151        PREVIEW_NORMAL_HINTS
152    }
153}
154
155pub fn render_hints_line(hints: &[ShortcutHint]) -> Line<'static> {
156    let mut spans = Vec::new();
157
158    for (idx, hint) in hints.iter().enumerate() {
159        if idx > 0 {
160            spans.push(Span::styled(" · ", Style::default().fg(Color::DarkGray)));
161        }
162        spans.push(Span::styled("<", Style::default().fg(Color::DarkGray)));
163        spans.push(Span::styled(
164            hint.keys,
165            Style::default().fg(Color::LightCyan),
166        ));
167        spans.push(Span::styled(">", Style::default().fg(Color::DarkGray)));
168        spans.push(Span::styled(
169            format!(" {}", hint.description),
170            Style::default().fg(Color::DarkGray),
171        ));
172    }
173
174    if !spans.is_empty() {
175        spans.push(Span::raw(" "));
176    }
177    Line::from(spans)
178}