rush_sync_server/ui/
screen.rs

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