binocular/ui/
shortcuts.rs1use 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}