03_theme_switcher/
03_theme_switcher.rs1use crossterm::event::KeyCode;
8use telex::prelude::*;
9use telex::theme::{current_theme, set_theme, supports_true_color, terminal_name, Theme};
10
11telex::require_api!(0, 2);
12
13fn main() {
14 telex::run(App).unwrap();
15}
16
17struct App;
18
19impl Component for App {
20 fn render(&self, cx: Scope) -> View {
21 let selected = state!(cx, || 0usize);
22 let show_help = state!(cx, || false);
23
24 cx.use_command(
26 KeyBinding::key(KeyCode::F(1)),
27 with!(show_help => move || show_help.update(|v| *v = !*v)),
28 );
29
30 let theme_names = vec![
31 "Dark".to_string(),
32 "Light".to_string(),
33 "Nord".to_string(),
34 "Monokai".to_string(),
35 "Catppuccin Mocha".to_string(),
36 "Catppuccin Latte".to_string(),
37 "Dracula".to_string(),
38 "Gruvbox Dark".to_string(),
39 "Solarized Dark".to_string(),
40 "Rosé Pine".to_string(),
41 "Tokyo Night".to_string(),
42 "HaX0R Blue".to_string(),
43 "HaX0R Green".to_string(),
44 "HaX0R Red".to_string(),
45 ];
46
47 let on_select = with!(selected => move |idx: usize| {
49 selected.set(idx);
50 let theme = match idx {
51 0 => Theme::dark(),
52 1 => Theme::light(),
53 2 => Theme::nord(),
54 3 => Theme::monokai(),
55 4 => Theme::catppuccin_mocha(),
56 5 => Theme::catppuccin_latte(),
57 6 => Theme::dracula(),
58 7 => Theme::gruvbox_dark(),
59 8 => Theme::solarized_dark(),
60 9 => Theme::rose_pine(),
61 10 => Theme::tokyo_night(),
62 11 => Theme::hax0r_blue(),
63 12 => Theme::hax0r_green(),
64 _ => Theme::hax0r_red(),
65 };
66 set_theme(theme);
67 });
68
69 let theme = current_theme();
70 let true_color = supports_true_color();
71
72 let mut stack = View::vstack();
73
74 if !true_color {
76 let term = terminal_name().unwrap_or_else(|| "Unknown".to_string());
77 stack = stack
78 .child(
79 View::styled_text(format!("Warning: {} doesn't support true color", term))
80 .color(theme.warning)
81 .bold()
82 .build(),
83 )
84 .child(
85 View::styled_text("Only 'Dark' and 'Light' themes will display correctly")
86 .color(theme.muted)
87 .build(),
88 )
89 .child(View::gap(1));
90 }
91
92 stack
93 .child(
94 View::styled_text("Theme Switcher")
95 .color(theme.primary)
96 .bold()
97 .build(),
98 )
99 .child(
100 View::styled_text("Select a theme from the list")
101 .color(theme.muted)
102 .italic()
103 .build(),
104 )
105 .child(View::gap(1))
106 .child(
107 View::hstack()
108 .spacing(2)
109 .child(
110 View::boxed()
111 .border(true)
112 .min_width(25)
113 .child(
114 View::list()
115 .items(theme_names)
116 .selected(selected.get())
117 .on_select(on_select)
118 .build(),
119 )
120 .build(),
121 )
122 .child(
123 View::boxed()
124 .border(true)
125 .padding(1)
126 .child(
127 View::vstack()
128 .child(View::styled_text("Preview").bold().build())
129 .child(View::gap(1))
130 .child(
131 View::hstack()
132 .child(
133 View::styled_text("Primary")
134 .color(theme.primary)
135 .build(),
136 )
137 .child(View::text(" "))
138 .child(
139 View::styled_text("Secondary")
140 .color(theme.secondary)
141 .build(),
142 )
143 .build(),
144 )
145 .child(
146 View::hstack()
147 .child(
148 View::styled_text("Muted")
149 .color(theme.muted)
150 .build(),
151 )
152 .child(View::text(" "))
153 .child(
154 View::styled_text("Success")
155 .color(theme.success)
156 .build(),
157 )
158 .build(),
159 )
160 .child(
161 View::hstack()
162 .child(
163 View::styled_text("Warning")
164 .color(theme.warning)
165 .build(),
166 )
167 .child(View::text(" "))
168 .child(
169 View::styled_text("Error")
170 .color(theme.error)
171 .build(),
172 )
173 .build(),
174 )
175 .build(),
176 )
177 .build(),
178 )
179 .build(),
180 )
181 .child(View::gap(1))
182 .child(
183 View::styled_text("↑/↓ select • F1 help • Ctrl+Q quit")
184 .color(theme.muted)
185 .build(),
186 )
187 .child(
188 View::modal()
189 .visible(show_help.get())
190 .title("Example 03: Theme Switcher")
191 .on_dismiss(with!(show_help => move || show_help.set(false)))
192 .child(
193 View::vstack()
194 .child(View::styled_text("What you're seeing").bold().build())
195 .child(View::text("• Built-in theme system with 14 themes"))
196 .child(View::text("• View::list() for selection UI"))
197 .child(View::text("• Live preview as you navigate"))
198 .child(View::gap(1))
199 .child(View::styled_text("Key concepts").bold().build())
200 .child(View::text("• current_theme() gets active theme colors"))
201 .child(View::text("• set_theme() changes theme globally"))
202 .child(View::text(
203 "• Themes provide semantic colors (primary, error, etc.)",
204 ))
205 .child(View::gap(1))
206 .child(View::styled_text("Try this").bold().build())
207 .child(View::text(
208 "• Navigate with ↑/↓ to see themes change instantly",
209 ))
210 .child(View::text(
211 "• Notice the preview panel updates with theme colors",
212 ))
213 .child(View::gap(1))
214 .child(View::styled_text("Next up").bold().build())
215 .child(View::text("→ 04_timer: streaming data without interaction"))
216 .child(View::gap(1))
217 .child(View::styled_text("Press Escape to close").dim().build())
218 .build(),
219 )
220 .build(),
221 )
222 .build()
223 }
224}