WIP

Marshall Bowers created

Change summary

Cargo.lock                             |   1 
crates/gpui2/src/color.rs              |  24 ++
crates/theme2/Cargo.toml               |   1 
crates/theme2/src/registry.rs          |  41 ++++
crates/theme2/src/settings.rs          | 205 ++++++++++++++++++++++
crates/theme2/src/theme2.rs            | 115 +++++++++++-
crates/theme2/src/themes/mod.rs        |   3 
crates/theme2/src/themes/one_dark.rs   |  90 +++++++++
crates/ui2/src/color.rs                | 115 ++++++++++++
crates/ui2/src/components/workspace.rs |   4 
theme.txt                              | 254 ++++++++++++++++++++++++++++
11 files changed, 842 insertions(+), 11 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -8459,6 +8459,7 @@ dependencies = [
  "gpui2",
  "indexmap 1.9.3",
  "parking_lot 0.11.2",
+ "schemars",
  "serde",
  "serde_derive",
  "serde_json",

crates/gpui2/src/color.rs 🔗

@@ -11,6 +11,14 @@ pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
     Rgba { r, g, b, a: 1.0 }.into()
 }
 
+pub fn rgba(hex: u32) -> Rgba {
+    let r = ((hex >> 24) & 0xFF) as f32 / 255.0;
+    let g = ((hex >> 16) & 0xFF) as f32 / 255.0;
+    let b = ((hex >> 8) & 0xFF) as f32 / 255.0;
+    let a = (hex & 0xFF) as f32 / 255.0;
+    Rgba { r, g, b, a }
+}
+
 #[derive(Clone, Copy, Default, Debug)]
 pub struct Rgba {
     pub r: f32,
@@ -34,6 +42,14 @@ impl Rgba {
             };
         }
     }
+
+    pub fn to_hex(&self) -> String {
+        let r = (self.r * 255.0) as u32;
+        let g = (self.g * 255.0) as u32;
+        let b = (self.b * 255.0) as u32;
+        let a = (self.a * 255.0) as u32;
+        format!("rgba(0x{:02x}{:02x}{:02x}{:02x}).into()", r, g, b, a)
+    }
 }
 
 struct RgbaVisitor;
@@ -126,8 +142,16 @@ pub struct Hsla {
     pub a: f32,
 }
 
+impl Hsla {
+    pub fn to_rgb(self) -> Rgba {
+        self.into()
+    }
+}
+
+
 impl Eq for Hsla {}
 
+
 pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
     Hsla {
         h: h.clamp(0., 1.),

crates/theme2/Cargo.toml 🔗

@@ -18,6 +18,7 @@ doctest = false
 [dependencies]
 gpui2 = { path = "../gpui2" }
 fs = { path = "../fs" }
+schemars.workspace = true
 settings2 = { path = "../settings2" }
 util = { path = "../util" }
 

crates/theme2/src/registry.rs 🔗

@@ -0,0 +1,41 @@
+use crate::{Theme, ThemeMetadata, themes::one_dark};
+use anyhow::Result;
+use gpui2::{AnyAssetSource, SharedString};
+use std::{
+    collections::HashMap,
+    sync::Arc
+};
+
+pub struct ThemeRegistry {
+    themes: HashMap<SharedString, Arc<Theme>>,
+}
+
+impl ThemeRegistry {
+    pub fn new(assets: AnyAssetSource) -> Self {
+        let mut this = Self {
+            themes: HashMap::default(),
+        };
+
+        this.insert_themes([one_dark()]);
+
+        this
+    }
+
+    fn insert_themes(&mut self, themes: impl IntoIterator<Item = Theme>) {
+        for theme in themes.into_iter() {
+            self.themes.insert(theme.metadata.name.clone(), Arc::new(theme));
+        }
+    }
+
+    pub fn list_names(&self, staff: bool) -> impl Iterator<Item = SharedString> + '_ {
+        None.into_iter()
+    }
+
+    pub fn list(&self, staff: bool) -> impl Iterator<Item = ThemeMetadata> + '_ {
+        None.into_iter()
+    }
+
+    pub fn get(&self, name: impl Into<SharedString>) -> Result<Arc<Theme>> {
+        todo!()
+    }
+}

crates/theme2/src/settings.rs 🔗

