Skip to main content

axonml_tui/views/
help.rs

1//! Help View - Keyboard Shortcuts and Commands
2//!
3//! Displays all available keyboard shortcuts and commands organized
4//! by category for easy reference.
5//!
6//! @version 0.1.0
7//! @author AutomataNexus Development Team
8
9use ratatui::{
10    layout::{Alignment, Constraint, Direction, Layout, Rect},
11    style::Modifier,
12    text::{Line, Span},
13    widgets::{Block, Borders, Paragraph},
14    Frame,
15};
16
17use crate::theme::AxonmlTheme;
18
19// =============================================================================
20// Types
21// =============================================================================
22
23/// Key binding entry
24#[derive(Debug, Clone)]
25pub struct KeyBinding {
26    pub key: &'static str,
27    pub description: &'static str,
28}
29
30/// Help category
31#[derive(Debug, Clone)]
32pub struct HelpCategory {
33    pub name: &'static str,
34    pub bindings: Vec<KeyBinding>,
35}
36
37// =============================================================================
38// Help View
39// =============================================================================
40
41/// Help view displaying keyboard shortcuts and commands
42pub struct HelpView {
43    /// Help categories
44    pub categories: Vec<HelpCategory>,
45
46    /// Scroll offset
47    pub scroll_offset: usize,
48
49    /// Show compact view
50    pub compact: bool,
51}
52
53impl HelpView {
54    /// Create a new help view
55    pub fn new() -> Self {
56        Self {
57            categories: Self::default_categories(),
58            scroll_offset: 0,
59            compact: false,
60        }
61    }
62
63    /// Get default key binding categories
64    fn default_categories() -> Vec<HelpCategory> {
65        vec![
66            HelpCategory {
67                name: "Navigation",
68                bindings: vec![
69                    KeyBinding { key: "Tab / Shift+Tab", description: "Switch between views" },
70                    KeyBinding { key: "1-6", description: "Jump to specific view (Model, Data, Training, Graphs, Files, Help)" },
71                    KeyBinding { key: "j / Down", description: "Move selection down" },
72                    KeyBinding { key: "k / Up", description: "Move selection up" },
73                    KeyBinding { key: "h / Left", description: "Collapse / Previous" },
74                    KeyBinding { key: "l / Right", description: "Expand / Next" },
75                    KeyBinding { key: "g / Home", description: "Go to first item" },
76                    KeyBinding { key: "G / End", description: "Go to last item" },
77                    KeyBinding { key: "Ctrl+u / PageUp", description: "Page up" },
78                    KeyBinding { key: "Ctrl+d / PageDown", description: "Page down" },
79                ],
80            },
81            HelpCategory {
82                name: "Model View",
83                bindings: vec![
84                    KeyBinding { key: "o", description: "Open model file" },
85                    KeyBinding { key: "Enter", description: "View layer details" },
86                    KeyBinding { key: "d", description: "Toggle detailed view" },
87                    KeyBinding { key: "e", description: "Export model info" },
88                    KeyBinding { key: "v", description: "Visualize model graph" },
89                ],
90            },
91            HelpCategory {
92                name: "Data View",
93                bindings: vec![
94                    KeyBinding { key: "o", description: "Open dataset file" },
95                    KeyBinding { key: "Tab", description: "Switch panel (classes/features)" },
96                    KeyBinding { key: "s", description: "View data statistics" },
97                    KeyBinding { key: "p", description: "Preview samples" },
98                ],
99            },
100            HelpCategory {
101                name: "Training View",
102                bindings: vec![
103                    KeyBinding { key: "t", description: "Start training" },
104                    KeyBinding { key: "Space", description: "Pause/Resume training" },
105                    KeyBinding { key: "s", description: "Stop training" },
106                    KeyBinding { key: "c", description: "Open training config" },
107                    KeyBinding { key: "Ctrl+s", description: "Save checkpoint" },
108                    KeyBinding { key: "d", description: "Toggle detailed metrics" },
109                ],
110            },
111            HelpCategory {
112                name: "Graphs View",
113                bindings: vec![
114                    KeyBinding { key: "< / >", description: "Switch chart type" },
115                    KeyBinding { key: "+/-", description: "Zoom in/out" },
116                    KeyBinding { key: "r", description: "Reset zoom" },
117                    KeyBinding { key: "e", description: "Export chart as image" },
118                    KeyBinding { key: "l", description: "Toggle legend" },
119                ],
120            },
121            HelpCategory {
122                name: "Files View",
123                bindings: vec![
124                    KeyBinding { key: "Enter", description: "Open file/Toggle directory" },
125                    KeyBinding { key: "Backspace", description: "Go to parent directory" },
126                    KeyBinding { key: ".", description: "Toggle hidden files" },
127                    KeyBinding { key: "/", description: "Search files" },
128                    KeyBinding { key: "n", description: "New file/directory" },
129                    KeyBinding { key: "Delete", description: "Delete file" },
130                    KeyBinding { key: "r", description: "Rename file" },
131                ],
132            },
133            HelpCategory {
134                name: "Global",
135                bindings: vec![
136                    KeyBinding { key: "?", description: "Show/Hide help" },
137                    KeyBinding { key: ":", description: "Command mode" },
138                    KeyBinding { key: "Ctrl+c / q", description: "Quit application" },
139                    KeyBinding { key: "Ctrl+l", description: "Redraw screen" },
140                    KeyBinding { key: "Esc", description: "Cancel / Close popup" },
141                ],
142            },
143        ]
144    }
145
146    /// Scroll up
147    pub fn scroll_up(&mut self) {
148        if self.scroll_offset > 0 {
149            self.scroll_offset -= 1;
150        }
151    }
152
153    /// Scroll down
154    pub fn scroll_down(&mut self) {
155        self.scroll_offset += 1;
156    }
157
158    /// Toggle compact view
159    pub fn toggle_compact(&mut self) {
160        self.compact = !self.compact;
161    }
162
163    /// Render the help view
164    pub fn render(&mut self, frame: &mut Frame, area: Rect) {
165        let chunks = Layout::default()
166            .direction(Direction::Vertical)
167            .constraints([
168                Constraint::Length(5),  // Header
169                Constraint::Min(10),    // Keybindings
170                Constraint::Length(3),  // Footer
171            ])
172            .split(area);
173
174        self.render_header(frame, chunks[0]);
175        self.render_keybindings(frame, chunks[1]);
176        self.render_footer(frame, chunks[2]);
177    }
178
179    fn render_header(&self, frame: &mut Frame, area: Rect) {
180        let header_text = vec![
181            Line::from(Span::styled(
182                "Axonml TUI - Keyboard Shortcuts",
183                AxonmlTheme::title(),
184            )),
185            Line::from(""),
186            Line::from(Span::styled(
187                "Press '?' to toggle help, 'q' to close",
188                AxonmlTheme::muted(),
189            )),
190        ];
191
192        let header = Paragraph::new(header_text)
193            .block(
194                Block::default()
195                    .borders(Borders::ALL)
196                    .border_style(AxonmlTheme::border())
197                    .title(Span::styled(" Help ", AxonmlTheme::header())),
198            )
199            .alignment(Alignment::Center);
200
201        frame.render_widget(header, area);
202    }
203
204    fn render_keybindings(&self, frame: &mut Frame, area: Rect) {
205        // Calculate column layout based on available width
206        let num_columns = if area.width > 120 { 3 } else if area.width > 80 { 2 } else { 1 };
207
208        let column_constraints: Vec<Constraint> = (0..num_columns)
209            .map(|_| Constraint::Percentage(100 / num_columns as u16))
210            .collect();
211
212        let columns = Layout::default()
213            .direction(Direction::Horizontal)
214            .constraints(column_constraints)
215            .split(area);
216
217        // Distribute categories across columns
218        let categories_per_column = (self.categories.len() + num_columns - 1) / num_columns;
219
220        for (col_idx, column_area) in columns.iter().enumerate() {
221            let start_idx = col_idx * categories_per_column;
222            let end_idx = (start_idx + categories_per_column).min(self.categories.len());
223
224            if start_idx >= self.categories.len() {
225                continue;
226            }
227
228            let column_categories = &self.categories[start_idx..end_idx];
229            self.render_column(frame, *column_area, column_categories);
230        }
231    }
232
233    fn render_column(&self, frame: &mut Frame, area: Rect, categories: &[HelpCategory]) {
234        let mut lines: Vec<Line> = Vec::new();
235
236        for category in categories {
237            // Category header
238            lines.push(Line::from(Span::styled(
239                format!(" {} ", category.name),
240                AxonmlTheme::header().add_modifier(Modifier::UNDERLINED),
241            )));
242            lines.push(Line::from(""));
243
244            // Key bindings
245            for binding in &category.bindings {
246                lines.push(Line::from(vec![
247                    Span::styled(format!("  {:20}", binding.key), AxonmlTheme::key()),
248                    Span::styled(binding.description, AxonmlTheme::key_desc()),
249                ]));
250            }
251
252            lines.push(Line::from(""));
253        }
254
255        let content = Paragraph::new(lines)
256            .block(
257                Block::default()
258                    .borders(Borders::ALL)
259                    .border_style(AxonmlTheme::border()),
260            );
261
262        frame.render_widget(content, area);
263    }
264
265    fn render_footer(&self, frame: &mut Frame, area: Rect) {
266        let footer_text = Line::from(vec![
267            Span::styled("Tip: ", AxonmlTheme::muted()),
268            Span::styled(
269                "Use Tab to switch views, number keys (1-6) for quick access",
270                AxonmlTheme::info(),
271            ),
272        ]);
273
274        let footer = Paragraph::new(footer_text)
275            .block(
276                Block::default()
277                    .borders(Borders::ALL)
278                    .border_style(AxonmlTheme::border())
279                    .title(Span::styled(" Quick Tips ", AxonmlTheme::header())),
280            )
281            .alignment(Alignment::Center);
282
283        frame.render_widget(footer, area);
284    }
285}
286
287impl Default for HelpView {
288    fn default() -> Self {
289        Self::new()
290    }
291}