Skip to main content

flow_tui/views/
help.rs

1use crate::app::App;
2use ratatui::{
3    layout::{Alignment, Constraint, Direction, Layout, Rect},
4    style::{Modifier, Style},
5    text::{Line, Span},
6    widgets::{Block, Borders, Clear, Paragraph},
7    Frame,
8};
9
10pub fn render(frame: &mut Frame, app: &App) {
11    let theme = &app.tui_theme;
12
13    // Create centered popup
14    let area = centered_rect(60, 70, frame.area());
15
16    // Clear the background
17    frame.render_widget(Clear, area);
18
19    let block = Block::default()
20        .title("Help - Key Bindings")
21        .borders(Borders::ALL)
22        .border_style(Style::default().fg(theme.primary))
23        .title_style(
24            Style::default()
25                .fg(theme.primary)
26                .add_modifier(Modifier::BOLD),
27        )
28        .style(Style::default().bg(theme.background));
29
30    let inner = block.inner(area);
31    frame.render_widget(block, area);
32
33    let help_text = vec![
34        Line::from(""),
35        Line::from(vec![Span::styled(
36            "Views:",
37            Style::default()
38                .fg(theme.primary)
39                .add_modifier(Modifier::BOLD),
40        )]),
41        Line::from(vec![
42            Span::styled("  1       ", Style::default().fg(theme.accent)),
43            Span::styled("Kanban board view", Style::default().fg(theme.foreground)),
44        ]),
45        Line::from(vec![
46            Span::styled("  2       ", Style::default().fg(theme.accent)),
47            Span::styled("Agent dashboard", Style::default().fg(theme.foreground)),
48        ]),
49        Line::from(vec![
50            Span::styled("  3       ", Style::default().fg(theme.accent)),
51            Span::styled("Log viewer", Style::default().fg(theme.foreground)),
52        ]),
53        Line::from(vec![
54            Span::styled("  4       ", Style::default().fg(theme.accent)),
55            Span::styled("Dependency graph", Style::default().fg(theme.foreground)),
56        ]),
57        Line::from(vec![
58            Span::styled("  Tab     ", Style::default().fg(theme.accent)),
59            Span::styled("Cycle through views", Style::default().fg(theme.foreground)),
60        ]),
61        Line::from(""),
62        Line::from(vec![Span::styled(
63            "Navigation:",
64            Style::default()
65                .fg(theme.primary)
66                .add_modifier(Modifier::BOLD),
67        )]),
68        Line::from(vec![
69            Span::styled("  j/Down  ", Style::default().fg(theme.accent)),
70            Span::styled("Next item", Style::default().fg(theme.foreground)),
71        ]),
72        Line::from(vec![
73            Span::styled("  k/Up    ", Style::default().fg(theme.accent)),
74            Span::styled("Previous item", Style::default().fg(theme.foreground)),
75        ]),
76        Line::from(""),
77        Line::from(vec![Span::styled(
78            "Actions:",
79            Style::default()
80                .fg(theme.primary)
81                .add_modifier(Modifier::BOLD),
82        )]),
83        Line::from(vec![
84            Span::styled("  Enter/Space", Style::default().fg(theme.accent)),
85            Span::styled(
86                "Claim selected feature",
87                Style::default().fg(theme.foreground),
88            ),
89        ]),
90        Line::from(vec![
91            Span::styled("  p       ", Style::default().fg(theme.accent)),
92            Span::styled(
93                "Mark selected as passing",
94                Style::default().fg(theme.foreground),
95            ),
96        ]),
97        Line::from(vec![
98            Span::styled("  f       ", Style::default().fg(theme.accent)),
99            Span::styled(
100                "Mark selected as failing",
101                Style::default().fg(theme.foreground),
102            ),
103        ]),
104        Line::from(vec![
105            Span::styled("  c       ", Style::default().fg(theme.accent)),
106            Span::styled(
107                "Clear in-progress flag",
108                Style::default().fg(theme.foreground),
109            ),
110        ]),
111        Line::from(vec![
112            Span::styled("  r       ", Style::default().fg(theme.accent)),
113            Span::styled(
114                "Refresh from database",
115                Style::default().fg(theme.foreground),
116            ),
117        ]),
118        Line::from(""),
119        Line::from(vec![Span::styled(
120            "Other:",
121            Style::default()
122                .fg(theme.primary)
123                .add_modifier(Modifier::BOLD),
124        )]),
125        Line::from(vec![
126            Span::styled("  t       ", Style::default().fg(theme.accent)),
127            Span::styled("Cycle theme", Style::default().fg(theme.foreground)),
128        ]),
129        Line::from(vec![
130            Span::styled("  ?       ", Style::default().fg(theme.accent)),
131            Span::styled("Toggle this help", Style::default().fg(theme.foreground)),
132        ]),
133        Line::from(vec![
134            Span::styled("  q/Ctrl-C", Style::default().fg(theme.accent)),
135            Span::styled("Quit", Style::default().fg(theme.foreground)),
136        ]),
137        Line::from(""),
138        Line::from(vec![Span::styled(
139            "Press any key to close",
140            Style::default()
141                .fg(theme.muted)
142                .add_modifier(Modifier::ITALIC),
143        )]),
144    ];
145
146    let paragraph = Paragraph::new(help_text)
147        .alignment(Alignment::Left)
148        .style(Style::default().fg(theme.foreground));
149
150    frame.render_widget(paragraph, inner);
151}
152
153fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
154    let popup_layout = Layout::default()
155        .direction(Direction::Vertical)
156        .constraints([
157            Constraint::Percentage((100 - percent_y) / 2),
158            Constraint::Percentage(percent_y),
159            Constraint::Percentage((100 - percent_y) / 2),
160        ])
161        .split(r);
162
163    Layout::default()
164        .direction(Direction::Horizontal)
165        .constraints([
166            Constraint::Percentage((100 - percent_x) / 2),
167            Constraint::Percentage(percent_x),
168            Constraint::Percentage((100 - percent_x) / 2),
169        ])
170        .split(popup_layout[1])[1]
171}