Skip to main content

vtcode_core/terminal_setup/features/
theme_sync.rs

1//! Theme synchronization feature configuration generator.
2//!
3//! Generates terminal-specific color scheme configuration to match VT Code themes.
4//! Supports dark and light theme variants.
5
6use crate::terminal_setup::detector::TerminalType;
7use anyhow::{Result, anyhow};
8
9/// Default VT Code dark theme colors
10pub struct VTCodeDarkTheme {
11    pub background: &'static str,
12    pub foreground: &'static str,
13    pub cursor: &'static str,
14    pub selection_bg: &'static str,
15    // ANSI colors (0-7)
16    pub black: &'static str,
17    pub red: &'static str,
18    pub green: &'static str,
19    pub yellow: &'static str,
20    pub blue: &'static str,
21    pub magenta: &'static str,
22    pub cyan: &'static str,
23    pub white: &'static str,
24    // Bright ANSI colors (8-15)
25    pub bright_black: &'static str,
26    pub bright_red: &'static str,
27    pub bright_green: &'static str,
28    pub bright_yellow: &'static str,
29    pub bright_blue: &'static str,
30    pub bright_magenta: &'static str,
31    pub bright_cyan: &'static str,
32    pub bright_white: &'static str,
33}
34
35impl Default for VTCodeDarkTheme {
36    fn default() -> Self {
37        Self {
38            background: "#1e1e1e",
39            foreground: "#d4d4d4",
40            cursor: "#ffffff",
41            selection_bg: "#264f78",
42            // ANSI colors
43            black: "#000000",
44            red: "#cd3131",
45            green: "#0dbc79",
46            yellow: "#e5e510",
47            blue: "#2472c8",
48            magenta: "#bc3fbc",
49            cyan: "#11a8cd",
50            white: "#e5e5e5",
51            // Bright variants
52            bright_black: "#666666",
53            bright_red: "#f14c4c",
54            bright_green: "#23d18b",
55            bright_yellow: "#f5f543",
56            bright_blue: "#3b8eea",
57            bright_magenta: "#d670d6",
58            bright_cyan: "#29b8db",
59            bright_white: "#ffffff",
60        }
61    }
62}
63
64impl VTCodeDarkTheme {
65    fn base16_colors(&self) -> [&'static str; 16] {
66        [
67            self.black,
68            self.red,
69            self.green,
70            self.yellow,
71            self.blue,
72            self.magenta,
73            self.cyan,
74            self.white,
75            self.bright_black,
76            self.bright_red,
77            self.bright_green,
78            self.bright_yellow,
79            self.bright_blue,
80            self.bright_magenta,
81            self.bright_cyan,
82            self.bright_white,
83        ]
84    }
85}
86
87#[derive(Clone, Copy, Debug)]
88struct Rgb {
89    r: u8,
90    g: u8,
91    b: u8,
92}
93
94#[derive(Clone, Copy, Debug)]
95struct Lab {
96    l: f64,
97    a: f64,
98    b: f64,
99}
100
101impl Rgb {
102    fn from_hex(hex: &str) -> Result<Self> {
103        let trimmed = hex.trim_start_matches('#');
104        if trimmed.len() != 6 {
105            return Err(anyhow!("Invalid hex color '{}': expected #RRGGBB", hex));
106        }
107
108        let r = u8::from_str_radix(&trimmed[0..2], 16)
109            .map_err(|_| anyhow!("Invalid red component in '{}'", hex))?;
110        let g = u8::from_str_radix(&trimmed[2..4], 16)
111            .map_err(|_| anyhow!("Invalid green component in '{}'", hex))?;
112        let b = u8::from_str_radix(&trimmed[4..6], 16)
113            .map_err(|_| anyhow!("Invalid blue component in '{}'", hex))?;
114
115        Ok(Self { r, g, b })
116    }
117
118    fn to_hex(self) -> String {
119        format!("#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
120    }
121
122    fn to_lab(self) -> Lab {
123        let r = srgb_to_linear(self.r as f64 / 255.0);
124        let g = srgb_to_linear(self.g as f64 / 255.0);
125        let b = srgb_to_linear(self.b as f64 / 255.0);
126
127        let x = r * 0.412_456_4 + g * 0.357_576_1 + b * 0.180_437_5;
128        let y = r * 0.212_672_9 + g * 0.715_152_2 + b * 0.072_175;
129        let z = r * 0.019_333_9 + g * 0.119_192 + b * 0.950_304_1;
130
131        let fx = lab_f(x / 0.95047);
132        let fy = lab_f(y);
133        let fz = lab_f(z / 1.08883);
134
135        Lab {
136            l: 116.0 * fy - 16.0,
137            a: 500.0 * (fx - fy),
138            b: 200.0 * (fy - fz),
139        }
140    }
141
142    fn from_lab(lab: Lab) -> Self {
143        let fy = (lab.l + 16.0) / 116.0;
144        let fx = fy + (lab.a / 500.0);
145        let fz = fy - (lab.b / 200.0);
146
147        let x = 0.95047 * lab_f_inv(fx);
148        let y = lab_f_inv(fy);
149        let z = 1.08883 * lab_f_inv(fz);
150
151        let r_linear = x * 3.240_454_2 + y * -1.537_138_5 + z * -0.498_531_4;
152        let g_linear = x * -0.969_266 + y * 1.876_010_8 + z * 0.041_556;
153        let b_linear = x * 0.055_643_4 + y * -0.204_025_9 + z * 1.057_225_2;
154
155        Self {
156            r: to_u8(linear_to_srgb(r_linear)),
157            g: to_u8(linear_to_srgb(g_linear)),
158            b: to_u8(linear_to_srgb(b_linear)),
159        }
160    }
161}
162
163fn srgb_to_linear(channel: f64) -> f64 {
164    if channel <= 0.04045 {
165        channel / 12.92
166    } else {
167        ((channel + 0.055) / 1.055).powf(2.4)
168    }
169}
170
171fn linear_to_srgb(channel: f64) -> f64 {
172    if channel <= 0.0031308 {
173        12.92 * channel
174    } else {
175        1.055 * channel.powf(1.0 / 2.4) - 0.055
176    }
177}
178
179fn lab_f(value: f64) -> f64 {
180    if value > 216.0 / 24389.0 {
181        value.cbrt()
182    } else {
183        (24389.0 / 27.0 * value + 16.0) / 116.0
184    }
185}
186
187fn lab_f_inv(value: f64) -> f64 {
188    let cube = value * value * value;
189    if cube > 216.0 / 24389.0 {
190        cube
191    } else {
192        (116.0 * value - 16.0) / (24389.0 / 27.0)
193    }
194}
195
196fn to_u8(value: f64) -> u8 {
197    (value.clamp(0.0, 1.0) * 255.0).round() as u8
198}
199
200fn lerp_lab(t: f64, start: Lab, end: Lab) -> Lab {
201    Lab {
202        l: start.l + t * (end.l - start.l),
203        a: start.a + t * (end.a - start.a),
204        b: start.b + t * (end.b - start.b),
205    }
206}
207
208fn generate_256_palette(theme: &VTCodeDarkTheme, harmonious: bool) -> Result<Vec<Rgb>> {
209    let base16 = theme
210        .base16_colors()
211        .iter()
212        .map(|color| Rgb::from_hex(color))
213        .collect::<Result<Vec<_>>>()?;
214
215    let background = Rgb::from_hex(theme.background)?;
216    let foreground = Rgb::from_hex(theme.foreground)?;
217
218    let mut base8_lab = [
219        background.to_lab(),
220        base16[1].to_lab(),
221        base16[2].to_lab(),
222        base16[3].to_lab(),
223        base16[4].to_lab(),
224        base16[5].to_lab(),
225        base16[6].to_lab(),
226        foreground.to_lab(),
227    ];
228
229    let is_light_theme = base8_lab[7].l < base8_lab[0].l;
230    if is_light_theme && !harmonious {
231        base8_lab.swap(0, 7);
232    }
233
234    let mut palette = base16;
235
236    for r in 0..6 {
237        let t_r = r as f64 / 5.0;
238        let c0 = lerp_lab(t_r, base8_lab[0], base8_lab[1]);
239        let c1 = lerp_lab(t_r, base8_lab[2], base8_lab[3]);
240        let c2 = lerp_lab(t_r, base8_lab[4], base8_lab[5]);
241        let c3 = lerp_lab(t_r, base8_lab[6], base8_lab[7]);
242
243        for g in 0..6 {
244            let t_g = g as f64 / 5.0;
245            let c4 = lerp_lab(t_g, c0, c1);
246            let c5 = lerp_lab(t_g, c2, c3);
247
248            for b in 0..6 {
249                let t_b = b as f64 / 5.0;
250                let color = lerp_lab(t_b, c4, c5);
251                palette.push(Rgb::from_lab(color));
252            }
253        }
254    }
255
256    for shade in 0..24 {
257        let t = (shade as f64 + 1.0) / 25.0;
258        let color = lerp_lab(t, base8_lab[0], base8_lab[7]);
259        palette.push(Rgb::from_lab(color));
260    }
261
262    Ok(palette)
263}
264
265fn ghostty_palette_lines(palette: &[Rgb]) -> String {
266    palette
267        .iter()
268        .enumerate()
269        .map(|(index, color)| format!("palette = {index}={}", color.to_hex()))
270        .collect::<Vec<_>>()
271        .join("\n")
272}
273
274fn kitty_palette_lines(palette: &[Rgb]) -> String {
275    palette
276        .iter()
277        .enumerate()
278        .map(|(index, color)| format!("color{index} {}", color.to_hex()))
279        .collect::<Vec<_>>()
280        .join("\n")
281}
282
283/// Generate theme configuration for the specified terminal
284pub fn generate_config(terminal: TerminalType) -> Result<String> {
285    let theme = VTCodeDarkTheme::default();
286    let generated_palette = generate_256_palette(&theme, false)?;
287
288    let config = match terminal {
289        TerminalType::Ghostty => {
290            let mut config = format!(
291                r#"# VT Code Dark Theme for Ghostty
292background = {background}
293foreground = {foreground}
294cursor-color = {cursor}
295selection-background = {selection_bg}
296"#,
297                background = theme.background,
298                foreground = theme.foreground,
299                cursor = theme.cursor,
300                selection_bg = theme.selection_bg,
301            );
302            config.push_str("\n# ANSI + 256-color palette generated from base colors\n");
303            config.push_str(&ghostty_palette_lines(&generated_palette));
304            config.push('\n');
305            config
306        }
307
308        TerminalType::Kitty => {
309            let mut config = format!(
310                r#"# VT Code Dark Theme for Kitty
311background {background}
312foreground {foreground}
313cursor {cursor}
314selection_background {selection_bg}
315"#,
316                background = theme.background,
317                foreground = theme.foreground,
318                cursor = theme.cursor,
319                selection_bg = theme.selection_bg,
320            );
321            config.push_str("\n# ANSI + 256-color palette generated from base colors\n");
322            config.push_str(&kitty_palette_lines(&generated_palette));
323            config.push('\n');
324            config
325        }
326
327        TerminalType::Alacritty => {
328            let mut config = format!(
329                r#"# VT Code Dark Theme for Alacritty
330[colors.primary]
331background = '{background}'
332foreground = '{foreground}'
333
334[colors.cursor]
335cursor = '{cursor}'
336
337[colors.selection]
338background = '{selection_bg}'
339
340[colors.normal]
341black = '{black}'
342red = '{red}'
343green = '{green}'
344yellow = '{yellow}'
345blue = '{blue}'
346magenta = '{magenta}'
347cyan = '{cyan}'
348white = '{white}'
349
350[colors.bright]
351black = '{bright_black}'
352red = '{bright_red}'
353green = '{bright_green}'
354yellow = '{bright_yellow}'
355blue = '{bright_blue}'
356magenta = '{bright_magenta}'
357cyan = '{bright_cyan}'
358white = '{bright_white}'
359"#,
360                background = theme.background,
361                foreground = theme.foreground,
362                cursor = theme.cursor,
363                selection_bg = theme.selection_bg,
364                black = theme.black,
365                red = theme.red,
366                green = theme.green,
367                yellow = theme.yellow,
368                blue = theme.blue,
369                magenta = theme.magenta,
370                cyan = theme.cyan,
371                white = theme.white,
372                bright_black = theme.bright_black,
373                bright_red = theme.bright_red,
374                bright_green = theme.bright_green,
375                bright_yellow = theme.bright_yellow,
376                bright_blue = theme.bright_blue,
377                bright_magenta = theme.bright_magenta,
378                bright_cyan = theme.bright_cyan,
379                bright_white = theme.bright_white,
380            );
381
382            config.push_str("\n# Extended indexed colors (16-255)\n");
383            for (index, color) in generated_palette.iter().enumerate().skip(16) {
384                config.push_str("[[colors.indexed_colors]]\n");
385                config.push_str(&format!("index = {index}\n"));
386                config.push_str(&format!("color = '{}'\n\n", color.to_hex()));
387            }
388
389            config
390        }
391
392        TerminalType::WezTerm => {
393            format!(
394                r#"-- VT Code Dark Theme for WezTerm
395return {{
396  colors = {{
397    background = "{background}",
398    foreground = "{foreground}",
399    cursor_bg = "{cursor}",
400    selection_bg = "{selection_bg}",
401  }},
402}}
403"#,
404                background = theme.background,
405                foreground = theme.foreground,
406                cursor = theme.cursor,
407                selection_bg = theme.selection_bg,
408            )
409        }
410
411        TerminalType::TerminalApp => {
412            r#"Terminal.app theme sync requires profile color configuration.
413Configure profile colors in Terminal → Settings → Profiles.
414"#
415            .to_string()
416        }
417
418        TerminalType::Xterm => {
419            r#"xterm theme sync is configured via X resources (e.g. ~/.Xresources).
420"#
421            .to_string()
422        }
423
424        TerminalType::Zed => {
425            format!(
426                r#"// VT Code Dark Theme for Zed
427{{
428  "theme": {{
429    "mode": "dark",
430    "terminal": {{
431      "background": "{background}",
432      "foreground": "{foreground}",
433      "cursor": "{cursor}",
434      "selectionBackground": "{selection_bg}",
435      "ansiBlack": "{black}",
436      "ansiRed": "{red}",
437      "ansiGreen": "{green}",
438      "ansiYellow": "{yellow}",
439      "ansiBlue": "{blue}",
440      "ansiMagenta": "{magenta}",
441      "ansiCyan": "{cyan}",
442      "ansiWhite": "{white}",
443      "ansiBrightBlack": "{bright_black}",
444      "ansiBrightRed": "{bright_red}",
445      "ansiBrightGreen": "{bright_green}",
446      "ansiBrightYellow": "{bright_yellow}",
447      "ansiBrightBlue": "{bright_blue}",
448      "ansiBrightMagenta": "{bright_magenta}",
449      "ansiBrightCyan": "{bright_cyan}",
450      "ansiBrightWhite": "{bright_white}"
451    }}
452  }}
453}}
454"#,
455                background = theme.background,
456                foreground = theme.foreground,
457                cursor = theme.cursor,
458                selection_bg = theme.selection_bg,
459                black = theme.black,
460                red = theme.red,
461                green = theme.green,
462                yellow = theme.yellow,
463                blue = theme.blue,
464                magenta = theme.magenta,
465                cyan = theme.cyan,
466                white = theme.white,
467                bright_black = theme.bright_black,
468                bright_red = theme.bright_red,
469                bright_green = theme.bright_green,
470                bright_yellow = theme.bright_yellow,
471                bright_blue = theme.bright_blue,
472                bright_magenta = theme.bright_magenta,
473                bright_cyan = theme.bright_cyan,
474                bright_white = theme.bright_white,
475            )
476        }
477
478        TerminalType::Warp => r#"# Warp Theme Synchronization
479# Warp uses its own theme system
480# To create a custom theme:
481# 1. Open Warp Settings
482# 2. Go to Appearance → Themes
483# 3. Click "New Theme" or "Import Theme"
484# 4. Use the VT Code color values provided in the wizard
485
486# VT Code colors are displayed in the terminal setup output
487# You can manually configure them in Warp's theme editor
488"#
489        .to_string(),
490
491        TerminalType::WindowsTerminal => {
492            format!(
493                r#"{{
494  "schemes": [
495    {{
496      "name": "VT Code Dark",
497      "background": "{background}",
498      "foreground": "{foreground}",
499      "cursorColor": "{cursor}",
500      "selectionBackground": "{selection_bg}",
501      "black": "{black}",
502      "red": "{red}",
503      "green": "{green}",
504      "yellow": "{yellow}",
505      "blue": "{blue}",
506      "purple": "{magenta}",
507      "cyan": "{cyan}",
508      "white": "{white}",
509      "brightBlack": "{bright_black}",
510      "brightRed": "{bright_red}",
511      "brightGreen": "{bright_green}",
512      "brightYellow": "{bright_yellow}",
513      "brightBlue": "{bright_blue}",
514      "brightPurple": "{bright_magenta}",
515      "brightCyan": "{bright_cyan}",
516      "brightWhite": "{bright_white}"
517    }}
518  ],
519  "profiles": {{
520    "defaults": {{
521      "colorScheme": "VT Code Dark"
522    }}
523  }}
524}}
525"#,
526                background = theme.background,
527                foreground = theme.foreground,
528                cursor = theme.cursor,
529                selection_bg = theme.selection_bg,
530                black = theme.black,
531                red = theme.red,
532                green = theme.green,
533                yellow = theme.yellow,
534                blue = theme.blue,
535                magenta = theme.magenta,
536                cyan = theme.cyan,
537                white = theme.white,
538                bright_black = theme.bright_black,
539                bright_red = theme.bright_red,
540                bright_green = theme.bright_green,
541                bright_yellow = theme.bright_yellow,
542                bright_blue = theme.bright_blue,
543                bright_magenta = theme.bright_magenta,
544                bright_cyan = theme.bright_cyan,
545                bright_white = theme.bright_white,
546            )
547        }
548
549        TerminalType::Hyper => {
550            format!(
551                r#"// VT Code Dark Theme for Hyper
552module.exports = {{
553  config: {{
554    backgroundColor: '{background}',
555    foregroundColor: '{foreground}',
556    cursorColor: '{cursor}',
557    selectionColor: '{selection_bg}',
558    colors: {{
559      black: '{black}',
560      red: '{red}',
561      green: '{green}',
562      yellow: '{yellow}',
563      blue: '{blue}',
564      magenta: '{magenta}',
565      cyan: '{cyan}',
566      white: '{white}',
567      lightBlack: '{bright_black}',
568      lightRed: '{bright_red}',
569      lightGreen: '{bright_green}',
570      lightYellow: '{bright_yellow}',
571      lightBlue: '{bright_blue}',
572      lightMagenta: '{bright_magenta}',
573      lightCyan: '{bright_cyan}',
574      lightWhite: '{bright_white}',
575    }}
576  }}
577}};
578"#,
579                background = theme.background,
580                foreground = theme.foreground,
581                cursor = theme.cursor,
582                selection_bg = theme.selection_bg,
583                black = theme.black,
584                red = theme.red,
585                green = theme.green,
586                yellow = theme.yellow,
587                blue = theme.blue,
588                magenta = theme.magenta,
589                cyan = theme.cyan,
590                white = theme.white,
591                bright_black = theme.bright_black,
592                bright_red = theme.bright_red,
593                bright_green = theme.bright_green,
594                bright_yellow = theme.bright_yellow,
595                bright_blue = theme.bright_blue,
596                bright_magenta = theme.bright_magenta,
597                bright_cyan = theme.bright_cyan,
598                bright_white = theme.bright_white,
599            )
600        }
601
602        TerminalType::Tabby => {
603            format!(
604                r#"# VT Code Dark Theme for Tabby
605appearance:
606  colorScheme:
607    name: "VT Code Dark"
608    foreground: "{foreground}"
609    background: "{background}"
610    cursor: "{cursor}"
611    selection: "{selection_bg}"
612    colors:
613      - "{black}"
614      - "{red}"
615      - "{green}"
616      - "{yellow}"
617      - "{blue}"
618      - "{magenta}"
619      - "{cyan}"
620      - "{white}"
621      - "{bright_black}"
622      - "{bright_red}"
623      - "{bright_green}"
624      - "{bright_yellow}"
625      - "{bright_blue}"
626      - "{bright_magenta}"
627      - "{bright_cyan}"
628      - "{bright_white}"
629"#,
630                background = theme.background,
631                foreground = theme.foreground,
632                cursor = theme.cursor,
633                selection_bg = theme.selection_bg,
634                black = theme.black,
635                red = theme.red,
636                green = theme.green,
637                yellow = theme.yellow,
638                blue = theme.blue,
639                magenta = theme.magenta,
640                cyan = theme.cyan,
641                white = theme.white,
642                bright_black = theme.bright_black,
643                bright_red = theme.bright_red,
644                bright_green = theme.bright_green,
645                bright_yellow = theme.bright_yellow,
646                bright_blue = theme.bright_blue,
647                bright_magenta = theme.bright_magenta,
648                bright_cyan = theme.bright_cyan,
649                bright_white = theme.bright_white,
650            )
651        }
652
653        TerminalType::ITerm2 => r#"Manual iTerm2 Theme Configuration:
654
6551. Open iTerm2 Preferences (Cmd+,)
6562. Go to Profiles → Colors
6573. Click "Color Presets..." → "Import..."
6584. Or manually configure colors:
659
660Background: #1e1e1e
661Foreground: #d4d4d4
662Cursor: #ffffff
663Selection: #264f78
664
665ANSI Colors:
666Black: #000000, Red: #cd3131, Green: #0dbc79, Yellow: #e5e510
667Blue: #2472c8, Magenta: #bc3fbc, Cyan: #11a8cd, White: #e5e5e5
668
669Bright Colors:
670Black: #666666, Red: #f14c4c, Green: #23d18b, Yellow: #f5f543
671Blue: #3b8eea, Magenta: #d670d6, Cyan: #29b8db, White: #ffffff
672
673Alternative: Download VT Code.itermcolors file and import
674"#
675        .to_string(),
676
677        TerminalType::VSCode => {
678            format!(
679                r#"VS Code Terminal Theme Configuration:
680
681The terminal automatically inherits your VS Code theme colors.
682
683To customize terminal colors independently, add to settings.json:
684{{
685  "workbench.colorCustomizations": {{
686    "terminal.background": "{background}",
687    "terminal.foreground": "{foreground}",
688    "terminalCursor.background": "{cursor}",
689    "terminal.selectionBackground": "{selection_bg}",
690    "terminal.ansiBlack": "{black}",
691    "terminal.ansiRed": "{red}",
692    "terminal.ansiGreen": "{green}",
693    "terminal.ansiYellow": "{yellow}",
694    "terminal.ansiBlue": "{blue}",
695    "terminal.ansiMagenta": "{magenta}",
696    "terminal.ansiCyan": "{cyan}",
697    "terminal.ansiWhite": "{white}",
698    "terminal.ansiBrightBlack": "{bright_black}",
699    "terminal.ansiBrightRed": "{bright_red}",
700    "terminal.ansiBrightGreen": "{bright_green}",
701    "terminal.ansiBrightYellow": "{bright_yellow}",
702    "terminal.ansiBrightBlue": "{bright_blue}",
703    "terminal.ansiBrightMagenta": "{bright_magenta}",
704    "terminal.ansiBrightCyan": "{bright_cyan}",
705    "terminal.ansiBrightWhite": "{bright_white}"
706  }}
707}}
708"#,
709                background = theme.background,
710                foreground = theme.foreground,
711                cursor = theme.cursor,
712                selection_bg = theme.selection_bg,
713                black = theme.black,
714                red = theme.red,
715                green = theme.green,
716                yellow = theme.yellow,
717                blue = theme.blue,
718                magenta = theme.magenta,
719                cyan = theme.cyan,
720                white = theme.white,
721                bright_black = theme.bright_black,
722                bright_red = theme.bright_red,
723                bright_green = theme.bright_green,
724                bright_yellow = theme.bright_yellow,
725                bright_blue = theme.bright_blue,
726                bright_magenta = theme.bright_magenta,
727                bright_cyan = theme.bright_cyan,
728                bright_white = theme.bright_white,
729            )
730        }
731
732        TerminalType::Unknown => {
733            anyhow::bail!("Cannot generate theme config for unknown terminal type");
734        }
735    };
736
737    Ok(config)
738}
739
740#[cfg(test)]
741mod tests {
742    use super::*;
743
744    #[test]
745    fn test_vtcode_dark_theme_defaults() {
746        let theme = VTCodeDarkTheme::default();
747        assert_eq!(theme.background, "#1e1e1e");
748        assert_eq!(theme.foreground, "#d4d4d4");
749        assert_eq!(theme.cursor, "#ffffff");
750    }
751
752    #[test]
753    fn test_generate_ghostty_config() {
754        let config = generate_config(TerminalType::Ghostty).unwrap();
755        assert!(config.contains("palette = 0="));
756        assert!(config.contains("palette = 255="));
757        assert!(config.contains("#1e1e1e"));
758    }
759
760    #[test]
761    fn test_generate_kitty_config() {
762        let config = generate_config(TerminalType::Kitty).unwrap();
763        assert!(config.contains("color0 "));
764        assert!(config.contains("color255 "));
765    }
766
767    #[test]
768    fn test_generate_alacritty_config() {
769        let config = generate_config(TerminalType::Alacritty).unwrap();
770        assert!(config.contains("[colors"));
771        assert!(config.contains("primary"));
772        assert!(config.contains("index = 255"));
773    }
774
775    #[test]
776    fn test_generate_windows_terminal_config() {
777        let config = generate_config(TerminalType::WindowsTerminal).unwrap();
778        assert!(config.contains("schemes"));
779        assert!(config.contains("VT Code Dark"));
780    }
781
782    #[test]
783    fn test_generate_vscode_instructions() {
784        let config = generate_config(TerminalType::VSCode).unwrap();
785        assert!(config.contains("workbench.colorCustomizations"));
786        assert!(config.contains("terminal.ansi"));
787    }
788
789    #[test]
790    fn test_unknown_terminal_error() {
791        let result = generate_config(TerminalType::Unknown);
792        result.unwrap_err();
793    }
794
795    #[test]
796    fn test_generate_config() {
797        // This test exists for backward compatibility with the stub
798        generate_config(TerminalType::Kitty).unwrap();
799    }
800
801    #[test]
802    fn test_generated_palette_has_256_entries_and_preserves_base16() {
803        let theme = VTCodeDarkTheme::default();
804        let palette = generate_256_palette(&theme, false).unwrap();
805
806        assert_eq!(palette.len(), 256);
807
808        let expected_base16 = theme
809            .base16_colors()
810            .iter()
811            .map(|c| Rgb::from_hex(c).unwrap().to_hex())
812            .collect::<Vec<_>>();
813        let actual_base16 = palette
814            .iter()
815            .take(16)
816            .map(|rgb| rgb.to_hex())
817            .collect::<Vec<_>>();
818
819        assert_eq!(actual_base16, expected_base16);
820    }
821}