@@ -0,0 +1,205 @@
+use crate::{Theme, ThemeRegistry};
+use anyhow::Result;
+use gpui2::{FontFeatures, SharedString, Font, AppContext, Pixels, px, FontWeight, FontStyle};
+use schemars::{
+    gen::SchemaGenerator,
+    schema::{InstanceType, Schema, SchemaObject},
+    JsonSchema,
+};
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+use settings2::SettingsJsonSchemaParams;
+use std::sync::Arc;
+use util::ResultExt as _;
+
+const MIN_FONT_SIZE: Pixels = px(6.0);
+const MIN_LINE_HEIGHT: f32 = 1.0;
+
+#[derive(Clone, JsonSchema)]
+pub struct ThemeSettings {
+    pub buffer_font: Font,
+    pub buffer_font_size: Pixels,
+    pub buffer_line_height: BufferLineHeight,
+    #[serde(skip)]
+    pub theme: Arc<Theme>,
+}
+
+pub struct AdjustedBufferFontSize(pub f32);
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
+pub struct ThemeSettingsContent {
+    #[serde(default)]
+    pub buffer_font_family: Option<SharedString>,
+    #[serde(default)]
+    pub buffer_font_size: Option<f32>,
+    #[serde(default)]
+    pub buffer_line_height: Option<BufferLineHeight>,
+    #[serde(default)]
+    pub buffer_font_features: Option<FontFeatures>,
+    #[serde(default)]
+    pub theme: Option<SharedString>,
+}
+
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
+#[serde(rename_all = "snake_case")]
+pub enum BufferLineHeight {
+    #[default]
+    Comfortable,
+    Standard,
+    Custom(f32),
+}
+
+impl BufferLineHeight {
+    pub fn value(&self) -> f32 {
+        match self {
+            BufferLineHeight::Comfortable => 1.618,
+            BufferLineHeight::Standard => 1.3,
+            BufferLineHeight::Custom(line_height) => *line_height,
+        }
+    }
+}
+
+impl ThemeSettings {
+    pub fn buffer_font_size(&self, cx: &AppContext) -> f32 {
+        if cx.has_global::<AdjustedBufferFontSize>() {
+            cx.global::<AdjustedBufferFontSize>().0
+        } else {
+            self.buffer_font_size
+        }
+        .max(MIN_FONT_SIZE)
+    }
+
+    pub fn line_height(&self) -> f32 {
+        f32::max(self.buffer_line_height.value(), MIN_LINE_HEIGHT)
+    }
+}
+
+pub fn adjusted_font_size(size: f32, cx: &AppContext) -> f32 {
+    if let Some(adjusted_size) = cx.try_global::<AdjustedBufferFontSize>() {
+        let buffer_font_size = settings2::get::<ThemeSettings>(cx).buffer_font_size;
+        let delta = adjusted_size - buffer_font_size;
+        size + delta
+    } else {
+        size
+    }.max(MIN_FONT_SIZE)
+}
+
+pub fn adjust_font_size(cx: &mut AppContext, f: fn(&mut Pixels)) {
+    if !cx.has_global::<AdjustedBufferFontSize>() {
+        let buffer_font_size = settings2::get::<ThemeSettings>(cx).buffer_font_size;
+        cx.set_global(AdjustedBufferFontSize(buffer_font_size));
+    }
+    let mut delta = cx.global_mut::<AdjustedBufferFontSize>();
+    f(&mut delta.0);
+    delta.0 = delta
+        .0
+        .max(MIN_FONT_SIZE - settings2::get::<ThemeSettings>(cx).buffer_font_size);
+    cx.refresh_windows();
+}
+
+pub fn reset_font_size(cx: &mut AppContext) {
+    if cx.has_global::<AdjustedBufferFontSize>() {
+        cx.remove_global::<AdjustedBufferFontSize>();
+        cx.refresh_windows();
+    }
+}
+
+impl settings2::Setting for ThemeSettings {
+    const KEY: Option<&'static str> = None;
+
+    type FileContent = ThemeSettingsContent;
+
+    fn load(
+        defaults: &Self::FileContent,
+        user_values: &[&Self::FileContent],
+        cx: &AppContext,
+    ) -> Result<Self> {
+        let themes = cx.global::<Arc<ThemeRegistry>>();
+
+        let mut this = Self {
+            buffer_font: Font {
+                family: defaults.buffer_font_family.clone().unwrap(),
+                features: defaults.buffer_font_features.clone().unwrap(),
+                weight: FontWeight::default(),
+                style: FontStyle::default(),
+            },
+            buffer_font_size: defaults.buffer_font_size.unwrap(),
+            buffer_line_height: defaults.buffer_line_height.unwrap(),
+            theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
+        };
+
+        for value in user_values.into_iter().copied().cloned() {
+            let font_cache = cx.font_cache();
+            let mut family_changed = false;
+            if let Some(value) = value.buffer_font_family {
+                this.buffer_font_family_name = value;
+                family_changed = true;
+            }
+            if let Some(value) = value.buffer_font_features {
+                this.buffer_font_features = value;
+                family_changed = true;
+            }
+            if family_changed {
+                if let Some(id) = font_cache
+                    .load_family(&[&this.buffer_font_family_name], &this.buffer_font_features)
+                    .log_err()
+                {
+                    this.buffer_font_family = id;
+                }
+            }
+
+            if let Some(value) = &value.theme {
+                if let Some(theme) = themes.get(value).log_err() {
+                    this.theme = theme;
+                }
+            }
+
+            merge(&mut this.buffer_font_size, value.buffer_font_size);
+            merge(&mut this.buffer_line_height, value.buffer_line_height);
+        }
+
+        Ok(this)
+    }
+
+    fn json_schema(
+        generator: &mut SchemaGenerator,
+        params: &SettingsJsonSchemaParams,
+        cx: &AppContext,
+    ) -> schemars::schema::RootSchema {
+        let mut root_schema = generator.root_schema_for::<ThemeSettingsContent>();
+        let theme_names = cx
+            .global::<Arc<ThemeRegistry>>()
+            .list_names(params.staff_mode)
+            .map(|theme_name| Value::String(theme_name.to_string()))
+            .collect();
+
+        let theme_name_schema = SchemaObject {
+            instance_type: Some(InstanceType::String.into()),
+            enum_values: Some(theme_names),
+            ..Default::default()
+        };
+
+        root_schema
+            .definitions
+            .extend([("ThemeName".into(), theme_name_schema.into())]);
+
+        root_schema
+            .schema
+            .object
+            .as_mut()
+            .unwrap()
+            .properties
+            .extend([(
+                "theme".to_owned(),
+                Schema::new_ref("#/definitions/ThemeName".into()),
+            )]);
+
+        root_schema
+    }
+}
+
+fn merge<T: Copy>(target: &mut T, value: Option<T>) {
+    if let Some(value) = value {
+        *target = value;
+    }
+}

