Skip to main content

lattice_theme/
palette.rs

1//! The palette — a named set of base colors owned by the active
2//! theme. Swapping the palette re-colors every element that
3//! references it (the multi-theme primitive). A [`StyleSpec`]'s
4//! `ColorRef::Palette(key)` resolves against the active palette at
5//! theme-build time.
6//!
7//! The default palette below holds three families so the T.2
8//! builtin elements can reference it and still resolve to **exactly**
9//! today's literal colors (the `resolved_builtins_match_legacy_literals`
10//! parity pin):
11//!
12//! - **Catppuccin Mocha accents** (RGB) — what syntax highlighting
13//!   already uses.
14//! - **ANSI-named entries** (`ansi.*` → [`Color::Named`]) — the
15//!   chrome (pane status, diagnostics, file-tree, messages, diff
16//!   signs) deliberately uses named colors so it degrades on 16-color
17//!   terminals; the palette preserves that intent rather than
18//!   flattening to RGB.
19//! - **Specific tints** (diff backgrounds, current-line) — one-off
20//!   colors a palette entry names so a theme can still re-tint them.
21//!
22//! A future theme (T.9) redefines these keys; chrome can migrate to
23//! semantic accent roles once a second theme exists to force the
24//! abstraction.
25//!
26//! Design: `docs/dev/architecture/theme-system.md` §3.3.
27
28use std::borrow::Cow;
29use std::collections::HashMap;
30
31use crate::{Color, NamedColor};
32
33/// A palette entry's name (`"purple"`, `"ansi.red"`, `"diff.add.bg"`).
34#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35pub struct PaletteKey(Cow<'static, str>);
36
37impl PaletteKey {
38    pub const fn from_static(s: &'static str) -> Self {
39        PaletteKey(Cow::Borrowed(s))
40    }
41    pub fn as_str(&self) -> &str {
42        &self.0
43    }
44}
45
46impl From<&'static str> for PaletteKey {
47    fn from(s: &'static str) -> Self {
48        PaletteKey::from_static(s)
49    }
50}
51
52impl From<String> for PaletteKey {
53    fn from(s: String) -> Self {
54        PaletteKey(Cow::Owned(s))
55    }
56}
57
58/// A named set of base colors. `StyleSpec` colors reference these by
59/// key; resolution looks them up here.
60#[derive(Debug, Clone, Default, PartialEq, Eq)]
61pub struct Palette {
62    colors: HashMap<PaletteKey, Color>,
63}
64
65impl Palette {
66    pub fn new() -> Self {
67        Self::default()
68    }
69
70    /// Builder-style insert.
71    pub fn with(mut self, key: impl Into<PaletteKey>, color: Color) -> Self {
72        self.colors.insert(key.into(), color);
73        self
74    }
75
76    pub fn insert(&mut self, key: impl Into<PaletteKey>, color: Color) {
77        self.colors.insert(key.into(), color);
78    }
79
80    /// Look up a color by key. `None` if the active palette lacks it
81    /// — resolution then falls back (inherit chain → hard default)
82    /// and logs once rather than panicking.
83    pub fn get(&self, key: &PaletteKey) -> Option<Color> {
84        self.colors.get(key).copied()
85    }
86
87    /// Iterate over every key this palette defines. Used by the
88    /// completeness test to assert each builtin theme covers the full
89    /// role-key set `default_palette` defines (no resolution fallback).
90    pub fn keys(&self) -> impl Iterator<Item = &PaletteKey> {
91        self.colors.keys()
92    }
93
94    pub fn len(&self) -> usize {
95        self.colors.len()
96    }
97
98    pub fn is_empty(&self) -> bool {
99        self.colors.is_empty()
100    }
101}
102
103/// The default palette (Catppuccin Mocha accents + ANSI chrome +
104/// tints). See module docs for why all three families exist.
105pub fn default_palette() -> Palette {
106    use NamedColor as N;
107    let rgb = Color::Rgb;
108    Palette::new()
109        // ---- Catppuccin Mocha accents (syntax) ----
110        .with("text", rgb(0xcd, 0xd6, 0xf4))
111        .with("overlay", rgb(0x6c, 0x70, 0x86))
112        .with("subtext", rgb(0x93, 0x99, 0xb2))
113        .with("green", rgb(0xa6, 0xe3, 0xa1))
114        .with("purple", rgb(0xcb, 0xa6, 0xf7))
115        .with("yellow", rgb(0xf9, 0xe2, 0xaf))
116        .with("orange", rgb(0xfa, 0xb3, 0x87))
117        .with("blue", rgb(0x89, 0xb4, 0xfa))
118        .with("teal", rgb(0x94, 0xe2, 0xd5))
119        .with("red", rgb(0xf3, 0x8b, 0xa8))
120        .with("maroon", rgb(0xeb, 0xa0, 0xac))
121        .with("pink", rgb(0xf5, 0xc2, 0xe7))
122        .with("cyan", rgb(0x74, 0xc7, 0xec))
123        // ---- ANSI-named chrome (degrades on 16-color terminals) ----
124        .with("ansi.red", Color::Named(N::Red))
125        .with("ansi.green", Color::Named(N::Green))
126        .with("ansi.yellow", Color::Named(N::Yellow))
127        .with("ansi.blue", Color::Named(N::Blue))
128        .with("ansi.magenta", Color::Named(N::Magenta))
129        .with("ansi.cyan", Color::Named(N::Cyan))
130        .with("ansi.darkgray", Color::Named(N::DarkGray))
131        // ---- Background family (canvas surfaces, T.11.0b) ----
132        .with("base", rgb(0x1e, 0x1e, 0x2e))
133        .with("mantle", rgb(0x18, 0x18, 0x25))
134        .with("crust", rgb(0x11, 0x11, 0x1b))
135        .with("surface0", rgb(0x31, 0x32, 0x44))
136        .with("surface1", rgb(0x45, 0x47, 0x5a))
137        .with("surface2", rgb(0x58, 0x5b, 0x70))
138        // ---- Specific tints (one-off backgrounds) ----
139        .with("diff.add.bg", rgb(0, 50, 0))
140        .with("diff.change.bg", rgb(50, 50, 0))
141        .with("diff.deletion.bg", rgb(60, 0, 0))
142        .with("diff.conflict.bg", rgb(60, 0, 60))
143        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
144        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
145        .with("cursor_line.bg", Color::Indexed(236))
146}
147
148/// The Catppuccin **Macchiato** palette (T.9.b). Same key SET as
149/// [`default_palette`] — every element references the same keys — but
150/// the accent RGB values are Macchiato's. The `ansi.*` named entries
151/// stay IDENTICAL to mocha's: the chrome's degradation-on-16-color
152/// intent is theme-independent, so a colorscheme swap must not flip a
153/// named ANSI entry to a truecolor. The one-off tints also stay
154/// identical EXCEPT `cursor_line.bg`, which Macchiato carries as a
155/// distinct RGB tint matching its lighter surface.
156pub fn macchiato_palette() -> Palette {
157    use NamedColor as N;
158    let rgb = Color::Rgb;
159    Palette::new()
160        // ---- Catppuccin Macchiato accents (syntax) ----
161        .with("text", rgb(0xca, 0xd3, 0xf5))
162        .with("overlay", rgb(0x6e, 0x73, 0x8d))
163        .with("subtext", rgb(0x93, 0x9a, 0xb7))
164        .with("green", rgb(0xa6, 0xda, 0x95))
165        .with("purple", rgb(0xc6, 0xa0, 0xf6))
166        .with("yellow", rgb(0xee, 0xd4, 0x9f))
167        .with("orange", rgb(0xf5, 0xa9, 0x7f))
168        .with("blue", rgb(0x8a, 0xad, 0xf4))
169        .with("teal", rgb(0x8b, 0xd5, 0xca))
170        .with("red", rgb(0xed, 0x87, 0x96))
171        .with("maroon", rgb(0xee, 0x99, 0xa0))
172        .with("pink", rgb(0xf5, 0xbd, 0xe6))
173        .with("cyan", rgb(0x7d, 0xc4, 0xe4))
174        // ---- ANSI-named chrome (IDENTICAL to mocha; theme-independent) ----
175        .with("ansi.red", Color::Named(N::Red))
176        .with("ansi.green", Color::Named(N::Green))
177        .with("ansi.yellow", Color::Named(N::Yellow))
178        .with("ansi.blue", Color::Named(N::Blue))
179        .with("ansi.magenta", Color::Named(N::Magenta))
180        .with("ansi.cyan", Color::Named(N::Cyan))
181        .with("ansi.darkgray", Color::Named(N::DarkGray))
182        // ---- Background family (canvas surfaces, T.11.0b) ----
183        .with("base", rgb(0x24, 0x27, 0x3a))
184        .with("mantle", rgb(0x1e, 0x20, 0x30))
185        .with("crust", rgb(0x18, 0x19, 0x26))
186        .with("surface0", rgb(0x36, 0x3a, 0x4f))
187        .with("surface1", rgb(0x49, 0x4d, 0x64))
188        .with("surface2", rgb(0x5b, 0x60, 0x78))
189        // ---- Specific tints (diff bgs identical to mocha; cursor line distinct) ----
190        .with("diff.add.bg", rgb(0, 50, 0))
191        .with("diff.change.bg", rgb(50, 50, 0))
192        .with("diff.deletion.bg", rgb(60, 0, 0))
193        .with("diff.conflict.bg", rgb(60, 0, 60))
194        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
195        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
196        .with("cursor_line.bg", rgb(0x36, 0x3a, 0x4f))
197}
198
199/// The Catppuccin **Latte** palette (T.11.1) — the project's first
200/// LIGHT theme. Same key SET as [`default_palette`] (every element
201/// references the same keys; the palette indirection re-colours the
202/// whole surface), but with Latte's light `base` + dark `text` + Latte
203/// accent RGBs. This is the proof that T.11.0b made the canvas
204/// palette-driven: `editor.background` resolves to the light `base` and
205/// `editor.foreground` to the dark `text`, so swapping to Latte
206/// recolours the GPUI canvas light. `ansi.*` stay terminal-named
207/// (theme-independent). The diff tints + cursor-line are LIGHT variants
208/// (the Mocha dark tints would read as near-black blocks on a light
209/// canvas).
210pub fn catppuccin_latte_palette() -> Palette {
211    use NamedColor as N;
212    let rgb = Color::Rgb;
213    Palette::new()
214        // ---- Catppuccin Latte accents (syntax) ----
215        .with("text", rgb(0x4c, 0x4f, 0x69))
216        .with("overlay", rgb(0x9c, 0xa0, 0xb0))
217        .with("subtext", rgb(0x7c, 0x7f, 0x93))
218        .with("green", rgb(0x40, 0xa0, 0x2b))
219        .with("purple", rgb(0x88, 0x39, 0xef))
220        .with("yellow", rgb(0xdf, 0x8e, 0x1d))
221        .with("orange", rgb(0xfe, 0x64, 0x0b))
222        .with("blue", rgb(0x1e, 0x66, 0xf5))
223        .with("teal", rgb(0x17, 0x92, 0x99))
224        .with("red", rgb(0xd2, 0x0f, 0x39))
225        .with("maroon", rgb(0xe6, 0x45, 0x53))
226        .with("pink", rgb(0xea, 0x76, 0xcb))
227        .with("cyan", rgb(0x20, 0x9f, 0xb5))
228        // ---- ANSI-named chrome (theme-independent; same as the others) ----
229        .with("ansi.red", Color::Named(N::Red))
230        .with("ansi.green", Color::Named(N::Green))
231        .with("ansi.yellow", Color::Named(N::Yellow))
232        .with("ansi.blue", Color::Named(N::Blue))
233        .with("ansi.magenta", Color::Named(N::Magenta))
234        .with("ansi.cyan", Color::Named(N::Cyan))
235        .with("ansi.darkgray", Color::Named(N::DarkGray))
236        // ---- Background family (LIGHT canvas surfaces) ----
237        .with("base", rgb(0xef, 0xf1, 0xf5))
238        .with("mantle", rgb(0xe6, 0xe9, 0xef))
239        .with("crust", rgb(0xdc, 0xe0, 0xe8))
240        .with("surface0", rgb(0xcc, 0xd0, 0xda))
241        .with("surface1", rgb(0xbc, 0xc0, 0xcc))
242        .with("surface2", rgb(0xac, 0xb0, 0xbe))
243        // ---- Specific tints (LIGHT variants for a light canvas) ----
244        .with("diff.add.bg", rgb(0xd8, 0xee, 0xd2))
245        .with("diff.change.bg", rgb(0xf0, 0xe8, 0xc8))
246        .with("diff.deletion.bg", rgb(0xf5, 0xd5, 0xd5))
247        .with("diff.conflict.bg", rgb(0xee, 0xd8, 0xee))
248        .with("diff.add.refine.bg", rgb(0x9b, 0xee, 0x97))
249        .with("diff.remove.refine.bg", rgb(0xf5, 0x99, 0x99))
250        .with("cursor_line.bg", rgb(0xcc, 0xd0, 0xda))
251}
252
253// ============================================================
254// T.11.1 — the multi-theme library: 9 cross-editor families ×
255// {dark, light}. Every palette below fills the FULL role-key set
256// `default_palette` defines, so resolution never falls back. The
257// `ansi.*` block is theme-independent (copied verbatim from the
258// Catppuccin palettes): chrome degrades to terminal-named colors
259// the same way regardless of colorscheme.
260// ============================================================
261
262/// Gruvbox **dark** (official). Warm retro palette: dark bg0 base,
263/// light fg foreground, the canonical bright aqua/green/orange accents.
264pub fn gruvbox_dark_palette() -> Palette {
265    use NamedColor as N;
266    let rgb = Color::Rgb;
267    Palette::new()
268        // ---- Gruvbox dark accents ----
269        .with("text", rgb(0xeb, 0xdb, 0xb2))
270        .with("overlay", rgb(0x92, 0x83, 0x74))
271        .with("subtext", rgb(0xa8, 0x99, 0x84))
272        .with("green", rgb(0xb8, 0xbb, 0x26))
273        .with("purple", rgb(0xd3, 0x86, 0x9b))
274        .with("yellow", rgb(0xfa, 0xbd, 0x2f))
275        .with("orange", rgb(0xfe, 0x80, 0x19))
276        .with("blue", rgb(0x83, 0xa5, 0x98))
277        .with("teal", rgb(0x8e, 0xc0, 0x7c))
278        .with("red", rgb(0xfb, 0x49, 0x34))
279        .with("maroon", rgb(0xcc, 0x24, 0x1d))
280        .with("pink", rgb(0xd3, 0x86, 0x9b))
281        .with("cyan", rgb(0x8e, 0xc0, 0x7c))
282        // ---- ANSI-named chrome (theme-independent) ----
283        .with("ansi.red", Color::Named(N::Red))
284        .with("ansi.green", Color::Named(N::Green))
285        .with("ansi.yellow", Color::Named(N::Yellow))
286        .with("ansi.blue", Color::Named(N::Blue))
287        .with("ansi.magenta", Color::Named(N::Magenta))
288        .with("ansi.cyan", Color::Named(N::Cyan))
289        .with("ansi.darkgray", Color::Named(N::DarkGray))
290        // ---- Background family (dark) ----
291        .with("base", rgb(0x28, 0x28, 0x28))
292        .with("mantle", rgb(0x1d, 0x20, 0x21))
293        .with("crust", rgb(0x16, 0x18, 0x19))
294        .with("surface0", rgb(0x3c, 0x38, 0x36))
295        .with("surface1", rgb(0x50, 0x49, 0x45))
296        .with("surface2", rgb(0x66, 0x5c, 0x54))
297        // ---- Specific tints (dark) ----
298        .with("diff.add.bg", rgb(0, 50, 0))
299        .with("diff.change.bg", rgb(50, 50, 0))
300        .with("diff.deletion.bg", rgb(60, 0, 0))
301        .with("diff.conflict.bg", rgb(60, 0, 60))
302        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
303        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
304        .with("cursor_line.bg", rgb(0x3c, 0x38, 0x36))
305}
306
307/// Gruvbox **light** (official). Light bg0 base (`#fbf1c7`), dark fg
308/// text, accents are the deeper "faded" gruvbox-light variants for
309/// contrast on a cream canvas.
310pub fn gruvbox_light_palette() -> Palette {
311    use NamedColor as N;
312    let rgb = Color::Rgb;
313    Palette::new()
314        // ---- Gruvbox light accents (faded, for contrast) ----
315        .with("text", rgb(0x3c, 0x38, 0x36))
316        .with("overlay", rgb(0x7c, 0x6f, 0x64))
317        .with("subtext", rgb(0x66, 0x5c, 0x54))
318        .with("green", rgb(0x79, 0x74, 0x0e))
319        .with("purple", rgb(0xb1, 0x62, 0x86))
320        .with("yellow", rgb(0xb5, 0x76, 0x14))
321        .with("orange", rgb(0xaf, 0x3a, 0x03))
322        .with("blue", rgb(0x07, 0x66, 0x78))
323        .with("teal", rgb(0x42, 0x7b, 0x58))
324        .with("red", rgb(0x9d, 0x00, 0x06))
325        .with("maroon", rgb(0xcc, 0x24, 0x1d))
326        .with("pink", rgb(0x8f, 0x3f, 0x71))
327        .with("cyan", rgb(0x42, 0x7b, 0x58))
328        // ---- ANSI-named chrome (theme-independent) ----
329        .with("ansi.red", Color::Named(N::Red))
330        .with("ansi.green", Color::Named(N::Green))
331        .with("ansi.yellow", Color::Named(N::Yellow))
332        .with("ansi.blue", Color::Named(N::Blue))
333        .with("ansi.magenta", Color::Named(N::Magenta))
334        .with("ansi.cyan", Color::Named(N::Cyan))
335        .with("ansi.darkgray", Color::Named(N::DarkGray))
336        // ---- Background family (LIGHT) ----
337        .with("base", rgb(0xfb, 0xf1, 0xc7))
338        .with("mantle", rgb(0xf2, 0xe5, 0xbc))
339        .with("crust", rgb(0xeb, 0xdb, 0xb2))
340        .with("surface0", rgb(0xeb, 0xdb, 0xb2))
341        .with("surface1", rgb(0xd5, 0xc4, 0xa1))
342        .with("surface2", rgb(0xbd, 0xae, 0x93))
343        // ---- Specific tints (LIGHT) ----
344        .with("diff.add.bg", rgb(0xd5, 0xe8, 0xc8))
345        .with("diff.change.bg", rgb(0xf0, 0xe8, 0xc0))
346        .with("diff.deletion.bg", rgb(0xf5, 0xd5, 0xd0))
347        .with("diff.conflict.bg", rgb(0xee, 0xd8, 0xe6))
348        .with("diff.add.refine.bg", rgb(0x99, 0xe8, 0x90))
349        .with("diff.remove.refine.bg", rgb(0xf5, 0x99, 0x95))
350        .with("cursor_line.bg", rgb(0xeb, 0xdb, 0xb2))
351}
352
353/// Tokyo Night **dark** ("Night"). Deep blue-black base, soft blue-white
354/// text, the signature blue/cyan/purple/green accents.
355pub fn tokyonight_dark_palette() -> Palette {
356    use NamedColor as N;
357    let rgb = Color::Rgb;
358    Palette::new()
359        // ---- Tokyo Night (Night) accents ----
360        .with("text", rgb(0xc0, 0xca, 0xf5))
361        .with("overlay", rgb(0x56, 0x5f, 0x89))
362        .with("subtext", rgb(0x9a, 0xa5, 0xce))
363        .with("green", rgb(0x9e, 0xce, 0x6a))
364        .with("purple", rgb(0xbb, 0x9a, 0xf7))
365        .with("yellow", rgb(0xe0, 0xaf, 0x68))
366        .with("orange", rgb(0xff, 0x9e, 0x64))
367        .with("blue", rgb(0x7a, 0xa2, 0xf7))
368        .with("teal", rgb(0x1a, 0xbc, 0x9c))
369        .with("red", rgb(0xf7, 0x76, 0x8e))
370        .with("maroon", rgb(0xdb, 0x4b, 0x4b))
371        .with("pink", rgb(0xff, 0x75, 0xa0))
372        .with("cyan", rgb(0x7d, 0xcf, 0xff))
373        // ---- ANSI-named chrome (theme-independent) ----
374        .with("ansi.red", Color::Named(N::Red))
375        .with("ansi.green", Color::Named(N::Green))
376        .with("ansi.yellow", Color::Named(N::Yellow))
377        .with("ansi.blue", Color::Named(N::Blue))
378        .with("ansi.magenta", Color::Named(N::Magenta))
379        .with("ansi.cyan", Color::Named(N::Cyan))
380        .with("ansi.darkgray", Color::Named(N::DarkGray))
381        // ---- Background family (dark) ----
382        .with("base", rgb(0x1a, 0x1b, 0x26))
383        .with("mantle", rgb(0x16, 0x16, 0x1e))
384        .with("crust", rgb(0x13, 0x14, 0x1a))
385        .with("surface0", rgb(0x24, 0x28, 0x3b))
386        .with("surface1", rgb(0x29, 0x2e, 0x42))
387        .with("surface2", rgb(0x41, 0x48, 0x68))
388        // ---- Specific tints (dark) ----
389        .with("diff.add.bg", rgb(0, 50, 0))
390        .with("diff.change.bg", rgb(50, 50, 0))
391        .with("diff.deletion.bg", rgb(60, 0, 0))
392        .with("diff.conflict.bg", rgb(60, 0, 60))
393        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
394        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
395        .with("cursor_line.bg", rgb(0x29, 0x2e, 0x42))
396}
397
398/// Tokyo Night **light** ("Day"). Light gray-blue base, dark navy text,
399/// deeper accent variants tuned for the bright canvas.
400pub fn tokyonight_light_palette() -> Palette {
401    use NamedColor as N;
402    let rgb = Color::Rgb;
403    Palette::new()
404        // ---- Tokyo Night Day accents (deeper for contrast) ----
405        .with("text", rgb(0x34, 0x35, 0x5e))
406        .with("overlay", rgb(0x9d, 0xa0, 0xc2))
407        .with("subtext", rgb(0x6c, 0x6e, 0x8f))
408        .with("green", rgb(0x58, 0x7d, 0x39))
409        .with("purple", rgb(0x9d, 0x59, 0xcf))
410        .with("yellow", rgb(0x8c, 0x6c, 0x3e))
411        .with("orange", rgb(0xb1, 0x5c, 0x00))
412        .with("blue", rgb(0x2e, 0x7d, 0xe9))
413        .with("teal", rgb(0x11, 0x8c, 0x74))
414        .with("red", rgb(0xf5, 0x2a, 0x65))
415        .with("maroon", rgb(0xc6, 0x47, 0x60))
416        .with("pink", rgb(0xd2, 0x0d, 0x8b))
417        .with("cyan", rgb(0x00, 0x7e, 0xc5))
418        // ---- ANSI-named chrome (theme-independent) ----
419        .with("ansi.red", Color::Named(N::Red))
420        .with("ansi.green", Color::Named(N::Green))
421        .with("ansi.yellow", Color::Named(N::Yellow))
422        .with("ansi.blue", Color::Named(N::Blue))
423        .with("ansi.magenta", Color::Named(N::Magenta))
424        .with("ansi.cyan", Color::Named(N::Cyan))
425        .with("ansi.darkgray", Color::Named(N::DarkGray))
426        // ---- Background family (LIGHT) ----
427        .with("base", rgb(0xe1, 0xe2, 0xe7))
428        .with("mantle", rgb(0xd6, 0xd8, 0xdf))
429        .with("crust", rgb(0xc4, 0xc8, 0xda))
430        .with("surface0", rgb(0xc4, 0xc8, 0xda))
431        .with("surface1", rgb(0xb6, 0xba, 0xcb))
432        .with("surface2", rgb(0xa8, 0xae, 0xc1))
433        // ---- Specific tints (LIGHT) ----
434        .with("diff.add.bg", rgb(0xd5, 0xe8, 0xcb))
435        .with("diff.change.bg", rgb(0xe8, 0xe4, 0xc8))
436        .with("diff.deletion.bg", rgb(0xf2, 0xd2, 0xd5))
437        .with("diff.conflict.bg", rgb(0xe6, 0xd5, 0xee))
438        .with("diff.add.refine.bg", rgb(0x99, 0xe8, 0x92))
439        .with("diff.remove.refine.bg", rgb(0xf2, 0x97, 0x99))
440        .with("cursor_line.bg", rgb(0xc4, 0xc8, 0xda))
441}
442
443/// Dracula **dark** (official). Dark slate base (`#282a36`), bright
444/// foreground (`#f8f8f2`), the signature pink/purple/green/cyan accents.
445pub fn dracula_dark_palette() -> Palette {
446    use NamedColor as N;
447    let rgb = Color::Rgb;
448    Palette::new()
449        // ---- Dracula accents ----
450        .with("text", rgb(0xf8, 0xf8, 0xf2))
451        .with("overlay", rgb(0x62, 0x72, 0xa4))
452        .with("subtext", rgb(0x9a, 0xa5, 0xce))
453        .with("green", rgb(0x50, 0xfa, 0x7b))
454        .with("purple", rgb(0xbd, 0x93, 0xf9))
455        .with("yellow", rgb(0xf1, 0xfa, 0x8c))
456        .with("orange", rgb(0xff, 0xb8, 0x6c))
457        .with("blue", rgb(0x8b, 0xe9, 0xfd))
458        .with("teal", rgb(0x8b, 0xe9, 0xfd))
459        .with("red", rgb(0xff, 0x55, 0x55))
460        .with("maroon", rgb(0xe0, 0x40, 0x40))
461        .with("pink", rgb(0xff, 0x79, 0xc6))
462        .with("cyan", rgb(0x8b, 0xe9, 0xfd))
463        // ---- ANSI-named chrome (theme-independent) ----
464        .with("ansi.red", Color::Named(N::Red))
465        .with("ansi.green", Color::Named(N::Green))
466        .with("ansi.yellow", Color::Named(N::Yellow))
467        .with("ansi.blue", Color::Named(N::Blue))
468        .with("ansi.magenta", Color::Named(N::Magenta))
469        .with("ansi.cyan", Color::Named(N::Cyan))
470        .with("ansi.darkgray", Color::Named(N::DarkGray))
471        // ---- Background family (dark) ----
472        .with("base", rgb(0x28, 0x2a, 0x36))
473        .with("mantle", rgb(0x22, 0x24, 0x2e))
474        .with("crust", rgb(0x1a, 0x1c, 0x24))
475        .with("surface0", rgb(0x44, 0x47, 0x5a))
476        .with("surface1", rgb(0x53, 0x57, 0x6e))
477        .with("surface2", rgb(0x62, 0x72, 0xa4))
478        // ---- Specific tints (dark) ----
479        .with("diff.add.bg", rgb(0, 50, 0))
480        .with("diff.change.bg", rgb(50, 50, 0))
481        .with("diff.deletion.bg", rgb(60, 0, 0))
482        .with("diff.conflict.bg", rgb(60, 0, 60))
483        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
484        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
485        .with("cursor_line.bg", rgb(0x44, 0x47, 0x5a))
486}
487
488/// Dracula **light** ("Alucard", the official light variant). Light base
489/// (`#f8f8f2`), dark text, deeper accent variants for contrast.
490pub fn dracula_light_palette() -> Palette {
491    use NamedColor as N;
492    let rgb = Color::Rgb;
493    Palette::new()
494        // ---- Alucard accents (deeper for contrast) ----
495        .with("text", rgb(0x1f, 0x1f, 0x1f))
496        .with("overlay", rgb(0x6c, 0x66, 0x4b))
497        .with("subtext", rgb(0x52, 0x4c, 0x3f))
498        .with("green", rgb(0x14, 0x71, 0x0a))
499        .with("purple", rgb(0x64, 0x4a, 0xc9))
500        .with("yellow", rgb(0x84, 0x6e, 0x15))
501        .with("orange", rgb(0xa1, 0x55, 0x07))
502        .with("blue", rgb(0x03, 0x6a, 0x96))
503        .with("teal", rgb(0x03, 0x6a, 0x96))
504        .with("red", rgb(0xcb, 0x3a, 0x2a))
505        .with("maroon", rgb(0xa3, 0x2c, 0x20))
506        .with("pink", rgb(0xa3, 0x12, 0x7e))
507        .with("cyan", rgb(0x03, 0x6a, 0x96))
508        // ---- ANSI-named chrome (theme-independent) ----
509        .with("ansi.red", Color::Named(N::Red))
510        .with("ansi.green", Color::Named(N::Green))
511        .with("ansi.yellow", Color::Named(N::Yellow))
512        .with("ansi.blue", Color::Named(N::Blue))
513        .with("ansi.magenta", Color::Named(N::Magenta))
514        .with("ansi.cyan", Color::Named(N::Cyan))
515        .with("ansi.darkgray", Color::Named(N::DarkGray))
516        // ---- Background family (LIGHT) ----
517        .with("base", rgb(0xf8, 0xf8, 0xf2))
518        .with("mantle", rgb(0xef, 0xef, 0xe4))
519        .with("crust", rgb(0xe4, 0xe4, 0xd8))
520        .with("surface0", rgb(0xe4, 0xe4, 0xd8))
521        .with("surface1", rgb(0xd4, 0xd4, 0xc8))
522        .with("surface2", rgb(0xc4, 0xc4, 0xb8))
523        // ---- Specific tints (LIGHT) ----
524        .with("diff.add.bg", rgb(0xd5, 0xe8, 0xc8))
525        .with("diff.change.bg", rgb(0xf0, 0xea, 0xc4))
526        .with("diff.deletion.bg", rgb(0xf5, 0xd2, 0xcc))
527        .with("diff.conflict.bg", rgb(0xee, 0xd2, 0xe8))
528        .with("diff.add.refine.bg", rgb(0x99, 0xe8, 0x90))
529        .with("diff.remove.refine.bg", rgb(0xf5, 0x97, 0x92))
530        .with("cursor_line.bg", rgb(0xe4, 0xe4, 0xd8))
531}
532
533/// Nord **dark** (official). Polar-night bg (`#2e3440`), snow-storm fg
534/// (`#d8dee9`), frost (teal/blue/cyan) + aurora (red/orange/yellow/
535/// green/purple) accents.
536pub fn nord_dark_palette() -> Palette {
537    use NamedColor as N;
538    let rgb = Color::Rgb;
539    Palette::new()
540        // ---- Nord accents (frost + aurora) ----
541        .with("text", rgb(0xd8, 0xde, 0xe9))
542        .with("overlay", rgb(0x61, 0x6e, 0x88))
543        .with("subtext", rgb(0xab, 0xb2, 0xbf))
544        .with("green", rgb(0xa3, 0xbe, 0x8c))
545        .with("purple", rgb(0xb4, 0x8e, 0xad))
546        .with("yellow", rgb(0xeb, 0xcb, 0x8b))
547        .with("orange", rgb(0xd0, 0x87, 0x70))
548        .with("blue", rgb(0x81, 0xa1, 0xc1))
549        .with("teal", rgb(0x8f, 0xbc, 0xbb))
550        .with("red", rgb(0xbf, 0x61, 0x6a))
551        .with("maroon", rgb(0xb4, 0x57, 0x5f))
552        .with("pink", rgb(0xb4, 0x8e, 0xad))
553        .with("cyan", rgb(0x88, 0xc0, 0xd0))
554        // ---- ANSI-named chrome (theme-independent) ----
555        .with("ansi.red", Color::Named(N::Red))
556        .with("ansi.green", Color::Named(N::Green))
557        .with("ansi.yellow", Color::Named(N::Yellow))
558        .with("ansi.blue", Color::Named(N::Blue))
559        .with("ansi.magenta", Color::Named(N::Magenta))
560        .with("ansi.cyan", Color::Named(N::Cyan))
561        .with("ansi.darkgray", Color::Named(N::DarkGray))
562        // ---- Background family (polar night) ----
563        .with("base", rgb(0x2e, 0x34, 0x40))
564        .with("mantle", rgb(0x29, 0x2e, 0x39))
565        .with("crust", rgb(0x24, 0x29, 0x33))
566        .with("surface0", rgb(0x3b, 0x42, 0x52))
567        .with("surface1", rgb(0x43, 0x4c, 0x5e))
568        .with("surface2", rgb(0x4c, 0x56, 0x6a))
569        // ---- Specific tints (dark) ----
570        .with("diff.add.bg", rgb(0, 50, 0))
571        .with("diff.change.bg", rgb(50, 50, 0))
572        .with("diff.deletion.bg", rgb(60, 0, 0))
573        .with("diff.conflict.bg", rgb(60, 0, 60))
574        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
575        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
576        .with("cursor_line.bg", rgb(0x3b, 0x42, 0x52))
577}
578
579/// Nord **light** (inverted: snow-storm bg, polar-night text). Community-
580/// grade — Nord has no official light flavour, so this inverts the
581/// base/text and deepens the aurora/frost accents for contrast.
582pub fn nord_light_palette() -> Palette {
583    use NamedColor as N;
584    let rgb = Color::Rgb;
585    Palette::new()
586        // ---- Nord light accents (polar-night text + deepened accents) ----
587        .with("text", rgb(0x2e, 0x34, 0x40))
588        .with("overlay", rgb(0x7b, 0x84, 0x94))
589        .with("subtext", rgb(0x4c, 0x56, 0x6a))
590        .with("green", rgb(0x5c, 0x74, 0x47))
591        .with("purple", rgb(0x84, 0x5c, 0x7e))
592        .with("yellow", rgb(0x9a, 0x7d, 0x36))
593        .with("orange", rgb(0xa6, 0x53, 0x36))
594        .with("blue", rgb(0x40, 0x5d, 0x7e))
595        .with("teal", rgb(0x3e, 0x6d, 0x6b))
596        .with("red", rgb(0x99, 0x3b, 0x44))
597        .with("maroon", rgb(0x86, 0x32, 0x3a))
598        .with("pink", rgb(0x84, 0x5c, 0x7e))
599        .with("cyan", rgb(0x3a, 0x6d, 0x80))
600        // ---- ANSI-named chrome (theme-independent) ----
601        .with("ansi.red", Color::Named(N::Red))
602        .with("ansi.green", Color::Named(N::Green))
603        .with("ansi.yellow", Color::Named(N::Yellow))
604        .with("ansi.blue", Color::Named(N::Blue))
605        .with("ansi.magenta", Color::Named(N::Magenta))
606        .with("ansi.cyan", Color::Named(N::Cyan))
607        .with("ansi.darkgray", Color::Named(N::DarkGray))
608        // ---- Background family (snow storm, LIGHT) ----
609        .with("base", rgb(0xec, 0xef, 0xf4))
610        .with("mantle", rgb(0xe5, 0xe9, 0xf0))
611        .with("crust", rgb(0xd8, 0xde, 0xe9))
612        .with("surface0", rgb(0xd8, 0xde, 0xe9))
613        .with("surface1", rgb(0xc8, 0xd0, 0xde))
614        .with("surface2", rgb(0xb4, 0xbe, 0xd0))
615        // ---- Specific tints (LIGHT) ----
616        .with("diff.add.bg", rgb(0xd5, 0xe8, 0xcc))
617        .with("diff.change.bg", rgb(0xee, 0xe8, 0xcc))
618        .with("diff.deletion.bg", rgb(0xf2, 0xd5, 0xd8))
619        .with("diff.conflict.bg", rgb(0xe8, 0xd8, 0xe8))
620        .with("diff.add.refine.bg", rgb(0x99, 0xe8, 0x92))
621        .with("diff.remove.refine.bg", rgb(0xf2, 0x99, 0x9b))
622        .with("cursor_line.bg", rgb(0xd8, 0xde, 0xe9))
623}
624
625/// Solarized **dark** (official). base03 background (`#002b36`), base0
626/// body text, the fixed 8-accent wheel (violet→purple, magenta→pink,
627/// orange→orange, cyan→cyan).
628pub fn solarized_dark_palette() -> Palette {
629    use NamedColor as N;
630    let rgb = Color::Rgb;
631    Palette::new()
632        // ---- Solarized accents ----
633        .with("text", rgb(0x83, 0x94, 0x96)) // base0
634        .with("overlay", rgb(0x58, 0x6e, 0x75)) // base01
635        .with("subtext", rgb(0x65, 0x7b, 0x83)) // base00
636        .with("green", rgb(0x85, 0x99, 0x00))
637        .with("purple", rgb(0x6c, 0x71, 0xc4)) // violet
638        .with("yellow", rgb(0xb5, 0x89, 0x00))
639        .with("orange", rgb(0xcb, 0x4b, 0x16))
640        .with("blue", rgb(0x26, 0x8b, 0xd2))
641        .with("teal", rgb(0x2a, 0xa1, 0x98)) // cyan slot reused for teal
642        .with("red", rgb(0xdc, 0x32, 0x2f))
643        .with("maroon", rgb(0xb5, 0x26, 0x23))
644        .with("pink", rgb(0xd3, 0x36, 0x82)) // magenta
645        .with("cyan", rgb(0x2a, 0xa1, 0x98))
646        // ---- ANSI-named chrome (theme-independent) ----
647        .with("ansi.red", Color::Named(N::Red))
648        .with("ansi.green", Color::Named(N::Green))
649        .with("ansi.yellow", Color::Named(N::Yellow))
650        .with("ansi.blue", Color::Named(N::Blue))
651        .with("ansi.magenta", Color::Named(N::Magenta))
652        .with("ansi.cyan", Color::Named(N::Cyan))
653        .with("ansi.darkgray", Color::Named(N::DarkGray))
654        // ---- Background family (base03..base01, dark) ----
655        .with("base", rgb(0x00, 0x2b, 0x36)) // base03
656        .with("mantle", rgb(0x00, 0x26, 0x30))
657        .with("crust", rgb(0x00, 0x1e, 0x26))
658        .with("surface0", rgb(0x07, 0x36, 0x42)) // base02
659        .with("surface1", rgb(0x0c, 0x40, 0x4e))
660        .with("surface2", rgb(0x58, 0x6e, 0x75)) // base01
661        // ---- Specific tints (dark) ----
662        .with("diff.add.bg", rgb(0, 50, 0))
663        .with("diff.change.bg", rgb(50, 50, 0))
664        .with("diff.deletion.bg", rgb(60, 0, 0))
665        .with("diff.conflict.bg", rgb(60, 0, 60))
666        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
667        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
668        .with("cursor_line.bg", rgb(0x07, 0x36, 0x42))
669}
670
671/// Solarized **light** (official). base3 background (`#fdf6e3`), base00
672/// body text. Same fixed accent wheel as the dark variant — Solarized's
673/// accents are shared across both modes by design.
674pub fn solarized_light_palette() -> Palette {
675    use NamedColor as N;
676    let rgb = Color::Rgb;
677    Palette::new()
678        // ---- Solarized accents (same wheel; text/overlay flip) ----
679        .with("text", rgb(0x65, 0x7b, 0x83)) // base00
680        .with("overlay", rgb(0x93, 0xa1, 0xa1)) // base1
681        .with("subtext", rgb(0x83, 0x94, 0x96)) // base0
682        .with("green", rgb(0x85, 0x99, 0x00))
683        .with("purple", rgb(0x6c, 0x71, 0xc4)) // violet
684        .with("yellow", rgb(0xb5, 0x89, 0x00))
685        .with("orange", rgb(0xcb, 0x4b, 0x16))
686        .with("blue", rgb(0x26, 0x8b, 0xd2))
687        .with("teal", rgb(0x2a, 0xa1, 0x98))
688        .with("red", rgb(0xdc, 0x32, 0x2f))
689        .with("maroon", rgb(0xb5, 0x26, 0x23))
690        .with("pink", rgb(0xd3, 0x36, 0x82)) // magenta
691        .with("cyan", rgb(0x2a, 0xa1, 0x98))
692        // ---- ANSI-named chrome (theme-independent) ----
693        .with("ansi.red", Color::Named(N::Red))
694        .with("ansi.green", Color::Named(N::Green))
695        .with("ansi.yellow", Color::Named(N::Yellow))
696        .with("ansi.blue", Color::Named(N::Blue))
697        .with("ansi.magenta", Color::Named(N::Magenta))
698        .with("ansi.cyan", Color::Named(N::Cyan))
699        .with("ansi.darkgray", Color::Named(N::DarkGray))
700        // ---- Background family (base3..base1, LIGHT) ----
701        .with("base", rgb(0xfd, 0xf6, 0xe3)) // base3
702        .with("mantle", rgb(0xf5, 0xee, 0xdb))
703        .with("crust", rgb(0xee, 0xe8, 0xd5)) // base2
704        .with("surface0", rgb(0xee, 0xe8, 0xd5)) // base2
705        .with("surface1", rgb(0xdc, 0xd6, 0xc4))
706        .with("surface2", rgb(0x93, 0xa1, 0xa1)) // base1
707        // ---- Specific tints (LIGHT) ----
708        .with("diff.add.bg", rgb(0xdc, 0xe8, 0xc0))
709        .with("diff.change.bg", rgb(0xf0, 0xe6, 0xbe))
710        .with("diff.deletion.bg", rgb(0xf2, 0xd5, 0xc8))
711        .with("diff.conflict.bg", rgb(0xea, 0xd6, 0xdc))
712        .with("diff.add.refine.bg", rgb(0x9e, 0xe8, 0x8a))
713        .with("diff.remove.refine.bg", rgb(0xf2, 0x99, 0x90))
714        .with("cursor_line.bg", rgb(0xee, 0xe8, 0xd5))
715}
716
717/// Atom **One Dark** (official). Slate base (`#282c34`), light gray-blue
718/// text, the One blue/purple/green/red/cyan accents.
719pub fn one_dark_palette() -> Palette {
720    use NamedColor as N;
721    let rgb = Color::Rgb;
722    Palette::new()
723        // ---- One Dark accents ----
724        .with("text", rgb(0xab, 0xb2, 0xbf))
725        .with("overlay", rgb(0x5c, 0x63, 0x70))
726        .with("subtext", rgb(0x82, 0x89, 0x97))
727        .with("green", rgb(0x98, 0xc3, 0x79))
728        .with("purple", rgb(0xc6, 0x78, 0xdd))
729        .with("yellow", rgb(0xe5, 0xc0, 0x7b))
730        .with("orange", rgb(0xd1, 0x9a, 0x66))
731        .with("blue", rgb(0x61, 0xaf, 0xef))
732        .with("teal", rgb(0x56, 0xb6, 0xc2))
733        .with("red", rgb(0xe0, 0x6c, 0x75))
734        .with("maroon", rgb(0xbe, 0x50, 0x46))
735        .with("pink", rgb(0xc6, 0x78, 0xdd))
736        .with("cyan", rgb(0x56, 0xb6, 0xc2))
737        // ---- ANSI-named chrome (theme-independent) ----
738        .with("ansi.red", Color::Named(N::Red))
739        .with("ansi.green", Color::Named(N::Green))
740        .with("ansi.yellow", Color::Named(N::Yellow))
741        .with("ansi.blue", Color::Named(N::Blue))
742        .with("ansi.magenta", Color::Named(N::Magenta))
743        .with("ansi.cyan", Color::Named(N::Cyan))
744        .with("ansi.darkgray", Color::Named(N::DarkGray))
745        // ---- Background family (dark) ----
746        .with("base", rgb(0x28, 0x2c, 0x34))
747        .with("mantle", rgb(0x23, 0x27, 0x2e))
748        .with("crust", rgb(0x1e, 0x22, 0x27))
749        .with("surface0", rgb(0x33, 0x37, 0x40))
750        .with("surface1", rgb(0x3e, 0x44, 0x51))
751        .with("surface2", rgb(0x4b, 0x52, 0x63))
752        // ---- Specific tints (dark) ----
753        .with("diff.add.bg", rgb(0, 50, 0))
754        .with("diff.change.bg", rgb(50, 50, 0))
755        .with("diff.deletion.bg", rgb(60, 0, 0))
756        .with("diff.conflict.bg", rgb(60, 0, 60))
757        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
758        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
759        .with("cursor_line.bg", rgb(0x2c, 0x31, 0x3a))
760}
761
762/// Atom **One Light** (official). Near-white base (`#fafafa`), dark slate
763/// text, the deeper One-Light accent variants for contrast.
764pub fn one_light_palette() -> Palette {
765    use NamedColor as N;
766    let rgb = Color::Rgb;
767    Palette::new()
768        // ---- One Light accents ----
769        .with("text", rgb(0x38, 0x3a, 0x42))
770        .with("overlay", rgb(0xa0, 0xa1, 0xa7))
771        .with("subtext", rgb(0x69, 0x6c, 0x77))
772        .with("green", rgb(0x50, 0xa1, 0x4f))
773        .with("purple", rgb(0xa6, 0x26, 0xa4))
774        .with("yellow", rgb(0xc1, 0x84, 0x01))
775        .with("orange", rgb(0x98, 0x66, 0x01))
776        .with("blue", rgb(0x40, 0x78, 0xf2))
777        .with("teal", rgb(0x01, 0x84, 0xbc))
778        .with("red", rgb(0xe4, 0x50, 0x49))
779        .with("maroon", rgb(0xca, 0x12, 0x43))
780        .with("pink", rgb(0xa6, 0x26, 0xa4))
781        .with("cyan", rgb(0x01, 0x84, 0xbc))
782        // ---- ANSI-named chrome (theme-independent) ----
783        .with("ansi.red", Color::Named(N::Red))
784        .with("ansi.green", Color::Named(N::Green))
785        .with("ansi.yellow", Color::Named(N::Yellow))
786        .with("ansi.blue", Color::Named(N::Blue))
787        .with("ansi.magenta", Color::Named(N::Magenta))
788        .with("ansi.cyan", Color::Named(N::Cyan))
789        .with("ansi.darkgray", Color::Named(N::DarkGray))
790        // ---- Background family (LIGHT) ----
791        .with("base", rgb(0xfa, 0xfa, 0xfa))
792        .with("mantle", rgb(0xf0, 0xf0, 0xf0))
793        .with("crust", rgb(0xe5, 0xe5, 0xe6))
794        .with("surface0", rgb(0xe5, 0xe5, 0xe6))
795        .with("surface1", rgb(0xd4, 0xd4, 0xd5))
796        .with("surface2", rgb(0xc2, 0xc2, 0xc3))
797        // ---- Specific tints (LIGHT) ----
798        .with("diff.add.bg", rgb(0xd5, 0xe8, 0xc8))
799        .with("diff.change.bg", rgb(0xf0, 0xe8, 0xc4))
800        .with("diff.deletion.bg", rgb(0xf5, 0xd2, 0xd2))
801        .with("diff.conflict.bg", rgb(0xee, 0xd2, 0xe8))
802        .with("diff.add.refine.bg", rgb(0x99, 0xe8, 0x90))
803        .with("diff.remove.refine.bg", rgb(0xf5, 0x97, 0x97))
804        .with("cursor_line.bg", rgb(0xee, 0xee, 0xee))
805}
806
807/// Everforest **dark** (official, medium contrast). Warm green-tinted
808/// dark base (`#2d353b`), soft fg, the muted forest accents.
809pub fn everforest_dark_palette() -> Palette {
810    use NamedColor as N;
811    let rgb = Color::Rgb;
812    Palette::new()
813        // ---- Everforest dark accents (medium) ----
814        .with("text", rgb(0xd3, 0xc6, 0xaa))
815        .with("overlay", rgb(0x7a, 0x84, 0x78))
816        .with("subtext", rgb(0x9d, 0xa9, 0xa0))
817        .with("green", rgb(0xa7, 0xc0, 0x80))
818        .with("purple", rgb(0xd6, 0x99, 0xb6))
819        .with("yellow", rgb(0xdb, 0xbc, 0x7f))
820        .with("orange", rgb(0xe6, 0x98, 0x75))
821        .with("blue", rgb(0x7f, 0xbb, 0xb3))
822        .with("teal", rgb(0x83, 0xc0, 0x92))
823        .with("red", rgb(0xe6, 0x7e, 0x80))
824        .with("maroon", rgb(0xd6, 0x69, 0x6b))
825        .with("pink", rgb(0xd6, 0x99, 0xb6))
826        .with("cyan", rgb(0x7f, 0xbb, 0xb3))
827        // ---- ANSI-named chrome (theme-independent) ----
828        .with("ansi.red", Color::Named(N::Red))
829        .with("ansi.green", Color::Named(N::Green))
830        .with("ansi.yellow", Color::Named(N::Yellow))
831        .with("ansi.blue", Color::Named(N::Blue))
832        .with("ansi.magenta", Color::Named(N::Magenta))
833        .with("ansi.cyan", Color::Named(N::Cyan))
834        .with("ansi.darkgray", Color::Named(N::DarkGray))
835        // ---- Background family (dark) ----
836        .with("base", rgb(0x2d, 0x35, 0x3b))
837        .with("mantle", rgb(0x27, 0x2e, 0x33))
838        .with("crust", rgb(0x23, 0x2a, 0x2e))
839        .with("surface0", rgb(0x34, 0x3f, 0x44))
840        .with("surface1", rgb(0x3d, 0x48, 0x4d))
841        .with("surface2", rgb(0x47, 0x54, 0x58))
842        // ---- Specific tints (dark) ----
843        .with("diff.add.bg", rgb(0, 50, 0))
844        .with("diff.change.bg", rgb(50, 50, 0))
845        .with("diff.deletion.bg", rgb(60, 0, 0))
846        .with("diff.conflict.bg", rgb(60, 0, 60))
847        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
848        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
849        .with("cursor_line.bg", rgb(0x34, 0x3f, 0x44))
850}
851
852/// Everforest **light** (official, medium contrast). Warm cream base
853/// (`#fdf6e3`), dark green-gray text, the deeper light-mode forest accents.
854pub fn everforest_light_palette() -> Palette {
855    use NamedColor as N;
856    let rgb = Color::Rgb;
857    Palette::new()
858        // ---- Everforest light accents (medium) ----
859        .with("text", rgb(0x5c, 0x6a, 0x72))
860        .with("overlay", rgb(0xa6, 0xb0, 0xa0))
861        .with("subtext", rgb(0x82, 0x91, 0x86))
862        .with("green", rgb(0x8d, 0xa1, 0x01))
863        .with("purple", rgb(0xdf, 0x69, 0xba))
864        .with("yellow", rgb(0xdf, 0xa0, 0x00))
865        .with("orange", rgb(0xf5, 0x7d, 0x26))
866        .with("blue", rgb(0x35, 0x79, 0x6e))
867        .with("teal", rgb(0x35, 0xa7, 0x7c))
868        .with("red", rgb(0xf8, 0x55, 0x52))
869        .with("maroon", rgb(0xc2, 0x36, 0x33))
870        .with("pink", rgb(0xdf, 0x69, 0xba))
871        .with("cyan", rgb(0x35, 0x79, 0x6e))
872        // ---- ANSI-named chrome (theme-independent) ----
873        .with("ansi.red", Color::Named(N::Red))
874        .with("ansi.green", Color::Named(N::Green))
875        .with("ansi.yellow", Color::Named(N::Yellow))
876        .with("ansi.blue", Color::Named(N::Blue))
877        .with("ansi.magenta", Color::Named(N::Magenta))
878        .with("ansi.cyan", Color::Named(N::Cyan))
879        .with("ansi.darkgray", Color::Named(N::DarkGray))
880        // ---- Background family (LIGHT) ----
881        .with("base", rgb(0xfd, 0xf6, 0xe3))
882        .with("mantle", rgb(0xf4, 0xf0, 0xd9))
883        .with("crust", rgb(0xef, 0xeb, 0xd4))
884        .with("surface0", rgb(0xef, 0xeb, 0xd4))
885        .with("surface1", rgb(0xe6, 0xe2, 0xcc))
886        .with("surface2", rgb(0xbd, 0xc3, 0xaf))
887        // ---- Specific tints (LIGHT) ----
888        .with("diff.add.bg", rgb(0xdc, 0xe8, 0xc0))
889        .with("diff.change.bg", rgb(0xf0, 0xe6, 0xbe))
890        .with("diff.deletion.bg", rgb(0xf2, 0xd5, 0xc8))
891        .with("diff.conflict.bg", rgb(0xea, 0xd6, 0xdc))
892        .with("diff.add.refine.bg", rgb(0x9e, 0xe8, 0x8a))
893        .with("diff.remove.refine.bg", rgb(0xf2, 0x99, 0x90))
894        .with("cursor_line.bg", rgb(0xef, 0xeb, 0xd4))
895}
896
897/// Rosé Pine **dark** (official "main"). Base `#191724`, soft `text`
898/// `#e0def4`, the muted rose/pine/foam/iris accents.
899pub fn rosepine_dark_palette() -> Palette {
900    use NamedColor as N;
901    let rgb = Color::Rgb;
902    Palette::new()
903        // ---- Rosé Pine accents ----
904        .with("text", rgb(0xe0, 0xde, 0xf4))
905        .with("overlay", rgb(0x6e, 0x6a, 0x86))
906        .with("subtext", rgb(0x90, 0x8c, 0xaa))
907        .with("green", rgb(0x9c, 0xcf, 0xd8)) // foam (no true green; nearest)
908        .with("purple", rgb(0xc4, 0xa7, 0xe7)) // iris
909        .with("yellow", rgb(0xf6, 0xc1, 0x77)) // gold
910        .with("orange", rgb(0xeb, 0xbc, 0xba)) // rose (warm) nearest orange
911        .with("blue", rgb(0x31, 0x74, 0x8f)) // pine
912        .with("teal", rgb(0x9c, 0xcf, 0xd8)) // foam
913        .with("red", rgb(0xeb, 0x6f, 0x92)) // love
914        .with("maroon", rgb(0xb4, 0x63, 0x7a))
915        .with("pink", rgb(0xeb, 0xbc, 0xba)) // rose
916        .with("cyan", rgb(0x9c, 0xcf, 0xd8)) // foam
917        // ---- ANSI-named chrome (theme-independent) ----
918        .with("ansi.red", Color::Named(N::Red))
919        .with("ansi.green", Color::Named(N::Green))
920        .with("ansi.yellow", Color::Named(N::Yellow))
921        .with("ansi.blue", Color::Named(N::Blue))
922        .with("ansi.magenta", Color::Named(N::Magenta))
923        .with("ansi.cyan", Color::Named(N::Cyan))
924        .with("ansi.darkgray", Color::Named(N::DarkGray))
925        // ---- Background family (dark) ----
926        .with("base", rgb(0x19, 0x17, 0x24))
927        .with("mantle", rgb(0x1f, 0x1d, 0x2e)) // surface
928        .with("crust", rgb(0x15, 0x13, 0x20))
929        .with("surface0", rgb(0x1f, 0x1d, 0x2e)) // surface
930        .with("surface1", rgb(0x26, 0x23, 0x3a)) // overlay
931        .with("surface2", rgb(0x40, 0x3d, 0x52)) // highlight med
932        // ---- Specific tints (dark) ----
933        .with("diff.add.bg", rgb(0, 50, 0))
934        .with("diff.change.bg", rgb(50, 50, 0))
935        .with("diff.deletion.bg", rgb(60, 0, 0))
936        .with("diff.conflict.bg", rgb(60, 0, 60))
937        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
938        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
939        .with("cursor_line.bg", rgb(0x21, 0x1f, 0x30))
940}
941
942/// Rosé Pine **Dawn** (official light flavour). Base `#faf4ed`, dark text
943/// `#575279`, the warmer light-mode rose/pine/foam/iris accents.
944pub fn rosepine_light_palette() -> Palette {
945    use NamedColor as N;
946    let rgb = Color::Rgb;
947    Palette::new()
948        // ---- Rosé Pine Dawn accents ----
949        .with("text", rgb(0x57, 0x52, 0x79))
950        .with("overlay", rgb(0x98, 0x93, 0xa5))
951        .with("subtext", rgb(0x79, 0x74, 0x93))
952        .with("green", rgb(0x56, 0x94, 0x9f)) // foam
953        .with("purple", rgb(0x90, 0x7a, 0xa9)) // iris
954        .with("yellow", rgb(0xea, 0x9d, 0x34)) // gold
955        .with("orange", rgb(0xd7, 0x82, 0x7e)) // rose
956        .with("blue", rgb(0x28, 0x69, 0x83)) // pine
957        .with("teal", rgb(0x56, 0x94, 0x9f)) // foam
958        .with("red", rgb(0xb4, 0x63, 0x7a)) // love
959        .with("maroon", rgb(0x9d, 0x4f, 0x66))
960        .with("pink", rgb(0xd7, 0x82, 0x7e)) // rose
961        .with("cyan", rgb(0x56, 0x94, 0x9f)) // foam
962        // ---- ANSI-named chrome (theme-independent) ----
963        .with("ansi.red", Color::Named(N::Red))
964        .with("ansi.green", Color::Named(N::Green))
965        .with("ansi.yellow", Color::Named(N::Yellow))
966        .with("ansi.blue", Color::Named(N::Blue))
967        .with("ansi.magenta", Color::Named(N::Magenta))
968        .with("ansi.cyan", Color::Named(N::Cyan))
969        .with("ansi.darkgray", Color::Named(N::DarkGray))
970        // ---- Background family (LIGHT) ----
971        .with("base", rgb(0xfa, 0xf4, 0xed))
972        .with("mantle", rgb(0xff, 0xfa, 0xf3)) // surface
973        .with("crust", rgb(0xf2, 0xe9, 0xe1))
974        .with("surface0", rgb(0xff, 0xfa, 0xf3)) // surface
975        .with("surface1", rgb(0xf4, 0xed, 0xe8)) // overlay
976        .with("surface2", rgb(0xdf, 0xda, 0xd9)) // highlight med
977        // ---- Specific tints (LIGHT) ----
978        .with("diff.add.bg", rgb(0xdc, 0xe8, 0xcc))
979        .with("diff.change.bg", rgb(0xf0, 0xe6, 0xc8))
980        .with("diff.deletion.bg", rgb(0xf2, 0xd8, 0xd5))
981        .with("diff.conflict.bg", rgb(0xea, 0xd8, 0xe2))
982        .with("diff.add.refine.bg", rgb(0x9e, 0xe8, 0x92))
983        .with("diff.remove.refine.bg", rgb(0xf2, 0x9b, 0x99))
984        .with("cursor_line.bg", rgb(0xf4, 0xed, 0xe8))
985}
986
987/// Monokai **dark** (classic). Base `#272822`, fg `#f8f8f2`, the iconic
988/// pink/green/orange/blue/purple/yellow accents.
989pub fn monokai_dark_palette() -> Palette {
990    use NamedColor as N;
991    let rgb = Color::Rgb;
992    Palette::new()
993        // ---- Monokai accents ----
994        .with("text", rgb(0xf8, 0xf8, 0xf2))
995        .with("overlay", rgb(0x75, 0x71, 0x5e))
996        .with("subtext", rgb(0xa5, 0xa2, 0x8f))
997        .with("green", rgb(0xa6, 0xe2, 0x2e))
998        .with("purple", rgb(0xae, 0x81, 0xff))
999        .with("yellow", rgb(0xe6, 0xdb, 0x74))
1000        .with("orange", rgb(0xfd, 0x97, 0x1f))
1001        .with("blue", rgb(0x66, 0xd9, 0xef))
1002        .with("teal", rgb(0x66, 0xd9, 0xef))
1003        .with("red", rgb(0xf9, 0x26, 0x72))
1004        .with("maroon", rgb(0xd9, 0x1e, 0x5e))
1005        .with("pink", rgb(0xf9, 0x26, 0x72))
1006        .with("cyan", rgb(0x66, 0xd9, 0xef))
1007        // ---- ANSI-named chrome (theme-independent) ----
1008        .with("ansi.red", Color::Named(N::Red))
1009        .with("ansi.green", Color::Named(N::Green))
1010        .with("ansi.yellow", Color::Named(N::Yellow))
1011        .with("ansi.blue", Color::Named(N::Blue))
1012        .with("ansi.magenta", Color::Named(N::Magenta))
1013        .with("ansi.cyan", Color::Named(N::Cyan))
1014        .with("ansi.darkgray", Color::Named(N::DarkGray))
1015        // ---- Background family (dark) ----
1016        .with("base", rgb(0x27, 0x28, 0x22))
1017        .with("mantle", rgb(0x22, 0x23, 0x1e))
1018        .with("crust", rgb(0x1d, 0x1e, 0x19))
1019        .with("surface0", rgb(0x3e, 0x3d, 0x32))
1020        .with("surface1", rgb(0x49, 0x48, 0x3e))
1021        .with("surface2", rgb(0x75, 0x71, 0x5e))
1022        // ---- Specific tints (dark) ----
1023        .with("diff.add.bg", rgb(0, 50, 0))
1024        .with("diff.change.bg", rgb(50, 50, 0))
1025        .with("diff.deletion.bg", rgb(60, 0, 0))
1026        .with("diff.conflict.bg", rgb(60, 0, 60))
1027        .with("diff.add.refine.bg", rgb(0x00, 0x5c, 0x00))
1028        .with("diff.remove.refine.bg", rgb(0x6f, 0x00, 0x00))
1029        .with("cursor_line.bg", rgb(0x3e, 0x3d, 0x32))
1030}
1031
1032/// Monokai **light** (community-grade adaptation). Light base, dark text,
1033/// the Monokai accents deepened for legibility on a bright canvas. No
1034/// official Monokai light exists; this is a tuned community-style variant.
1035pub fn monokai_light_palette() -> Palette {
1036    use NamedColor as N;
1037    let rgb = Color::Rgb;
1038    Palette::new()
1039        // ---- Monokai light accents (deepened) ----
1040        .with("text", rgb(0x29, 0x2a, 0x24))
1041        .with("overlay", rgb(0x9b, 0x97, 0x82))
1042        .with("subtext", rgb(0x6e, 0x6a, 0x57))
1043        .with("green", rgb(0x67, 0x8c, 0x0e))
1044        .with("purple", rgb(0x7c, 0x4d, 0xd8))
1045        .with("yellow", rgb(0xa6, 0x8c, 0x0c))
1046        .with("orange", rgb(0xc7, 0x66, 0x06))
1047        .with("blue", rgb(0x18, 0x8a, 0xa6))
1048        .with("teal", rgb(0x18, 0x8a, 0xa6))
1049        .with("red", rgb(0xd1, 0x0d, 0x52))
1050        .with("maroon", rgb(0xa8, 0x0a, 0x42))
1051        .with("pink", rgb(0xd1, 0x0d, 0x52))
1052        .with("cyan", rgb(0x18, 0x8a, 0xa6))
1053        // ---- ANSI-named chrome (theme-independent) ----
1054        .with("ansi.red", Color::Named(N::Red))
1055        .with("ansi.green", Color::Named(N::Green))
1056        .with("ansi.yellow", Color::Named(N::Yellow))
1057        .with("ansi.blue", Color::Named(N::Blue))
1058        .with("ansi.magenta", Color::Named(N::Magenta))
1059        .with("ansi.cyan", Color::Named(N::Cyan))
1060        .with("ansi.darkgray", Color::Named(N::DarkGray))
1061        // ---- Background family (LIGHT) ----
1062        .with("base", rgb(0xfa, 0xfa, 0xf4))
1063        .with("mantle", rgb(0xf1, 0xf1, 0xe8))
1064        .with("crust", rgb(0xe6, 0xe6, 0xda))
1065        .with("surface0", rgb(0xe6, 0xe6, 0xda))
1066        .with("surface1", rgb(0xd6, 0xd6, 0xc8))
1067        .with("surface2", rgb(0xc4, 0xc4, 0xb4))
1068        // ---- Specific tints (LIGHT) ----
1069        .with("diff.add.bg", rgb(0xdc, 0xe8, 0xc4))
1070        .with("diff.change.bg", rgb(0xf0, 0xe8, 0xbe))
1071        .with("diff.deletion.bg", rgb(0xf2, 0xd2, 0xd8))
1072        .with("diff.conflict.bg", rgb(0xea, 0xd2, 0xe2))
1073        .with("diff.add.refine.bg", rgb(0x9e, 0xe8, 0x8d))
1074        .with("diff.remove.refine.bg", rgb(0xf2, 0x97, 0x9b))
1075        .with("cursor_line.bg", rgb(0xe6, 0xe6, 0xda))
1076}
1077
1078#[cfg(test)]
1079mod tests {
1080    use super::*;
1081
1082    #[test]
1083    fn default_palette_resolves_catppuccin_and_ansi() {
1084        let p = default_palette();
1085        assert_eq!(p.get(&"purple".into()), Some(Color::Rgb(0xcb, 0xa6, 0xf7)));
1086        assert_eq!(
1087            p.get(&"ansi.red".into()),
1088            Some(Color::Named(NamedColor::Red))
1089        );
1090        assert_eq!(p.get(&"cursor_line.bg".into()), Some(Color::Indexed(236)));
1091    }
1092
1093    #[test]
1094    fn missing_key_is_none_not_panic() {
1095        let p = default_palette();
1096        assert_eq!(p.get(&"no.such.key".into()), None);
1097    }
1098
1099    #[test]
1100    fn macchiato_palette_differs_in_accents_shares_ansi() {
1101        // T.9.b: Macchiato's accents differ from mocha's, but the
1102        // `ansi.*` chrome entries stay identical (degradation intent),
1103        // and `cursor_line.bg` is a distinct Macchiato tint.
1104        let mocha = default_palette();
1105        let mac = macchiato_palette();
1106        assert_eq!(
1107            mac.get(&"purple".into()),
1108            Some(Color::Rgb(0xc6, 0xa0, 0xf6))
1109        );
1110        assert_ne!(mac.get(&"purple".into()), mocha.get(&"purple".into()));
1111        assert_eq!(mac.get(&"text".into()), Some(Color::Rgb(0xca, 0xd3, 0xf5)));
1112        // ANSI chrome identical to mocha.
1113        assert_eq!(mac.get(&"ansi.red".into()), mocha.get(&"ansi.red".into()));
1114        assert_eq!(
1115            mac.get(&"ansi.darkgray".into()),
1116            mocha.get(&"ansi.darkgray".into())
1117        );
1118        // cursor_line.bg is a distinct Macchiato RGB tint.
1119        assert_eq!(
1120            mac.get(&"cursor_line.bg".into()),
1121            Some(Color::Rgb(0x36, 0x3a, 0x4f))
1122        );
1123        assert_ne!(
1124            mac.get(&"cursor_line.bg".into()),
1125            mocha.get(&"cursor_line.bg".into())
1126        );
1127        // diff bgs identical to mocha.
1128        assert_eq!(
1129            mac.get(&"diff.add.bg".into()),
1130            mocha.get(&"diff.add.bg".into())
1131        );
1132        // Same key set (same count).
1133        assert_eq!(mac.len(), mocha.len());
1134    }
1135}