1use crate::output::OutputStyle;
4
5pub struct KeyboardShortcuts;
7
8impl KeyboardShortcuts {
9 pub fn all() -> Vec<(&'static str, &'static str, &'static str)> {
11 vec![
12 ("Navigation", "↑/↓", "Navigate through items"),
14 ("Navigation", "Page Up/Down", "Scroll through content"),
15 ("Navigation", "Home/End", "Jump to start/end"),
16 ("Navigation", "Tab", "Move to next field"),
17 ("Navigation", "Shift+Tab", "Move to previous field"),
18
19 ("Editing", "Ctrl+A", "Select all"),
21 ("Editing", "Ctrl+C", "Copy"),
22 ("Editing", "Ctrl+V", "Paste"),
23 ("Editing", "Ctrl+X", "Cut"),
24 ("Editing", "Ctrl+Z", "Undo"),
25 ("Editing", "Ctrl+Y", "Redo"),
26
27 ("Chat", "Enter", "Send message"),
29 ("Chat", "Shift+Enter", "New line in message"),
30 ("Chat", "Ctrl+L", "Clear chat history"),
31 ("Chat", "Ctrl+P", "Previous message"),
32 ("Chat", "Ctrl+N", "Next message"),
33 ("Chat", "Escape", "Cancel input"),
34
35 ("General", "Ctrl+H", "Show help"),
37 ("General", "Ctrl+Q", "Quit"),
38 ("General", "Ctrl+D", "Exit"),
39 ("General", "?", "Show help"),
40 ("General", "Ctrl+/", "Toggle help"),
41 ]
42 }
43
44 pub fn by_category(category: &str) -> Vec<(&'static str, &'static str)> {
46 Self::all()
47 .into_iter()
48 .filter(|(cat, _, _)| *cat == category)
49 .map(|(_, key, desc)| (key, desc))
50 .collect()
51 }
52
53 pub fn print_all() {
55 let style = OutputStyle::default();
56 println!("{}", style.section("Keyboard Shortcuts"));
57 println!();
58
59 let mut current_category = "";
60 for (category, key, description) in Self::all() {
61 if category != current_category {
62 println!("{}", style.header(category));
63 current_category = category;
64 }
65 println!(" {:<20} {}", key, description);
66 }
67 println!();
68 }
69
70 pub fn print_category(category: &str) {
72 let style = OutputStyle::default();
73 println!("{}", style.section(&format!("{} Shortcuts", category)));
74 println!();
75
76 for (key, description) in Self::by_category(category) {
77 println!(" {:<20} {}", key, description);
78 }
79 println!();
80 }
81}
82
83pub struct AccessibilityFeatures;
85
86impl AccessibilityFeatures {
87 pub fn screen_reader_enabled() -> bool {
89 std::env::var("RICECODER_SCREEN_READER")
90 .map(|v| v.to_lowercase() == "true" || v == "1")
91 .unwrap_or(false)
92 }
93
94 pub fn high_contrast_enabled() -> bool {
96 std::env::var("RICECODER_HIGH_CONTRAST")
97 .map(|v| v.to_lowercase() == "true" || v == "1")
98 .unwrap_or(false)
99 }
100
101 pub fn reduced_motion_enabled() -> bool {
103 std::env::var("RICECODER_REDUCED_MOTION")
104 .map(|v| v.to_lowercase() == "true" || v == "1")
105 .unwrap_or(false)
106 }
107
108 pub fn get_settings() -> AccessibilitySettings {
110 AccessibilitySettings {
111 screen_reader: Self::screen_reader_enabled(),
112 high_contrast: Self::high_contrast_enabled(),
113 reduced_motion: Self::reduced_motion_enabled(),
114 }
115 }
116
117 pub fn print_settings() {
119 let style = OutputStyle::default();
120 let settings = Self::get_settings();
121
122 println!("{}", style.section("Accessibility Settings"));
123 println!();
124 println!(
125 "{}",
126 style.key_value(
127 "Screen Reader",
128 if settings.screen_reader { "Enabled" } else { "Disabled" }
129 )
130 );
131 println!(
132 "{}",
133 style.key_value(
134 "High Contrast",
135 if settings.high_contrast { "Enabled" } else { "Disabled" }
136 )
137 );
138 println!(
139 "{}",
140 style.key_value(
141 "Reduced Motion",
142 if settings.reduced_motion { "Enabled" } else { "Disabled" }
143 )
144 );
145 println!();
146
147 println!("{}", style.section("How to Enable"));
148 println!();
149 println!("Set environment variables:");
150 println!();
151 println!(" # Enable screen reader mode");
152 println!(" export RICECODER_SCREEN_READER=true");
153 println!();
154 println!(" # Enable high contrast mode");
155 println!(" export RICECODER_HIGH_CONTRAST=true");
156 println!();
157 println!(" # Enable reduced motion");
158 println!(" export RICECODER_REDUCED_MOTION=true");
159 println!();
160 }
161
162 pub fn print_guide() {
164 let style = OutputStyle::default();
165 println!("{}", style.section("Accessibility Guide"));
166 println!();
167
168 println!("{}", style.header("Screen Reader Support"));
169 println!();
170 println!("RiceCoder supports screen readers through:");
171 println!("{}", style.list_item("Clear, descriptive text labels"));
172 println!("{}", style.list_item("Semantic HTML structure"));
173 println!("{}", style.list_item("ARIA attributes for dynamic content"));
174 println!();
175 println!("Enable screen reader mode:");
176 println!(" export RICECODER_SCREEN_READER=true");
177 println!();
178
179 println!("{}", style.header("High Contrast Mode"));
180 println!();
181 println!("For users with low vision:");
182 println!("{}", style.list_item("Increased color contrast"));
183 println!("{}", style.list_item("Larger text"));
184 println!("{}", style.list_item("Bold fonts"));
185 println!();
186 println!("Enable high contrast mode:");
187 println!(" export RICECODER_HIGH_CONTRAST=true");
188 println!();
189
190 println!("{}", style.header("Keyboard Navigation"));
191 println!();
192 println!("Full keyboard support:");
193 println!("{}", style.list_item("Tab to navigate"));
194 println!("{}", style.list_item("Arrow keys to move"));
195 println!("{}", style.list_item("Enter to select"));
196 println!("{}", style.list_item("Escape to cancel"));
197 println!();
198 println!("View all shortcuts:");
199 println!(" rice help shortcuts");
200 println!();
201
202 println!("{}", style.header("Reduced Motion"));
203 println!();
204 println!("For users sensitive to motion:");
205 println!("{}", style.list_item("Minimal animations"));
206 println!("{}", style.list_item("No auto-scrolling"));
207 println!("{}", style.list_item("Instant transitions"));
208 println!();
209 println!("Enable reduced motion:");
210 println!(" export RICECODER_REDUCED_MOTION=true");
211 println!();
212
213 println!("{}", style.header("Text Size"));
214 println!();
215 println!("Adjust terminal font size:");
216 println!("{}", style.list_item("Most terminals: Ctrl+Plus to increase"));
217 println!("{}", style.list_item("Most terminals: Ctrl+Minus to decrease"));
218 println!();
219
220 println!("{}", style.header("Color Blindness"));
221 println!();
222 println!("RiceCoder uses symbols in addition to colors:");
223 println!("{}", style.list_item("✓ for success"));
224 println!("{}", style.list_item("✗ for errors"));
225 println!("{}", style.list_item("⚠ for warnings"));
226 println!("{}", style.list_item("ℹ for information"));
227 println!();
228
229 println!("{}", style.header("Getting Help"));
230 println!();
231 println!("For accessibility issues:");
232 println!("{}", style.list_item("Report on GitHub: https://github.com/ricecoder/ricecoder/issues"));
233 println!("{}", style.list_item("Include your accessibility needs"));
234 println!("{}", style.list_item("Describe the issue in detail"));
235 println!();
236 }
237}
238
239#[derive(Debug, Clone)]
241pub struct AccessibilitySettings {
242 pub screen_reader: bool,
243 pub high_contrast: bool,
244 pub reduced_motion: bool,
245}
246
247impl AccessibilitySettings {
248 pub fn default() -> Self {
250 Self {
251 screen_reader: false,
252 high_contrast: false,
253 reduced_motion: false,
254 }
255 }
256
257 pub fn from_env() -> Self {
259 AccessibilityFeatures::get_settings()
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[test]
268 fn test_keyboard_shortcuts_not_empty() {
269 assert!(!KeyboardShortcuts::all().is_empty());
270 }
271
272 #[test]
273 fn test_keyboard_shortcuts_by_category() {
274 let nav_shortcuts = KeyboardShortcuts::by_category("Navigation");
275 assert!(!nav_shortcuts.is_empty());
276 }
277
278 #[test]
279 fn test_accessibility_settings_default() {
280 let settings = AccessibilitySettings::default();
281 assert!(!settings.screen_reader);
282 assert!(!settings.high_contrast);
283 assert!(!settings.reduced_motion);
284 }
285}