1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//! Themes: plot colors as a plain value you pass — never a global.
use crate::render::Color;
/// The colors a plot draws with, independent of any terminal.
///
/// The default works on dark backgrounds. [`Theme::LIGHT`] swaps out the colors that
/// vanish on white paper-like backgrounds. [`Theme::detect`] picks one from the
/// environment; a custom palette is just a struct literal.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Theme {
/// Colors assigned to layers that set none explicitly, in layer order.
pub palette: [Color; 6],
}
impl Theme {
/// The dark-background palette (the default).
pub const DARK: Theme = Theme {
palette: [
Color::Cyan,
Color::Yellow,
Color::Green,
Color::Magenta,
Color::Blue,
Color::Red,
],
};
/// A palette that stays readable on light backgrounds (no yellow on white).
pub const LIGHT: Theme = Theme {
palette: [
Color::Blue,
Color::Red,
Color::Green,
Color::Magenta,
Color::Cyan,
Color::Black,
],
};
/// Picks a theme from the environment: `COLORFGBG` with a light background
/// (last segment `7` or `15`) selects [`Theme::LIGHT`]; anything else — including
/// no information at all — selects [`Theme::DARK`], the safer default.
pub fn detect() -> Theme {
let light = std::env::var("COLORFGBG")
.ok()
.and_then(|value| value.rsplit(';').next().map(str::to_string))
.is_some_and(|background| background == "7" || background == "15");
if light { Theme::LIGHT } else { Theme::DARK }
}
}
impl Default for Theme {
fn default() -> Theme {
Theme::DARK
}
}