Skip to main content

caixa_theme/
palette.rs

1//! Nord — Arctic-themed palette. Reference: <https://www.nordtheme.com/>.
2//!
3//! Polar Night → Snow Storm → Frost → Aurora. All members are const — use
4//! them directly, e.g. `Nord::NORD11` for red.
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct Rgb {
10    pub r: u8,
11    pub g: u8,
12    pub b: u8,
13}
14
15impl Rgb {
16    #[must_use]
17    pub const fn new(r: u8, g: u8, b: u8) -> Self {
18        Self { r, g, b }
19    }
20
21    #[must_use]
22    pub const fn from_hex(hex: u32) -> Self {
23        Self {
24            r: ((hex >> 16) & 0xFF) as u8,
25            g: ((hex >> 8) & 0xFF) as u8,
26            b: (hex & 0xFF) as u8,
27        }
28    }
29
30    #[must_use]
31    pub fn to_hex(self) -> String {
32        format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
33    }
34
35    /// ANSI 24-bit SGR foreground sequence — `\x1b[38;2;R;G;Bm`.
36    #[must_use]
37    pub fn fg_ansi(self) -> String {
38        format!("\x1b[38;2;{};{};{}m", self.r, self.g, self.b)
39    }
40
41    /// ANSI 24-bit SGR background sequence — `\x1b[48;2;R;G;Bm`.
42    #[must_use]
43    pub fn bg_ansi(self) -> String {
44        format!("\x1b[48;2;{};{};{}m", self.r, self.g, self.b)
45    }
46}
47
48/// Reset SGR — ends a color.
49pub const ANSI_RESET: &str = "\x1b[0m";
50
51/// Nord palette — 16 entries, named Nord0 … Nord15 per the canonical spec.
52#[allow(non_snake_case)]
53pub struct Nord;
54
55impl Nord {
56    // Polar Night — backgrounds, UI chrome.
57    pub const NORD0: Rgb = Rgb::from_hex(0x2E3440);
58    pub const NORD1: Rgb = Rgb::from_hex(0x3B4252);
59    pub const NORD2: Rgb = Rgb::from_hex(0x434C5E);
60    pub const NORD3: Rgb = Rgb::from_hex(0x4C566A);
61    // Snow Storm — foregrounds.
62    pub const NORD4: Rgb = Rgb::from_hex(0xD8DEE9);
63    pub const NORD5: Rgb = Rgb::from_hex(0xE5E9F0);
64    pub const NORD6: Rgb = Rgb::from_hex(0xECEFF4);
65    // Frost — classes, types, primary accent.
66    pub const NORD7: Rgb = Rgb::from_hex(0x8FBCBB);
67    pub const NORD8: Rgb = Rgb::from_hex(0x88C0D0);
68    pub const NORD9: Rgb = Rgb::from_hex(0x81A1C1);
69    pub const NORD10: Rgb = Rgb::from_hex(0x5E81AC);
70    // Aurora — diagnostics, literals.
71    pub const NORD11: Rgb = Rgb::from_hex(0xBF616A); // red   — errors
72    pub const NORD12: Rgb = Rgb::from_hex(0xD08770); // orange — warnings
73    pub const NORD13: Rgb = Rgb::from_hex(0xEBCB8B); // yellow — hints
74    pub const NORD14: Rgb = Rgb::from_hex(0xA3BE8C); // green  — added / info
75    pub const NORD15: Rgb = Rgb::from_hex(0xB48EAD); // purple — strings / symbols
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn hex_round_trip() {
84        let c = Rgb::from_hex(0x5E81AC);
85        assert_eq!(c.to_hex(), "#5E81AC");
86    }
87
88    #[test]
89    fn ansi_fg_shape() {
90        let c = Nord::NORD11;
91        let s = c.fg_ansi();
92        assert!(s.starts_with("\x1b[38;2;"));
93    }
94}