Added theme support

Mikayla Maki created

Change summary

crates/terminal/src/element.rs     | 72 ++++++++++++++++---------------
crates/theme/src/theme.rs          | 34 +++++++++++++++
styles/src/styleTree/app.ts        |  2 
styles/src/styleTree/terminal.ts   | 35 +++++++++++++++
styles/src/themes/cave.ts          |  3 +
styles/src/themes/common/base16.ts | 22 +++++++--
styles/src/themes/common/theme.ts  |  3 +
7 files changed, 131 insertions(+), 40 deletions(-)

Detailed changes

crates/terminal/src/element.rs 🔗

@@ -23,6 +23,7 @@ use mio_extras::channel::Sender;
 use ordered_float::OrderedFloat;
 use settings::Settings;
 use std::sync::Arc;
+use theme::TerminalStyle;
 
 use crate::{Input, ZedListener};
 
@@ -62,7 +63,8 @@ impl Element for TerminalEl {
     ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
         let size = constraint.max;
         let settings = cx.global::<Settings>();
-        let theme = &settings.theme.editor;
+        let editor_theme = &settings.theme.editor;
+        let terminal_theme = &settings.theme.terminal;
         //Get terminal
         let mut term = self.term.lock();
 
@@ -78,7 +80,7 @@ impl Element for TerminalEl {
         let font_size = settings.buffer_font_size;
 
         let text_style = TextStyle {
-            color: theme.text_color,
+            color: editor_theme.text_color,
             font_family_id: settings.buffer_font_family,
             font_family_name,
             font_id,
@@ -122,7 +124,7 @@ impl Element for TerminalEl {
                 }, //TODO: Learn what 'CellExtra does'
             } = cell;
 
-            let new_highlight = make_style_from_cell(fg, flags);
+            let new_highlight = make_style_from_cell(fg, flags, &terminal_theme);
 
             if line != last_line {
                 line_count += 1;
@@ -248,8 +250,8 @@ impl Element for TerminalEl {
     }
 }
 
-fn make_style_from_cell(fg: &AnsiColor, flags: &Flags) -> HighlightStyle {
-    let fg = Some(alac_color_to_gpui_color(fg));
+fn make_style_from_cell(fg: &AnsiColor, flags: &Flags, style: &TerminalStyle) -> HighlightStyle {
+    let fg = Some(alac_color_to_gpui_color(fg, style));
     let underline = if flags.contains(Flags::UNDERLINE) {
         Some(Underline {
             color: fg,
@@ -266,38 +268,38 @@ fn make_style_from_cell(fg: &AnsiColor, flags: &Flags) -> HighlightStyle {
     }
 }
 
-fn alac_color_to_gpui_color(allac_color: &AnsiColor) -> Color {
+fn alac_color_to_gpui_color(allac_color: &AnsiColor, style: &TerminalStyle) -> Color {
     match allac_color {
         alacritty_terminal::ansi::Color::Named(n) => match n {
-            alacritty_terminal::ansi::NamedColor::Black => Color::black(),
-            alacritty_terminal::ansi::NamedColor::Red => Color::red(),
-            alacritty_terminal::ansi::NamedColor::Green => Color::green(),
-            alacritty_terminal::ansi::NamedColor::Yellow => Color::yellow(),
-            alacritty_terminal::ansi::NamedColor::Blue => Color::blue(),
-            alacritty_terminal::ansi::NamedColor::Magenta => Color::new(188, 63, 188, 1),
-            alacritty_terminal::ansi::NamedColor::Cyan => Color::new(17, 168, 205, 1),
-            alacritty_terminal::ansi::NamedColor::White => Color::white(),
-            alacritty_terminal::ansi::NamedColor::BrightBlack => Color::new(102, 102, 102, 1),
-            alacritty_terminal::ansi::NamedColor::BrightRed => Color::new(102, 102, 102, 1),
-            alacritty_terminal::ansi::NamedColor::BrightGreen => Color::new(35, 209, 139, 1),
-            alacritty_terminal::ansi::NamedColor::BrightYellow => Color::new(245, 245, 67, 1),
-            alacritty_terminal::ansi::NamedColor::BrightBlue => Color::new(59, 142, 234, 1),
-            alacritty_terminal::ansi::NamedColor::BrightMagenta => Color::new(214, 112, 214, 1),
-            alacritty_terminal::ansi::NamedColor::BrightCyan => Color::new(41, 184, 219, 1),
-            alacritty_terminal::ansi::NamedColor::BrightWhite => Color::new(229, 229, 229, 1),
-            alacritty_terminal::ansi::NamedColor::Foreground => Color::white(),
-            alacritty_terminal::ansi::NamedColor::Background => Color::black(),
-            alacritty_terminal::ansi::NamedColor::Cursor => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimBlack => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimRed => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimGreen => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimYellow => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimBlue => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimMagenta => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimCyan => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimWhite => Color::white(),
-            alacritty_terminal::ansi::NamedColor::BrightForeground => Color::white(),
-            alacritty_terminal::ansi::NamedColor::DimForeground => Color::white(),
+            alacritty_terminal::ansi::NamedColor::Black => style.black,
+            alacritty_terminal::ansi::NamedColor::Red => style.red,
+            alacritty_terminal::ansi::NamedColor::Green => style.green,
+            alacritty_terminal::ansi::NamedColor::Yellow => style.yellow,
+            alacritty_terminal::ansi::NamedColor::Blue => style.blue,
+            alacritty_terminal::ansi::NamedColor::Magenta => style.magenta,
+            alacritty_terminal::ansi::NamedColor::Cyan => style.cyan,
+            alacritty_terminal::ansi::NamedColor::White => style.white,
+            alacritty_terminal::ansi::NamedColor::BrightBlack => style.bright_black,
+            alacritty_terminal::ansi::NamedColor::BrightRed => style.bright_red,
+            alacritty_terminal::ansi::NamedColor::BrightGreen => style.bright_green,
+            alacritty_terminal::ansi::NamedColor::BrightYellow => style.bright_yellow,
+            alacritty_terminal::ansi::NamedColor::BrightBlue => style.bright_blue,
+            alacritty_terminal::ansi::NamedColor::BrightMagenta => style.bright_magenta,
+            alacritty_terminal::ansi::NamedColor::BrightCyan => style.bright_cyan,
+            alacritty_terminal::ansi::NamedColor::BrightWhite => style.bright_white,
+            alacritty_terminal::ansi::NamedColor::Foreground => style.foreground,
+            alacritty_terminal::ansi::NamedColor::Background => style.background,
+            alacritty_terminal::ansi::NamedColor::Cursor => style.cursor,
+            alacritty_terminal::ansi::NamedColor::DimBlack => style.dim_black,
+            alacritty_terminal::ansi::NamedColor::DimRed => style.dim_red,
+            alacritty_terminal::ansi::NamedColor::DimGreen => style.dim_green,
+            alacritty_terminal::ansi::NamedColor::DimYellow => style.dim_yellow,
+            alacritty_terminal::ansi::NamedColor::DimBlue => style.dim_blue,
+            alacritty_terminal::ansi::NamedColor::DimMagenta => style.dim_magenta,
+            alacritty_terminal::ansi::NamedColor::DimCyan => style.dim_cyan,
+            alacritty_terminal::ansi::NamedColor::DimWhite => style.dim_white,
+            alacritty_terminal::ansi::NamedColor::BrightForeground => style.bright_foreground,
+            alacritty_terminal::ansi::NamedColor::DimForeground => style.dim_foreground,
         }, //Theme defined
         alacritty_terminal::ansi::Color::Spec(rgb) => Color::new(rgb.r, rgb.g, rgb.b, 1),
         alacritty_terminal::ansi::Color::Indexed(_) => Color::white(), //Color cube weirdness

crates/theme/src/theme.rs 🔗

@@ -33,6 +33,7 @@ pub struct Theme {
     pub contact_notification: ContactNotification,
     pub update_notification: UpdateNotification,
     pub tooltip: TooltipStyle,
+    pub terminal: TerminalStyle,
 }
 
 #[derive(Deserialize, Default)]
@@ -633,3 +634,36 @@ pub struct HoverPopover {
     pub prose: TextStyle,
     pub highlight: Color,
 }
+
+#[derive(Clone, Deserialize, Default)]
+pub struct TerminalStyle {
+    pub black: Color,
+    pub red: Color,
+    pub green: Color,
+    pub yellow: Color,
+    pub blue: Color,
+    pub magenta: Color,
+    pub cyan: Color,
+    pub white: Color,
+    pub bright_black: Color,
+    pub bright_red: Color,
+    pub bright_green: Color,
+    pub bright_yellow: Color,
+    pub bright_blue: Color,
+    pub bright_magenta: Color,
+    pub bright_cyan: Color,
+    pub bright_white: Color,
+    pub foreground: Color,
+    pub background: Color,
+    pub cursor: Color,
+    pub dim_black: Color,
+    pub dim_red: Color,
+    pub dim_green: Color,
+    pub dim_yellow: Color,
+    pub dim_blue: Color,
+    pub dim_magenta: Color,
+    pub dim_cyan: Color,
+    pub dim_white: Color,
+    pub bright_foreground: Color,
+    pub dim_foreground: Color,
+}

styles/src/styleTree/app.ts 🔗

@@ -14,6 +14,7 @@ import projectDiagnostics from "./projectDiagnostics";
 import contactNotification from "./contactNotification";
 import updateNotification from "./updateNotification";
 import tooltip from "./tooltip";
+import terminal from "./terminal";
 
 export const panel = {
   padding: { top: 12, bottom: 12 },
@@ -41,5 +42,6 @@ export default function app(theme: Theme): Object {
     contactNotification: contactNotification(theme),
     updateNotification: updateNotification(theme),
     tooltip: tooltip(theme),
+    terminal: terminal(theme),
   };
 }

styles/src/styleTree/terminal.ts 🔗

@@ -0,0 +1,35 @@
+import Theme from "../themes/common/theme";
+
+export default function terminal(theme: Theme) {
+  return {
+    black: theme.ramps.neutral(0).hex(),
+    red: theme.ramps.red(0.5).hex(),
+    green: theme.ramps.green(0.5).hex(),
+    yellow: theme.ramps.yellow(0.5).hex(),
+    blue: theme.ramps.blue(0.5).hex(),
+    magenta: theme.ramps.magenta(0.5).hex(),
+    cyan: theme.ramps.cyan(0.5).hex(),
+    white: theme.ramps.neutral(7).hex(),
+    brightBlack: theme.ramps.neutral(2).hex(),
+    brightRed: theme.ramps.red(0.25).hex(),
+    brightGreen: theme.ramps.green(0.25).hex(),
+    brightYellow: theme.ramps.yellow(0.25).hex(),
+    brightBlue: theme.ramps.blue(0.25).hex(),
+    brightMagenta: theme.ramps.magenta(0.25).hex(),
+    brightCyan: theme.ramps.cyan(0.25).hex(),
+    brightWhite: theme.ramps.neutral(7).hex(),
+    foreground: theme.ramps.neutral(7).hex(),
+    background: theme.ramps.neutral(0).hex(),
+    cursor: theme.ramps.neutral(7).hex(),
+    dimBlack: theme.ramps.neutral(7).hex(),
+    dimRed: theme.ramps.red(0.75).hex(),
+    dimGreen: theme.ramps.green(0.75).hex(),
+    dimYellow: theme.ramps.yellow(0.75).hex(),
+    dimBlue: theme.ramps.blue(0.75).hex(),
+    dimMagenta: theme.ramps.magenta(0.75).hex(),
+    dimCyan: theme.ramps.cyan(0.75).hex(),
+    dimWhite: theme.ramps.neutral(5).hex(),
+    brightForeground: theme.ramps.neutral(7).hex(),
+    dimForeground: theme.ramps.neutral(0).hex(),
+  };
+}

styles/src/themes/cave.ts 🔗

@@ -26,3 +26,6 @@ const ramps = {
 
 export const dark = createTheme(`${name}-dark`, false, ramps);
 export const light = createTheme(`${name}-light`, true, ramps);
+
+console.log(JSON.stringify(dark.ramps.neutral.domain()))
+console.log(JSON.stringify(light.ramps.neutral.domain()))

styles/src/themes/common/base16.ts 🔗

@@ -13,15 +13,25 @@ export function colorRamp(color: Color): Scale {
 export function createTheme(
   name: string,
   isLight: boolean,
-  ramps: { [rampName: string]: Scale },
+  color_ramps: { [rampName: string]: Scale },
 ): Theme {
+  let ramps: typeof color_ramps = {};
+  // Chromajs mutates the underlying ramp when you call domain. This causes problems because
+  // we now store the ramps object in the theme so that we can pull colors out of them. 
+  // So instead of calling domain and storing the result, we have to construct new ramps for each
+  // theme so that we don't modify the passed in ramps.
+  // This combined with an error in the type definitions for chroma js means we have to cast the colors
+  // function to any in order to get the colors back out from the original ramps.
   if (isLight) {
-    for (var rampName in ramps) {
-      ramps[rampName] = ramps[rampName].domain([1, 0]);
+    for (var rampName in color_ramps) {
+      ramps[rampName] = chroma.scale((color_ramps[rampName].colors as any)()).domain([1, 0]);
     }
-    ramps.neutral = ramps.neutral.domain([7, 0]);
+    ramps.neutral = chroma.scale((color_ramps.neutral.colors as any)()).domain([7, 0]);
   } else {
-    ramps.neutral = ramps.neutral.domain([0, 7]);
+    for (var rampName in color_ramps) {
+      ramps[rampName] = chroma.scale((color_ramps[rampName].colors as any)()).domain([0, 1]);
+    }
+    ramps.neutral = chroma.scale((color_ramps.neutral.colors as any)()).domain([0, 7]);
   }
 
   let blend = isLight ? 0.12 : 0.24;
@@ -237,6 +247,7 @@ export function createTheme(
 
   return {
     name,
+    isLight,
     backgroundColor,
     borderColor,
     textColor,
@@ -245,5 +256,6 @@ export function createTheme(
     syntax,
     player,
     shadow,
+    ramps,
   };
 }

styles/src/themes/common/theme.ts 🔗

@@ -1,3 +1,4 @@
+import { Scale } from "chroma-js";
 import { FontWeight } from "../../common";
 import { withOpacity } from "../../utils/color";
 
@@ -60,6 +61,7 @@ export interface Syntax {
 
 export default interface Theme {
   name: string;
+  isLight: boolean,
   backgroundColor: {
     // Basically just Title Bar
     // Lowest background level
@@ -155,4 +157,5 @@ export default interface Theme {
     8: Player;
   },
   shadow: string;
+  ramps: { [rampName: string]: Scale };
 }