crates/theme2/src/theme2.rs 🔗

@@ -1,21 +1,114 @@
-use gpui2::HighlightStyle;
+mod registry;
+mod settings;
+mod themes;
+
+pub use registry::*;
+pub use settings::*;
+
+use gpui2::{Hsla, SharedString};
 use std::sync::Arc;
 
 pub struct Theme {
-    pub editor: Editor,
-}
+    pub metadata: ThemeMetadata,
 
-pub struct Editor {
-    pub syntax: Arc<SyntaxTheme>,
+    pub transparent: Hsla,
+    pub mac_os_traffic_light_red: Hsla,
+    pub mac_os_traffic_light_yellow: Hsla,
+    pub mac_os_traffic_light_green: Hsla,
+    pub border: Hsla,
+    pub border_variant: Hsla,
+    pub border_focused: Hsla,
+    pub border_transparent: Hsla,
+    /// The background color of an elevated surface, like a modal, tooltip or toast.
+    pub elevated_surface: Hsla,
+    pub surface: Hsla,
+    /// Window background color of the base app
+    pub background: Hsla,
+    /// Default background for elements like filled buttons,
+    /// text fields, checkboxes, radio buttons, etc.
+    /// - TODO: Map to step 3.
+    pub filled_element: Hsla,
+    /// The background color of a hovered element, like a button being hovered
+    /// with a mouse, or hovered on a touch screen.
+    /// - TODO: Map to step 4.
+    pub filled_element_hover: Hsla,
+    /// The background color of an active element, like a button being pressed,
+    /// or tapped on a touch screen.
+    /// - TODO: Map to step 5.
+    pub filled_element_active: Hsla,
+    /// The background color of a selected element, like a selected tab,
+    /// a button toggled on, or a checkbox that is checked.
+    pub filled_element_selected: Hsla,
+    pub filled_element_disabled: Hsla,
+    pub ghost_element: Hsla,
+    /// The background color of a hovered element with no default background,
+    /// like a ghost-style button or an interactable list item.
+    /// - TODO: Map to step 3.
+    pub ghost_element_hover: Hsla,
+    /// - TODO: Map to step 4.
+    pub ghost_element_active: Hsla,
+    pub ghost_element_selected: Hsla,
+    pub ghost_element_disabled: Hsla,
+    pub text: Hsla,
+    pub text_muted: Hsla,
+    pub text_placeholder: Hsla,
+    pub text_disabled: Hsla,
+    pub text_accent: Hsla,
+    pub icon_muted: Hsla,
+    pub syntax: SyntaxTheme,
+
+    pub status_bar: Hsla,
+    pub title_bar: Hsla,
+    pub toolbar: Hsla,
+    pub tab_bar: Hsla,
+    /// The background of the editor
+    pub editor: Hsla,
+    pub editor_subheader: Hsla,
+    pub editor_active_line: Hsla,
+    pub terminal: Hsla,
+    pub image_fallback_background: Hsla,
+
+    pub git_created: Hsla,
+    pub git_modified: Hsla,
+    pub git_deleted: Hsla,
+    pub git_conflict: Hsla,
+    pub git_ignored: Hsla,
+    pub git_renamed: Hsla,
+
+    pub player: [PlayerTheme; 8],
 }
 
