返回 CodeWhale
tui_palettes.rs
根目录 / vendor / codewhale-ratatui / src / gallery / tui_palettes.rs
1 //! All the palettes available in Codewhale's terminal theme picker.
2
3 use ratatui::{
4 buffer::Buffer,
5 layout::Rect,
6 style::{Modifier, Style},
7 text::Line,
8 };
9
10 use super::Entry;
11 use crate::{Caps, Paint, Role, Theme, TuiInk, TuiPalette, WorkbarPanel, detect::Appearance, text};
12
13 /// Known terminal grounds use the preset's appearance; an unknown ground
14 /// continues to use the host's conservative capability fallback.
15 pub(crate) fn theme_for(palette: TuiPalette, input: &Theme) -> Theme {
16 let caps = input.caps();
17 Theme::new(Caps {
18 appearance: if caps.appearance == Appearance::Unknown {
19 Appearance::Unknown
20 } else if palette.light() {
21 Appearance::Light
22 } else {
23 Appearance::Dark
24 },
25 ..caps
26 })
27 .tui_palette(palette)
28 }
29
30 pub(crate) fn palette_for_name(name: &str) -> Option<TuiPalette> {
31 let name = name.strip_prefix("tui-theme-")?;
32 TuiPalette::ALL
33 .into_iter()
34 .find(|palette| palette.name() == name)
35 }
36
37 fn band(area: Rect, top: u16, height: u16) -> Rect {
38 let top = top.min(area.height);
39 Rect::new(
40 area.x,
41 area.y.saturating_add(top),
42 area.width,
43 height.min(area.height.saturating_sub(top)),
44 )
45 }
46
47 fn row(area: Rect, buf: &mut Buffer, label: &str, style: Style, theme: &Theme) {
48 let area = area.intersection(buf.area);
49 if area.is_empty() {
50 return;
51 }
52 let label = text::display_safe(label);
53 let label = text::truncate_words(&label, usize::from(area.width), theme.ascii());
54 buf.set_line(area.x, area.y, &Line::styled(label, style), area.width);
55 }
56
57 fn clear(area: Rect, buf: &mut Buffer, ground: Role, theme: &Theme) {
58 for y in area.y..area.bottom() {
59 row(
60 Rect::new(area.x, y, area.width, 1),
61 buf,
62 &" ".repeat(usize::from(area.width)),
63 theme.fg(Role::Foreground).patch(theme.bg(ground)),
64 theme,
65 );
66 }
67 }
68
69 fn columns(area: Rect, count: usize) -> Vec<Rect> {
70 (0..count)
71 .map(|index| {
72 let left = u32::from(area.width) * index as u32 / count as u32;
73 let right = u32::from(area.width) * (index as u32 + 1) / count as u32;
74 Rect::new(
75 area.x.saturating_add(left as u16),
76 area.y,
77 (right - left) as u16,
78 area.height,
79 )
80 })
81 .collect()
82 }
83
84 fn inks(area: Rect, buf: &mut Buffer, labels: &[(&str, Style)], theme: &Theme) {
85 for (column, (label, style)) in columns(area, labels.len()).into_iter().zip(labels) {
86 let inset = u16::from(column.width > 2);
87 row(
88 Rect {
89 x: column.x.saturating_add(inset),
90 width: column.width.saturating_sub(inset * 2),
91 ..column
92 },
93 buf,
94 label,
95 *style,
96 theme,
97 );
98 }
99 }
100
101 pub(crate) fn sample(palette: TuiPalette, area: Rect, buf: &mut Buffer, input: &Theme) {
102 let area = area.intersection(buf.area);
103 if area.is_empty() {
104 return;
105 }
106 let theme = theme_for(palette, input);
107 clear(area, buf, Role::Background, &theme);
108 clear(band(area, 0, 2), buf, Role::Sidebar, &theme);
109 row(
110 band(area, 0, 1),
111 buf,
112 &format!("Codewhale / {}", palette.name()),
113 theme.fg(Role::Foreground).add_modifier(Modifier::BOLD),
114 &theme,
115 );
116 row(
117 band(area, 1, 1),
118 buf,
119 if palette.light() {
120 "Light theme"
121 } else {
122 "Dark theme"
123 },
124 theme.fg(Role::Muted),
125 &theme,
126 );
127
128 row(
129 band(area, 3, 1),
130 buf,
131 "Build with the colors already in Codewhale.",
132 theme.fg(Role::Foreground),
133 &theme,
134 );
135 for (column, (label, ground)) in columns(band(area, 5, 3), 5).into_iter().zip([
136 ("Stage", Role::Background),
137 ("Panel", Role::Surface),
138 ("Raised", Role::Hover),
139 ("Chrome", Role::Sidebar),
140 ("Selected", Role::Selected),
141 ]) {
142 clear(column, buf, ground, &theme);
143 let inset = u16::from(column.width > 2);
144 row(
145 Rect {
146 x: column.x.saturating_add(inset),
147 y: column.y.saturating_add(u16::from(column.height > 1)),
148 width: column.width.saturating_sub(inset * 2),
149 height: u16::from(column.height > 0),
150 },
151 buf,
152 label,
153 theme.fg(Role::Foreground).patch(theme.bg(ground)),
154 &theme,
155 );
156 }
157 inks(
158 band(area, 10, 1),
159 buf,
160 &[
161 ("Working", theme.tui_ink(TuiInk::Working)),
162 ("Completed", theme.tui_ink(TuiInk::Success)),
163 ("Needs you", theme.tui_ink(TuiInk::Warning)),
164 ("Failed", theme.fg(Role::Danger)),
165 ],
166 &theme,
167 );
168 inks(
169 band(area, 12, 1),
170 buf,
171 &[
172 ("Work", theme.tui_ink(TuiInk::ModeWork)),
173 ("Plan", theme.tui_ink(TuiInk::ModePlan)),
174 ("Operate", theme.tui_ink(TuiInk::ModeOperate)),
175 ],
176 &theme,
177 );
178 inks(
179 band(area, 14, 1),
180 buf,
181 &[
182 ("Ask", theme.tui_ink(TuiInk::PermissionAsk)),
183 ("Auto review", theme.tui_ink(TuiInk::PermissionAutoReview)),
184 ("Full access", theme.tui_ink(TuiInk::PermissionFullAccess)),
185 ],
186 &theme,
187 );
188 row(
189 band(area, 16, 1),
190 buf,
191 "Keep the work visible. Stay in control.",
192 theme.tui_ink(TuiInk::Soft),
193 &theme,
194 );
195 if area.height >= 22 {
196 super::workbar::sample(WorkbarPanel::Tasks)
197 .offset(1)
198 .selected("graph:extract")
199 .paint(band(area, area.height.saturating_sub(5), 5), buf, &theme);
200 }
201 }
202
203 macro_rules! palette_draw {
204 ($name:ident, $palette:ident) => {
205 fn $name(area: Rect, buf: &mut Buffer, theme: &Theme) {
206 sample(TuiPalette::$palette, area, buf, theme);
207 }
208 };
209 }
210 palette_draw!(underwater, Underwater);
211 palette_draw!(underwater_retro, UnderwaterRetro);
212 palette_draw!(shoreline, Shoreline);
213 palette_draw!(shoreline_light, ShorelineLight);
214 palette_draw!(whale, Whale);
215 palette_draw!(whale_light, WhaleLight);
216 palette_draw!(terminal, Terminal);
217 palette_draw!(grayscale, Grayscale);
218 palette_draw!(catppuccin_mocha, CatppuccinMocha);
219 palette_draw!(tokyo_night, TokyoNight);
220 palette_draw!(dracula, Dracula);
221 palette_draw!(gruvbox_dark, GruvboxDark);
222 palette_draw!(claude, Claude);
223 palette_draw!(matrix, Matrix);
224 palette_draw!(solarized_light, SolarizedLight);
225 palette_draw!(uwu, Uwu);
226
227 pub(crate) fn entries() -> Vec<Entry> {
228 [
229 (
230 "tui-theme-underwater",
231 underwater as fn(Rect, &mut Buffer, &Theme),
232 ),
233 ("tui-theme-underwater-retro", underwater_retro),
234 ("tui-theme-shoreline", shoreline),
235 ("tui-theme-shoreline-light", shoreline_light),
236 ("tui-theme-whale", whale),
237 ("tui-theme-whale-light", whale_light),
238 ("tui-theme-terminal", terminal),
239 ("tui-theme-grayscale", grayscale),
240 ("tui-theme-catppuccin-mocha", catppuccin_mocha),
241 ("tui-theme-tokyo-night", tokyo_night),
242 ("tui-theme-dracula", dracula),
243 ("tui-theme-gruvbox-dark", gruvbox_dark),
244 ("tui-theme-claude", claude),
245 ("tui-theme-matrix", matrix),
246 ("tui-theme-solarized-light", solarized_light),
247 ("tui-theme-uwu", uwu),
248 ]
249 .into_iter()
250 .map(|(name, draw)| Entry {
251 name,
252 width: 104,
253 height: 24,
254 draw,
255 })
256 .collect()
257 }
258
258 lines RUST