rush_sync_server/input/
keyboard.rs

1// =====================================================
2// FILE: src/input/keyboard.rs - FIXED SHIFT SUPPORT
3// =====================================================
4
5use crate::core::constants::DOUBLE_ESC_THRESHOLD;
6use crate::core::prelude::*;
7use crossterm::event::KeyModifiers;
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum KeyAction {
11    MoveLeft,
12    MoveRight,
13    MoveToStart,
14    MoveToEnd,
15    InsertChar(char),
16    Backspace,
17    Delete,
18    Submit,
19    Cancel,
20    Quit,
21    ClearLine,
22    CopySelection,
23    PasteBuffer,
24    NoAction,
25    ScrollUp,
26    ScrollDown,
27    PageUp,
28    PageDown,
29}
30
31use lazy_static::lazy_static;
32use std::sync::Mutex;
33
34lazy_static! {
35    static ref LAST_ESC_PRESS: Mutex<Option<Instant>> = Mutex::new(None);
36}
37
38pub struct KeyboardManager {
39    double_press_threshold: Duration,
40}
41
42impl KeyboardManager {
43    pub fn new() -> Self {
44        Self {
45            double_press_threshold: Duration::from_millis(DOUBLE_ESC_THRESHOLD),
46        }
47    }
48
49    fn detect_broken_cmd_event(&self) -> bool {
50        false // Placeholder implementation
51    }
52
53    pub fn get_action(&mut self, key: &KeyEvent) -> KeyAction {
54        // Debug fΓΌr Mac Cmd-Events
55        if key.modifiers.contains(KeyModifiers::SUPER) {
56            log::warn!(
57                "🍎 RAW Cmd Event: code={:?}, modifiers={:?}, char={}",
58                key.code,
59                key.modifiers,
60                match key.code {
61                    KeyCode::Char(c) => format!("'{}'", c),
62                    _ => "none".to_string(),
63                }
64            );
65        }
66
67        // ESC Behandlung
68        if key.code == KeyCode::Esc {
69            let now = Instant::now();
70            let mut last_press = LAST_ESC_PRESS.lock().unwrap_or_else(|poisoned| {
71                log::warn!("Recovered from poisoned mutex");
72                poisoned.into_inner()
73            });
74
75            if let Some(prev_press) = *last_press {
76                if now.duration_since(prev_press) <= self.double_press_threshold {
77                    *last_press = None;
78                    log::info!("πŸšͺ Double ESC detected - Quit requested");
79                    return KeyAction::Quit;
80                }
81            }
82
83            *last_press = Some(now);
84            return KeyAction::NoAction;
85        }
86
87        // βœ… KORRIGIERTER MATCH - keine Duplikate mehr!
88        match (key.code, key.modifiers) {
89            // ========== BEWEGUNG ==========
90            (KeyCode::Left, KeyModifiers::NONE) => KeyAction::MoveLeft,
91            (KeyCode::Right, KeyModifiers::NONE) => KeyAction::MoveRight,
92            (KeyCode::Home, KeyModifiers::NONE) => KeyAction::MoveToStart,
93            (KeyCode::End, KeyModifiers::NONE) => KeyAction::MoveToEnd,
94
95            // ========== SUBMIT ==========
96            (KeyCode::Enter, KeyModifiers::NONE) => KeyAction::Submit,
97
98            // ========== SCROLLING ==========
99            (KeyCode::Up, KeyModifiers::SHIFT) => KeyAction::ScrollUp,
100            (KeyCode::Down, KeyModifiers::SHIFT) => KeyAction::ScrollDown,
101            (KeyCode::PageUp, KeyModifiers::NONE) => KeyAction::PageUp,
102            (KeyCode::PageDown, KeyModifiers::NONE) => KeyAction::PageDown,
103
104            // ========== TEXT-BEARBEITUNG ==========
105            (KeyCode::Backspace, KeyModifiers::NONE) => KeyAction::Backspace,
106            (KeyCode::Delete, KeyModifiers::NONE) => KeyAction::Delete,
107
108            // ========== 🚨 MAC CMD-EVENT FALLBACK (vor normalen Chars!) ==========
109            (KeyCode::Char('v'), KeyModifiers::NONE) if self.detect_broken_cmd_event() => {
110                log::warn!("🍎 Detected broken Cmd+V event, treating as paste");
111                KeyAction::PasteBuffer
112            }
113            (KeyCode::Char('c'), KeyModifiers::NONE) if self.detect_broken_cmd_event() => {
114                log::warn!("🍎 Detected broken Cmd+C event, treating as copy");
115                KeyAction::CopySelection
116            }
117            (KeyCode::Char('a'), KeyModifiers::NONE) if self.detect_broken_cmd_event() => {
118                log::warn!("🍎 Detected broken Cmd+A event, treating as move to start");
119                KeyAction::MoveToStart
120            }
121
122            // ========== 🍎 MAC: CMD-SHORTCUTS ==========
123            (KeyCode::Char('a'), KeyModifiers::SUPER) => {
124                log::debug!("🍎 Cmd+A β†’ Start");
125                KeyAction::MoveToStart
126            }
127            (KeyCode::Char('e'), KeyModifiers::SUPER) => {
128                log::debug!("🍎 Cmd+E β†’ End");
129                KeyAction::MoveToEnd
130            }
131            (KeyCode::Char('u'), KeyModifiers::SUPER) => {
132                log::debug!("🍎 Cmd+U β†’ Clear");
133                KeyAction::ClearLine
134            }
135            (KeyCode::Char('c'), KeyModifiers::SUPER) => {
136                log::debug!("🍎 Cmd+C β†’ Copy");
137                KeyAction::CopySelection
138            }
139            (KeyCode::Char('v'), KeyModifiers::SUPER) => {
140                log::debug!("🍎 Cmd+V β†’ Paste");
141                KeyAction::PasteBuffer
142            }
143
144            // ========== 🍎 MAC: ALT/OPTION-SHORTCUTS ==========
145            (KeyCode::Char('a'), KeyModifiers::ALT) => {
146                log::debug!("🍎 Opt+A β†’ Start");
147                KeyAction::MoveToStart
148            }
149            (KeyCode::Char('e'), KeyModifiers::ALT) => {
150                log::debug!("🍎 Opt+E β†’ End");
151                KeyAction::MoveToEnd
152            }
153            (KeyCode::Char('u'), KeyModifiers::ALT) => {
154                log::debug!("🍎 Opt+U β†’ Clear");
155                KeyAction::ClearLine
156            }
157            (KeyCode::Char('c'), KeyModifiers::ALT) => {
158                log::debug!("🍎 Opt+C β†’ Copy");
159                KeyAction::CopySelection
160            }
161            (KeyCode::Char('v'), KeyModifiers::ALT) => {
162                log::debug!("🍎 Opt+V β†’ Paste");
163                KeyAction::PasteBuffer
164            }
165
166            // ========== πŸ–₯️ WINDOWS/LINUX: CTRL-SHORTCUTS ==========
167            (KeyCode::Char('a'), KeyModifiers::CONTROL) => {
168                log::debug!("πŸ–₯️ Ctrl+A β†’ Start");
169                KeyAction::MoveToStart
170            }
171            (KeyCode::Char('e'), KeyModifiers::CONTROL) => {
172                log::debug!("πŸ–₯️ Ctrl+E β†’ End");
173                KeyAction::MoveToEnd
174            }
175            (KeyCode::Char('u'), KeyModifiers::CONTROL) => {
176                log::debug!("πŸ–₯️ Ctrl+U β†’ Clear");
177                KeyAction::ClearLine
178            }
179            (KeyCode::Char('c'), KeyModifiers::CONTROL) => {
180                log::debug!("πŸ–₯️ Ctrl+C β†’ Copy");
181                KeyAction::CopySelection
182            }
183            (KeyCode::Char('v'), KeyModifiers::CONTROL) => {
184                log::debug!("πŸ–₯️ Ctrl+V β†’ Paste");
185                KeyAction::PasteBuffer
186            }
187
188            // ========== BACKSPACE-KOMBINATIONEN ==========
189            (KeyCode::Backspace, KeyModifiers::SUPER) => {
190                log::debug!("🍎 Cmd+⌫ β†’ Clear");
191                KeyAction::ClearLine
192            }
193            (KeyCode::Backspace, KeyModifiers::ALT) => {
194                log::debug!("🍎 Opt+⌫ β†’ Clear");
195                KeyAction::ClearLine
196            }
197            (KeyCode::Backspace, KeyModifiers::CONTROL) => {
198                log::debug!("πŸ–₯️ Ctrl+⌫ β†’ Clear");
199                KeyAction::ClearLine
200            }
201
202            // ========== BACKSLASH-ALTERNATIVEN ==========
203            (KeyCode::Char('\\'), KeyModifiers::SUPER) => {
204                log::debug!("🍎 Cmd+\\ β†’ Clear");
205                KeyAction::ClearLine
206            }
207            (KeyCode::Char('\\'), KeyModifiers::ALT) => {
208                log::debug!("🍎 Opt+\\ β†’ Clear");
209                KeyAction::ClearLine
210            }
211
212            // ========== PFEILTASTEN MIT MODIFIERS ==========
213            (KeyCode::Left, KeyModifiers::SUPER) => {
214                log::debug!("🍎 Cmd+← β†’ Start");
215                KeyAction::MoveToStart
216            }
217            (KeyCode::Right, KeyModifiers::SUPER) => {
218                log::debug!("🍎 Cmd+β†’ β†’ End");
219                KeyAction::MoveToEnd
220            }
221            (KeyCode::Left, KeyModifiers::CONTROL) => {
222                log::debug!("πŸ–₯️ Ctrl+← β†’ Start");
223                KeyAction::MoveToStart
224            }
225            (KeyCode::Right, KeyModifiers::CONTROL) => {
226                log::debug!("πŸ–₯️ Ctrl+β†’ β†’ End");
227                KeyAction::MoveToEnd
228            }
229            (KeyCode::Left, KeyModifiers::ALT) => {
230                log::debug!("🍎 Opt+← β†’ Start");
231                KeyAction::MoveToStart
232            }
233            (KeyCode::Right, KeyModifiers::ALT) => {
234                log::debug!("🍎 Opt+β†’ β†’ End");
235                KeyAction::MoveToEnd
236            }
237
238            // ========== NORMALE ZEICHEN-EINGABE (MUSS AM ENDE STEHEN!) ==========
239            (KeyCode::Char(c), KeyModifiers::NONE) => KeyAction::InsertChar(c),
240            (KeyCode::Char(c), KeyModifiers::SHIFT) => KeyAction::InsertChar(c),
241
242            // ========== FALLBACK fΓΌr unbekannte Kombinationen ==========
243            (code, modifiers) => {
244                log::debug!("❓ Unhandled key combination: {:?} + {:?}", code, modifiers);
245                KeyAction::NoAction
246            }
247        }
248    }
249}
250
251impl Default for KeyboardManager {
252    fn default() -> Self {
253        Self::new()
254    }
255}
256
257// βœ… TESTS um sicherzustellen, dass Shift funktioniert
258#[cfg(test)]
259mod tests {
260    use super::*;
261    use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
262
263    #[test]
264    fn test_shift_support() {
265        let mut manager = KeyboardManager::new();
266
267        // Lowercase ohne Shift
268        let key_a = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::NONE);
269        assert_eq!(manager.get_action(&key_a), KeyAction::InsertChar('a'));
270
271        // Uppercase mit Shift (das sollte jetzt funktionieren!)
272        let key_a_upper = KeyEvent::new(KeyCode::Char('A'), KeyModifiers::SHIFT);
273        assert_eq!(manager.get_action(&key_a_upper), KeyAction::InsertChar('A'));
274
275        // Zahlen mit Shift (fΓΌr Symbole)
276        let key_exclamation = KeyEvent::new(KeyCode::Char('!'), KeyModifiers::SHIFT);
277        assert_eq!(
278            manager.get_action(&key_exclamation),
279            KeyAction::InsertChar('!')
280        );
281
282        // Symbols mit Shift
283        let key_at = KeyEvent::new(KeyCode::Char('@'), KeyModifiers::SHIFT);
284        assert_eq!(manager.get_action(&key_at), KeyAction::InsertChar('@'));
285    }
286
287    #[test]
288    fn test_mac_specific_shortcuts() {
289        let mut manager = KeyboardManager::new();
290
291        // βœ… MAC: Option-basierte Shortcuts (funktionieren besser als Cmd)
292        let opt_a = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::ALT);
293        assert_eq!(manager.get_action(&opt_a), KeyAction::MoveToStart);
294
295        let opt_e = KeyEvent::new(KeyCode::Char('e'), KeyModifiers::ALT);
296        assert_eq!(manager.get_action(&opt_e), KeyAction::MoveToEnd);
297
298        let opt_u = KeyEvent::new(KeyCode::Char('u'), KeyModifiers::ALT);
299        assert_eq!(manager.get_action(&opt_u), KeyAction::ClearLine);
300
301        // βœ… MAC: Option+Backspace und Cmd+Backspace fΓΌr Zeile lΓΆschen
302        let opt_backspace = KeyEvent::new(KeyCode::Backspace, KeyModifiers::ALT);
303        assert_eq!(manager.get_action(&opt_backspace), KeyAction::ClearLine);
304
305        let cmd_backspace = KeyEvent::new(KeyCode::Backspace, KeyModifiers::SUPER);
306        assert_eq!(manager.get_action(&cmd_backspace), KeyAction::ClearLine);
307
308        let ctrl_backspace = KeyEvent::new(KeyCode::Backspace, KeyModifiers::CONTROL);
309        assert_eq!(manager.get_action(&ctrl_backspace), KeyAction::ClearLine);
310
311        // βœ… BONUS: Backslash-Alternativen (falls Backspace nicht funktioniert)
312        let cmd_backslash = KeyEvent::new(KeyCode::Char('\\'), KeyModifiers::SUPER);
313        assert_eq!(manager.get_action(&cmd_backslash), KeyAction::ClearLine);
314
315        let opt_backslash = KeyEvent::new(KeyCode::Char('\\'), KeyModifiers::ALT);
316        assert_eq!(manager.get_action(&opt_backslash), KeyAction::ClearLine);
317
318        // βœ… WINDOWS/LINUX: Ctrl-basierte Shortcuts
319        let ctrl_a = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
320        assert_eq!(manager.get_action(&ctrl_a), KeyAction::MoveToStart);
321
322        let ctrl_e = KeyEvent::new(KeyCode::Char('e'), KeyModifiers::CONTROL);
323        assert_eq!(manager.get_action(&ctrl_e), KeyAction::MoveToEnd);
324
325        // βœ… PFEILTASTEN: Verschiedene Modifier
326        let cmd_left = KeyEvent::new(KeyCode::Left, KeyModifiers::SUPER);
327        assert_eq!(manager.get_action(&cmd_left), KeyAction::MoveToStart);
328
329        let opt_left = KeyEvent::new(KeyCode::Left, KeyModifiers::ALT);
330        assert_eq!(manager.get_action(&opt_left), KeyAction::MoveToStart);
331
332        let ctrl_left = KeyEvent::new(KeyCode::Left, KeyModifiers::CONTROL);
333        assert_eq!(manager.get_action(&ctrl_left), KeyAction::MoveToStart);
334    }
335
336    #[test]
337    fn test_special_characters() {
338        let mut manager = KeyboardManager::new();
339
340        // Deutsche Umlaute
341        let key_Γ€ = KeyEvent::new(KeyCode::Char('Γ€'), KeyModifiers::NONE);
342        assert_eq!(manager.get_action(&key_Γ€), KeyAction::InsertChar('Γ€'));
343
344        let key_ae_upper = KeyEvent::new(KeyCode::Char('Γ„'), KeyModifiers::SHIFT);
345        assert_eq!(
346            manager.get_action(&key_ae_upper),
347            KeyAction::InsertChar('Γ„')
348        );
349
350        // Emoji und Unicode
351        let key_emoji = KeyEvent::new(KeyCode::Char('πŸš€'), KeyModifiers::NONE);
352        assert_eq!(manager.get_action(&key_emoji), KeyAction::InsertChar('πŸš€'));
353    }
354}