-#[derive(Default)]
+#[derive(Clone, Copy)]
 pub struct SyntaxTheme {
-    pub highlights: Vec<(String, HighlightStyle)>,
+    pub comment: Hsla,
+    pub string: Hsla,
+    pub function: Hsla,
+    pub keyword: Hsla,
 }
 
-impl SyntaxTheme {
-    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
-        Self { highlights }
-    }
+#[derive(Clone, Copy)]
+pub struct PlayerTheme {
+    pub cursor: Hsla,
+    pub selection: Hsla,
 }
+
+pub struct ThemeMetadata {
+    pub id: usize,
+    pub name: SharedString,
+    pub is_light: bool,
+}
+
+pub struct Editor {
+    pub syntax: Arc<SyntaxTheme>,
+}
+
+// #[derive(Default)]
+// pub struct SyntaxTheme {
+//     pub highlights: Vec<(String, HighlightStyle)>,
+// }
+
+// impl SyntaxTheme {
+//     pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
+//         Self { highlights }
+//     }
+// }

crates/theme2/src/themes/one_dark.rs 🔗

@@ -0,0 +1,90 @@
+use gpui2::rgba;
+
+use crate::{PlayerTheme, SyntaxTheme, Theme};
+
+pub fn one_dark() -> Theme {
+    Theme {
+        transparent: rgba(0x00000000).into(),
+        mac_os_traffic_light_red: rgba(0xec695eff).into(),
+        mac_os_traffic_light_yellow: rgba(0xf4bf4eff).into(),
+        mac_os_traffic_light_green: rgba(0x61c553ff).into(),
+        border: rgba(0x464b57ff).into(),
+        border_variant: rgba(0x464b57ff).into(),
+        border_focused: rgba(0x293b5bff).into(),
+        border_transparent: rgba(0x00000000).into(),
+        elevated_surface: rgba(0x3b414dff).into(),
+        surface: rgba(0x2f343eff).into(),
+        background: rgba(0x3b414dff).into(),
+        filled_element: rgba(0x3b414dff).into(),
+        filled_element_hover: rgba(0xffffff1e).into(),
+        filled_element_active: rgba(0xffffff28).into(),
+        filled_element_selected: rgba(0x18243dff).into(),
+        filled_element_disabled: rgba(0x00000000).into(),
+        ghost_element: rgba(0x00000000).into(),
+        ghost_element_hover: rgba(0xffffff14).into(),
+        ghost_element_active: rgba(0xffffff1e).into(),
+        ghost_element_selected: rgba(0x18243dff).into(),
+        ghost_element_disabled: rgba(0x00000000).into(),
+        text: rgba(0xc8ccd4ff).into(),
+        text_muted: rgba(0x838994ff).into(),
+        text_placeholder: rgba(0xd07277ff).into(),
+        text_disabled: rgba(0x555a63ff).into(),
+        text_accent: rgba(0x74ade8ff).into(),
+        icon_muted: rgba(0x838994ff).into(),
+        syntax: SyntaxTheme {
+            comment: rgba(0x5d636fff).into(),
+            string: rgba(0xa1c181ff).into(),
+            function: rgba(0x73ade9ff).into(),
+            keyword: rgba(0xb477cfff).into(),
+        },
+        status_bar: rgba(0x3b414dff).into(),
+        title_bar: rgba(0x3b414dff).into(),
+        toolbar: rgba(0x282c33ff).into(),
+        tab_bar: rgba(0x2f343eff).into(),
+        editor: rgba(0x282c33ff).into(),
+        editor_subheader: rgba(0x2f343eff).into(),
+        editor_active_line: rgba(0x2f343eff).into(),
+        terminal: rgba(0x282c33ff).into(),
+        image_fallback_background: rgba(0x3b414dff).into(),
+        git_created: rgba(0xa1c181ff).into(),
+        git_modified: rgba(0x74ade8ff).into(),
+        git_deleted: rgba(0xd07277ff).into(),
+        git_conflict: rgba(0xdec184ff).into(),
+        git_ignored: rgba(0x555a63ff).into(),
+        git_renamed: rgba(0xdec184ff).into(),
+        player: [
+            PlayerTheme {
+                cursor: rgba(0x74ade8ff).into(),
+                selection: rgba(0x74ade83d).into(),
+            },
+            PlayerTheme {
+                cursor: rgba(0xa1c181ff).into(),
+                selection: rgba(0xa1c1813d).into(),
+            },
+            PlayerTheme {
+                cursor: rgba(0xbe5046ff).into(),
+                selection: rgba(0xbe50463d).into(),
+            },
+            PlayerTheme {
+                cursor: rgba(0xbf956aff).into(),
+                selection: rgba(0xbf956a3d).into(),
+            },
+            PlayerTheme {
+                cursor: rgba(0xb477cfff).into(),
+                selection: rgba(0xb477cf3d).into(),
+            },
+            PlayerTheme {
+                cursor: rgba(0x6eb4bfff).into(),
+                selection: rgba(0x6eb4bf3d).into(),
+            },
+            PlayerTheme {
+                cursor: rgba(0xd07277ff).into(),
+                selection: rgba(0xd072773d).into(),
+            },
+            PlayerTheme {
+                cursor: rgba(0xdec184ff).into(),
+                selection: rgba(0xdec1843d).into(),
+            },
+        ],
+    }
+}

