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