rush_sync_server/commands/theme/
command.rs1use super::ThemeSystem;
2use crate::commands::command::Command;
3use crate::core::prelude::*;
4use std::future::Future;
5use std::pin::Pin;
6
7#[derive(Debug)]
8pub struct ThemeCommand {
9 theme_system: std::sync::Mutex<Option<ThemeSystem>>,
10}
11
12impl ThemeCommand {
13 pub fn new() -> Self {
14 Self {
15 theme_system: std::sync::Mutex::new(None),
16 }
17 }
18
19 fn get_or_init_theme_system(&self) -> Result<std::sync::MutexGuard<Option<ThemeSystem>>> {
20 let mut guard = self.theme_system.lock().unwrap_or_else(|poisoned| {
21 log::warn!("Recovered from poisoned mutex");
22 poisoned.into_inner()
23 });
24 if guard.is_none() {
25 *guard = Some(ThemeSystem::load()?);
26 }
27 Ok(guard)
28 }
29}
30
31impl Command for ThemeCommand {
32 fn name(&self) -> &'static str {
33 "theme"
34 }
35
36 fn description(&self) -> &'static str {
37 "Change application theme (live update without restart, loaded from TOML)"
38 }
39
40 fn matches(&self, command: &str) -> bool {
41 command.trim().to_lowercase().starts_with("theme")
42 }
43
44 fn execute_sync(&self, args: &[&str]) -> Result<String> {
45 let mut guard = self.get_or_init_theme_system()?;
46 let theme_system = guard.as_mut().unwrap();
47
48 match args.first() {
49 None => Ok(theme_system.show_status()),
50 Some(&"--help" | &"-h") => Ok(Self::create_help_text(theme_system)),
51 Some(&"debug") => match args.get(1) {
52 Some(&theme_name) => Ok(theme_system.debug_theme_details(theme_name)),
53 None => Ok("❌ Theme name missing. Usage: theme debug <name>".to_string()),
54 },
55 Some(&"preview") => match args.get(1) {
56 Some(&theme_name) => theme_system.preview_theme(theme_name),
57 None => Ok("❌ Theme name missing. Usage: theme preview <name>".to_string()),
58 },
59 Some(&theme_name) => theme_system.change_theme(theme_name),
60 }
61 }
62
63 fn execute_async<'a>(
64 &'a self,
65 args: &'a [&'a str],
66 ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
67 Box::pin(async move { self.execute_sync(args) })
68 }
69
70 fn supports_async(&self) -> bool {
71 true
72 }
73
74 fn priority(&self) -> u8 {
75 65
76 }
77}
78
79impl ThemeCommand {
80 fn create_help_text(theme_system: &ThemeSystem) -> String {
81 let available_themes = theme_system.get_available_names();
82
83 if available_themes.is_empty() {
84 return "❌ Keine Themes verfügbar!\n\n📝 Füge [theme.xyz] Sektionen zur rush.toml hinzu:\n\n[theme.mein_theme]\ninput_text = \"White\"\ninput_bg = \"Black\"\ncursor = \"Green\"\noutput_text = \"Gray\"\noutput_bg = \"Black\"\nprompt_text = \">> \"\nprompt_color = \"Cyan\"\noutput_cursor = \"BLOCK\" # ✅ NEU!\noutput_color = \"LightGreen\" # ✅ NEU!".to_string();
85 }
86
87 let themes_list = available_themes.join(", ");
88
89 format!(
90 "🎨 TOML-Theme Commands (Live Update - Geladen aus rush.toml!):\n\
91 theme Show available TOML-themes\n\
92 theme <name> Select theme: {}\n\
93 theme preview <name> Preview theme colors + cursor config ✅ NEW!\n\
94 theme -h Show this help\n\n\
95 ✨ Alle Themes werden LIVE aus [theme.*] Sektionen der rush.toml geladen!\n\
96 🎯 NEU: Cursor-Konfiguration per output_cursor + output_color!\n\
97 📁 Füge beliebige [theme.dein_name] Sektionen hinzu für neue Themes\n\
98 🔄 Änderungen werden sofort angewendet (kein Restart nötig)\n\n\
99 🎛️ Cursor-Optionen:\n\
100 • output_cursor: BLOCK, PIPE, UNDERSCORE\n\
101 • output_color: Jede unterstützte Farbe (White, Green, etc.)",
102 themes_list
103 )
104 }
105}
106
107impl Default for ThemeCommand {
108 fn default() -> Self {
109 Self::new()
110 }
111}