crates/ui2/src/color.rs 🔗

@@ -8,6 +8,15 @@ pub struct PlayerThemeColors {
     pub selection: Hsla,
 }
 
+impl std::fmt::Debug for PlayerThemeColors {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("PlayerThemeColors")
+            .field("cursor", &self.cursor.to_rgb().to_hex())
+            .field("selection", &self.selection.to_rgb().to_hex())
+            .finish()
+    }
+}
+
 impl PlayerThemeColors {
     pub fn new(cx: &WindowContext, ix: usize) -> Self {
         let theme = theme(cx);
@@ -34,6 +43,17 @@ pub struct SyntaxColor {
     pub keyword: Hsla,
 }
 
+impl std::fmt::Debug for SyntaxColor {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("SyntaxColor")
+            .field("comment", &self.comment.to_rgb().to_hex())
+            .field("string", &self.string.to_rgb().to_hex())
+            .field("function", &self.function.to_rgb().to_hex())
+            .field("keyword", &self.keyword.to_rgb().to_hex())
+            .finish()
+    }
+}
+
 impl SyntaxColor {
     pub fn new(cx: &WindowContext) -> Self {
         let theme = theme(cx);
@@ -137,6 +157,101 @@ pub struct ThemeColor {
     pub player: [PlayerThemeColors; 8],
 }
 
+impl std::fmt::Debug for ThemeColor {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        dbg!("ThemeColor debug");
+
+        f.debug_struct("ThemeColor")
+            .field("transparent", &self.transparent.to_rgb().to_hex())
+            .field(
+                "mac_os_traffic_light_red",
+                &self.mac_os_traffic_light_red.to_rgb().to_hex(),
+            )
+            .field(
+                "mac_os_traffic_light_yellow",
+                &self.mac_os_traffic_light_yellow.to_rgb().to_hex(),
+            )
+            .field(
+                "mac_os_traffic_light_green",
+                &self.mac_os_traffic_light_green.to_rgb().to_hex(),
+            )
+            .field("border", &self.border.to_rgb().to_hex())
+            .field("border_variant", &self.border_variant.to_rgb().to_hex())
+            .field("border_focused", &self.border_focused.to_rgb().to_hex())
+            .field(
+                "border_transparent",
+                &self.border_transparent.to_rgb().to_hex(),
+            )
+            .field("elevated_surface", &self.elevated_surface.to_rgb().to_hex())
+            .field("surface", &self.surface.to_rgb().to_hex())
+            .field("background", &self.background.to_rgb().to_hex())
+            .field("filled_element", &self.filled_element.to_rgb().to_hex())
+            .field(
+                "filled_element_hover",
+                &self.filled_element_hover.to_rgb().to_hex(),
+            )
+            .field(
+                "filled_element_active",
+                &self.filled_element_active.to_rgb().to_hex(),
+            )
+            .field(
+                "filled_element_selected",
+                &self.filled_element_selected.to_rgb().to_hex(),
+            )
+            .field(
+                "filled_element_disabled",
+                &self.filled_element_disabled.to_rgb().to_hex(),
+            )
+            .field("ghost_element", &self.ghost_element.to_rgb().to_hex())
+            .field(
+                "ghost_element_hover",
+                &self.ghost_element_hover.to_rgb().to_hex(),
+            )
+            .field(
+                "ghost_element_active",
+                &self.ghost_element_active.to_rgb().to_hex(),
+            )
+            .field(
+                "ghost_element_selected",
+                &self.ghost_element_selected.to_rgb().to_hex(),
+            )
+            .field(
+                "ghost_element_disabled",
+                &self.ghost_element_disabled.to_rgb().to_hex(),
+            )
+            .field("text", &self.text.to_rgb().to_hex())
+            .field("text_muted", &self.text_muted.to_rgb().to_hex())
+            .field("text_placeholder", &self.text_placeholder.to_rgb().to_hex())
+            .field("text_disabled", &self.text_disabled.to_rgb().to_hex())
+            .field("text_accent", &self.text_accent.to_rgb().to_hex())
+            .field("icon_muted", &self.icon_muted.to_rgb().to_hex())
+            .field("syntax", &self.syntax)
+            .field("status_bar", &self.status_bar.to_rgb().to_hex())
+            .field("title_bar", &self.title_bar.to_rgb().to_hex())
+            .field("toolbar", &self.toolbar.to_rgb().to_hex())
+            .field("tab_bar", &self.tab_bar.to_rgb().to_hex())
+            .field("editor", &self.editor.to_rgb().to_hex())
+            .field("editor_subheader", &self.editor_subheader.to_rgb().to_hex())
+            .field(
+                "editor_active_line",
+                &self.editor_active_line.to_rgb().to_hex(),
+            )
+            .field("terminal", &self.terminal.to_rgb().to_hex())
+            .field(
+                "image_fallback_background",
+                &self.image_fallback_background.to_rgb().to_hex(),
+            )
+            .field("git_created", &self.git_created.to_rgb().to_hex())
+            .field("git_modified", &self.git_modified.to_rgb().to_hex())
+            .field("git_deleted", &self.git_deleted.to_rgb().to_hex())
+            .field("git_conflict", &self.git_conflict.to_rgb().to_hex())
+            .field("git_ignored", &self.git_ignored.to_rgb().to_hex())
+            .field("git_renamed", &self.git_renamed.to_rgb().to_hex())
+            .field("player", &self.player)
+            .finish()
+    }
+}
+
 impl ThemeColor {
     pub fn new(cx: &WindowContext) -> Self {
         let theme = theme(cx);

crates/ui2/src/components/workspace.rs 🔗

@@ -177,6 +177,10 @@ impl Workspace {
     pub fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element<ViewState = Self> {
         let theme = theme(cx).clone();
 
+        let color = ThemeColor::new(cx);
+
+        dbg!(color);
+
         // HACK: This should happen inside of `debug_toggle_user_settings`, but
         // we don't have `cx.global::<FakeSettings>()` in event handlers at the moment.
         // Need to talk with Nathan/Antonio about this.

theme.txt 🔗

@@ -0,0 +1,254 @@
+    Finished dev [unoptimized + debuginfo] target(s) in 0.39s
+     Running `target/debug/storybook`
+[crates/ui2/src/components/workspace.rs:182] color = [crates/ui2/src/color.rs:162] "ThemeColor debug" = "ThemeColor debug"
+ThemeColor {
+    transparent: "rgba(0x00000000).into()",
+    mac_os_traffic_light_red: "rgba(0xec695eff).into()",
+    mac_os_traffic_light_yellow: "rgba(0xf4bf4eff).into()",
+    mac_os_traffic_light_green: "rgba(0x61c553ff).into()",
+    border: "rgba(0x464b57ff).into()",
+    border_variant: "rgba(0x464b57ff).into()",
+    border_focused: "rgba(0x293b5bff).into()",
+    border_transparent: "rgba(0x00000000).into()",
+    elevated_surface: "rgba(0x3b414dff).into()",
+    surface: "rgba(0x2f343eff).into()",
+    background: "rgba(0x3b414dff).into()",
+    filled_element: "rgba(0x3b414dff).into()",
+    filled_element_hover: "rgba(0xffffff1e).into()",
+    filled_element_active: "rgba(0xffffff28).into()",
+    filled_element_selected: "rgba(0x18243dff).into()",
+    filled_element_disabled: "rgba(0x00000000).into()",
+    ghost_element: "rgba(0x00000000).into()",
+    ghost_element_hover: "rgba(0xffffff14).into()",
+    ghost_element_active: "rgba(0xffffff1e).into()",
+    ghost_element_selected: "rgba(0x18243dff).into()",
+    ghost_element_disabled: "rgba(0x00000000).into()",
+    text: "rgba(0xc8ccd4ff).into()",
+    text_muted: "rgba(0x838994ff).into()",
+    text_placeholder: "rgba(0xd07277ff).into()",
+    text_disabled: "rgba(0x555a63ff).into()",
+    text_accent: "rgba(0x74ade8ff).into()",
+    icon_muted: "rgba(0x838994ff).into()",
+    syntax: SyntaxColor {
+        comment: "rgba(0x5d636fff).into()",
+        string: "rgba(0xa1c181ff).into()",
+        function: "rgba(0x73ade9ff).into()",
+        keyword: "rgba(0xb477cfff).into()",
+    },
+    status_bar: "rgba(0x3b414dff).into()",
+    title_bar: "rgba(0x3b414dff).into()",
+    toolbar: "rgba(0x282c33ff).into()",
+    tab_bar: "rgba(0x2f343eff).into()",
+    editor_subheader: "rgba(0x2f343eff).into()",
+    editor_active_line: "rgba(0x2f343eff).into()",
+    terminal: "rgba(0x282c33ff).into()",
+    image_fallback_background: "rgba(0x3b414dff).into()",
+    git_created: "rgba(0xa1c181ff).into()",
+    git_modified: "rgba(0x74ade8ff).into()",
+    git_deleted: "rgba(0xd07277ff).into()",
+    git_conflict: "rgba(0xdec184ff).into()",
+    git_ignored: "rgba(0x555a63ff).into()",
+    git_renamed: "rgba(0xdec184ff).into()",
+    player: [
+        PlayerThemeColors {
+            cursor: "rgba(0x74ade8ff).into()",
+            selection: "rgba(0x74ade83d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xa1c181ff).into()",
+            selection: "rgba(0xa1c1813d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xbe5046ff).into()",
+            selection: "rgba(0xbe50463d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xbf956aff).into()",
+            selection: "rgba(0xbf956a3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xb477cfff).into()",
+            selection: "rgba(0xb477cf3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0x6eb4bfff).into()",
+            selection: "rgba(0x6eb4bf3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xd07277ff).into()",
+            selection: "rgba(0xd072773d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xdec184ff).into()",
+            selection: "rgba(0xdec1843d).into()",
+        },
+    ],
+}
+[crates/ui2/src/components/workspace.rs:182] color = [crates/ui2/src/color.rs:162] "ThemeColor debug" = "ThemeColor debug"
+ThemeColor {
+    transparent: "rgba(0x00000000).into()",
+    mac_os_traffic_light_red: "rgba(0xec695eff).into()",
+    mac_os_traffic_light_yellow: "rgba(0xf4bf4eff).into()",
+    mac_os_traffic_light_green: "rgba(0x61c553ff).into()",
+    border: "rgba(0x464b57ff).into()",
+    border_variant: "rgba(0x464b57ff).into()",
+    border_focused: "rgba(0x293b5bff).into()",
+    border_transparent: "rgba(0x00000000).into()",
+    elevated_surface: "rgba(0x3b414dff).into()",
+    surface: "rgba(0x2f343eff).into()",
+    background: "rgba(0x3b414dff).into()",
+    filled_element: "rgba(0x3b414dff).into()",
+    filled_element_hover: "rgba(0xffffff1e).into()",
+    filled_element_active: "rgba(0xffffff28).into()",
+    filled_element_selected: "rgba(0x18243dff).into()",
+    filled_element_disabled: "rgba(0x00000000).into()",
+    ghost_element: "rgba(0x00000000).into()",
+    ghost_element_hover: "rgba(0xffffff14).into()",
+    ghost_element_active: "rgba(0xffffff1e).into()",
+    ghost_element_selected: "rgba(0x18243dff).into()",
+    ghost_element_disabled: "rgba(0x00000000).into()",
+    text: "rgba(0xc8ccd4ff).into()",
+    text_muted: "rgba(0x838994ff).into()",
+    text_placeholder: "rgba(0xd07277ff).into()",
+    text_disabled: "rgba(0x555a63ff).into()",
+    text_accent: "rgba(0x74ade8ff).into()",
+    icon_muted: "rgba(0x838994ff).into()",
+    syntax: SyntaxColor {
+        comment: "rgba(0x5d636fff).into()",
+        string: "rgba(0xa1c181ff).into()",
+        function: "rgba(0x73ade9ff).into()",
+        keyword: "rgba(0xb477cfff).into()",
+    },
+    status_bar: "rgba(0x3b414dff).into()",
+    title_bar: "rgba(0x3b414dff).into()",
+    toolbar: "rgba(0x282c33ff).into()",
+    tab_bar: "rgba(0x2f343eff).into()",
+    editor_subheader: "rgba(0x2f343eff).into()",
+    editor_active_line: "rgba(0x2f343eff).into()",
+    terminal: "rgba(0x282c33ff).into()",
+    image_fallback_background: "rgba(0x3b414dff).into()",
+    git_created: "rgba(0xa1c181ff).into()",
+    git_modified: "rgba(0x74ade8ff).into()",
+    git_deleted: "rgba(0xd07277ff).into()",
+    git_conflict: "rgba(0xdec184ff).into()",
+    git_ignored: "rgba(0x555a63ff).into()",
+    git_renamed: "rgba(0xdec184ff).into()",
+    player: [
+        PlayerThemeColors {
+            cursor: "rgba(0x74ade8ff).into()",
+            selection: "rgba(0x74ade83d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xa1c181ff).into()",
+            selection: "rgba(0xa1c1813d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xbe5046ff).into()",
+            selection: "rgba(0xbe50463d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xbf956aff).into()",
+            selection: "rgba(0xbf956a3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xb477cfff).into()",
+            selection: "rgba(0xb477cf3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0x6eb4bfff).into()",
+            selection: "rgba(0x6eb4bf3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xd07277ff).into()",
+            selection: "rgba(0xd072773d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xdec184ff).into()",
+            selection: "rgba(0xdec1843d).into()",
+        },
+    ],
+}
+[crates/ui2/src/components/workspace.rs:182] color = [crates/ui2/src/color.rs:162] "ThemeColor debug" = "ThemeColor debug"
+ThemeColor {
+    transparent: "rgba(0x00000000).into()",
+    mac_os_traffic_light_red: "rgba(0xec695eff).into()",
+    mac_os_traffic_light_yellow: "rgba(0xf4bf4eff).into()",
+    mac_os_traffic_light_green: "rgba(0x61c553ff).into()",
+    border: "rgba(0x464b57ff).into()",
+    border_variant: "rgba(0x464b57ff).into()",
+    border_focused: "rgba(0x293b5bff).into()",
+    border_transparent: "rgba(0x00000000).into()",
+    elevated_surface: "rgba(0x3b414dff).into()",
+    surface: "rgba(0x2f343eff).into()",
+    background: "rgba(0x3b414dff).into()",
+    filled_element: "rgba(0x3b414dff).into()",
+    filled_element_hover: "rgba(0xffffff1e).into()",
+    filled_element_active: "rgba(0xffffff28).into()",
+    filled_element_selected: "rgba(0x18243dff).into()",
+    filled_element_disabled: "rgba(0x00000000).into()",
+    ghost_element: "rgba(0x00000000).into()",
+    ghost_element_hover: "rgba(0xffffff14).into()",
+    ghost_element_active: "rgba(0xffffff1e).into()",
+    ghost_element_selected: "rgba(0x18243dff).into()",
+    ghost_element_disabled: "rgba(0x00000000).into()",
+    text: "rgba(0xc8ccd4ff).into()",
+    text_muted: "rgba(0x838994ff).into()",
+    text_placeholder: "rgba(0xd07277ff).into()",
+    text_disabled: "rgba(0x555a63ff).into()",
+    text_accent: "rgba(0x74ade8ff).into()",
+    icon_muted: "rgba(0x838994ff).into()",
+    syntax: SyntaxColor {
+        comment: "rgba(0x5d636fff).into()",
+        string: "rgba(0xa1c181ff).into()",
+        function: "rgba(0x73ade9ff).into()",
+        keyword: "rgba(0xb477cfff).into()",
+    },
+    status_bar: "rgba(0x3b414dff).into()",
+    title_bar: "rgba(0x3b414dff).into()",
+    toolbar: "rgba(0x282c33ff).into()",
+    tab_bar: "rgba(0x2f343eff).into()",
+    editor_subheader: "rgba(0x2f343eff).into()",
+    editor_active_line: "rgba(0x2f343eff).into()",
+    terminal: "rgba(0x282c33ff).into()",
+    image_fallback_background: "rgba(0x3b414dff).into()",
+    git_created: "rgba(0xa1c181ff).into()",
+    git_modified: "rgba(0x74ade8ff).into()",
+    git_deleted: "rgba(0xd07277ff).into()",
+    git_conflict: "rgba(0xdec184ff).into()",
+    git_ignored: "rgba(0x555a63ff).into()",
+    git_renamed: "rgba(0xdec184ff).into()",
+    player: [
+        PlayerThemeColors {
+            cursor: "rgba(0x74ade8ff).into()",
+            selection: "rgba(0x74ade83d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xa1c181ff).into()",
+            selection: "rgba(0xa1c1813d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xbe5046ff).into()",
+            selection: "rgba(0xbe50463d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xbf956aff).into()",
+            selection: "rgba(0xbf956a3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xb477cfff).into()",
+            selection: "rgba(0xb477cf3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0x6eb4bfff).into()",
+            selection: "rgba(0x6eb4bf3d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xd07277ff).into()",
+            selection: "rgba(0xd072773d).into()",
+        },
+        PlayerThemeColors {
+            cursor: "rgba(0xdec184ff).into()",
+            selection: "rgba(0xdec1843d).into()",
+        },
+    ],
+}