rush_sync_server/commands/theme/
manager.rs

1// =====================================================
2// FILE: src/commands/theme/manager.rs - TOML-BASIERTE THEME VERWALTUNG
3// =====================================================
4
5use super::themes::TomlThemeLoader;
6use crate::core::prelude::*;
7
8pub struct ThemeManager;
9
10impl ThemeManager {
11    /// ✅ ZEIGT aktuellen Theme-Status (aus TOML)
12    pub fn show_status() -> String {
13        let current_theme =
14            Self::get_current_theme_from_config().unwrap_or_else(|| "dark".to_string());
15
16        // ✅ TOML-basierte verfügbare Themes (Runtime-Aufruf)
17        let available_themes = match Self::get_available_themes_sync() {
18            Ok(themes) => themes.join(", "),
19            Err(_) => "dark, light, matrix, blue".to_string(), // Fallback
20        };
21
22        format!(
23            "Current theme: {}\nAvailable themes: {}",
24            current_theme.to_uppercase(),
25            available_themes
26        )
27    }
28
29    /// ✅ ASYNC LIVE THEME CHANGE - Lädt aus TOML, keine Hardcodierung!
30    pub async fn change_theme(theme_name: &str) -> Result<String> {
31        let theme_name_lower = theme_name.to_lowercase();
32
33        // ✅ VALIDIERUNG: Prüfe gegen TOML-Themes
34        if !TomlThemeLoader::theme_exists_sync(&theme_name_lower) {
35            let available = TomlThemeLoader::get_available_names().await.join(", ");
36            return Ok(format!(
37                "❌ Invalid theme: '{}'. Available: {}",
38                theme_name, available
39            ));
40        }
41
42        // ✅ LADE aktuelle Config
43        match crate::core::config::Config::load_with_messages(false).await {
44            Ok(mut config) => {
45                // ✅ UPDATE Theme in Config + speichern
46                match config.change_theme(&theme_name_lower).await {
47                    Ok(()) => {
48                        log::info!(
49                            "✅ Theme '{}' saved to config (loaded from TOML)",
50                            theme_name_lower.to_uppercase()
51                        );
52
53                        // ✅ LIVE UPDATE MESSAGE (Screen Manager wird das verarbeiten)
54                        Ok(format!(
55                            "__LIVE_THEME_UPDATE__{}__MESSAGE__🎨 Theme changed to: {} (from TOML)",
56                            theme_name_lower,
57                            theme_name_lower.to_uppercase()
58                        ))
59                    }
60                    Err(e) => {
61                        log::error!("❌ Failed to save theme: {}", e);
62                        Ok(format!("❌ Failed to save theme: {}", e))
63                    }
64                }
65            }
66            Err(e) => {
67                log::error!("❌ Failed to load config: {}", e);
68                Ok(format!("❌ Failed to load config: {}", e))
69            }
70        }
71    }
72
73    /// ✅ SYNC Version - Immediate feedback + background config save
74    pub fn change_theme_sync(theme_name: &str) -> Result<String> {
75        let theme_name_lower = theme_name.to_lowercase();
76
77        // ✅ VALIDIERUNG: Prüfe gegen TOML-Themes
78        if !TomlThemeLoader::theme_exists_sync(&theme_name_lower) {
79            // ✅ SYNC VERSION: Lade verfügbare Themes aus TOML
80            let available = match Self::get_available_themes_sync() {
81                Ok(themes) => themes.join(", "),
82                Err(_) => "dark, light, matrix, blue".to_string(),
83            };
84
85            return Ok(format!(
86                "❌ Invalid theme: '{}'. Available: {}",
87                theme_name, available
88            ));
89        }
90
91        // ✅ SOFORTIGER Live-Update Message (kein Restart!)
92        let live_update_msg = format!(
93            "__LIVE_THEME_UPDATE__{}__MESSAGE__🎨 Theme changed to: {} (from TOML)",
94            theme_name_lower,
95            theme_name_lower.to_uppercase()
96        );
97
98        // ✅ BACKGROUND Task für Config-Persistierung
99        let theme_name_clone = theme_name_lower.clone();
100        tokio::spawn(async move {
101            // ✅ Kurze Verzögerung für bessere UX
102            tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
103
104            match crate::core::config::Config::load_with_messages(false).await {
105                Ok(mut config) => match config.change_theme(&theme_name_clone).await {
106                    Ok(()) => {
107                        log::info!(
108                            "✅ Theme '{}' persisted to config (background, from TOML)",
109                            theme_name_clone.to_uppercase()
110                        );
111                    }
112                    Err(e) => {
113                        log::error!("❌ Background theme save failed: {}", e);
114                    }
115                },
116                Err(e) => {
117                    log::error!("❌ Background config load failed: {}", e);
118                }
119            }
120        });
121
122        Ok(live_update_msg)
123    }
124
125    /// ✅ THEME PREVIEW (aus TOML)
126    pub fn preview_theme(theme_name: &str) -> Result<String> {
127        let theme_name_lower = theme_name.to_lowercase();
128
129        // ✅ LADE THEME AUS TOML
130        if let Some(theme_def) = TomlThemeLoader::load_theme_by_name_sync(&theme_name_lower) {
131            Ok(format!(
132                "🎨 Theme '{}' Preview (from TOML):\n  Input: {} on {}\n  Output: {} on {}\n  Cursor: {}",
133                theme_name_lower.to_uppercase(),
134                theme_def.input_text,
135                theme_def.input_bg,
136                theme_def.output_text,
137                theme_def.output_bg,
138                theme_def.cursor
139            ))
140        } else {
141            let available = match Self::get_available_themes_sync() {
142                Ok(themes) => themes.join(", "),
143                Err(_) => "dark, light, matrix, blue".to_string(),
144            };
145
146            Ok(format!(
147                "❌ Invalid theme: '{}'. Available: {}",
148                theme_name, available
149            ))
150        }
151    }
152
153    /// ✅ HELPER: Lädt aktuelles Theme aus Config (robust + cached)
154    fn get_current_theme_from_config() -> Option<String> {
155        let config_paths = crate::setup::setup_toml::get_config_paths();
156
157        for path in config_paths {
158            if path.exists() {
159                // ✅ ROBUST: Fehler-Handling für jede Datei einzeln
160                match std::fs::read_to_string(&path) {
161                    Ok(content) => {
162                        if let Some(theme) = Self::extract_current_theme_from_toml(&content) {
163                            return Some(theme);
164                        }
165                    }
166                    Err(e) => {
167                        log::debug!("Could not read config file '{}': {}", path.display(), e);
168                        continue; // Versuche nächste Datei
169                    }
170                }
171            }
172        }
173
174        // ✅ FALLBACK: Default theme falls keine Config gefunden
175        log::debug!("No config file found, using default theme");
176        Some("dark".to_string())
177    }
178
179    /// ✅ HELPER: Extrahiert current_theme aus TOML (robust)
180    fn extract_current_theme_from_toml(content: &str) -> Option<String> {
181        let mut in_general_section = false;
182
183        for line in content.lines() {
184            let trimmed = line.trim();
185
186            // ✅ IGNORE comments und empty lines
187            if trimmed.is_empty() || trimmed.starts_with('#') {
188                continue;
189            }
190
191            if trimmed == "[general]" {
192                in_general_section = true;
193                continue;
194            }
195
196            if trimmed.starts_with('[') && trimmed.ends_with(']') && trimmed != "[general]" {
197                in_general_section = false;
198                continue;
199            }
200
201            if in_general_section && trimmed.starts_with("current_theme") {
202                if let Some(value_part) = trimmed.split('=').nth(1) {
203                    let cleaned = value_part
204                        .trim()
205                        .trim_matches('"')
206                        .trim_matches('\'')
207                        .trim();
208
209                    if !cleaned.is_empty() {
210                        return Some(cleaned.to_string());
211                    }
212                }
213            }
214        }
215        None
216    }
217
218    /// ✅ HELPER: Verfügbare Themes aus TOML (sync version)
219    fn get_available_themes_sync() -> Result<Vec<String>> {
220        let config_paths = crate::setup::setup_toml::get_config_paths();
221
222        for path in config_paths {
223            if path.exists() {
224                if let Ok(content) = std::fs::read_to_string(&path) {
225                    if let Ok(themes) = TomlThemeLoader::parse_themes_from_toml(&content) {
226                        let mut names: Vec<String> = themes.keys().cloned().collect();
227                        names.sort();
228                        return Ok(names);
229                    }
230                }
231            }
232        }
233
234        // ✅ FALLBACK
235        Ok(vec![
236            "dark".to_string(),
237            "light".to_string(),
238            "green".to_string(),
239            "blue".to_string(),
240        ])
241    }
242}
243
244// =====================================================
245// TEST EXAMPLES:
246// =====================================================
247
248/*
249// ✅ THEME AUS TOML LADEN:
250let theme = TomlThemeLoader::load_theme_by_name_sync("dark");
251
252// ✅ LIVE THEME CHANGE (aus TOML):
253let result = ThemeManager::change_theme_sync("matrix").unwrap();
254// Result: "__LIVE_THEME_UPDATE__matrix__MESSAGE__🎨 Theme changed to: MATRIX (from TOML)"
255
256// ✅ THEME PREVIEW (aus TOML):
257let preview = ThemeManager::preview_theme("blue").unwrap();
258// Result: "🎨 Theme 'BLUE' Preview (from TOML): ..."
259
260// ✅ VERFÜGBARE THEMES (aus TOML):
261let status = ThemeManager::show_status();
262// Result: "Current theme: DARK\nAvailable themes: blue, dark, light, matrix"
263*/