rush_sync_server/ui/
screen.rs

1use crate::commands::history::HistoryKeyboardHandler;
2use crate::commands::lang::LanguageService;
3use crate::commands::theme::ThemeSystem;
4use crate::core::prelude::*;
5use crate::input::{
6    keyboard::{KeyAction, KeyboardManager},
7    state::InputState,
8};
9use crate::input::{AppEvent, EventHandler};
10use crate::output::display::MessageDisplay;
11use crate::ui::{
12    color::AppColor, terminal::TerminalManager, viewport::ScrollDirection, widget::Widget,
13};
14
15use crossterm::event::KeyEvent;
16use ratatui::{backend::CrosstermBackend, Terminal};
17use std::io::{self, Stdout};
18
19pub type TerminalBackend = Terminal<CrosstermBackend<Stdout>>;
20
21use crossterm::execute;
22
23pub struct ScreenManager {
24    terminal: TerminalBackend,
25    message_display: MessageDisplay,
26    input_state: Box<dyn Widget>,
27    config: Config,
28    terminal_mgr: TerminalManager,
29    events: EventHandler,
30    keyboard_manager: KeyboardManager,
31    waiting_for_restart_confirmation: bool,
32}
33
34impl ScreenManager {
35    pub async fn new(config: &Config) -> Result<Self> {
36        let mut terminal_mgr = TerminalManager::new().await?;
37        terminal_mgr.setup().await?;
38
39        let backend = CrosstermBackend::new(io::stdout());
40        let terminal = Terminal::new(backend)?;
41        let size = terminal.size()?;
42
43        let message_display = MessageDisplay::new(config, size.width, size.height);
44        let owned_config = config.clone();
45
46        Ok(Self {
47            terminal,
48            terminal_mgr,
49            message_display,
50            input_state: Box::new(InputState::new(config)),
51            config: owned_config,
52            events: EventHandler::new(config.poll_rate),
53            keyboard_manager: KeyboardManager::new(),
54            waiting_for_restart_confirmation: false,
55        })
56    }
57
58    pub async fn run(&mut self) -> Result<()> {
59        let result = loop {
60            if let Some(event) = self.events.next().await {
61                match event {
62                    AppEvent::Input(key) => {
63                        if self.handle_input_event(key).await? {
64                            self.events.shutdown().await;
65                            break Ok(());
66                        }
67                    }
68                    AppEvent::Resize(width, height) => {
69                        self.handle_resize_event(width, height).await?;
70                    }
71                    AppEvent::Tick => {
72                        self.handle_tick_event().await?;
73                    }
74                }
75            }
76
77            self.render().await?;
78        };
79
80        self.terminal_mgr.cleanup().await?;
81        result
82    }
83
84    async fn handle_input_event(&mut self, key: KeyEvent) -> Result<bool> {
85        // HISTORY HANDLING ZUERST
86        if HistoryKeyboardHandler::get_history_action(&key).is_some() {
87            if let Some(new_input) = self.input_state.handle_input(key) {
88                if let Some(processed) = LanguageService::process_save_message(&new_input).await {
89                    self.message_display.add_message(processed);
90                    return Ok(false);
91                }
92
93                if let Some(processed) = self.process_live_theme_update(&new_input).await {
94                    self.message_display.add_message(processed);
95                    return Ok(false);
96                }
97
98                self.message_display.add_message(new_input.clone());
99
100                if new_input.starts_with("__CLEAR__") {
101                    self.message_display.clear_messages();
102                } else if new_input.starts_with("__EXIT__") {
103                    return Ok(true);
104                }
105            }
106            return Ok(false);
107        }
108
109        // SCROLL-HANDLING MIT VIEWPORT
110        match self.keyboard_manager.get_action(&key) {
111            KeyAction::ScrollUp => {
112                self.message_display.handle_scroll(ScrollDirection::Up, 1);
113                return Ok(false);
114            }
115            KeyAction::ScrollDown => {
116                self.message_display.handle_scroll(ScrollDirection::Down, 1);
117                return Ok(false);
118            }
119            KeyAction::PageUp => {
120                self.message_display
121                    .handle_scroll(ScrollDirection::PageUp, 0);
122                return Ok(false);
123            }
124            KeyAction::PageDown => {
125                self.message_display
126                    .handle_scroll(ScrollDirection::PageDown, 0);
127                return Ok(false);
128            }
129            KeyAction::Submit => {
130                // ✅ DEBUG: Log vor handle_input
131                log::info!("🖥️ SCREEN: About to call input_state.handle_input()");
132
133                if let Some(new_input) = self.input_state.handle_input(key) {
134                    // ✅ DEBUG: Was kommt zurück?
135                    log::info!(
136                        "🖥️ SCREEN: input_state returned {} chars: '{}'",
137                        new_input.len(),
138                        &new_input[..new_input.len().min(100)]
139                    );
140
141                    let input_command = new_input.trim().to_lowercase();
142                    let is_performance_command = input_command == "perf"
143                        || input_command == "performance"
144                        || input_command == "stats";
145
146                    if let Some(processed) = LanguageService::process_save_message(&new_input).await
147                    {
148                        log::info!("🖥️ SCREEN: LanguageService processed message");
149                        self.message_display.add_message(processed);
150                        return Ok(false);
151                    }
152
153                    if let Some(processed) = self.process_live_theme_update(&new_input).await {
154                        log::info!("🖥️ SCREEN: ThemeUpdate processed message");
155                        self.message_display.add_message(processed);
156                        return Ok(false);
157                    }
158
159                    // ✅ DEBUG: Normal message add
160                    log::info!(
161                        "🖥️ SCREEN: Adding normal message to display: '{}'",
162                        &new_input[..new_input.len().min(100)]
163                    );
164
165                    self.message_display.add_message(new_input.clone());
166
167                    if is_performance_command {
168                        log::info!("🖥️ SCREEN: Performance command detected, forcing auto-scroll");
169                        tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
170                        self.message_display.viewport_mut().force_auto_scroll();
171                    }
172
173                    if new_input.starts_with("__CLEAR__") {
174                        log::info!("🖥️ SCREEN: Clearing messages");
175                        self.message_display.clear_messages();
176                    } else if new_input.starts_with("__EXIT__") {
177                        log::info!("🖥️ SCREEN: Exit requested");
178                        return Ok(true);
179                    } else if new_input.starts_with("__RESTART_WITH_MSG__") {
180                        log::info!("🖥️ SCREEN: Restart with message requested");
181                        let feedback_msg = new_input
182                            .replace("__RESTART_WITH_MSG__", "")
183                            .trim()
184                            .to_string();
185
186                        if !feedback_msg.is_empty() {
187                            self.message_display.add_message(feedback_msg);
188                            tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
189                        }
190
191                        if let Err(e) = self.perform_restart().await {
192                            self.message_display
193                                .add_message(format!("Restart failed: {}", e));
194                        }
195                    } else if new_input.starts_with("__RESTART_FORCE__")
196                        || new_input == "__RESTART__"
197                    {
198                        log::info!("🖥️ SCREEN: Restart requested");
199                        if let Err(e) = self.perform_restart().await {
200                            self.message_display
201                                .add_message(format!("Restart failed: {}", e));
202                        }
203                    }
204                } else {
205                    // ✅ DEBUG: Kein Input zurückgegeben
206                    log::info!("🖥️ SCREEN: input_state.handle_input() returned None");
207                }
208            }
209            KeyAction::Quit => return Ok(true),
210            _ => {
211                if let Some(new_input) = self.input_state.handle_input(key) {
212                    if let Some(processed) = LanguageService::process_save_message(&new_input).await
213                    {
214                        self.message_display.add_message(processed);
215                        return Ok(false);
216                    }
217
218                    if let Some(processed) = self.process_live_theme_update(&new_input).await {
219                        self.message_display.add_message(processed);
220                        return Ok(false);
221                    }
222
223                    self.message_display.add_message(new_input);
224                }
225            }
226        }
227        Ok(false)
228    }
229
230    // ✅ FIXED: Live-Theme-Update mit korrekter Cursor-Farb-Übertragung
231    async fn process_live_theme_update(&mut self, message: &str) -> Option<String> {
232        if !message.starts_with("__LIVE_THEME_UPDATE__") {
233            return None;
234        }
235
236        let parts: Vec<&str> = message.split("__MESSAGE__").collect();
237        if parts.len() != 2 {
238            log::error!("{}", t!("screen.theme.invalid_format"));
239            return None;
240        }
241
242        let theme_part = parts[0].replace("__LIVE_THEME_UPDATE__", "");
243        let display_message = parts[1];
244
245        log::info!(
246            "🎨 LIVE THEME UPDATE STARTING: '{}' → '{}'",
247            self.config.current_theme_name,
248            theme_part
249        );
250
251        let theme_system = match ThemeSystem::load() {
252            Ok(system) => system,
253            Err(e) => {
254                log::error!("{} {}", t!("screen.theme.load_failed"), e);
255                return Some(tc!("screen.theme.failed", &e.to_string()));
256            }
257        };
258
259        if let Some(theme_def) = theme_system.get_theme(&theme_part) {
260            // ✅ KRITISCHER FIX: Theme-Definition Details loggen
261            log::info!(
262                "📋 THEME DEFINITION LOADED:\n  \
263                input_cursor_prefix: '{}'\n  \
264                input_cursor_color: '{}'\n  \
265                input_cursor: '{}'\n  \
266                output_cursor: '{}'\n  \
267                output_cursor_color: '{}'",
268                theme_def.input_cursor_prefix,
269                theme_def.input_cursor_color,
270                theme_def.input_cursor,
271                theme_def.output_cursor,
272                theme_def.output_cursor_color
273            );
274
275            match self.create_theme_from_definition(theme_def) {
276                Ok(new_theme) => {
277                    let backup = self.input_state.get_backup_data().unwrap_or_default();
278
279                    // ✅ KRITISCHER FIX: Theme-Konvertierung Details loggen
280                    log::info!(
281                        "🔄 THEME CONVERSION COMPLETE:\n  \
282                        OLD Config: input_cursor='{}', input_cursor_color='{}'\n  \
283                        NEW Config: input_cursor='{}', input_cursor_color='{}'",
284                        self.config.theme.input_cursor,
285                        self.config.theme.input_cursor_color.to_name(),
286                        new_theme.input_cursor,
287                        new_theme.input_cursor_color.to_name()
288                    );
289
290                    // ✅ CRITICAL: Clear ALL UI state first
291                    self.message_display.clear_messages();
292
293                    // ✅ UPDATE CONFIG COMPLETELY
294                    self.config.theme = new_theme;
295                    self.config.current_theme_name = theme_part.clone();
296
297                    // ✅ FORCE COMPLETE UI RECREATION
298                    self.message_display.update_config(&self.config);
299
300                    log::info!("🔄 RECREATING InputState with central cursor API...");
301                    self.input_state = Box::new(InputState::new(&self.config));
302
303                    // ✅ KRITISCHER FIX: Cursor-Details nach Recreation verifizieren
304                    if let Some(_input_widget) = self.input_state.as_input_state() {
305                        log::info!(
306                            "✅ INPUT-CURSOR CREATED:\n  \
307                            Expected: cursor='{}' (color: {})\n  \
308                            Theme config: prefix='{}' (color: {})",
309                            self.config.theme.input_cursor,
310                            self.config.theme.input_cursor_color.to_name(),
311                            self.config.theme.input_cursor_prefix,
312                            self.config.theme.input_cursor_color.to_name()
313                        );
314                    }
315
316                    self.input_state.restore_backup_data(backup.clone());
317
318                    // ✅ FINAL VERIFICATION
319                    log::info!(
320                        "✅ LIVE THEME APPLIED SUCCESSFULLY:\n  \
321                        theme='{}'\n  \
322                        prefix='{}'\n  \
323                        input_cursor='{}'\n  \
324                        input_cursor_color='{}'\n  \
325                        output_cursor='{}'\n  \
326                        output_cursor_color='{}'\n  \
327                        history={} entries",
328                        theme_part.to_uppercase(),
329                        self.config.theme.input_cursor_prefix,
330                        self.config.theme.input_cursor,
331                        self.config.theme.input_cursor_color.to_name(),
332                        self.config.theme.output_cursor,
333                        self.config.theme.output_cursor_color.to_name(),
334                        backup.history.len()
335                    );
336
337                    Some(display_message.to_string())
338                }
339                Err(e) => {
340                    log::error!("{} {}", t!("screen.theme.load_failed"), e);
341                    Some(tc!("screen.theme.failed", &e.to_string()))
342                }
343            }
344        } else {
345            log::error!("{} {}", t!("screen.theme.not_found"), theme_part);
346            Some(tc!("screen.theme.not_found_feedback", theme_part.as_str()))
347        }
348    }
349
350    // ✅ FIXED: Theme-Konvertierung mit detailliertem Logging
351    fn create_theme_from_definition(
352        &self,
353        theme_def: &crate::commands::theme::ThemeDefinition,
354    ) -> Result<crate::core::config::Theme> {
355        use crate::ui::color::AppColor;
356
357        let input_cursor_color = AppColor::from_string(&theme_def.input_cursor_color)?;
358        let output_cursor_color = AppColor::from_string(&theme_def.output_cursor_color)?;
359
360        Ok(crate::core::config::Theme {
361            input_text: AppColor::from_string(&theme_def.input_text)?,
362            input_bg: AppColor::from_string(&theme_def.input_bg)?,
363            output_text: AppColor::from_string(&theme_def.output_text)?,
364            output_bg: AppColor::from_string(&theme_def.output_bg)?,
365
366            // ✅ PERFEKTE CURSOR-KONFIGURATION
367            input_cursor_prefix: theme_def.input_cursor_prefix.clone(),
368            input_cursor_color,
369            input_cursor: theme_def.input_cursor.clone(),
370            output_cursor: theme_def.output_cursor.clone(),
371            output_cursor_color,
372        })
373    }
374
375    async fn handle_resize_event(&mut self, width: u16, height: u16) -> Result<()> {
376        log::info!(
377            "{}",
378            t!(
379                "screen.resize_event",
380                &self
381                    .message_display
382                    .viewport()
383                    .terminal_size()
384                    .0
385                    .to_string(),
386                &self
387                    .message_display
388                    .viewport()
389                    .terminal_size()
390                    .1
391                    .to_string(),
392                &width.to_string(),
393                &height.to_string()
394            )
395        );
396
397        let changed = self.message_display.handle_resize(width, height);
398
399        if changed {
400            log::info!(
401                "{}",
402                t!(
403                    "screen.resize_completed",
404                    &self.message_display.viewport().debug_info()
405                )
406            );
407        }
408
409        Ok(())
410    }
411
412    async fn handle_tick_event(&mut self) -> Result<()> {
413        // ✅ TYPEWRITER-CURSOR UPDATE: Blinken + Progression
414        self.message_display.update_typewriter();
415
416        // ✅ INPUT-CURSOR UPDATE: Nur blinken (zentrale API)
417        if let Some(input_state) = self.input_state.as_input_state() {
418            input_state.update_cursor_blink();
419        }
420        Ok(())
421    }
422
423    /// ✅ 2-LAYER RENDER: Text + Terminal-Cursor getrennt!
424    async fn render(&mut self) -> Result<()> {
425        // ✅ 1. CURSOR-INFO VOR draw() holen
426        let (input_widget, cursor_pos) = self.input_state.render_with_cursor();
427
428        self.terminal.draw(|frame| {
429            let size = frame.size();
430
431            if size.width < 10 || size.height < 5 {
432                log::error!(
433                    "{}",
434                    t!(
435                        "screen.render.too_small_log",
436                        &size.width.to_string(),
437                        &size.height.to_string()
438                    )
439                );
440
441                let emergency_area = ratatui::layout::Rect {
442                    x: 0,
443                    y: 0,
444                    width: size.width.max(1),
445                    height: size.height.max(1),
446                };
447
448                let emergency_widget =
449                    ratatui::widgets::Paragraph::new(t!("screen.render.too_small.text"))
450                        .block(ratatui::widgets::Block::default());
451
452                frame.render_widget(emergency_widget, emergency_area);
453                return;
454            }
455
456            let viewport = self.message_display.viewport();
457
458            if !viewport.is_usable() {
459                log::error!("{}", t!("screen.render.viewport_not_usable_log"));
460
461                let error_area = ratatui::layout::Rect {
462                    x: 0,
463                    y: 0,
464                    width: size.width,
465                    height: size.height,
466                };
467
468                let error_widget =
469                    ratatui::widgets::Paragraph::new(t!("screen.render.viewport_error.text"))
470                        .block(ratatui::widgets::Block::default());
471
472                frame.render_widget(error_widget, error_area);
473                return;
474            }
475
476            let output_area = viewport.output_area();
477            let input_area = viewport.input_area();
478
479            if !output_area.is_valid() || !input_area.is_valid() {
480                log::error!(
481                    "{}",
482                    t!(
483                        "screen.render.invalid_layout_log",
484                        &output_area.width.to_string(),
485                        &output_area.height.to_string(),
486                        &output_area.x.to_string(),
487                        &output_area.y.to_string(),
488                        &input_area.width.to_string(),
489                        &input_area.height.to_string(),
490                        &input_area.x.to_string(),
491                        &input_area.y.to_string()
492                    )
493                );
494                return;
495            }
496
497            if output_area.x + output_area.width > size.width
498                || output_area.y + output_area.height > size.height
499                || input_area.x + input_area.width > size.width
500                || input_area.y + input_area.height > size.height
501            {
502                log::error!(
503                    "{}",
504                    t!(
505                        "screen.render.exceed_bounds_log",
506                        &size.width.to_string(),
507                        &size.height.to_string()
508                    )
509                );
510                return;
511            }
512
513            // ✅ TYPEWRITER-CURSOR AWARE RENDERING
514            let (visible_messages, config, output_layout, cursor_state) =
515                self.message_display.create_output_widget_for_rendering();
516
517            let message_refs: Vec<(String, usize, bool, bool, bool)> = visible_messages;
518
519            let output_widget = crate::output::display::create_output_widget(
520                &message_refs,
521                output_layout,
522                &config,
523                cursor_state,
524            );
525
526            // ✅ LAYER 1: Widgets rendern
527            frame.render_widget(output_widget, output_area.as_rect());
528            frame.render_widget(input_widget, input_area.as_rect());
529
530            // ✅ ZURÜCK ZU TERMINAL-CURSOR: Echter separater Layer!
531            if let Some((rel_x, rel_y)) = cursor_pos {
532                // Padding berücksichtigen: links=3, oben=1
533                let padding_left = 3u16;
534                let padding_top = 1u16;
535
536                let abs_x = input_area.x + padding_left + rel_x;
537                let abs_y = input_area.y + padding_top + rel_y;
538
539                // ✅ ECHTER TERMINAL-CURSOR: Separate Ebene über dem Text!
540                frame.set_cursor(abs_x, abs_y);
541            }
542        })?;
543
544        // ✅ TERMINAL-CURSOR-STIL UND -FARBE setzen (nach dem draw!)
545        if cursor_pos.is_some() {
546            // Cursor ist sichtbar → Cursor-Stil UND -Farbe setzen
547            let cursor_commands = self.get_terminal_cursor_commands();
548            for command in cursor_commands {
549                execute!(std::io::stdout(), crossterm::style::Print(command))?;
550            }
551        } else {
552            // Cursor ist unsichtbar (Blinken) → Cursor verstecken
553            execute!(
554                std::io::stdout(),
555                crossterm::style::Print("\x1B[?25l") // Hide cursor
556            )?;
557        }
558
559        Ok(())
560    }
561
562    /// ✅ ERWEITERTE Terminal-Cursor-Kommandos mit DEBUGGING
563    fn get_terminal_cursor_commands(&self) -> Vec<String> {
564        let mut commands = Vec::new();
565
566        // ✅ 1. CURSOR-FORM setzen (universal)
567        let form_command = match self.config.theme.input_cursor.to_uppercase().as_str() {
568            "PIPE" => "\x1B[6 q",       // Blinking bar (pipe)
569            "UNDERSCORE" => "\x1B[4 q", // Blinking underscore
570            "BLOCK" => "\x1B[2 q",      // Blinking block
571            _ => "\x1B[6 q",            // Default: Blinking bar
572        };
573        commands.push(form_command.to_string());
574
575        // ✅ 2. CURSOR-FARBE setzen (terminal-spezifisch)
576        let cursor_color = &self.config.theme.input_cursor_color;
577        let color_commands = self.get_all_cursor_color_sequences(cursor_color);
578        commands.extend(color_commands);
579
580        // ✅ 3. CURSOR anzeigen (universal)
581        commands.push("\x1B[?25h".to_string()); // Show cursor
582
583        commands
584    }
585
586    /// ✅ ALLE MÖGLICHEN Cursor-Farb-Sequenzen senden (Shotgun-Approach)
587    fn get_all_cursor_color_sequences(&self, color: &AppColor) -> Vec<String> {
588        let mut sequences = Vec::new();
589        let (r, g, b) = self.get_rgb_values(color);
590
591        // ✅ TERMINAL-ERKENNUNG
592        let term_program = std::env::var("TERM_PROGRAM").unwrap_or_default();
593        let term = std::env::var("TERM").unwrap_or_default();
594        let tmux = std::env::var("TMUX").is_ok();
595
596        log::info!("🖥️ TERMINAL DETECTION:");
597        log::info!("   TERM_PROGRAM: '{}'", term_program);
598        log::info!("   TERM: '{}'", term);
599        log::info!("   TMUX: {}", tmux);
600
601        // ✅ TMUX: Nur wenn wirklich in tmux
602        if tmux {
603            log::info!("🟢 TMUX detected - using tmux cursor sequences");
604            sequences.push(format!(
605                "\x1BPtmux;\x1B\x1B]12;#{:02x}{:02x}{:02x}\x07\x1B\\",
606                r, g, b
607            ));
608            return sequences; // Nur tmux-Sequenz senden
609        }
610
611        // ✅ MACOS TERMINAL: Apple Terminal.app
612        if term_program == "Apple_Terminal" {
613            log::info!("🍎 Apple Terminal detected - using macOS sequences");
614            // Standard Xterm-Sequenz für macOS Terminal
615            sequences.push(format!("\x1B]12;#{:02x}{:02x}{:02x}\x07", r, g, b));
616            return sequences;
617        }
618
619        // ✅ ITERM2: iTerm2
620        if term_program.starts_with("iTerm") {
621            log::info!("🟣 iTerm2 detected - using iTerm2 sequences");
622            // iTerm2 spezifische Sequenz
623            sequences.push(format!("\x1B]Pl{:02x}{:02x}{:02x}\x1B\\", r, g, b));
624            // Backup: Standard Xterm
625            sequences.push(format!("\x1B]12;#{:02x}{:02x}{:02x}\x07", r, g, b));
626            return sequences;
627        }
628
629        // ✅ VSCODE TERMINAL: Visual Studio Code
630        if term_program == "vscode" || std::env::var("VSCODE_INJECTION").is_ok() {
631            log::info!("💙 VSCode Terminal detected - using standard sequences");
632            sequences.push(format!("\x1B]12;#{:02x}{:02x}{:02x}\x07", r, g, b));
633            return sequences;
634        }
635
636        // ✅ KONSOLE: KDE Konsole
637        if term_program.contains("konsole") || term.contains("konsole") {
638            log::info!("🔵 Konsole detected - using KDE sequences");
639            sequences.push(format!(
640                "\x1B]50;CursorShape=1;CursorColor=#{:02x}{:02x}{:02x}\x07",
641                r, g, b
642            ));
643        }
644
645        // ✅ GNOME TERMINAL: VTE-based (Gnome Terminal, etc.)
646        if std::env::var("VTE_VERSION").is_ok() {
647            log::info!("🟠 VTE Terminal detected - using VTE sequences");
648            sequences.push(format!("\x1B]12;#{:02x}{:02x}{:02x}\x1B\\", r, g, b));
649        }
650
651        // ✅ FALLBACK: Standard Xterm-Sequenz (funktioniert meist)
652        if sequences.is_empty() {
653            log::info!("⚪ Unknown terminal - using standard Xterm sequences");
654            sequences.push(format!("\x1B]12;#{:02x}{:02x}{:02x}\x07", r, g, b));
655
656            // Alternative mit RGB-Format
657            sequences.push(format!(
658                "\x1B]12;rgb:{:02x}{:02x}/{:02x}{:02x}/{:02x}{:02x}\x07",
659                r, r, g, g, b, b
660            ));
661        }
662
663        log::info!(
664            "✅ Sending {} cursor sequences for terminal '{}'",
665            sequences.len(),
666            term_program
667        );
668
669        sequences
670    }
671
672    /// ✅ PRÄZISE RGB-Werte aus AppColor
673    fn get_rgb_values(&self, color: &AppColor) -> (u8, u8, u8) {
674        let rgb = match color.to_name() {
675            "black" => (0, 0, 0),
676            "red" => (255, 0, 0),
677            "green" => (0, 255, 0),
678            "yellow" => (255, 255, 0), // ✅ GELB für deinen Test!
679            "blue" => (0, 0, 255),
680            "magenta" => (255, 0, 255),
681            "cyan" => (0, 255, 255),
682            "white" => (255, 255, 255),
683            "gray" => (128, 128, 128),
684            "darkgray" => (64, 64, 64),
685            "lightred" => (255, 128, 128),
686            "lightgreen" => (128, 255, 128),
687            "lightyellow" => (255, 255, 128),
688            "lightblue" => (128, 128, 255),
689            "lightmagenta" => (255, 128, 255),
690            "lightcyan" => (128, 255, 255),
691            _ => (255, 255, 255), // Default: white
692        };
693
694        log::trace!(
695            "🎨 COLOR MAPPING: '{}' → RGB({}, {}, {})",
696            color.to_name(),
697            rgb.0,
698            rgb.1,
699            rgb.2
700        );
701        rgb
702    }
703
704    async fn perform_restart(&mut self) -> Result<()> {
705        log::info!("{}", t!("screen.restart.start"));
706
707        // ✅ CURSOR-STIL zurücksetzen vor Cleanup
708        execute!(
709            std::io::stdout(),
710            crossterm::style::Print("\x1B[0 q"), // Default cursor
711            crossterm::style::Print("\x1B[?25h")  // Show cursor
712        )?;
713
714        self.terminal_mgr.cleanup().await?;
715        self.terminal_mgr = TerminalManager::new().await?;
716        self.terminal_mgr.setup().await?;
717
718        let backend = CrosstermBackend::new(io::stdout());
719        self.terminal = Terminal::new(backend)?;
720        let size = self.terminal.size()?;
721
722        self.message_display = MessageDisplay::new(&self.config, size.width, size.height);
723        self.input_state = Box::new(InputState::new(&self.config));
724        self.waiting_for_restart_confirmation = false;
725
726        self.message_display
727            .add_message(tc!("system.commands.restart.success"));
728
729        log::info!("{}", t!("screen.restart.done"));
730        Ok(())
731    }
732}
733
734/// ✅ i18n Integration Helper (unverändert)
735impl ScreenManager {
736    pub fn validate_i18n_keys() -> Vec<String> {
737        let required_keys = [
738            "screen.performance_command_detected",
739            "screen.performance_command_viewport_reset_applied",
740            "screen.theme.invalid_format",
741            "screen.theme.processing",
742            "screen.theme.load_failed",
743            "screen.theme.failed",
744            "screen.theme.applied",
745            "screen.theme.not_found",
746            "screen.theme.not_found_feedback",
747            "screen.render.too_small_log",
748            "screen.render.too_small.text",
749            "screen.render.viewport_not_usable_log",
750            "screen.render.viewport_error.text",
751            "screen.render.invalid_layout_log",
752            "screen.render.exceed_bounds_log",
753            "screen.restart.start",
754            "screen.restart.done",
755            "system.commands.restart.success",
756        ];
757
758        let mut missing = Vec::new();
759        for key in required_keys {
760            if !crate::i18n::has_translation(key) {
761                missing.push(key.to_string());
762            }
763        }
764        missing
765    }
766
767    pub fn get_i18n_debug_info() -> HashMap<String, usize> {
768        let mut info = HashMap::new();
769        let stats = crate::i18n::get_translation_stats();
770
771        info.insert("screen_manager_keys".to_string(), 18);
772        info.extend(stats);
773
774        info
775    }
776}