1use duat::prelude::*;
33
34pub struct Catppuccin {
35 no_background: bool,
36 modifications: Box<dyn Fn(Colors) + Send + Sync + 'static>,
37}
38
39impl Catppuccin {
40 pub fn new() -> Self {
42 Self {
43 no_background: false,
44 modifications: Box::new(|_| {}),
45 }
46 }
47}
48
49impl duat::prelude::Plugin for Catppuccin {
50 fn plug(self, _: &Plugins) {
55 let no_bg = self.no_background;
56 let m = Box::leak(self.modifications);
57 form::add_colorscheme(ColorScheme::latte(m).no_bg(no_bg));
58 form::add_colorscheme(ColorScheme::frappe(m).no_bg(no_bg));
59 form::add_colorscheme(ColorScheme::macchiato(m).no_bg(no_bg));
60 form::add_colorscheme(ColorScheme::mocha(m).no_bg(no_bg));
61 }
62}
63
64impl Catppuccin {
65 pub fn no_background(self) -> Self {
70 Self { no_background: true, ..self }
71 }
72
73 pub fn modify(self, modifications: impl Fn(Colors) + Send + Sync + 'static) -> Self {
88 let modifications = Box::new(move |c| {
89 modifications(c);
90 });
91 Self { modifications, ..self }
92 }
93}
94
95impl Default for Catppuccin {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101#[derive(Default)]
102enum Flavour {
103 Latte,
104 Frappe,
105 Macchiato,
106 #[default]
107 Mocha,
108}
109
110struct ColorScheme {
111 flavour: Flavour,
112 no_background: bool,
113 modifications: &'static (dyn Fn(Colors) + Send + Sync),
114}
115
116impl form::ColorScheme for ColorScheme {
117 fn apply(&self) {
118 let c = match self.flavour {
119 Flavour::Latte => LATTE,
120 Flavour::Frappe => FRAPPE,
121 Flavour::Macchiato => MACCHIATO,
122 Flavour::Mocha => MOCHA,
123 };
124
125 if self.no_background {
126 form::set("default", Form::with(c.text));
127 } else {
128 form::set("default", Form::with(c.text).on(c.base));
129 }
130
131 form::set_many!(
132 ("accent", Form::with(c.rosewater).bold()),
134 ("default.error", Form::with(c.maroon)),
135 ("accent.error", Form::with(c.red).bold()),
136 ("default.warn", Form::with(c.yellow)),
137 ("accent.warn", Form::with(c.peach).bold()),
138 ("default.info", Form::with(c.sapphire)),
139 ("accent.info", Form::with(c.sky).bold()),
140 ("default.debug", Form::with(c.subtext1)),
141 ("accent.debug", Form::with(c.lavender).bold()),
142 ("caret.main", Form::reverse()),
143 ("caret.extra", Form::reverse()),
144 ("selection.main", Form::with(c.base).on(c.overlay1)),
145 ("selection.extra", Form::with(c.base).on(c.overlay0)),
146 ("cloak", Form::with(c.overlay1).on(c.base)),
147 ("character.control", Form::with(c.overlay1)),
148 ("linenum.main", Form::with(c.yellow)),
150 ("linenum.wrapped", Form::with(c.teal)),
151 ("file", Form::with(c.yellow)),
152 ("selections", Form::with(c.blue)),
153 ("coord", Form::with(c.peach)),
154 ("separator", "punctuation.delimiter"),
155 ("mode", Form::with(c.green)),
156 ("terminal.border", Form::with(c.surface0).on(c.base)),
157 ("terminal.frame", Form::with(c.text).on(c.base)),
158 ("notifs.colon", Form::with(c.subtext0)),
159 ("prompt", Form::with(c.green)),
160 ("prompt.colon", Form::with(c.subtext0)),
161 ("default.StatusLine", Form::on(c.surface0)),
162 ("default.LogBook", Form::on(c.surface0)),
163 ("default.VertRule", Form::with(c.surface0)),
164 ("default.LineNumbers", Form::with(c.overlay0)),
165 ("matched_pair", Form::with(c.peach).on(c.surface1).bold()),
166 ("log_book.location", Form::with(c.subtext1)),
167 ("default.Completions", Form::on(c.surface1)),
168 ("selected.Completions", Form::with(c.base).on(c.overlay0)),
169 ("default.WhichKey", Form::with(c.text)),
170 ("key", Form::with(c.peach)),
171 ("key.special", Form::with(c.teal)),
172 ("caret.main.Normal", Form::with(c.base).on(c.text)),
174 ("caret.extra.Normal", Form::with(c.base).on(c.sapphire)),
175 ("caret.main.Insert", Form::with(c.base).on(c.mauve)),
176 ("caret.extra.Insert", Form::with(c.base).on(c.yellow)),
177 ("param", Form::with(c.lavender)),
178 ("param.flag", Form::with(c.pink)),
179 ("variable", Form::with(c.text)),
181 ("variable.builtin", Form::with(c.peach)),
182 ("variable.member", Form::with(c.lavender)),
183 ("constant", Form::with(c.peach)),
184 ("constant.builtin", Form::with(c.peach)),
185 ("module", Form::with(c.blue).italic()),
186 ("label", Form::with(c.green)),
187 ("string", Form::with(c.green)),
188 ("string.escape", Form::with(c.peach)),
189 ("string.special.path", Form::with(c.sky).underlined()),
190 ("character", Form::with(c.peach)),
191 ("boolean", Form::with(c.peach)),
192 ("number", Form::with(c.peach)),
193 ("type", Form::with(c.yellow).italic()),
194 ("type.builtin", Form::with(c.yellow).reset()),
195 ("attribute", Form::with(c.green)),
196 ("property", Form::with(c.text)),
197 ("function", Form::with(c.blue).reset()),
198 ("function.macro", Form::with(c.lavender).italic()),
199 ("constructor", Form::with(c.peach)),
200 ("operator", Form::with(c.sapphire)),
201 ("keyword", Form::with(c.mauve)),
202 ("punctuation.bracket", Form::with(c.subtext0)),
203 ("punctuation.delimiter", Form::with(c.subtext0)),
204 ("comment", Form::with(c.overlay1)),
205 ("comment.documentation", Form::with(c.overlay1).bold()),
206 ("markup", Form::new()),
207 ("markup.strong", Form::with(c.maroon).bold()),
208 ("markup.italic", Form::with(c.maroon).italic()),
209 ("markup.strikethrough", Form::new().crossed_out()),
210 ("markup.underline", Form::underlined()),
211 ("markup.heading", Form::with(c.blue).bold()),
212 ("markup.math", Form::with(c.yellow)),
213 ("markup.quote", Form::with(c.maroon).bold()),
214 ("markup.environment", Form::with(c.pink)),
215 ("markup.environment.name", Form::with(c.blue)),
216 ("markup.link", Form::with(c.lavender).underlined()),
217 ("markup.raw", Form::with(c.teal)),
218 ("markup.list", Form::with(c.yellow)),
219 ("markup.list.checked", Form::with(c.green)),
220 ("markup.list.unchecked", Form::with(c.overlay1)),
221 ("diff.plus", Form::with(c.green)),
222 ("diff.delta", Form::with(c.blue)),
223 ("diff.delta.renamed", Form::with(c.yellow)),
224 ("diff.minus", Form::with(c.red)),
225 );
226
227 (self.modifications)(c)
228 }
229
230 fn name(&self) -> &'static str {
231 match self.flavour {
232 Flavour::Latte => "catppuccin-latte",
233 Flavour::Frappe => "catppuccin-frappe",
234 Flavour::Macchiato => "catppuccin-macchiato",
235 Flavour::Mocha => "catppuccin-mocha",
236 }
237 }
238}
239
240impl ColorScheme {
241 fn latte(modifications: &'static (dyn Fn(Colors) + Send + Sync)) -> Self {
243 Self {
244 flavour: Flavour::Latte,
245 no_background: false,
246 modifications,
247 }
248 }
249
250 fn frappe(modifications: &'static (dyn Fn(Colors) + Send + Sync)) -> Self {
252 Self {
253 flavour: Flavour::Frappe,
254 no_background: false,
255 modifications,
256 }
257 }
258
259 fn macchiato(modifications: &'static (dyn Fn(Colors) + Send + Sync)) -> Self {
262 Self {
263 flavour: Flavour::Macchiato,
264 no_background: false,
265 modifications,
266 }
267 }
268
269 fn mocha(modifications: &'static (dyn Fn(Colors) + Send + Sync)) -> Self {
271 Self {
272 flavour: Flavour::Mocha,
273 no_background: false,
274 modifications,
275 }
276 }
277
278 fn no_bg(self, bool: bool) -> Self {
283 Self { no_background: bool, ..self }
284 }
285}
286
287pub struct Colors {
288 pub rosewater: &'static str,
289 pub flamingo: &'static str,
290 pub pink: &'static str,
291 pub mauve: &'static str,
292 pub red: &'static str,
293 pub maroon: &'static str,
294 pub peach: &'static str,
295 pub yellow: &'static str,
296 pub green: &'static str,
297 pub teal: &'static str,
298 pub sky: &'static str,
299 pub sapphire: &'static str,
300 pub blue: &'static str,
301 pub lavender: &'static str,
302 pub text: &'static str,
303 pub subtext1: &'static str,
304 pub subtext0: &'static str,
305 pub overlay2: &'static str,
306 pub overlay1: &'static str,
307 pub overlay0: &'static str,
308 pub surface2: &'static str,
309 pub surface1: &'static str,
310 pub surface0: &'static str,
311 pub base: &'static str,
312 pub mantle: &'static str,
313 pub crust: &'static str,
314}
315
316const LATTE: Colors = Colors {
317 rosewater: "#dc8a78",
318 flamingo: "#dd7878",
319 pink: "#ea76cb",
320 mauve: "#8839ef",
321 red: "#d20f39",
322 maroon: "#e64553",
323 peach: "#fe640b",
324 yellow: "#df8e1d",
325 green: "#40a02b",
326 teal: "#179299",
327 sky: "#04a5e5",
328 sapphire: "#209fb5",
329 blue: "#1e66f5",
330 lavender: "#7287fd",
331 text: "#4c4f69",
332 subtext1: "#5c5f77",
333 subtext0: "#6c6f85",
334 overlay2: "#7c7f93",
335 overlay1: "#8c8fa1",
336 overlay0: "#9ca0b0",
337 surface2: "#acb0be",
338 surface1: "#bcc0cc",
339 surface0: "#ccd0da",
340 base: "#eff1f5",
341 mantle: "#e6e9ef",
342 crust: "#dce0e8",
343};
344const FRAPPE: Colors = Colors {
345 rosewater: "#f2d5cf",
346 flamingo: "#eebebe",
347 pink: "#f4b8e4",
348 mauve: "#ca9ee6",
349 red: "#e78284",
350 maroon: "#ea999c",
351 peach: "#ef9f76",
352 yellow: "#e5c890",
353 green: "#a6d189",
354 teal: "#81c8be",
355 sky: "#99d1db",
356 sapphire: "#85c1dc",
357 blue: "#8caaee",
358 lavender: "#babbf1",
359 text: "#c6d0f5",
360 subtext1: "#b5bfe2",
361 subtext0: "#a5adce",
362 overlay2: "#949cbb",
363 overlay1: "#838ba7",
364 overlay0: "#737994",
365 surface2: "#626880",
366 surface1: "#51576d",
367 surface0: "#414559",
368 base: "#303446",
369 mantle: "#292c3c",
370 crust: "#232634",
371};
372
373const MACCHIATO: Colors = Colors {
374 rosewater: "#f4dbd6",
375 flamingo: "#f0c6c6",
376 pink: "#f5bde6",
377 mauve: "#c6a0f6",
378 red: "#ed8796",
379 maroon: "#ee99a0",
380 peach: "#f5a97f",
381 yellow: "#eed49f",
382 green: "#a6da95",
383 teal: "#8bd5ca",
384 sky: "#91d7e3",
385 sapphire: "#7dc4e4",
386 blue: "#8aadf4",
387 lavender: "#b7bdf8",
388 text: "#cad3f5",
389 subtext1: "#b8c0e0",
390 subtext0: "#a5adcb",
391 overlay2: "#939ab7",
392 overlay1: "#8087a2",
393 overlay0: "#6e738d",
394 surface2: "#5b6078",
395 surface1: "#494d64",
396 surface0: "#363a4f",
397 base: "#24273a",
398 mantle: "#1e2030",
399 crust: "#181926",
400};
401
402const MOCHA: Colors = Colors {
403 rosewater: "#f5e0dc",
404 flamingo: "#f2cdcd",
405 pink: "#f5c2e7",
406 mauve: "#cba6f7",
407 red: "#f38ba8",
408 maroon: "#eba0ac",
409 peach: "#fab387",
410 yellow: "#f9e2af",
411 green: "#a6e3a1",
412 teal: "#94e2d5",
413 sky: "#89dceb",
414 sapphire: "#74c7ec",
415 blue: "#89b4fa",
416 lavender: "#b4befe",
417 text: "#cdd6f4",
418 subtext1: "#bac2de",
419 subtext0: "#a6adc8",
420 overlay2: "#9399b2",
421 overlay1: "#7f849c",
422 overlay0: "#6c7086",
423 surface2: "#585b70",
424 surface1: "#45475a",
425 surface0: "#313244",
426 base: "#1e1e2e",
427 mantle: "#181825",
428 crust: "#11111b",
429};