Redesign for the v1 stable release (#3)
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m26s

Only half done. Will continue in another PR.

Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
2026-02-04 01:43:21 +00:00
parent 014757cfc9
commit 32201554ec
174 changed files with 6165 additions and 8112 deletions

View File

@@ -8,13 +8,10 @@ use crate::scale::{ColorScale, ColorScaleSet, ColorScales};
pub struct ThemeColors {
// Surface colors
pub background: Hsla,
pub overlay: Hsla,
pub surface_background: Hsla,
pub elevated_surface_background: Hsla,
pub panel_background: Hsla,
pub overlay: Hsla,
pub title_bar: Hsla,
pub title_bar_inactive: Hsla,
pub window_border: Hsla,
// Border colors
pub border: Hsla,
@@ -78,8 +75,10 @@ pub struct ThemeColors {
// Tab colors
pub tab_inactive_background: Hsla,
pub tab_hover_background: Hsla,
pub tab_inactive_foreground: Hsla,
pub tab_active_background: Hsla,
pub tab_active_foreground: Hsla,
pub tab_hover_foreground: Hsla,
// Scrollbar colors
pub scrollbar_thumb_background: Hsla,
@@ -92,107 +91,26 @@ pub struct ThemeColors {
pub drop_target_background: Hsla,
pub cursor: Hsla,
pub selection: Hsla,
// System
pub titlebar: Hsla,
pub titlebar_inactive: Hsla,
}
/// The default colors for the theme.
///
/// Themes that do not specify all colors are refined off of these defaults.
impl ThemeColors {
/// Returns the default colors for light themes.
///
/// Themes that do not specify all colors are refined off of these defaults.
pub fn light() -> Self {
Self {
background: neutral().light().step_1(),
surface_background: neutral().light().step_2(),
elevated_surface_background: neutral().light().step_3(),
panel_background: gpui::white(),
overlay: neutral().light_alpha().step_3(),
title_bar: gpui::transparent_black(),
title_bar_inactive: neutral().light().step_1(),
window_border: hsl(240.0, 5.9, 78.0),
border: neutral().light().step_6(),
border_variant: neutral().light().step_5(),
border_focused: brand().light().step_7(),
border_selected: brand().light().step_7(),
border_transparent: gpui::transparent_black(),
border_disabled: neutral().light().step_3(),
ring: brand().light().step_8(),
text: neutral().light().step_12(),
text_muted: neutral().light().step_11(),
text_placeholder: neutral().light().step_10(),
text_accent: brand().light().step_11(),
icon: neutral().light().step_11(),
icon_muted: neutral().light().step_10(),
icon_accent: brand().light().step_11(),
element_foreground: brand().light().step_12(),
element_background: brand().light().step_9(),
element_hover: brand().light_alpha().step_10(),
element_active: brand().light().step_10(),
element_selected: brand().light().step_11(),
element_disabled: brand().light_alpha().step_3(),
secondary_foreground: brand().light().step_11(),
secondary_background: brand().light().step_3(),
secondary_hover: brand().light_alpha().step_4(),
secondary_active: brand().light().step_5(),
secondary_selected: brand().light().step_5(),
secondary_disabled: brand().light_alpha().step_3(),
danger_foreground: danger().light().step_12(),
danger_background: danger().light().step_3(),
danger_hover: danger().light_alpha().step_4(),
danger_active: danger().light().step_5(),
danger_selected: danger().light().step_5(),
danger_disabled: danger().light_alpha().step_3(),
warning_foreground: warning().light().step_12(),
warning_background: warning().light().step_3(),
warning_hover: warning().light_alpha().step_4(),
warning_active: warning().light().step_5(),
warning_selected: warning().light().step_5(),
warning_disabled: warning().light_alpha().step_3(),
ghost_element_background: gpui::transparent_black(),
ghost_element_background_alt: neutral().light().step_3(),
ghost_element_hover: neutral().light_alpha().step_4(),
ghost_element_active: neutral().light().step_5(),
ghost_element_selected: neutral().light().step_5(),
ghost_element_disabled: neutral().light_alpha().step_2(),
tab_inactive_background: neutral().light().step_3(),
tab_hover_background: neutral().light().step_4(),
tab_active_background: neutral().light().step_5(),
scrollbar_thumb_background: neutral().light_alpha().step_3(),
scrollbar_thumb_hover_background: neutral().light_alpha().step_4(),
scrollbar_thumb_border: gpui::transparent_black(),
scrollbar_track_background: gpui::transparent_black(),
scrollbar_track_border: neutral().light().step_5(),
drop_target_background: brand().light_alpha().step_2(),
cursor: hsl(200., 100., 50.),
selection: hsl(200., 100., 50.).alpha(0.25),
}
}
/// Returns the default colors for dark themes.
///
/// Themes that do not specify all colors are refined off of these defaults.
pub fn dark() -> Self {
pub fn colors() -> Self {
Self {
background: neutral().dark().step_1(),
surface_background: neutral().dark().step_2(),
elevated_surface_background: neutral().dark().step_3(),
panel_background: gpui::black(),
panel_background: neutral().dark().step_3(),
overlay: neutral().dark_alpha().step_3(),
title_bar: gpui::transparent_black(),
title_bar_inactive: neutral().dark().step_1(),
window_border: hsl(240.0, 3.7, 28.0),
border: neutral().dark().step_6(),
border_variant: neutral().dark().step_5(),
@@ -246,9 +164,11 @@ impl ThemeColors {
ghost_element_selected: neutral().dark().step_5(),
ghost_element_disabled: neutral().dark_alpha().step_2(),
tab_inactive_background: neutral().dark().step_3(),
tab_hover_background: neutral().dark().step_4(),
tab_active_background: neutral().dark().step_5(),
tab_inactive_background: neutral().dark().step_2(),
tab_inactive_foreground: neutral().dark().step_11(),
tab_active_background: neutral().dark().step_3(),
tab_active_foreground: neutral().dark().step_12(),
tab_hover_foreground: brand().dark().step_9(),
scrollbar_thumb_background: neutral().dark_alpha().step_3(),
scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(),
@@ -259,6 +179,9 @@ impl ThemeColors {
drop_target_background: brand().dark_alpha().step_2(),
cursor: hsl(200., 100., 50.),
selection: hsl(200., 100., 50.).alpha(0.25),
titlebar: neutral().dark_alpha().step_1(),
titlebar_inactive: neutral().dark_alpha().step_2(),
}
}
}

View File

@@ -4,12 +4,14 @@ use std::rc::Rc;
use gpui::{px, App, Global, Pixels, SharedString, Window};
mod colors;
mod platform_kind;
mod registry;
mod scale;
mod scrollbar_mode;
mod theme;
pub use colors::*;
pub use platform_kind::PlatformKind;
pub use registry::*;
pub use scale::*;
pub use scrollbar_mode::*;
@@ -21,6 +23,21 @@ pub const CLIENT_SIDE_DECORATION_ROUNDING: Pixels = px(10.0);
/// Defines window shadow size for platforms that use client side decorations.
pub const CLIENT_SIDE_DECORATION_SHADOW: Pixels = px(10.0);
/// Defines window border size for platforms that use client side decorations.
pub const CLIENT_SIDE_DECORATION_BORDER: Pixels = px(1.0);
/// Defines window titlebar height
pub const TITLEBAR_HEIGHT: Pixels = px(36.0);
/// Defines tabbar height
pub const TABBAR_HEIGHT: Pixels = px(30.);
/// Defines default sidebar width
pub const SIDEBAR_WIDTH: Pixels = px(240.);
/// Defines search input width
pub const SEARCH_INPUT_WIDTH: Pixels = px(420.);
pub fn init(cx: &mut App) {
registry::init(cx);
@@ -67,6 +84,9 @@ pub struct Theme {
/// Show the scrollbar mode, default: scrolling
pub scrollbar_mode: ScrollbarMode,
/// Platform
pub platform: PlatformKind,
}
impl Deref for Theme {
@@ -149,11 +169,7 @@ impl Theme {
theme.mode = mode;
// Set the theme colors
if mode.is_dark() {
theme.colors = *theme.theme.dark();
} else {
theme.colors = *theme.theme.light();
}
theme.colors = *theme.theme.colors();
// Refresh the window if available
if let Some(window) = window {
@@ -164,16 +180,20 @@ impl Theme {
impl From<ThemeFamily> for Theme {
fn from(family: ThemeFamily) -> Self {
let platform = PlatformKind::platform();
let mode = ThemeMode::default();
// Define the theme colors based on the appearance
let colors = match mode {
ThemeMode::Light => family.light(),
ThemeMode::Dark => family.dark(),
let colors = family.colors();
// Define the font family based on the platform.
// TODO: Use native fonts on Linux too.
let font_family = match platform {
PlatformKind::Linux => "Inter",
_ => ".SystemUIFont",
};
Theme {
font_size: px(15.),
font_family: ".SystemUIFont".into(),
font_family: font_family.into(),
radius: px(5.),
radius_lg: px(10.),
shadow: true,
@@ -181,6 +201,7 @@ impl From<ThemeFamily> for Theme {
mode,
colors: *colors,
theme: Rc::new(family),
platform,
}
}
}

View File

@@ -0,0 +1,33 @@
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum PlatformKind {
Mac,
Linux,
Windows,
}
impl PlatformKind {
pub const fn platform() -> Self {
if cfg!(any(target_os = "linux", target_os = "freebsd")) {
Self::Linux
} else if cfg!(target_os = "windows") {
Self::Windows
} else {
Self::Mac
}
}
#[allow(dead_code)]
pub fn is_linux(&self) -> bool {
matches!(self, Self::Linux)
}
#[allow(dead_code)]
pub fn is_windows(&self) -> bool {
matches!(self, Self::Windows)
}
#[allow(dead_code)]
pub fn is_mac(&self) -> bool {
matches!(self, Self::Mac)
}
}

View File

@@ -51,37 +51,27 @@ pub struct ThemeFamily {
/// The URL of the theme.
pub url: String,
/// The light colors for the theme.
pub light: ThemeColors,
/// The dark colors for the theme.
pub dark: ThemeColors,
/// The colors for the theme.
pub colors: ThemeColors,
}
impl Default for ThemeFamily {
fn default() -> Self {
ThemeFamily {
id: "coop".into(),
name: "Coop Default Theme".into(),
name: "Coop Dark".into(),
author: "Coop".into(),
url: "https://github.com/lumehq/coop".into(),
light: ThemeColors::light(),
dark: ThemeColors::dark(),
colors: ThemeColors::colors(),
}
}
}
impl ThemeFamily {
/// Returns the light colors for the theme.
/// Returns the colors for the theme.
#[inline(always)]
pub fn light(&self) -> &ThemeColors {
&self.light
}
/// Returns the dark colors for the theme.
#[inline(always)]
pub fn dark(&self) -> &ThemeColors {
&self.dark
pub fn colors(&self) -> &ThemeColors {
&self.colors
}
/// Load a theme family from a JSON file.