rush_sync_server/commands/theme/
manager.rs1use super::themes::TomlThemeLoader;
6use crate::core::prelude::*;
7
8pub struct ThemeManager;
9
10impl ThemeManager {
11 pub fn show_status() -> String {
13 let current_theme =
14 Self::get_current_theme_from_config().unwrap_or_else(|| "dark".to_string());
15
16 let available_themes = match Self::get_available_themes_sync() {
18 Ok(themes) => themes.join(", "),
19 Err(_) => "dark, light, matrix, blue".to_string(), };
21
22 format!(
23 "Current theme: {}\nAvailable themes: {}",
24 current_theme.to_uppercase(),
25 available_themes
26 )
27 }
28
29 pub async fn change_theme(theme_name: &str) -> Result<String> {
31 let theme_name_lower = theme_name.to_lowercase();
32
33 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 match crate::core::config::Config::load_with_messages(false).await {
44 Ok(mut config) => {
45 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 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 pub fn change_theme_sync(theme_name: &str) -> Result<String> {
75 let theme_name_lower = theme_name.to_lowercase();
76
77 if !TomlThemeLoader::theme_exists_sync(&theme_name_lower) {
79 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 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 let theme_name_clone = theme_name_lower.clone();
100 tokio::spawn(async move {
101 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 pub fn preview_theme(theme_name: &str) -> Result<String> {
127 let theme_name_lower = theme_name.to_lowercase();
128
129 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 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 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; }
170 }
171 }
172 }
173
174 log::debug!("No config file found, using default theme");
176 Some("dark".to_string())
177 }
178
179 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 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 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 Ok(vec![
236 "dark".to_string(),
237 "light".to_string(),
238 "green".to_string(),
239 "blue".to_string(),
240 ])
241 }
242}
243
244