1use crate::config::Config;
2use egui::{Color32, Context, Frame, RichText, Window, epaint::Shadow};
3use std::cell::Cell;
4
5pub struct HelpUI {
7 pub visible: bool,
9}
10
11impl HelpUI {
12 pub fn new() -> Self {
14 Self { visible: false }
15 }
16
17 pub fn toggle(&mut self) {
19 self.visible = !self.visible;
20 }
21
22 pub fn show(&mut self, ctx: &Context) {
24 if !self.visible {
25 return;
26 }
27
28 let mut style = (*ctx.style()).clone();
30 let solid_bg = Color32::from_rgba_unmultiplied(24, 24, 24, 255);
31 style.visuals.window_fill = solid_bg;
32 style.visuals.panel_fill = solid_bg;
33 style.visuals.widgets.noninteractive.bg_fill = solid_bg;
34 ctx.set_style(style);
35
36 let mut open = true;
37 let close_requested = Cell::new(false);
38
39 let viewport = ctx.input(|i| i.viewport_rect());
40 Window::new("Help")
41 .resizable(true)
42 .default_width(550.0)
43 .default_height(600.0)
44 .default_pos(viewport.center())
45 .pivot(egui::Align2::CENTER_CENTER)
46 .open(&mut open)
47 .frame(
48 Frame::window(&ctx.style())
49 .fill(solid_bg)
50 .stroke(egui::Stroke::NONE)
51 .shadow(Shadow {
52 offset: [0, 0],
53 blur: 0,
54 spread: 0,
55 color: Color32::TRANSPARENT,
56 }),
57 )
58 .show(ctx, |ui| {
59 egui::ScrollArea::vertical().show(ui, |ui| {
60 ui.heading("About par-term");
62 ui.separator();
63
64 ui.horizontal(|ui| {
65 ui.label("Version:");
66 ui.label(RichText::new(env!("CARGO_PKG_VERSION")).strong());
67 });
68
69 ui.add_space(4.0);
70 ui.label(env!("CARGO_PKG_DESCRIPTION"));
71
72 ui.add_space(4.0);
73 ui.horizontal(|ui| {
74 ui.label("Author:");
75 ui.label(env!("CARGO_PKG_AUTHORS"));
76 });
77
78 ui.horizontal(|ui| {
79 ui.label("License:");
80 ui.label(env!("CARGO_PKG_LICENSE"));
81 });
82
83 ui.horizontal(|ui| {
84 ui.label("Repository:");
85 ui.hyperlink_to(
86 env!("CARGO_PKG_REPOSITORY"),
87 env!("CARGO_PKG_REPOSITORY"),
88 );
89 });
90
91 ui.add_space(12.0);
92
93 ui.heading("Configuration Paths");
95 ui.separator();
96
97 let config_path = Config::config_path();
98 let shaders_dir = Config::shaders_dir();
99
100 ui.horizontal(|ui| {
101 ui.label("Config file:");
102 ui.label(RichText::new(config_path.display().to_string()).monospace());
103 });
104
105 ui.horizontal(|ui| {
106 ui.label("Shaders folder:");
107 ui.label(RichText::new(shaders_dir.display().to_string()).monospace());
108 });
109
110 ui.add_space(12.0);
111
112 ui.heading("Keyboard Shortcuts");
114 ui.separator();
115
116 egui::Grid::new("shortcuts_grid")
118 .num_columns(2)
119 .spacing([20.0, 4.0])
120 .striped(true)
121 .show(ui, |ui| {
122 ui.label(RichText::new("Navigation").strong().underline());
124 ui.end_row();
125
126 shortcut_row(ui, "PageUp", "Scroll up one page");
127 shortcut_row(ui, "PageDown", "Scroll down one page");
128 shortcut_row(ui, "Shift+Home", "Scroll to top");
129 shortcut_row(ui, "Shift+End", "Scroll to bottom");
130 shortcut_row(ui, "Mouse wheel", "Scroll up/down");
131
132 ui.end_row();
133
134 ui.label(RichText::new("Window & Display").strong().underline());
136 ui.end_row();
137
138 shortcut_row(ui, "F1", "Toggle this help panel");
139 shortcut_row(ui, "F3", "Toggle FPS overlay");
140 shortcut_row(ui, "F5", "Reload configuration");
141 shortcut_row(ui, "F11", "Toggle fullscreen / Shader editor");
142 shortcut_row(ui, "F12", "Toggle settings panel");
143
144 ui.end_row();
145
146 ui.label(RichText::new("Font & Text").strong().underline());
148 ui.end_row();
149
150 shortcut_row(ui, "Ctrl++", "Increase font size");
151 shortcut_row(ui, "Ctrl+-", "Decrease font size");
152 shortcut_row(ui, "Ctrl+0", "Reset font size to default");
153
154 ui.end_row();
155
156 ui.label(RichText::new("Selection & Clipboard").strong().underline());
158 ui.end_row();
159
160 shortcut_row(ui, "Click + Drag", "Select text");
161 shortcut_row(ui, "Double-click", "Select word");
162 shortcut_row(ui, "Triple-click", "Select line");
163 shortcut_row(ui, "Ctrl+Shift+C", "Copy selection");
164 shortcut_row(ui, "Ctrl+Shift+V", "Paste from clipboard");
165 shortcut_row(ui, "Ctrl+Shift+H", "Toggle clipboard history");
166 shortcut_row(ui, "Cmd/Ctrl+R", "Fuzzy command history search");
167 shortcut_row(ui, "Middle-click", "Paste (if enabled)");
168
169 ui.end_row();
170
171 ui.label(RichText::new("Search").strong().underline());
173 ui.end_row();
174
175 shortcut_row(ui, "Cmd/Ctrl+F", "Open search");
176 shortcut_row(ui, "Enter", "Find next match");
177 shortcut_row(ui, "Shift+Enter", "Find previous match");
178 shortcut_row(ui, "Escape", "Close search");
179
180 ui.end_row();
181
182 ui.label(RichText::new("Terminal").strong().underline());
184 ui.end_row();
185
186 shortcut_row(ui, "Ctrl+L", "Clear screen");
187 shortcut_row(ui, "Ctrl+Shift+S", "Take screenshot");
188 shortcut_row(ui, "Ctrl+Shift+R", "Toggle session recording");
189 shortcut_row(ui, "Ctrl+Shift+F5", "Fix rendering (after monitor change)");
190
191 ui.end_row();
192
193 ui.label(RichText::new("URL Handling").strong().underline());
195 ui.end_row();
196
197 shortcut_row(ui, "Ctrl+Click URL", "Open URL in browser");
198 });
199
200 ui.add_space(12.0);
201
202 ui.heading("Copy Mode (Vi-Style)");
204 ui.separator();
205
206 ui.label("Copy Mode provides keyboard-driven text selection and navigation through the terminal buffer, including scrollback history.");
207
208 ui.add_space(4.0);
209
210 egui::Grid::new("copy_mode_grid")
211 .num_columns(2)
212 .spacing([20.0, 4.0])
213 .striped(true)
214 .show(ui, |ui| {
215 ui.label(RichText::new("Enter / Exit").strong().underline());
216 ui.end_row();
217
218 #[cfg(target_os = "macos")]
219 shortcut_row(ui, "Cmd+Shift+C", "Toggle copy mode");
220 #[cfg(not(target_os = "macos"))]
221 shortcut_row(ui, "Ctrl+Shift+Space", "Toggle copy mode");
222 shortcut_row(ui, "q / Escape", "Exit copy mode");
223
224 ui.end_row();
225
226 ui.label(RichText::new("Navigation").strong().underline());
227 ui.end_row();
228
229 shortcut_row(ui, "h j k l", "Left / Down / Up / Right");
230 shortcut_row(ui, "w / b / e", "Word forward / back / end");
231 shortcut_row(ui, "W / B / E", "WORD forward / back / end");
232 shortcut_row(ui, "0", "Start of line");
233 shortcut_row(ui, "$", "End of line");
234 shortcut_row(ui, "^", "First non-blank character");
235 shortcut_row(ui, "gg", "Top of scrollback");
236 shortcut_row(ui, "G", "Bottom of buffer");
237 shortcut_row(ui, "Ctrl+U / Ctrl+D", "Half page up / down");
238 shortcut_row(ui, "Ctrl+B / Ctrl+F", "Full page up / down");
239
240 ui.end_row();
241
242 ui.label(RichText::new("Selection & Yank").strong().underline());
243 ui.end_row();
244
245 shortcut_row(ui, "v", "Character selection");
246 shortcut_row(ui, "V", "Line selection");
247 shortcut_row(ui, "y", "Yank (copy) selection to clipboard");
248 shortcut_row(ui, "1-9", "Count prefix (e.g. 5j = down 5 lines)");
249
250 ui.end_row();
251
252 ui.label(RichText::new("Search").strong().underline());
253 ui.end_row();
254
255 shortcut_row(ui, "/", "Search forward");
256 shortcut_row(ui, "?", "Search backward");
257 shortcut_row(ui, "n", "Next match");
258 shortcut_row(ui, "N", "Previous match");
259
260 ui.end_row();
261
262 ui.label(RichText::new("Marks").strong().underline());
263 ui.end_row();
264
265 shortcut_row(ui, "m + char", "Set mark at current position");
266 shortcut_row(ui, "' + char", "Jump to mark");
267 });
268
269 ui.add_space(12.0);
270
271 ui.heading("Mouse Actions");
273 ui.separator();
274
275 egui::Grid::new("mouse_grid")
276 .num_columns(2)
277 .spacing([20.0, 4.0])
278 .striped(true)
279 .show(ui, |ui| {
280 shortcut_row(ui, "Scrollbar drag", "Scroll through history");
281 shortcut_row(ui, "Scrollbar click", "Jump to position");
282 });
283
284 ui.add_space(12.0);
285
286 ui.heading("Tips");
288 ui.separator();
289
290 ui.label("• Configuration changes made via F12 settings are saved to the config file.");
291 ui.label("• Press F5 to reload config without restarting the terminal.");
292 ui.label("• Custom shaders can be placed in the shaders folder.");
293 ui.label("• The shader editor (F11) allows live editing when a shader is configured.");
294 ui.label("• If display looks corrupted after moving between monitors, press Ctrl+Shift+F5.");
295
296 ui.add_space(12.0);
297
298 ui.separator();
300 ui.horizontal(|ui| {
301 if ui.button("Close").clicked() {
302 close_requested.set(true);
303 }
304 ui.label(RichText::new("Press F1 or Escape to close").weak());
305 });
306 });
307 });
308
309 if !open || close_requested.get() {
311 self.visible = false;
312 }
313 }
314}
315
316impl Default for HelpUI {
317 fn default() -> Self {
318 Self::new()
319 }
320}
321
322fn shortcut_row(ui: &mut egui::Ui, shortcut: &str, description: &str) {
324 ui.label(RichText::new(shortcut).monospace().strong());
325 ui.label(description);
326 ui.end_row();
327}