rush_sync_server/ui/
color.rs

1// =====================================================
2// ANTI-FLICKER COLOR SYSTEM: src/ui/color.rs
3// =====================================================
4
5use crate::core::prelude::*;
6use log::Level;
7use once_cell::sync::Lazy;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct AppColor(Color);
12
13// ✅ BESTEHENDE COLOR_MAP (unverändert für Kategorien)
14static COLOR_MAP: Lazy<HashMap<&'static str, Color>> = Lazy::new(|| {
15    let mut map = HashMap::new();
16
17    // Standard-Farben
18    map.insert("black", Color::Black);
19    map.insert("red", Color::Red);
20    map.insert("green", Color::Green);
21    map.insert("yellow", Color::Yellow);
22    map.insert("blue", Color::Blue);
23    map.insert("magenta", Color::Magenta);
24    map.insert("cyan", Color::Cyan);
25    map.insert("gray", Color::Gray);
26    map.insert("darkgray", Color::DarkGray);
27    map.insert("lightred", Color::LightRed);
28    map.insert("lightgreen", Color::LightGreen);
29    map.insert("lightyellow", Color::LightYellow);
30    map.insert("lightblue", Color::LightBlue);
31    map.insert("lightmagenta", Color::LightMagenta);
32    map.insert("lightcyan", Color::LightCyan);
33    map.insert("white", Color::White);
34
35    // Kategorien
36    map.insert("error", Color::Red);
37    map.insert("warning", Color::Yellow);
38    map.insert("warn", Color::Yellow);
39    map.insert("info", Color::Green);
40    map.insert("debug", Color::Blue);
41    map.insert("trace", Color::White);
42    map.insert("lang", Color::Cyan);
43    map.insert("version", Color::LightBlue);
44    map.insert("startup", Color::Magenta);
45    map.insert("theme", Color::LightMagenta);
46
47    map
48});
49
50// ✅ NEUER ANTI-FLICKER: PRE-COMPILED DISPLAY -> COLOR MAP
51static DISPLAY_COLOR_MAP: Lazy<HashMap<&'static str, Color>> = Lazy::new(|| {
52    let mut map = HashMap::new();
53
54    // ✅ ALLE DISPLAY-TEXTE DIREKT ZU FARBEN (ZERO DELAY!)
55
56    // Bestätigungen → GELB
57    map.insert("CONFIRM", Color::Yellow);
58    map.insert("BESTÄTIGEN", Color::Yellow);
59
60    // Fehler → ROT
61    map.insert("ERROR", Color::Red);
62    map.insert("FEHLER", Color::Red);
63    map.insert("RENDER", Color::Red);
64
65    // Warnungen → GELB
66    map.insert("WARN", Color::Yellow);
67    map.insert("WARNING", Color::Yellow);
68    map.insert("TERMINAL", Color::Yellow);
69
70    // Info → GRÜN
71    map.insert("INFO", Color::Green);
72    map.insert("CLIPBOARD", Color::Green);
73    map.insert("HISTORY", Color::Green);
74    map.insert("HISTORIE", Color::Green);
75    map.insert("LOG_LEVEL", Color::Green);
76    map.insert("SYSTEM", Color::Green);
77
78    // Debug → BLAU
79    map.insert("DEBUG", Color::Blue);
80
81    // Trace → WEIß
82    map.insert("TRACE", Color::White);
83
84    // Spezielle → SPEZIALFARBEN
85    map.insert("THEME", Color::LightMagenta); // PINK!
86    map.insert("LANG", Color::Cyan); // CYAN
87    map.insert("SPRACHE", Color::Cyan); // CYAN
88    map.insert("VERSION", Color::LightBlue); // LIGHT_BLUE
89    map.insert("READY", Color::Magenta); // MAGENTA
90    map.insert("BEREIT", Color::Magenta); // MAGENTA
91
92    map
93});
94
95impl AppColor {
96    pub fn new(color: Color) -> Self {
97        Self(color)
98    }
99
100    // ✅ ANTI-FLICKER: ZERO-DELAY DISPLAY TEXT LOOKUP
101    pub fn from_display_text(display_text: &str) -> Self {
102        let normalized = display_text.trim().to_uppercase();
103
104        // 🚀 DIREKT HIT: O(1) lookup, KEIN calculation, KEIN fallback!
105        let color = DISPLAY_COLOR_MAP
106            .get(normalized.as_str())
107            .copied()
108            .unwrap_or(Color::Green); // info fallback
109
110        Self(color)
111    }
112
113    // ✅ PERFORMANCE-OPTIMIERT: Category lookup
114    pub fn from_category(category: &str) -> Self {
115        let normalized = category.trim().to_lowercase();
116        let color = COLOR_MAP
117            .get(normalized.as_str())
118            .copied()
119            .unwrap_or(Color::Green);
120        Self(color)
121    }
122
123    // ✅ LEGACY SUPPORT: Vereinfacht für andere Stellen
124    pub fn from_any<T: Into<String>>(source: T) -> Self {
125        let key = source.into().to_lowercase();
126        let color = COLOR_MAP.get(key.as_str()).copied().unwrap_or(Color::Green);
127        Self(color)
128    }
129
130    pub fn from_log_level(level: Level) -> Self {
131        Self::from_category(&level.to_string())
132    }
133
134    pub fn from_string(color_str: &str) -> crate::core::error::Result<Self> {
135        let normalized = color_str.trim().to_lowercase();
136        let color = COLOR_MAP
137            .get(normalized.as_str())
138            .copied()
139            .ok_or_else(|| AppError::Validation(format!("Invalid color: {}", color_str)))?;
140        Ok(Self(color))
141    }
142
143    // ✅ DEBUG: Performance monitoring
144    pub fn from_display_text_with_timing(display_text: &str) -> (Self, std::time::Duration) {
145        let start = std::time::Instant::now();
146        let color = Self::from_display_text(display_text);
147        let duration = start.elapsed();
148        (color, duration)
149    }
150
151    // ✅ UTILITIES
152    pub fn available_display_texts() -> Vec<&'static str> {
153        DISPLAY_COLOR_MAP.keys().copied().collect()
154    }
155
156    pub fn available_categories() -> Vec<&'static str> {
157        COLOR_MAP.keys().copied().collect()
158    }
159
160    // Bestehende Methoden...
161    pub fn format_message(&self, level: &str, message: &str) -> String {
162        if level.is_empty() {
163            format!("\x1B[{}m{}\x1B[0m", self.to_ansi_code(), message)
164        } else {
165            format!(
166                "\x1B[{}m[{}] {}\x1B[0m",
167                self.to_ansi_code(),
168                level,
169                message
170            )
171        }
172    }
173
174    pub fn to_ansi_code(&self) -> u8 {
175        match self.0 {
176            Color::Black => 30,
177            Color::Red => 31,
178            Color::Green => 32,
179            Color::Yellow => 33,
180            Color::Blue => 34,
181            Color::Magenta => 35,
182            Color::Cyan => 36,
183            Color::Gray => 37,
184            Color::DarkGray => 90,
185            Color::LightRed => 91,
186            Color::LightGreen => 92,
187            Color::LightYellow => 93,
188            Color::LightBlue => 94,
189            Color::LightMagenta => 95,
190            Color::LightCyan => 96,
191            Color::White => 97,
192            _ => 37,
193        }
194    }
195
196    pub fn to_name(&self) -> &'static str {
197        COLOR_MAP
198            .iter()
199            .find(|(_, &v)| v == self.0)
200            .map(|(k, _)| *k)
201            .unwrap_or("gray")
202    }
203}
204
205// Traits unverändert...
206impl From<AppColor> for Color {
207    fn from(app_color: AppColor) -> Self {
208        app_color.0
209    }
210}
211
212impl From<&AppColor> for Color {
213    fn from(app_color: &AppColor) -> Self {
214        app_color.0
215    }
216}
217
218impl Default for AppColor {
219    fn default() -> Self {
220        Self(Color::Gray)
221    }
222}