Add `One Dark` (#3314)

Marshall Bowers created

- Adds `one-dark`, and sets it as default for now.

Release Notes:

- N/A

Change summary

crates/editor2/src/items.rs          |  14 +
crates/storybook2/src/storybook2.rs  |   2 
crates/theme2/src/default_theme.rs   |  84 ++++++------
crates/theme2/src/one_themes.rs      | 198 ++++++++++++++++++++++++++++++
crates/theme2/src/registry.rs        |   6 
crates/theme2/src/settings.rs        |   3 
crates/theme2/src/theme2.rs          |   1 
crates/theme2/util/hex_to_hsla.py    |  35 +++++
crates/ui2/src/components/tooltip.rs |  15 +-
crates/workspace2/src/pane.rs        |  24 ++
10 files changed, 315 insertions(+), 67 deletions(-)

Detailed changes

crates/editor2/src/items.rs 🔗

@@ -30,6 +30,7 @@ use std::{
 };
 use text::Selection;
 use theme::{ActiveTheme, Theme};
+use ui::{Label, LabelColor};
 use util::{paths::PathExt, ResultExt, TryFutureExt};
 use workspace::item::{BreadcrumbText, FollowEvent, FollowableEvents, FollowableItemHandle};
 use workspace::{
@@ -595,16 +596,19 @@ impl Item for Editor {
                 .flex_row()
                 .items_center()
                 .gap_2()
-                .child(self.title(cx).to_string())
+                .child(Label::new(self.title(cx).to_string()))
                 .children(detail.and_then(|detail| {
                     let path = path_for_buffer(&self.buffer, detail, false, cx)?;
                     let description = path.to_string_lossy();
 
                     Some(
-                        div()
-                            .text_color(theme.colors().text_muted)
-                            .text_xs()
-                            .child(util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN)),
+                        div().child(
+                            Label::new(util::truncate_and_trailoff(
+                                &description,
+                                MAX_TAB_TITLE_LEN,
+                            ))
+                            .color(LabelColor::Muted),
+                        ),
                     )
                 })),
         )

crates/storybook2/src/storybook2.rs 🔗

@@ -48,7 +48,7 @@ fn main() {
     let args = Args::parse();
 
     let story_selector = args.story.clone();
-    let theme_name = args.theme.unwrap_or("Zed Pro Moonlight".to_string());
+    let theme_name = args.theme.unwrap_or("One Dark".to_string());
 
     let asset_source = Arc::new(Assets);
     gpui::App::production(asset_source).run(move |cx| {

crates/theme2/src/default_theme.rs 🔗

@@ -1,58 +1,56 @@
-use std::sync::Arc;
-
 use crate::{
-    default_color_scales, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme,
-    ThemeColors, ThemeFamily, ThemeStyles,
+    one_themes::{one_dark, one_family},
+    Theme, ThemeFamily,
 };
 
-fn zed_pro_daylight() -> Theme {
-    Theme {
-        id: "zed_pro_daylight".to_string(),
-        name: "Zed Pro Daylight".into(),
-        appearance: Appearance::Light,
-        styles: ThemeStyles {
-            system: SystemColors::default(),
-            colors: ThemeColors::light(),
-            status: StatusColors::light(),
-            player: PlayerColors::light(),
-            syntax: Arc::new(SyntaxTheme::light()),
-        },
-    }
-}
+// fn zed_pro_daylight() -> Theme {
+//     Theme {
+//         id: "zed_pro_daylight".to_string(),
+//         name: "Zed Pro Daylight".into(),
+//         appearance: Appearance::Light,
+//         styles: ThemeStyles {
+//             system: SystemColors::default(),
+//             colors: ThemeColors::light(),
+//             status: StatusColors::light(),
+//             player: PlayerColors::light(),
+//             syntax: Arc::new(SyntaxTheme::light()),
+//         },
+//     }
+// }
 
-pub(crate) fn zed_pro_moonlight() -> Theme {
-    Theme {
-        id: "zed_pro_moonlight".to_string(),
-        name: "Zed Pro Moonlight".into(),
-        appearance: Appearance::Dark,
-        styles: ThemeStyles {
-            system: SystemColors::default(),
-            colors: ThemeColors::dark(),
-            status: StatusColors::dark(),
-            player: PlayerColors::dark(),
-            syntax: Arc::new(SyntaxTheme::dark()),
-        },
-    }
-}
+// pub(crate) fn zed_pro_moonlight() -> Theme {
+//     Theme {
+//         id: "zed_pro_moonlight".to_string(),
+//         name: "Zed Pro Moonlight".into(),
+//         appearance: Appearance::Dark,
+//         styles: ThemeStyles {
+//             system: SystemColors::default(),
+//             colors: ThemeColors::dark(),
+//             status: StatusColors::dark(),
+//             player: PlayerColors::dark(),
+//             syntax: Arc::new(SyntaxTheme::dark()),
+//         },
+//     }
+// }
 
-pub fn zed_pro_family() -> ThemeFamily {
-    ThemeFamily {
-        id: "zed_pro".to_string(),
-        name: "Zed Pro".into(),
-        author: "Zed Team".into(),
-        themes: vec![zed_pro_daylight(), zed_pro_moonlight()],
-        scales: default_color_scales(),
-    }
-}
+// pub fn zed_pro_family() -> ThemeFamily {
+//     ThemeFamily {
+//         id: "zed_pro".to_string(),
+//         name: "Zed Pro".into(),
+//         author: "Zed Team".into(),
+//         themes: vec![zed_pro_daylight(), zed_pro_moonlight()],
+//         scales: default_color_scales(),
+//     }
+// }
 
 impl Default for ThemeFamily {
     fn default() -> Self {
-        zed_pro_family()
+        one_family()
     }
 }
 
 impl Default for Theme {
     fn default() -> Self {
-        zed_pro_daylight()
+        one_dark()
     }
 }

crates/theme2/src/one_themes.rs 🔗

@@ -0,0 +1,198 @@
+use std::sync::Arc;
+
+use gpui::{hsla, FontStyle, FontWeight, HighlightStyle};
+
+use crate::{
+    default_color_scales, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme,
+    ThemeColors, ThemeFamily, ThemeStyles,
+};
+
+pub fn one_family() -> ThemeFamily {
+    ThemeFamily {
+        id: "one".to_string(),
+        name: "One".into(),
+        author: "".into(),
+        themes: vec![one_dark()],
+        scales: default_color_scales(),
+    }
+}
+
+pub(crate) fn one_dark() -> Theme {
+    let bg = hsla(215. / 360., 12. / 100., 15. / 100., 1.);
+    let editor = hsla(220. / 360., 12. / 100., 18. / 100., 1.);
+    let elevated_surface = hsla(220. / 360., 12. / 100., 18. / 100., 1.);
+
+    let blue = hsla(207.8 / 360., 81. / 100., 66. / 100., 1.0);
+    let gray = hsla(218.8 / 360., 10. / 100., 40. / 100., 1.0);
+    let green = hsla(95. / 360., 38. / 100., 62. / 100., 1.0);
+    let orange = hsla(29. / 360., 54. / 100., 61. / 100., 1.0);
+    let purple = hsla(286. / 360., 51. / 100., 64. / 100., 1.0);
+    let red = hsla(355. / 360., 65. / 100., 65. / 100., 1.0);
+    let teal = hsla(187. / 360., 47. / 100., 55. / 100., 1.0);
+    let yellow = hsla(39. / 360., 67. / 100., 69. / 100., 1.0);
+
+    Theme {
+        id: "one_dark".to_string(),
+        name: "One Dark".into(),
+        appearance: Appearance::Dark,
+        styles: ThemeStyles {
+            system: SystemColors::default(),
+            colors: ThemeColors {
+                border: hsla(225. / 360., 13. / 100., 12. / 100., 1.),
+                border_variant: hsla(228. / 360., 8. / 100., 25. / 100., 1.),
+                border_focused: hsla(223. / 360., 78. / 100., 65. / 100., 1.),
+                border_selected: hsla(222.6 / 360., 77.5 / 100., 65.1 / 100., 1.0),
+                border_transparent: SystemColors::default().transparent,
+                border_disabled: hsla(222.0 / 360., 11.6 / 100., 33.7 / 100., 1.0),
+                elevated_surface_background: elevated_surface,
+                surface_background: bg,
+                background: bg,
+                element_background: elevated_surface,
+                element_hover: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0),
+                element_active: hsla(220.0 / 360., 11.8 / 100., 20.0 / 100., 1.0),
+                element_selected: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
+                element_disabled: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
+                drop_target_background: hsla(220.0 / 360., 8.3 / 100., 21.4 / 100., 1.0),
+                ghost_element_background: SystemColors::default().transparent,
+                ghost_element_hover: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0),
+                ghost_element_active: hsla(220.0 / 360., 11.8 / 100., 20.0 / 100., 1.0),
+                ghost_element_selected: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
+                ghost_element_disabled: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
+                text: hsla(222.9 / 360., 9.1 / 100., 84.9 / 100., 1.0),
+                text_muted: hsla(220.0 / 360., 6.4 / 100., 45.7 / 100., 1.0),
+                text_placeholder: hsla(220.0 / 360., 6.6 / 100., 44.5 / 100., 1.0),
+                text_disabled: hsla(220.0 / 360., 6.6 / 100., 44.5 / 100., 1.0),
+                text_accent: hsla(222.6 / 360., 77.5 / 100., 65.1 / 100., 1.0),
+                icon: hsla(222.9 / 360., 9.9 / 100., 86.1 / 100., 1.0),
+                icon_muted: hsla(220.0 / 360., 12.1 / 100., 66.1 / 100., 1.0),
+                icon_disabled: hsla(220.0 / 360., 6.4 / 100., 45.7 / 100., 1.0),
+                icon_placeholder: hsla(220.0 / 360., 6.4 / 100., 45.7 / 100., 1.0),
+                icon_accent: blue.into(),
+                status_bar_background: bg,
+                title_bar_background: bg,
+                toolbar_background: editor,
+                tab_bar_background: bg,
+                tab_inactive_background: bg,
+                tab_active_background: editor,
+                editor_background: editor,
+                editor_gutter_background: editor,
+                editor_subheader_background: bg,
+                editor_active_line_background: hsla(222.9 / 360., 13.5 / 100., 20.4 / 100., 1.0),
+                editor_highlighted_line_background: hsla(207.8 / 360., 81. / 100., 66. / 100., 0.1),
+                editor_line_number: hsla(222.0 / 360., 11.5 / 100., 34.1 / 100., 1.0),
+                editor_active_line_number: hsla(216.0 / 360., 5.9 / 100., 49.6 / 100., 1.0),
+                editor_invisible: hsla(222.0 / 360., 11.5 / 100., 34.1 / 100., 1.0),
+                editor_wrap_guide: gpui::black(),
+                editor_active_wrap_guide: gpui::red(),
+                editor_document_highlight_read_background: hsla(
+                    207.8 / 360.,
+                    81. / 100.,
+                    66. / 100.,
+                    0.2,
+                ),
+                editor_document_highlight_write_background: gpui::red(),
+                terminal_background: bg,
+                // todo!("Use one colors for terminal")
+                terminal_ansi_black: crate::black().dark().step_12(),
+                terminal_ansi_red: crate::red().dark().step_11(),
+                terminal_ansi_green: crate::green().dark().step_11(),
+                terminal_ansi_yellow: crate::yellow().dark().step_11(),
+                terminal_ansi_blue: crate::blue().dark().step_11(),
+                terminal_ansi_magenta: crate::violet().dark().step_11(),
+                terminal_ansi_cyan: crate::cyan().dark().step_11(),
+                terminal_ansi_white: crate::neutral().dark().step_12(),
+                terminal_ansi_bright_black: crate::black().dark().step_11(),
+                terminal_ansi_bright_red: crate::red().dark().step_10(),
+                terminal_ansi_bright_green: crate::green().dark().step_10(),
+                terminal_ansi_bright_yellow: crate::yellow().dark().step_10(),
+                terminal_ansi_bright_blue: crate::blue().dark().step_10(),
+                terminal_ansi_bright_magenta: crate::violet().dark().step_10(),
+                terminal_ansi_bright_cyan: crate::cyan().dark().step_10(),
+                terminal_ansi_bright_white: crate::neutral().dark().step_11(),
+            },
+            status: StatusColors {
+                conflict: yellow,
+                created: green,
+                deleted: red,
+                error: red,
+                hidden: gray,
+                hint: blue,
+                ignored: gray,
+                info: blue,
+                modified: yellow,
+                predictive: gray,
+                renamed: blue,
+                success: green,
+                unreachable: gray,
+                warning: yellow,
+            },
+            player: PlayerColors::dark(),
+            syntax: Arc::new(SyntaxTheme {
+                highlights: vec![
+                    ("attribute".into(), purple.into()),
+                    ("boolean".into(), orange.into()),
+                    ("comment".into(), gray.into()),
+                    ("comment.doc".into(), gray.into()),
+                    ("constant".into(), yellow.into()),
+                    ("constructor".into(), blue.into()),
+                    ("embedded".into(), HighlightStyle::default()),
+                    (
+                        "emphasis".into(),
+                        HighlightStyle {
+                            font_style: Some(FontStyle::Italic),
+                            ..HighlightStyle::default()
+                        },
+                    ),
+                    (
+                        "emphasis.strong".into(),
+                        HighlightStyle {
+                            font_weight: Some(FontWeight::BOLD),
+                            ..HighlightStyle::default()
+                        },
+                    ),
+                    ("enum".into(), HighlightStyle::default()),
+                    ("function".into(), blue.into()),
+                    ("function.method".into(), blue.into()),
+                    ("function.definition".into(), blue.into()),
+                    ("hint".into(), blue.into()),
+                    ("keyword".into(), purple.into()),
+                    ("label".into(), HighlightStyle::default()),
+                    ("link_text".into(), blue.into()),
+                    (
+                        "link_uri".into(),
+                        HighlightStyle {
+                            color: Some(teal.into()),
+                            font_style: Some(FontStyle::Italic),
+                            ..HighlightStyle::default()
+                        },
+                    ),
+                    ("number".into(), orange.into()),
+                    ("operator".into(), HighlightStyle::default()),
+                    ("predictive".into(), HighlightStyle::default()),
+                    ("preproc".into(), HighlightStyle::default()),
+                    ("primary".into(), HighlightStyle::default()),
+                    ("property".into(), red.into()),
+                    ("punctuation".into(), HighlightStyle::default()),
+                    ("punctuation.bracket".into(), HighlightStyle::default()),
+                    ("punctuation.delimiter".into(), HighlightStyle::default()),
+                    ("punctuation.list_marker".into(), HighlightStyle::default()),
+                    ("punctuation.special".into(), HighlightStyle::default()),
+                    ("string".into(), green.into()),
+                    ("string.escape".into(), HighlightStyle::default()),
+                    ("string.regex".into(), red.into()),
+                    ("string.special".into(), HighlightStyle::default()),
+                    ("string.special.symbol".into(), HighlightStyle::default()),
+                    ("tag".into(), HighlightStyle::default()),
+                    ("text.literal".into(), HighlightStyle::default()),
+                    ("title".into(), HighlightStyle::default()),
+                    ("type".into(), teal.into()),
+                    ("variable".into(), HighlightStyle::default()),
+                    ("variable.special".into(), red.into()),
+                    ("variant".into(), HighlightStyle::default()),
+                ],
+                inlay_style: HighlightStyle::default(),
+                suggestion_style: HighlightStyle::default(),
+            }),
+        },
+    }
+}

crates/theme2/src/registry.rs 🔗

@@ -6,8 +6,8 @@ use gpui::{HighlightStyle, SharedString};
 use refineable::Refineable;
 
 use crate::{
-    zed_pro_family, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme,
-    ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
+    one_themes::one_family, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors,
+    Theme, ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
 };
 
 pub struct ThemeRegistry {
@@ -108,7 +108,7 @@ impl Default for ThemeRegistry {
             themes: HashMap::default(),
         };
 
-        this.insert_theme_families([zed_pro_family()]);
+        this.insert_theme_families([one_family()]);
 
         #[cfg(not(feature = "importing-themes"))]
         this.insert_user_theme_familes(crate::all_user_themes());

crates/theme2/src/settings.rs 🔗

@@ -1,3 +1,4 @@
+use crate::one_themes::one_dark;
 use crate::{Theme, ThemeRegistry};
 use anyhow::Result;
 use gpui::{px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels};
@@ -129,7 +130,7 @@ impl settings::Settings for ThemeSettings {
             buffer_line_height: defaults.buffer_line_height.unwrap(),
             active_theme: themes
                 .get(defaults.theme.as_ref().unwrap())
-                .or(themes.get("Zed Pro Moonlight"))
+                .or(themes.get(&one_dark().name))
                 .unwrap(),
         };
 

crates/theme2/util/hex_to_hsla.py 🔗

@@ -0,0 +1,35 @@
+import colorsys
+import sys
+
+def hex_to_rgb(hex):
+    hex = hex.lstrip('#')
+    if len(hex) == 8: # 8 digit hex color
+        r, g, b, a = (int(hex[i:i+2], 16) for i in (0, 2, 4, 6))
+        return r, g, b, a / 255.0
+    else: # 6 digit hex color
+        return tuple(int(hex[i:i+2], 16) for i in (0, 2, 4)) + (1.0,)
+
+def rgb_to_hsla(rgb):
+    h, l, s = colorsys.rgb_to_hls(rgb[0]/255.0, rgb[1]/255.0, rgb[2]/255.0)
+    a = rgb[3] # alpha value
+    return (round(h * 360, 1), round(s * 100, 1), round(l * 100, 1), round(a, 3))
+
+def hex_to_hsla(hex):
+    return rgb_to_hsla(hex_to_rgb(hex))
+
+if len(sys.argv) != 2:
+    print("Usage: python util/hex_to_hsla.py <6 or 8 digit hex color or comma-separated list of colors>")
+else:
+    input_arg = sys.argv[1]
+    if ',' in input_arg: # comma-separated list of colors
+        hex_colors = input_arg.split(',')
+        hslas = [] # output array
+        for hex_color in hex_colors:
+            hex_color = hex_color.strip("'\" ")
+            h, s, l, a = hex_to_hsla(hex_color)
+            hslas.append(f"hsla({h} / 360., {s} / 100., {l} / 100., {a})")
+        print(hslas)
+    else: # single color
+        hex_color = input_arg.strip("'\"")
+        h, s, l, a = hex_to_hsla(hex_color)
+        print(f"hsla({h} / 360., {s} / 100., {l} / 100., {a})")

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

@@ -1,6 +1,8 @@
 use gpui::{div, Div, ParentElement, Render, SharedString, Styled, ViewContext};
 use theme2::ActiveTheme;
 
+use crate::StyledExt;
+
 #[derive(Clone, Debug)]
 pub struct TextTooltip {
     title: SharedString,
@@ -16,16 +18,13 @@ impl Render for TextTooltip {
     type Element = Div<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
-        let theme = cx.theme();
         div()
-            .bg(theme.colors().background)
-            .rounded_lg()
-            .border()
+            .elevation_2(cx)
             .font("Zed Sans")
-            .border_color(theme.colors().border)
-            .text_color(theme.colors().text)
-            .pl_2()
-            .pr_2()
+            .text_ui()
+            .text_color(cx.theme().colors().text)
+            .py_1()
+            .px_2()
             .child(self.title.clone())
     }
 }

crates/workspace2/src/pane.rs 🔗

@@ -1401,20 +1401,32 @@ impl Pane {
             // .on_drop(|_view, state: View<DraggedTab>, cx| {
             //     eprintln!("{:?}", state.read(cx));
             // })
-            .px_2()
-            .py_0p5()
             .flex()
             .items_center()
             .justify_center()
+            // todo!("Nate - I need to do some work to balance all the items in the tab once things stablize")
+            .map(|this| {
+                if close_right {
+                    this.pl_3().pr_1()
+                } else {
+                    this.pr_1().pr_3()
+                }
+            })
+            .py_1()
             .bg(tab_bg)
-            .hover(|h| h.bg(tab_hover_bg))
-            .active(|a| a.bg(tab_active_bg))
+            .border_color(cx.theme().colors().border)
+            .map(|this| match ix.cmp(&self.active_item_index) {
+                cmp::Ordering::Less => this.border_l(),
+                cmp::Ordering::Equal => this.border_r(),
+                cmp::Ordering::Greater => this.border_l().border_r(),
+            })
+            // .hover(|h| h.bg(tab_hover_bg))
+            // .active(|a| a.bg(tab_active_bg))
             .child(
                 div()
-                    .px_1()
                     .flex()
                     .items_center()
-                    .gap_1p5()
+                    .gap_1()
                     .text_color(text_color)
                     .children(if item.has_conflict(cx) {
                         Some(