ricecoder_cli/
accessibility.rs

1// Accessibility features and keyboard shortcuts documentation
2
3use crate::output::OutputStyle;
4
5/// Keyboard shortcuts for RiceCoder
6pub struct KeyboardShortcuts;
7
8impl KeyboardShortcuts {
9    /// Get all keyboard shortcuts
10    pub fn all() -> Vec<(&'static str, &'static str, &'static str)> {
11        vec![
12            // Navigation
13            ("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
20            ("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 Mode
28            ("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
36            ("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    /// Get shortcuts for a specific category
45    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    /// Print all shortcuts
54    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    /// Print shortcuts for a specific category
71    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
83/// Accessibility features
84pub struct AccessibilityFeatures;
85
86impl AccessibilityFeatures {
87    /// Check if screen reader mode is enabled
88    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    /// Check if high contrast mode is enabled
95    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    /// Check if reduced motion is preferred
102    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    /// Get accessibility settings
109    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    /// Print accessibility settings
118    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    /// Print accessibility guide
163    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/// Accessibility settings
240#[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    /// Create default settings
249    pub fn default() -> Self {
250        Self {
251            screen_reader: false,
252            high_contrast: false,
253            reduced_motion: false,
254        }
255    }
256
257    /// Load settings from environment
258    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}