rat_theme4/
lib.rs

1//!
2//! SalsaTheme provides a styling system for ratatui apps.
3//!
4//! It has a simple flat naming scheme.
5//!
6//! But it can store
7//! * [ratatui Style](ratatui::style::Style)
8//! * composite styles as used by [rat-widget](rat_widget).
9//!   eg [CheckboxStyle](rat_widget::checkbox::CheckboxStyle)
10//! * practically anything else.
11//!
12//! ## Naming styles
13//!
14//! * It has an extension trait for [Style](ratatui::style::Style) that
15//!   adds constants for known styles. In the same manner you can add your
16//!   application specific styles and have them with code completion.
17//!
18//! * For [rat-widget](rat_widget) composite style it defines an anchor struct
19//!   [WidgetStyle] that performs the same purpose.
20//!
21//! ## Usage
22//!
23//! ```rust
24//! # use ratatui::buffer::Buffer;
25//! # use ratatui::layout::Rect;
26//! # use ratatui::style::Style;
27//! # use ratatui::widgets::StatefulWidget;
28//! # use rat_theme4::theme::{SalsaTheme};
29//! # use rat_theme4::{StyleName, WidgetStyle};
30//! # use rat_theme4::palettes::core::BLACKOUT;
31//! # use rat_widget::checkbox::{Checkbox, CheckboxState, CheckboxStyle};
32//! # let theme = SalsaTheme::default();
33//! # let area = Rect::default();
34//! # let mut buf = Buffer::default();
35//! # let buf = &mut buf;
36//! # let mut state = CheckboxState::default();
37//!
38//! // ratatui Style
39//! let s: Style = theme.style(Style::SELECT);
40//!
41//! // composite style
42//! Checkbox::new()
43//!     .styles(theme.style(WidgetStyle::CHECKBOX))
44//!     .render(area, buf, &mut state);
45//! ```
46//!
47//! ## Palette
48//!
49//! Palette holds the color definitions and aliases for the
50//! colors. This is the part of the theme that can be persisted.
51//! It can be stored/loaded from file or put into a `static`.
52//!
53//! With [create_palette_theme] the theme can be reconstructed.
54//!
55
56use crate::palette::{Colors, Palette};
57use crate::palettes::shell;
58use crate::theme::SalsaTheme;
59use ratatui::style::{Color, Style};
60use std::borrow::Cow;
61use std::error::Error;
62use std::fmt::{Display, Formatter};
63use std::io;
64use std::sync::atomic::{AtomicBool, Ordering};
65
66pub mod palette;
67pub mod theme;
68
69/// Currently shipped palettes.
70pub mod palettes {
71    pub mod core;
72    pub mod dark;
73    pub mod light;
74    pub mod shell;
75}
76
77pub mod themes {
78    mod dark;
79    mod fallback;
80    mod light;
81    mod shell;
82
83    /// Creates a `dark` theme.
84    pub use dark::create_dark;
85    /// Creates a 'light' theme.
86    pub use light::create_light;
87    /// Creates a `shell` theme. This uses the dark palettes,
88    /// but sets almost no backgrounds. Instead, it lets the
89    /// terminal background shine.
90    pub use shell::create_shell;
91
92    /// Create the `fallback` theme.
93    /// This is more for testing widgets than anything else.
94    /// It just uses `Default::default()` for any style.
95    /// This helps to check if a widget is still functional
96    /// if no styling is applied.
97    pub use fallback::create_fallback;
98}
99
100/// Anchor struct for the names of composite styles used
101/// by rat-widget's.
102///
103/// Use as
104/// ```rust
105/// # use ratatui::style::Style;
106/// # use rat_theme4::theme::{SalsaTheme};
107/// # use rat_theme4::{ StyleName, WidgetStyle};
108/// # use rat_theme4::palettes::core::BLACKOUT;
109/// # use rat_widget::checkbox::CheckboxStyle;
110/// # let theme = SalsaTheme::default();
111///
112/// let s: CheckboxStyle = theme.style(WidgetStyle::CHECKBOX);
113/// ```
114/// or more likely
115/// ```rust
116/// # use ratatui::buffer::Buffer;
117/// # use ratatui::layout::Rect;
118/// # use ratatui::style::Style;
119/// # use ratatui::widgets::StatefulWidget;
120/// # use rat_theme4::theme::{SalsaTheme};
121/// # use rat_theme4::{ StyleName, WidgetStyle};
122/// # use rat_theme4::palettes::core::BLACKOUT;
123/// # use rat_widget::checkbox::{Checkbox, CheckboxState, CheckboxStyle};
124/// # let theme = SalsaTheme::default();
125/// # let area = Rect::default();
126/// # let mut buf = Buffer::default();
127/// # let buf = &mut buf;
128/// # let mut state = CheckboxState::default();
129///
130/// Checkbox::new()
131///     .styles(theme.style(WidgetStyle::CHECKBOX))
132///     .render(area, buf, &mut state);
133/// ```
134pub struct WidgetStyle;
135
136impl WidgetStyle {
137    pub const BUTTON: &'static str = "button";
138    pub const CALENDAR: &'static str = "calendar";
139    pub const CHECKBOX: &'static str = "checkbox";
140    pub const CHOICE: &'static str = "choice";
141    pub const CLIPPER: &'static str = "clipper";
142    #[cfg(feature = "color-input")]
143    pub const COLOR_INPUT: &'static str = "color-input";
144    pub const COMBOBOX: &'static str = "combobox";
145    pub const DIALOG_FRAME: &'static str = "dialog-frame";
146    pub const FILE_DIALOG: &'static str = "file-dialog";
147    pub const FORM: &'static str = "form";
148    pub const LINE_NR: &'static str = "line-nr";
149    pub const LIST: &'static str = "list";
150    pub const MENU: &'static str = "menu";
151    pub const MONTH: &'static str = "month";
152    pub const MSG_DIALOG: &'static str = "msg-dialog";
153    pub const PARAGRAPH: &'static str = "paragraph";
154    pub const RADIO: &'static str = "radio";
155    pub const SCROLL: &'static str = "scroll";
156    pub const SCROLL_DIALOG: &'static str = "scroll.dialog";
157    pub const SCROLL_POPUP: &'static str = "scroll.popup";
158    pub const SHADOW: &'static str = "shadow";
159    pub const SLIDER: &'static str = "slider";
160    pub const SPLIT: &'static str = "split";
161    pub const STATUSLINE: &'static str = "statusline";
162    pub const TABBED: &'static str = "tabbed";
163    pub const TABLE: &'static str = "table";
164    pub const TEXT: &'static str = "text";
165    pub const TEXTAREA: &'static str = "textarea";
166    pub const TEXTVIEW: &'static str = "textview";
167    pub const VIEW: &'static str = "view";
168}
169
170/// Extension trait for [Style](ratatui::style::Style) that defines
171/// some standard names used by rat-theme/rat-widget
172///
173/// Use as
174/// ```rust
175/// # use ratatui::style::Style;
176/// # use rat_theme4::theme::{SalsaTheme};
177/// # use rat_theme4::{ StyleName, WidgetStyle};
178/// # use rat_theme4::palettes::core::BLACKOUT;
179/// # let theme = SalsaTheme::default();
180///
181/// let s: Style = theme.style(Style::INPUT);
182/// ```
183pub trait StyleName {
184    const LABEL_FG: &'static str = "label-fg";
185    const INPUT: &'static str = "input";
186    const INPUT_FOCUS: &'static str = "text-focus";
187    const INPUT_SELECT: &'static str = "text-select";
188    const FOCUS: &'static str = "focus";
189    const SELECT: &'static str = "select";
190    const DISABLED: &'static str = "disabled";
191    const INVALID: &'static str = "invalid";
192
193    const TITLE: &'static str = "title";
194    const HEADER: &'static str = "header";
195    const FOOTER: &'static str = "footer";
196
197    const HOVER: &'static str = "hover";
198    const SHADOWS: &'static str = "shadows";
199
200    const WEEK_HEADER_FG: &'static str = "week-header-fg";
201    const MONTH_HEADER_FG: &'static str = "month-header-fg";
202
203    const KEY_BINDING: &'static str = "key-binding";
204    const BUTTON_BASE: &'static str = "button-base";
205    const MENU_BASE: &'static str = "menu-base";
206    const STATUS_BASE: &'static str = "status-base";
207
208    const CONTAINER_BASE: &'static str = "container-base";
209    const CONTAINER_BORDER_FG: &'static str = "container-border-fg";
210    const CONTAINER_ARROW_FG: &'static str = "container-arrows-fg";
211
212    const DOCUMENT_BASE: &'static str = "document-base";
213    const DOCUMENT_BORDER_FG: &'static str = "document-border-fg";
214    const DOCUMENT_ARROW_FG: &'static str = "document-arrows-fg";
215
216    const POPUP_BASE: &'static str = "popup-base";
217    const POPUP_BORDER_FG: &'static str = "popup-border-fg";
218    const POPUP_ARROW_FG: &'static str = "popup-arrow-fg";
219
220    const DIALOG_BASE: &'static str = "dialog-base";
221    const DIALOG_BORDER_FG: &'static str = "dialog-border-fg";
222    const DIALOG_ARROW_FG: &'static str = "dialog-arrow-fg";
223}
224impl StyleName for Style {}
225
226///
227/// Extension trait for [Color](ratatui::style::Color) that defines
228/// standard names used by rat-theme to define color-aliases.
229///
230/// Use as
231/// ```rust
232/// # use ratatui::style::{Style, Color};
233/// # use rat_theme4::theme::{SalsaTheme};
234/// # use rat_theme4::RatWidgetColor;
235/// # let theme = SalsaTheme::default();
236///
237/// let c: Color = theme.p.color_alias(Color::LABEL_FG);
238/// ```
239pub trait RatWidgetColor {
240    const LABEL_FG: &'static str = "label.fg";
241    const INPUT_BG: &'static str = "input.bg";
242    const INPUT_FOCUS_BG: &'static str = "input-focus.bg";
243    const INPUT_SELECT_BG: &'static str = "input-select.bg";
244    const FOCUS_BG: &'static str = "focus.bg";
245    const SELECT_BG: &'static str = "select.bg";
246    const DISABLED_BG: &'static str = "disabled.bg";
247    const INVALID_BG: &'static str = "invalid.bg";
248
249    const TITLE_FG: &'static str = "title.fg";
250    const TITLE_BG: &'static str = "title.bg";
251    const HEADER_FG: &'static str = "header.fg";
252    const HEADER_BG: &'static str = "header.bg";
253    const FOOTER_FG: &'static str = "footer.fg";
254    const FOOTER_BG: &'static str = "footer.bg";
255
256    const HOVER_BG: &'static str = "hover.bg";
257    const BUTTON_BASE_BG: &'static str = "button-base.bg";
258    const KEY_BINDING_BG: &'static str = "key-binding.bg";
259    const MENU_BASE_BG: &'static str = "menu-base.bg";
260    const STATUS_BASE_BG: &'static str = "status-base.bg";
261    const SHADOW_BG: &'static str = "shadow.bg";
262
263    const WEEK_HEADER_FG: &'static str = "week-header.fg";
264    const MONTH_HEADER_FG: &'static str = "month-header.fg";
265
266    const CONTAINER_BASE_BG: &'static str = "container-base.bg";
267    const CONTAINER_BORDER_FG: &'static str = "container-border.fg";
268    const CONTAINER_ARROW_FG: &'static str = "container-arrow.fg";
269    const DOCUMENT_BASE_BG: &'static str = "document-base.bg";
270    const DOCUMENT_BORDER_FG: &'static str = "document-border.fg";
271    const DOCUMENT_ARROW_FG: &'static str = "document-arrow.fg";
272    const POPUP_BASE_BG: &'static str = "popup-base.bg";
273    const POPUP_BORDER_FG: &'static str = "popup-border.fg";
274    const POPUP_ARROW_FG: &'static str = "popup-arrow.fg";
275    const DIALOG_BASE_BG: &'static str = "dialog-base.bg";
276    const DIALOG_BORDER_FG: &'static str = "dialog-border.fg";
277    const DIALOG_ARROW_FG: &'static str = "dialog-arrow.fg";
278}
279impl RatWidgetColor for Color {}
280
281static LOG_DEFINES: AtomicBool = AtomicBool::new(false);
282
283/// Log style definition.
284/// May help debugging styling problems ...
285pub fn log_style_define(log: bool) {
286    LOG_DEFINES.store(log, Ordering::Release);
287}
288
289fn is_log_style_define() -> bool {
290    LOG_DEFINES.load(Ordering::Acquire)
291}
292
293#[derive(Debug)]
294pub struct LoadPaletteErr(u8);
295
296impl Display for LoadPaletteErr {
297    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
298        write!(f, "load palette failed: {}", self.0)
299    }
300}
301
302impl Error for LoadPaletteErr {}
303
304/// Stora a Palette as a .pal file.
305pub fn store_palette(pal: &Palette, mut buf: impl io::Write) -> Result<(), io::Error> {
306    writeln!(buf, "[theme]")?;
307    writeln!(buf, "name={}", pal.theme_name)?;
308    writeln!(buf, "theme={}", pal.theme)?;
309    writeln!(buf)?;
310    writeln!(buf, "[palette]")?;
311    writeln!(buf, "name={}", pal.name)?;
312    writeln!(buf, "docs={}", pal.doc.replace('\n', "\\n"))?;
313    writeln!(buf, "generator={}", pal.generator)?;
314    writeln!(buf,)?;
315    writeln!(buf, "[color]")?;
316    for c in Colors::array() {
317        writeln!(
318            buf,
319            "{}={}, {}",
320            *c, pal.color[*c as usize][0], pal.color[*c as usize][3]
321        )?;
322    }
323    writeln!(buf,)?;
324    writeln!(buf, "[reference]")?;
325    for (r, i) in pal.aliased.as_ref() {
326        writeln!(buf, "{}={}", r, i)?;
327    }
328    Ok(())
329}
330
331/// Load a .pal file as a Palette.
332pub fn load_palette(mut r: impl io::Read) -> Result<Palette, io::Error> {
333    let mut buf = String::new();
334    r.read_to_string(&mut buf)?;
335
336    enum S {
337        Start,
338        Theme,
339        Palette,
340        Color,
341        Reference,
342        Fail(u8),
343    }
344
345    let mut pal = Palette::default();
346    let mut dark = 63u8;
347
348    let mut state = S::Start;
349    'm: for l in buf.lines() {
350        let l = l.trim();
351        match state {
352            S::Start => {
353                if l == "[theme]" {
354                    state = S::Theme;
355                } else if l == "[palette]" {
356                    state = S::Palette;
357                } else {
358                    state = S::Fail(1);
359                    break 'm;
360                }
361            }
362            S::Theme => {
363                if l == "[palette]" {
364                    state = S::Palette;
365                } else if l.is_empty() || l.starts_with("#") {
366                    // ok
367                } else if l.starts_with("name") {
368                    if let Some(s) = l.split('=').nth(1) {
369                        pal.theme_name = Cow::Owned(s.trim().to_string());
370                    }
371                } else if l.starts_with("theme") {
372                    if let Some(s) = l.split('=').nth(1) {
373                        pal.theme = Cow::Owned(s.trim().to_string());
374                    }
375                } else {
376                    state = S::Fail(2);
377                    break 'm;
378                }
379            }
380            S::Palette => {
381                if l == "[color]" {
382                    state = S::Color;
383                } else if l.is_empty() || l.starts_with("#") {
384                    // ok
385                } else if l.starts_with("name") {
386                    if let Some(s) = l.split('=').nth(1) {
387                        pal.name = Cow::Owned(s.trim().to_string());
388                    }
389                } else if l.starts_with("docs") {
390                    if let Some(s) = l.split('=').nth(1) {
391                        let doc = s.trim().replace("\\n", "\n");
392                        pal.doc = Cow::Owned(doc);
393                    }
394                } else if l.starts_with("generator") {
395                    if let Some(s) = l.split('=').nth(1) {
396                        pal.generator = Cow::Owned(s.trim().to_string());
397                        if s.starts_with("light-dark") {
398                            if let Some(s) = l.split(':').nth(1) {
399                                dark = s.trim().parse::<u8>().unwrap_or(63);
400                            }
401                        }
402                    }
403                } else if l.starts_with("dark") {
404                    if let Some(s) = l.split('=').nth(1) {
405                        if let Ok(v) = s.trim().parse::<u8>() {
406                            dark = v;
407                        } else {
408                            // skip
409                        }
410                    }
411                } else {
412                    state = S::Fail(3);
413                    break 'm;
414                }
415            }
416            S::Color => {
417                if l == "[reference]" {
418                    state = S::Reference;
419                } else if l.is_empty() || l.starts_with("#") {
420                    // ok
421                } else {
422                    let mut kv = l.split('=');
423                    let cn = if let Some(v) = kv.next() {
424                        let Ok(c) = v.trim().parse::<palette::Colors>() else {
425                            state = S::Fail(4);
426                            break 'm;
427                        };
428                        c
429                    } else {
430                        state = S::Fail(5);
431                        break 'm;
432                    };
433                    let (c0, c3) = if let Some(v) = kv.next() {
434                        let mut vv = v.split(',');
435                        let c0 = if let Some(v) = vv.next() {
436                            let Ok(v) = v.trim().parse::<Color>() else {
437                                state = S::Fail(6);
438                                break 'm;
439                            };
440                            v
441                        } else {
442                            state = S::Fail(7);
443                            break 'm;
444                        };
445                        let c3 = if let Some(v) = vv.next() {
446                            let Ok(v) = v.trim().parse::<Color>() else {
447                                state = S::Fail(8);
448                                break 'm;
449                            };
450                            v
451                        } else {
452                            state = S::Fail(9);
453                            break 'm;
454                        };
455                        (c0, c3)
456                    } else {
457                        state = S::Fail(10);
458                        break 'm;
459                    };
460
461                    if cn == Colors::TextLight || cn == Colors::TextDark {
462                        pal.color[cn as usize] =
463                            Palette::interpolatec2(c0, c3, Color::default(), Color::default())
464                    } else {
465                        pal.color[cn as usize] = Palette::interpolatec(c0, c3, dark);
466                    }
467                }
468            }
469            S::Reference => {
470                let mut kv = l.split('=');
471                let rn = if let Some(v) = kv.next() {
472                    v
473                } else {
474                    state = S::Fail(11);
475                    break 'm;
476                };
477                let ci = if let Some(v) = kv.next() {
478                    if let Ok(ci) = v.parse::<palette::ColorIdx>() {
479                        ci
480                    } else {
481                        state = S::Fail(12);
482                        break 'm;
483                    }
484                } else {
485                    state = S::Fail(13);
486                    break 'm;
487                };
488                pal.add_aliased(rn, ci);
489            }
490            S::Fail(_) => {
491                unreachable!()
492            }
493        }
494    }
495
496    match state {
497        S::Fail(n) => Err(io::Error::other(LoadPaletteErr(n))),
498        S::Start => Err(io::Error::other(LoadPaletteErr(100))),
499        S::Theme => Err(io::Error::other(LoadPaletteErr(101))),
500        S::Palette => Err(io::Error::other(LoadPaletteErr(102))),
501        S::Color | S::Reference => Ok(pal),
502    }
503}
504
505/// Create the Theme based on the given Palette.
506#[allow(clippy::result_large_err)]
507pub fn create_palette_theme(pal: Palette) -> Result<SalsaTheme, Palette> {
508    match pal.theme.as_ref() {
509        "Dark" => Ok(themes::create_dark(pal)),
510        "Light" => Ok(themes::create_light(pal)),
511        "Shell" => Ok(themes::create_shell(pal)),
512        _ => Err(pal),
513    }
514}
515
516static THEMES: &[&str] = &[
517    "Imperial",
518    "Black&White",
519    "EverForest",
520    "Embark",
521    "FalconDark",
522    "Gatekeeper",
523    "Material",
524    "Monekai",
525    "Monochrome",
526    "Nord",
527    "Ocean",
528    "OxoCarbon",
529    "Radium",
530    "Reds",
531    "Rust",
532    "Solarized",
533    "Tailwind",
534    "Tundra",
535    "VSCode",
536    //
537    "Imperial Light",
538    "Blossom Light",
539    "EverForest Light",
540    "Gatekeeper Light",
541    "Embark Light",
542    "Rust Light",
543    "SunriseBreeze Light",
544    "Tailwind Light",
545    //
546    "Imperial Shell",
547    "Black&White Shell",
548    "EverForest Shell",
549    "Embark Shell",
550    "Gatekeeper Shell",
551    "Material Shell",
552    "Monekai Shell",
553    "Monochrome Shell",
554    "Nord Shell",
555    "Ocean Shell",
556    "OxoCarbon Shell",
557    "Radium Shell",
558    "Reds Shell",
559    "Rust Shell",
560    "Solarized Shell",
561    "Tailwind Shell",
562    "Tundra Shell",
563    "VSCode Shell",
564    //
565    "Shell",
566    "Blackout",
567    "Fallback",
568];
569
570/// All predefined rat-salsa themes.
571#[deprecated(
572    since = "4.1.0",
573    note = "there is no separation between themes and palettes any more. use salsa_themes()"
574)]
575pub fn salsa_palettes() -> Vec<&'static str> {
576    let mut r = Vec::new();
577    for v in THEMES {
578        r.push(*v);
579    }
580    r
581}
582
583/// Create one of the predefined themes as a Palette.
584///
585/// The available themes can be queried by [salsa_themes].
586///
587/// Known palettes: Imperial, Radium, Tundra, Ocean, Monochrome,
588/// Black&White, Monekai, Solarized, OxoCarbon, EverForest,
589/// Nord, Rust, Material, Tailwind, VSCode, Reds, Blackout,
590/// Shell, Imperial Light, EverForest Light, Tailwind Light,
591/// Rust Light.
592#[deprecated(since = "4.1.0", note = "use create_salsa_palette() instead")]
593pub fn create_palette(name: &str) -> Option<Palette> {
594    create_salsa_palette(name)
595}
596
597/// Create one of the predefined themes as a Palette.
598///
599/// The available themes can be queried by [salsa_themes].
600///
601/// Known palettes: Imperial, Radium, Tundra, Ocean, Monochrome,
602/// Black&White, Monekai, Solarized, OxoCarbon, EverForest,
603/// Nord, Rust, Material, Tailwind, VSCode, Reds, Blackout,
604/// Shell, Imperial Light, EverForest Light, Tailwind Light,
605/// Rust Light.
606pub fn create_salsa_palette(name: &str) -> Option<Palette> {
607    use crate::palettes::core;
608    use crate::palettes::dark;
609    use crate::palettes::light;
610    match name {
611        "Imperial" => Some(dark::IMPERIAL),
612        "Black&White" => Some(dark::BLACK_WHITE),
613        "EverForest" => Some(dark::EVERFOREST),
614        "FalconDark" => Some(dark::FALCON_DARK),
615        "Gatekeeper" => Some(dark::GATEKEEPER),
616        "Embark" => Some(dark::EMBARK),
617        "Material" => Some(dark::MATERIAL),
618        "Monekai" => Some(dark::MONEKAI),
619        "Monochrome" => Some(dark::MONOCHROME),
620        "Nord" => Some(dark::NORD),
621        "Ocean" => Some(dark::OCEAN),
622        "OxoCarbon" => Some(dark::OXOCARBON),
623        "Radium" => Some(dark::RADIUM),
624        "Reds" => Some(dark::REDS),
625        "Rust" => Some(dark::RUST),
626        "Solarized" => Some(dark::SOLARIZED),
627        "Tailwind" => Some(dark::TAILWIND),
628        "Tundra" => Some(dark::TUNDRA),
629        "VSCode" => Some(dark::VSCODE),
630
631        "Imperial Light" => Some(light::IMPERIAL_LIGHT),
632        "Blossom Light" => Some(light::BLOSSOM_LIGHT),
633        "Embark Light" => Some(light::EMBARK_LIGHT),
634        "EverForest Light" => Some(light::EVERFOREST_LIGHT),
635        "Gatekeeper Light" => Some(light::GATEKEEPER_LIGHT),
636        "Rust Light" => Some(light::RUST_LIGHT),
637        "SunriseBreeze Light" => Some(light::SUNRISEBREEZE_LIGHT),
638        "Tailwind Light" => Some(light::TAILWIND_LIGHT),
639
640        "Imperial Shell" => Some(shell::IMPERIAL_SHELL),
641        "Black&White Shell" => Some(shell::BLACK_WHITE_SHELL),
642        "Embark Shell" => Some(shell::EMBARK_SHELL),
643        "EverForest Shell" => Some(shell::EVERFOREST_SHELL),
644        "Gatekeeper Shell" => Some(shell::GATEKEEPER_SHELL),
645        "Material Shell" => Some(shell::MATERIAL_SHELL),
646        "Monekai Shell" => Some(shell::MONEKAI_SHELL),
647        "Monochrome Shell" => Some(shell::MONOCHROME_SHELL),
648        "Nord Shell" => Some(shell::NORD_SHELL),
649        "Ocean Shell" => Some(shell::OCEAN_SHELL),
650        "OxoCarbon Shell" => Some(shell::OXOCARBON_SHELL),
651        "Radium Shell" => Some(shell::RADIUM_SHELL),
652        "Reds Shell" => Some(shell::REDS_SHELL),
653        "Rust Shell" => Some(shell::RUST_SHELL),
654        "Solarized Shell" => Some(shell::SOLARIZED_SHELL),
655        "Tailwind Shell" => Some(shell::TAILWIND_SHELL),
656        "Tundra Shell" => Some(shell::TUNDRA_SHELL),
657        "VSCode Shell" => Some(shell::VSCODE_SHELL),
658
659        "Shell" => Some(core::SHELL),
660        "Blackout" => Some(core::BLACKOUT),
661        "Fallback" => Some(core::FALLBACK),
662        _ => None,
663    }
664}
665
666/// All predefined rat-salsa themes.
667pub fn salsa_themes() -> Vec<&'static str> {
668    let mut r = Vec::new();
669    for v in THEMES {
670        r.push(*v);
671    }
672    r
673}
674
675#[deprecated(since = "4.1.0", note = "use create_salsa_theme() instead")]
676pub fn create_theme(theme_name: &str) -> SalsaTheme {
677    create_salsa_theme(theme_name)
678}
679
680/// Create one of the predefined themes.
681///
682/// The available themes can be queried by [salsa_themes].
683///
684/// Known themes: Imperial Dark, Radium Dark, Tundra Dark,
685/// Ocean Dark, Monochrome Dark, Black&White Dark, Monekai Dark,
686/// Solarized Dark, OxoCarbon Dark, EverForest Dark, Nord Dark,
687/// Rust Dark, Material Dark, Tailwind Dark, VSCode Dark,
688/// Imperial Light, EverForest Light, Tailwind Light, Rust Light,
689/// Imperial Shell, Radium Shell, Tundra Shell, Ocean Shell,
690/// Monochrome Shell, Black&White Shell, Monekai Shell,
691/// Solarized Shell, OxoCarbon Shell, EverForest Shell, Nord Shell,
692/// Rust Shell, Material Shell, Tailwind Shell, VSCode Shell,
693/// Shell, Blackout and Fallback.
694pub fn create_salsa_theme(theme_name: &str) -> SalsaTheme {
695    if let Some(pal) = create_salsa_palette(theme_name) {
696        match pal.theme.as_ref() {
697            "Dark" => themes::create_dark(pal),
698            "Light" => themes::create_light(pal),
699            "Shell" => themes::create_shell(pal),
700            "Fallback" => themes::create_fallback(pal),
701            _ => themes::create_shell(palettes::core::SHELL),
702        }
703    } else {
704        themes::create_shell(palettes::core::SHELL)
705    }
706}