From 39d10bf3d7125de9c2012915658d67650cf024aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 15 May 2026 20:22:51 -0400 Subject: [PATCH] Convert chrome-local color constants to theme-aware accessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous theme refactor only flipped `colors::*` tokens; the ~50 chrome-local `const FOO: u32 = 0xffffff…;` overlays kept their white-glass values, so dark mode still painted a light omnibar pill, light search field, light sidebar tab card, light command-overlay backdrop, light sync page chips, etc. Promote `colors::pick(light, dark)` to `pub` so component-internal constants can reach the same thread-local mode the global palette uses, then convert every white-glass (`0xffffff??`) and warm-dark wash (`0x281e14??`) constant in: - chrome/topbar (omnibar pill, hover chip) - chrome/sidebar_chrome + chrome/sidebar (rail nav row hover/active, highlight border, close-button hover, unread badge) - chrome/sidebar_header (workspace picker, disclosure card) - chrome/command_overlay + chrome/command_rows + chrome/command_footer - chrome/settings_layout, chrome/appearance_form, chrome/appearance_layout_cards, chrome/plugin_detail_view - chrome/home/style (search field, arrow chip, pill, card, add-tile) - internal_pages/sync (status pill, metrics card, button row) - internal_pages/plugin_catalog - shell/render (main pane highlight border) Each constant becomes `fn name() -> u32 { colors::pick(, ) }` where the dark counterpart preserves the original alpha but swaps the warm-white prefix `ffffff` for warm-graphite `1f1d1b` (or the inverse: `281e14` warm-dark wash → `f2efe9` warm-white wash). Brand accents (`0xc96442??`, `0xffaa7733`) and the global slate backdrop keep their single value across modes. --- .../src/shell/chrome/appearance_form.rs | 10 ++-- .../shell/chrome/appearance_layout_cards.rs | 18 +++++--- .../src/shell/chrome/command_footer.rs | 12 +++-- .../src/shell/chrome/command_overlay.rs | 24 ++++++---- .../ely_app/src/shell/chrome/command_rows.rs | 6 ++- .../src/shell/chrome/home/favorites.rs | 10 ++-- crates/ely_app/src/shell/chrome/home/hero.rs | 10 ++-- crates/ely_app/src/shell/chrome/home/recap.rs | 6 +-- crates/ely_app/src/shell/chrome/home/style.rs | 29 +++++++++--- .../src/shell/chrome/plugin_detail_view.rs | 30 ++++++++---- .../src/shell/chrome/settings_layout.rs | 12 +++-- crates/ely_app/src/shell/chrome/sidebar.rs | 20 ++++---- .../src/shell/chrome/sidebar_chrome.rs | 30 ++++++++---- .../src/shell/chrome/sidebar_header.rs | 46 ++++++++++++------- crates/ely_app/src/shell/chrome/topbar.rs | 16 ++++--- .../shell/internal_pages/plugin_catalog.rs | 24 ++++++---- .../ely_app/src/shell/internal_pages/sync.rs | 26 +++++++---- crates/ely_app/src/shell/render.rs | 6 ++- crates/ely_app/src/shell/sidebar.rs | 6 ++- crates/ely_design_system/src/colors.rs | 7 ++- 20 files changed, 227 insertions(+), 121 deletions(-) diff --git a/crates/ely_app/src/shell/chrome/appearance_form.rs b/crates/ely_app/src/shell/chrome/appearance_form.rs index a2b32c8..5e060c5 100644 --- a/crates/ely_app/src/shell/chrome/appearance_form.rs +++ b/crates/ely_app/src/shell/chrome/appearance_form.rs @@ -200,7 +200,7 @@ fn translucency_preset( .px(px(10.0)) .py(px(4.0)) .rounded(px(6.0)) - .bg(rgba(SEGMENT_BG)) + .bg(rgba(segment_bg())) .text_size(px(11.5)) .font_weight(FontWeight(500.0)) .text_color(rgb(colors::ink_2())) @@ -224,7 +224,7 @@ fn render_theme_mode_row(active: ThemeMode, cx: &mut Context) -> AnyEl .gap(px(2.0)) .p(px(2.0)) .rounded(px(8.0)) - .bg(rgba(SEGMENT_BG)) + .bg(rgba(segment_bg())) .child(theme_segment("System", ThemeMode::System, active, cx)) .child(theme_segment("Light", ThemeMode::Light, active, cx)) .child(theme_segment("Dark", ThemeMode::Dark, active, cx)) @@ -326,7 +326,7 @@ fn render_reset_row(cx: &mut Context) -> AnyElement { .px(px(12.0)) .py(px(7.0)) .rounded(px(8.0)) - .bg(rgba(SEGMENT_BG)) + .bg(rgba(segment_bg())) .text_size(px(12.0)) .font_weight(FontWeight(500.0)) .text_color(rgb(colors::ink_2())) @@ -399,4 +399,6 @@ fn transparent_like(color: Hsla) -> Hsla { hsla(color.h, color.s, color.l, 0.0) } -const SEGMENT_BG: u32 = 0x281e140d; +fn segment_bg() -> u32 { + colors::pick(0x281e140d, 0xf2efe90d) +} diff --git a/crates/ely_app/src/shell/chrome/appearance_layout_cards.rs b/crates/ely_app/src/shell/chrome/appearance_layout_cards.rs index d048c50..d4982ab 100644 --- a/crates/ely_app/src/shell/chrome/appearance_layout_cards.rs +++ b/crates/ely_app/src/shell/chrome/appearance_layout_cards.rs @@ -119,7 +119,7 @@ fn render_layout_card( .id(SharedString::from(mode.id())) .p(px(14.0)) .rounded(px(14.0)) - .bg(rgba(LAYOUT_CARD_BG)) + .bg(rgba(layout_card_bg())) .when(selected, |el| el.border_2().border_color(rgb(colors::accent()))) .when(!selected, |el| el.border_1().border_color(rgba(colors::stroke()))) .flex() @@ -165,7 +165,7 @@ fn render_layout_preview(mode: LayoutMode) -> AnyElement { .bottom(px(6.0)) .w(px(sidebar_width)) .rounded(px(5.0)) - .bg(rgba(LAYOUT_PANEL_BG)), + .bg(rgba(layout_panel_bg())), ) .child( div() @@ -175,11 +175,17 @@ fn render_layout_preview(mode: LayoutMode) -> AnyElement { .right(px(6.0)) .bottom(px(6.0)) .rounded(px(5.0)) - .bg(rgba(LAYOUT_CANVAS_BG)), + .bg(rgba(layout_canvas_bg())), ) .into_any_element() } -const LAYOUT_CARD_BG: u32 = 0xffffffd9; -const LAYOUT_PANEL_BG: u32 = 0x281e1414; -const LAYOUT_CANVAS_BG: u32 = 0x281e140a; +fn layout_card_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn layout_panel_bg() -> u32 { + colors::pick(0x281e1414, 0xf2efe914) +} +fn layout_canvas_bg() -> u32 { + colors::pick(0x281e140a, 0xf2efe90a) +} diff --git a/crates/ely_app/src/shell/chrome/command_footer.rs b/crates/ely_app/src/shell/chrome/command_footer.rs index 7bb1916..d23c6e3 100644 --- a/crates/ely_app/src/shell/chrome/command_footer.rs +++ b/crates/ely_app/src/shell/chrome/command_footer.rs @@ -13,7 +13,7 @@ pub(crate) fn render_command_footer() -> AnyElement { .border_color(rgba(colors::divider())) .text_size(px(10.5)) .text_color(rgb(colors::ink_3())) - .bg(rgba(FOOTER_BG)) + .bg(rgba(footer_bg())) // Only the shortcuts that actually do something today. // `⌘↵` (split-on-open) and `⇥` (filter) are real product // features but not yet wired through the command overlay's @@ -41,12 +41,16 @@ pub(crate) fn render_kbd(label: &'static str) -> AnyElement { .px(px(5.0)) .py(px(1.0)) .rounded(px(4.0)) - .bg(rgba(KBD_BG)) + .bg(rgba(kbd_bg())) .text_size(px(10.0)) .text_color(rgb(colors::ink_3())) .child(label) .into_any_element() } -const KBD_BG: u32 = 0xffffffd9; -const FOOTER_BG: u32 = 0xffffff8c; +fn kbd_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn footer_bg() -> u32 { + colors::pick(0xffffff8c, 0x1f1d1b8c) +} diff --git a/crates/ely_app/src/shell/chrome/command_overlay.rs b/crates/ely_app/src/shell/chrome/command_overlay.rs index 4de1ad8..169cc56 100644 --- a/crates/ely_app/src/shell/chrome/command_overlay.rs +++ b/crates/ely_app/src/shell/chrome/command_overlay.rs @@ -45,7 +45,7 @@ fn render_overlay( div() .absolute() .inset_0() - .bg(rgba(BACKDROP_BG)) + .bg(rgba(backdrop_bg())) .flex() .flex_col() .items_center() @@ -67,9 +67,9 @@ fn render_panel( div() .w(px(640.0)) .rounded(px(16.0)) - .bg(rgba(PANEL_BG)) + .bg(rgba(panel_bg())) .border_1() - .border_color(rgba(PANEL_BORDER)) + .border_color(rgba(panel_border())) .shadow(panel_shadow()) .overflow_hidden() .flex() @@ -111,7 +111,7 @@ fn render_header(query_label: String, is_empty: bool) -> AnyElement { .px(px(8.0)) .py(px(2.0)) .rounded(px(6.0)) - .bg(rgba(BADGE_BG)) + .bg(rgba(badge_bg())) .text_size(px(10.5)) .text_color(rgb(colors::ink_3())) .child("Switcher"), @@ -199,10 +199,18 @@ fn render_empty_state() -> AnyElement { .into_any_element() } -const PANEL_BG: u32 = 0xfffffff5; -const PANEL_BORDER: u32 = 0xffffff80; -const BACKDROP_BG: u32 = 0x140f0a3d; -const BADGE_BG: u32 = 0x281e140f; +fn panel_bg() -> u32 { + colors::pick(0xfffffff5, 0x1f1d1bf5) +} +fn panel_border() -> u32 { + colors::pick(0xffffff80, 0x1f1d1b80) +} +fn backdrop_bg() -> u32 { + colors::pick(0x140f0a3d, 0x0d0c0a3d) +} +fn badge_bg() -> u32 { + colors::pick(0x281e140f, 0xf2efe90f) +} fn panel_shadow() -> Vec { vec![BoxShadow { diff --git a/crates/ely_app/src/shell/chrome/command_rows.rs b/crates/ely_app/src/shell/chrome/command_rows.rs index a89ca20..7f8e898 100644 --- a/crates/ely_app/src/shell/chrome/command_rows.rs +++ b/crates/ely_app/src/shell/chrome/command_rows.rs @@ -175,7 +175,7 @@ where let leading = div() .size(px(24.0)) .rounded(px(6.0)) - .bg(rgba(ROW_ICON_BG)) + .bg(rgba(row_icon_bg())) .flex() .items_center() .justify_center() @@ -268,4 +268,6 @@ where const ROW_HOVER_BG: u32 = 0xc9644214; const ROW_SELECTED_BG: u32 = 0xc964421f; -const ROW_ICON_BG: u32 = 0xffffffd9; +fn row_icon_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} diff --git a/crates/ely_app/src/shell/chrome/home/favorites.rs b/crates/ely_app/src/shell/chrome/home/favorites.rs index 11e6502..d622f49 100644 --- a/crates/ely_app/src/shell/chrome/home/favorites.rs +++ b/crates/ely_app/src/shell/chrome/home/favorites.rs @@ -11,7 +11,7 @@ use crate::shell::ElyShell; use crate::shell::chrome::render_glyph_for; use super::section::render_section_chevron_label; -use super::style::{ADD_TILE_BG, CARD_BG, CARD_BG_HOVER, card_shadow}; +use super::style::{add_tile_bg, card_bg, card_bg_hover, card_shadow}; pub(crate) fn render_favorites_grid( snapshot: &BrowserSnapshot, @@ -53,7 +53,7 @@ fn render_favorite_tile(index: usize, tab: &BrowserTab, cx: &mut Context) -> AnyElement { .id(SharedString::from("fav-tile-add")) .h(px(96.0)) .rounded(px(16.0)) - .bg(rgba(ADD_TILE_BG)) + .bg(rgba(add_tile_bg())) .flex() .items_center() .justify_center() .text_color(rgb(colors::ink_4())) .cursor_pointer() - .hover(|style| style.bg(rgba(CARD_BG))) + .hover(|style| style.bg(rgba(card_bg()))) .on_click(cx.listener(|shell, _, window, cx| { shell.open_internal_tab("ely://bookmarks", window, cx); })) diff --git a/crates/ely_app/src/shell/chrome/home/hero.rs b/crates/ely_app/src/shell/chrome/home/hero.rs index f9b2459..37615bc 100644 --- a/crates/ely_app/src/shell/chrome/home/hero.rs +++ b/crates/ely_app/src/shell/chrome/home/hero.rs @@ -9,7 +9,7 @@ use crate::shell::ElyShell; use crate::shell::chrome::{SERIF_FAMILY, render_glyph_for}; -use super::style::{ARROW_CHIP_BG, PILL_BG, PILL_BG_HOVER, SEARCH_BG, card_shadow, soft_shadow}; +use super::style::{arrow_chip_bg, card_shadow, pill_bg, pill_bg_hover, search_bg, soft_shadow}; use super::time::DayPhase; pub(crate) fn render_hero( @@ -84,7 +84,7 @@ fn render_search_bar(shell: &ElyShell, cx: &mut Context) -> AnyElement .w(px(640.0)) .h(px(54.0)) .rounded(px(14.0)) - .bg(rgba(SEARCH_BG)) + .bg(rgba(search_bg())) .shadow(card_shadow()) .px(px(16.0)) .flex() @@ -110,7 +110,7 @@ fn render_search_bar(shell: &ElyShell, cx: &mut Context) -> AnyElement div() .size(px(28.0)) .rounded(px(8.0)) - .bg(rgba(ARROW_CHIP_BG)) + .bg(rgba(arrow_chip_bg())) .flex() .items_center() .justify_center() @@ -178,7 +178,7 @@ where div() .id(SharedString::from(id)) .rounded(px(999.0)) - .bg(rgba(PILL_BG)) + .bg(rgba(pill_bg())) .shadow(soft_shadow()) .px(px(12.0)) .py(px(6.0)) @@ -186,7 +186,7 @@ where .items_center() .gap(px(6.0)) .cursor_pointer() - .hover(|style| style.bg(rgba(PILL_BG_HOVER))) + .hover(|style| style.bg(rgba(pill_bg_hover()))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx))) .child(leading) diff --git a/crates/ely_app/src/shell/chrome/home/recap.rs b/crates/ely_app/src/shell/chrome/home/recap.rs index 5b4836e..bbfd1be 100644 --- a/crates/ely_app/src/shell/chrome/home/recap.rs +++ b/crates/ely_app/src/shell/chrome/home/recap.rs @@ -13,7 +13,7 @@ use gpui_component::IconName; use crate::shell::ElyShell; use crate::shell::chrome::{SERIF_FAMILY, render_glyph_for}; -use super::style::{CARD_BG, card_shadow}; +use super::style::{card_bg, card_shadow}; use super::time::relative_time_label; pub(crate) fn render_recap(snapshot: &BrowserSnapshot, cx: &mut Context) -> AnyElement { @@ -44,7 +44,7 @@ fn render_continue_card( div() .rounded(px(16.0)) - .bg(rgba(CARD_BG)) + .bg(rgba(card_bg())) .shadow(card_shadow()) .p(px(16.0)) .flex() @@ -244,7 +244,7 @@ fn render_activity_card( div() .rounded(px(16.0)) - .bg(rgba(CARD_BG)) + .bg(rgba(card_bg())) .shadow(card_shadow()) .p(px(16.0)) .flex() diff --git a/crates/ely_app/src/shell/chrome/home/style.rs b/crates/ely_app/src/shell/chrome/home/style.rs index 0830962..e48c9e6 100644 --- a/crates/ely_app/src/shell/chrome/home/style.rs +++ b/crates/ely_app/src/shell/chrome/home/style.rs @@ -1,12 +1,27 @@ +use ely_design_system::colors; use gpui::{BoxShadow, hsla, point, px}; -pub(crate) const SEARCH_BG: u32 = 0xffffffd9; -pub(crate) const ARROW_CHIP_BG: u32 = 0x281e140a; -pub(crate) const PILL_BG: u32 = 0xffffff8c; -pub(crate) const PILL_BG_HOVER: u32 = 0xffffffd9; -pub(crate) const CARD_BG: u32 = 0xffffffc7; -pub(crate) const CARD_BG_HOVER: u32 = 0xffffffeb; -pub(crate) const ADD_TILE_BG: u32 = 0xffffff7f; +pub(crate) fn search_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +pub(crate) fn arrow_chip_bg() -> u32 { + colors::pick(0x281e140a, 0xf2efe90a) +} +pub(crate) fn pill_bg() -> u32 { + colors::pick(0xffffff8c, 0x1f1d1b8c) +} +pub(crate) fn pill_bg_hover() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +pub(crate) fn card_bg() -> u32 { + colors::pick(0xffffffc7, 0x1f1d1bc7) +} +pub(crate) fn card_bg_hover() -> u32 { + colors::pick(0xffffffeb, 0x1f1d1beb) +} +pub(crate) fn add_tile_bg() -> u32 { + colors::pick(0xffffff7f, 0x1f1d1b7f) +} pub(crate) fn card_shadow() -> Vec { vec![BoxShadow { diff --git a/crates/ely_app/src/shell/chrome/plugin_detail_view.rs b/crates/ely_app/src/shell/chrome/plugin_detail_view.rs index c6d15d8..7a19825 100644 --- a/crates/ely_app/src/shell/chrome/plugin_detail_view.rs +++ b/crates/ely_app/src/shell/chrome/plugin_detail_view.rs @@ -32,7 +32,7 @@ pub(crate) fn render_plugin_detail_view( .mx_auto() .p(px(24.0)) .rounded(px(16.0)) - .bg(rgba(CARD_BG)) + .bg(rgba(card_bg())) .grid() .grid_cols(8) .gap(px(24.0)) @@ -138,13 +138,13 @@ where .id(SharedString::from("plugin-detail-secondary")) .size(px(40.0)) .rounded(px(10.0)) - .bg(rgba(SECONDARY_BG)) + .bg(rgba(secondary_bg())) .flex() .items_center() .justify_center() .text_color(rgb(colors::ink_3())) .cursor_pointer() - .hover(|style| style.bg(rgba(SECONDARY_BG_HOVER)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(secondary_bg_hover())).text_color(rgb(colors::ink()))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx))) .child(icon) @@ -304,7 +304,7 @@ fn stat_card(label: &'static str, value: String) -> AnyElement { .px(px(12.0)) .py(px(10.0)) .rounded(px(10.0)) - .bg(rgba(STAT_BG)) + .bg(rgba(stat_bg())) .flex() .flex_col() .gap(px(2.0)) @@ -358,7 +358,7 @@ fn render_contribution_row(contribution: &PluginContributionPoint) -> AnyElement .px(px(12.0)) .py(px(10.0)) .rounded(px(10.0)) - .bg(rgba(CONTRIBUTION_BG)) + .bg(rgba(contribution_bg())) .child( div() .size(px(18.0)) @@ -401,9 +401,19 @@ fn category_label(plugin: &InstalledPlugin) -> &'static str { if high_risk { "ELY · AUDIT NEEDED" } else { "ELY · SANDBOXED" } } -const CARD_BG: u32 = 0xffffffd9; -const SECONDARY_BG: u32 = 0xffffffd9; -const SECONDARY_BG_HOVER: u32 = 0xffffffeb; -const STAT_BG: u32 = 0xffffffb3; -const CONTRIBUTION_BG: u32 = 0xffffff8c; +fn card_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn secondary_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn secondary_bg_hover() -> u32 { + colors::pick(0xffffffeb, 0x1f1d1beb) +} +fn stat_bg() -> u32 { + colors::pick(0xffffffb3, 0x1f1d1bb3) +} +fn contribution_bg() -> u32 { + colors::pick(0xffffff8c, 0x1f1d1b8c) +} const CONTRIBUTION_BADGE_BG: u32 = 0xc964421f; diff --git a/crates/ely_app/src/shell/chrome/settings_layout.rs b/crates/ely_app/src/shell/chrome/settings_layout.rs index 9d3b79a..ec2ee95 100644 --- a/crates/ely_app/src/shell/chrome/settings_layout.rs +++ b/crates/ely_app/src/shell/chrome/settings_layout.rs @@ -202,7 +202,7 @@ fn render_nav_item( cx: &mut Context, ) -> AnyElement { let active = item.route == active_route; - let bg = if active { ACTIVE_BG } else { 0x00000000 }; + let bg = if active { active_bg() } else { 0x00000000 }; let text_color = if active { colors::ink() } else { colors::ink_2() }; let route = item.route; let icon = item.icon.clone(); @@ -219,7 +219,7 @@ fn render_nav_item( .text_size(px(13.0)) .text_color(rgb(text_color)) .cursor_pointer() - .hover(|style| style.bg(rgba(HOVER_BG))) + .hover(|style| style.bg(rgba(hover_bg()))) .active(|style| style.opacity(0.85)) .on_click(cx.listener(move |shell, _, window, cx| { shell.open_internal_tab(route, window, cx); @@ -229,5 +229,9 @@ fn render_nav_item( .into_any_element() } -const ACTIVE_BG: u32 = 0xffffffd9; -const HOVER_BG: u32 = 0xffffff8c; +fn active_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn hover_bg() -> u32 { + colors::pick(0xffffff8c, 0x1f1d1b8c) +} diff --git a/crates/ely_app/src/shell/chrome/sidebar.rs b/crates/ely_app/src/shell/chrome/sidebar.rs index 47cf22c..1791220 100644 --- a/crates/ely_app/src/shell/chrome/sidebar.rs +++ b/crates/ely_app/src/shell/chrome/sidebar.rs @@ -10,8 +10,8 @@ use gpui_component::{IconName, StyledExt, scroll::ScrollableElement}; use crate::shell::ElyShell; use crate::shell::chrome::sidebar_chrome::{ - ACTIVE_NAV_BG, ACTIVE_NAV_BG_HOVER, CLOSE_HOVER_BG, HIGHLIGHT_BORDER, HOVER_NAV_BG, - ROW_CLOSE_SIZE, panel_bg, panel_shadow, profile_initial, render_sidebar_resize_handle, + ROW_CLOSE_SIZE, active_nav_bg, active_nav_bg_hover, close_hover_bg, highlight_border, + hover_nav_bg, panel_bg, panel_shadow, profile_initial, render_sidebar_resize_handle, render_unread_badge, section_label, section_tabs_label, soft_shadow, }; use crate::shell::chrome::{render_glyph_for, render_sidebar_header}; @@ -43,7 +43,7 @@ impl ElyShell { .rounded(px(spacing::RADIUS_CARD)) .bg(rgba(panel_color)) .border_1() - .border_color(rgba(HIGHLIGHT_BORDER)) + .border_color(rgba(highlight_border())) .shadow(panel_shadow()) .overflow_hidden() .child(render_sidebar_header(self, snapshot, cx)) @@ -102,7 +102,7 @@ impl ElyShell { fn render_settings_row(&mut self, cx: &mut Context) -> AnyElement { // Settings is never an "active" row — only ever rest or hover — - // so it uses HOVER_NAV_BG directly. Keeping it lighter than the + // so it uses hover_nav_bg() directly. Keeping it lighter than the // active nav card means the eye still finds the active selection // first when both are visible. div() @@ -116,7 +116,7 @@ impl ElyShell { .text_size(px(13.0)) .text_color(rgb(colors::ink_2())) .cursor_pointer() - .hover(|style| style.bg(rgba(HOVER_NAV_BG)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(hover_nav_bg())).text_color(rgb(colors::ink()))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(|shell, _, window, cx| { shell.open_internal_tab("ely://settings", window, cx); @@ -142,7 +142,7 @@ impl ElyShell { .flex() .items_center() .cursor_pointer() - .hover(|style| style.bg(rgba(HOVER_NAV_BG))) + .hover(|style| style.bg(rgba(hover_nav_bg()))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(|shell, _, window, cx| { shell.open_internal_tab("ely://settings/profiles", window, cx); @@ -293,7 +293,7 @@ impl ElyShell { .text_color(rgb(colors::ink_3())) .text_size(px(13.0)) .cursor_pointer() - .hover(|style| style.bg(rgba(HOVER_NAV_BG)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(hover_nav_bg())).text_color(rgb(colors::ink()))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(|shell, _, window, cx| { shell.open_new_tab(window, cx); @@ -377,9 +377,9 @@ impl ElyShell { /// place rather than three transparent-sentinel triples. fn nav_row_palette(active: bool) -> NavRowPalette { if active { - NavRowPalette { bg: ACTIVE_NAV_BG, hover_bg: ACTIVE_NAV_BG_HOVER, text: colors::ink() } + NavRowPalette { bg: active_nav_bg(), hover_bg: active_nav_bg_hover(), text: colors::ink() } } else { - NavRowPalette { bg: 0x00000000, hover_bg: HOVER_NAV_BG, text: colors::ink_2() } + NavRowPalette { bg: 0x00000000, hover_bg: hover_nav_bg(), text: colors::ink_2() } } } @@ -417,7 +417,7 @@ where .text_color(rgb(colors::ink_4())) .opacity(0.0) .group_hover(group_name, |style| style.opacity(1.0)) - .hover(|style| style.bg(rgba(CLOSE_HOVER_BG)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(close_hover_bg())).text_color(rgb(colors::ink()))) .cursor_pointer() .on_click(on_click) .child(IconName::Close) diff --git a/crates/ely_app/src/shell/chrome/sidebar_chrome.rs b/crates/ely_app/src/shell/chrome/sidebar_chrome.rs index c4090c3..7176f1b 100644 --- a/crates/ely_app/src/shell/chrome/sidebar_chrome.rs +++ b/crates/ely_app/src/shell/chrome/sidebar_chrome.rs @@ -10,36 +10,48 @@ use crate::shell::ElyShell; /// Hover/active palette for the vertical sidebar rows. /// -/// The original code reused `ACTIVE_NAV_BG` for both `active` and +/// The original code reused `active_nav_bg()` for both `active` and /// `hover`, which made an already-active row visually inert under the /// cursor — the object refused to acknowledge the touch. The four /// tokens below split that single value into a four-step ladder: /// -/// rest (transparent) → HOVER_NAV_BG → ACTIVE_NAV_BG → ACTIVE_NAV_BG_HOVER +/// rest (transparent) → hover_nav_bg() → active_nav_bg() → active_nav_bg_hover() /// /// so every state transition produces a real, perceptible change. /// /// Values were picked by eye against the warm panel tint, then nudged /// until the wash on a Dawn-themed panel reads as "you touched this" /// without competing with the active selection's authority. -pub(crate) const HOVER_NAV_BG: u32 = 0xffffff66; // 40% white wash -pub(crate) const ACTIVE_NAV_BG: u32 = 0xffffffd9; // 85% white card -pub(crate) const ACTIVE_NAV_BG_HOVER: u32 = 0xfffffff2; // 95% white — active + hover +pub(crate) fn hover_nav_bg() -> u32 { + colors::pick(0xffffff66, 0x1f1d1b66) +} // 40% white wash +pub(crate) fn active_nav_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} // 85% white card +pub(crate) fn active_nav_bg_hover() -> u32 { + colors::pick(0xfffffff2, 0x1f1d1bf2) +} // 95% white — active + hover /// Hover tint behind the per-row close (×) button. Was 8% alpha, which /// was visually indistinguishable from the panel background and made /// the click target read as inert. Brought to ~30% alpha so the /// hover registers as a real "press here" surface, matching the /// confidence of close buttons in Arc/Dia/Zen. -pub(crate) const CLOSE_HOVER_BG: u32 = 0x281e144d; +pub(crate) fn close_hover_bg() -> u32 { + colors::pick(0x281e144d, 0xf2efe94d) +} -pub(crate) const UNREAD_BADGE_BG: u32 = 0x281e140f; +pub(crate) fn unread_badge_bg() -> u32 { + colors::pick(0x281e140f, 0xf2efe90f) +} /// 50% white inner border that traces every glass panel — the GPUI /// substitute for the design's `box-shadow: inset 0 0 0 1px rgba(255,255,255,0.5)`. /// Painted as the panel's own border so it stays part of the frame and /// never participates in hit testing. -pub(crate) const HIGHLIGHT_BORDER: u32 = 0xffffff80; +pub(crate) fn highlight_border() -> u32 { + colors::pick(0xffffff80, 0x1f1d1b80) +} pub(crate) const RESIZE_HANDLE_HOVER_BG: u32 = 0xffaa7733; @@ -62,7 +74,7 @@ pub(crate) fn render_unread_badge(count: u32) -> impl IntoElement { .px(px(6.0)) .py(px(1.0)) .rounded(px(999.0)) - .bg(rgba(UNREAD_BADGE_BG)) + .bg(rgba(unread_badge_bg())) .text_size(px(10.0)) .font_weight(FontWeight(500.0)) .text_color(rgb(colors::ink_3())) diff --git a/crates/ely_app/src/shell/chrome/sidebar_header.rs b/crates/ely_app/src/shell/chrome/sidebar_header.rs index 084b380..8f0581c 100644 --- a/crates/ely_app/src/shell/chrome/sidebar_header.rs +++ b/crates/ely_app/src/shell/chrome/sidebar_header.rs @@ -136,7 +136,7 @@ fn render_picker_pill( let space_name = active_space.map(|space| space.name().to_string()).unwrap_or_default(); let space_glyph = active_space.map(|space| space.icon().to_string()).unwrap_or_default(); let chevron = if picker_open { IconName::ChevronUp } else { IconName::ChevronDown }; - let bg = if picker_open { PICKER_BG_HOVER } else { PICKER_BG }; + let bg = if picker_open { picker_bg_hover() } else { picker_bg() }; div() .id(SharedString::from("workspace-picker")) @@ -151,7 +151,7 @@ fn render_picker_pill( .bg(rgba(bg)) .shadow(soft_shadow()) .cursor_pointer() - .hover(|style| style.bg(rgba(PICKER_BG_HOVER))) + .hover(|style| style.bg(rgba(picker_bg_hover()))) .active(|style| style.opacity(0.85)) .on_click(cx.listener(|shell, _, _, cx| { shell.toggle_workspace_picker(cx); @@ -237,9 +237,9 @@ pub(crate) fn render_workspace_disclosure( .gap(px(2.0)) .p(px(4.0)) .rounded(px(10.0)) - .bg(rgba(DISCLOSURE_BG)) + .bg(rgba(disclosure_bg())) .border_1() - .border_color(rgba(DISCLOSURE_BORDER)) + .border_color(rgba(disclosure_border())) .shadow(soft_shadow()) .children( snapshot @@ -280,7 +280,7 @@ fn render_disclosure_row( ) -> AnyElement { let space_id = space.id().clone(); let active = space.id() == active_id; - let bg = if active { DISCLOSURE_ROW_ACTIVE_BG } else { 0x00000000 }; + let bg = if active { disclosure_row_active_bg() } else { 0x00000000 }; div() .id(SharedString::from(format!("workspace-row-{index}"))) @@ -292,7 +292,7 @@ fn render_disclosure_row( .rounded(px(8.0)) .bg(rgba(bg)) .cursor_pointer() - .hover(|style| style.bg(rgba(DISCLOSURE_ROW_HOVER_BG))) + .hover(|style| style.bg(rgba(disclosure_row_hover_bg()))) .active(|style| style.opacity(0.85)) .on_click(cx.listener(move |shell, _, window, cx| { shell.select_space_from_picker(&space_id, window, cx); @@ -327,7 +327,7 @@ fn render_disclosure_footer(cx: &mut Context) -> AnyElement { .text_size(px(12.0)) .text_color(rgb(colors::ink_3())) .cursor_pointer() - .hover(|style| style.bg(rgba(DISCLOSURE_ROW_HOVER_BG)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(disclosure_row_hover_bg())).text_color(rgb(colors::ink()))) .active(|style| style.opacity(0.85)) .on_click(cx.listener(|shell, _, window, cx| { shell.close_workspace_picker(cx); @@ -361,14 +361,14 @@ fn render_add_workspace_button(cx: &mut Context) -> AnyElement { .id(SharedString::from("workspace-add")) .size(px(PICKER_BUTTON_SIZE)) .rounded(px(9.0)) - .bg(rgba(ADD_BUTTON_BG)) + .bg(rgba(add_button_bg())) .shadow(soft_shadow()) .flex() .items_center() .justify_center() .text_color(rgb(colors::ink_3())) .cursor_pointer() - .hover(|style| style.bg(rgba(PICKER_BG)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(picker_bg())).text_color(rgb(colors::ink()))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(|shell, _, window, cx| { shell.open_internal_tab("ely://settings/spaces", window, cx); @@ -377,13 +377,27 @@ fn render_add_workspace_button(cx: &mut Context) -> AnyElement { .into_any_element() } -const PICKER_BG: u32 = 0xffffff99; -const PICKER_BG_HOVER: u32 = 0xffffffd9; -const ADD_BUTTON_BG: u32 = 0xffffff66; -const DISCLOSURE_BG: u32 = 0xffffffd9; -const DISCLOSURE_BORDER: u32 = 0xffffff80; -const DISCLOSURE_ROW_ACTIVE_BG: u32 = 0xffffffeb; -const DISCLOSURE_ROW_HOVER_BG: u32 = 0xffffffb3; +fn picker_bg() -> u32 { + colors::pick(0xffffff99, 0x1f1d1b99) +} +fn picker_bg_hover() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn add_button_bg() -> u32 { + colors::pick(0xffffff66, 0x1f1d1b66) +} +fn disclosure_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn disclosure_border() -> u32 { + colors::pick(0xffffff80, 0x1f1d1b80) +} +fn disclosure_row_active_bg() -> u32 { + colors::pick(0xffffffeb, 0x1f1d1beb) +} +fn disclosure_row_hover_bg() -> u32 { + colors::pick(0xffffffb3, 0x1f1d1bb3) +} fn soft_shadow() -> Vec { vec![ diff --git a/crates/ely_app/src/shell/chrome/topbar.rs b/crates/ely_app/src/shell/chrome/topbar.rs index 945b41e..2ce55d5 100644 --- a/crates/ely_app/src/shell/chrome/topbar.rs +++ b/crates/ely_app/src/shell/chrome/topbar.rs @@ -79,7 +79,7 @@ fn render_omnibar( .flex_1() .h(px(spacing::OMNIBAR_HEIGHT)) .rounded(px(spacing::RADIUS_PILL)) - .bg(rgba(OMNIBAR_BG)) + .bg(rgba(omnibar_bg())) .shadow(soft_shadow()) .px(px(14.0)) .flex() @@ -168,7 +168,7 @@ where .justify_center() .text_color(rgb(color)) .cursor_pointer() - .hover(|style| style.bg(rgba(CHIP_HOVER_BG)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(chip_hover_bg())).text_color(rgb(colors::ink()))) .active(|style| style.opacity(0.7)) .on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx))) .child(icon) @@ -196,7 +196,7 @@ where .text_color(rgb(color)) .when(enabled, |el| { el.cursor_pointer() - .hover(|style| style.bg(rgba(OMNIBAR_BG)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(omnibar_bg())).text_color(rgb(colors::ink()))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx))) }) @@ -222,15 +222,19 @@ where .justify_center() .cursor_pointer() .text_color(rgb(colors::ink_3())) - .hover(|style| style.bg(rgba(OMNIBAR_BG)).text_color(rgb(colors::ink()))) + .hover(|style| style.bg(rgba(omnibar_bg())).text_color(rgb(colors::ink()))) .active(|style| style.opacity(0.82)) .on_click(cx.listener(move |shell, _, window, cx| handler(shell, window, cx))) .child(icon) .into_any_element() } -const OMNIBAR_BG: u32 = 0xffffff8c; -const CHIP_HOVER_BG: u32 = 0xffffffd9; +fn omnibar_bg() -> u32 { + colors::pick(0xffffff8c, 0x1f1d1b8c) +} +fn chip_hover_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} /// Topbar quick-toggle icon for the current theme mode. The button /// cycles System → Light → Dark → System, and the icon previews the diff --git a/crates/ely_app/src/shell/internal_pages/plugin_catalog.rs b/crates/ely_app/src/shell/internal_pages/plugin_catalog.rs index 75084ec..c0f5f8e 100644 --- a/crates/ely_app/src/shell/internal_pages/plugin_catalog.rs +++ b/crates/ely_app/src/shell/internal_pages/plugin_catalog.rs @@ -91,7 +91,7 @@ impl ElyShell { .h(px(42.0)) .px(px(16.0)) .rounded(px(12.0)) - .bg(rgba(SEARCH_BG)) + .bg(rgba(search_bg())) .flex() .items_center() .gap(px(10.0)) @@ -258,12 +258,12 @@ fn render_plugin_card( .id(SharedString::from(format!("plugin-card-{index}"))) .p(px(14.0)) .rounded(px(16.0)) - .bg(rgba(CARD_BG)) + .bg(rgba(card_bg())) .flex() .flex_col() .gap(px(10.0)) .cursor_pointer() - .hover(|style| style.bg(rgba(CARD_BG_HOVER))) + .hover(|style| style.bg(rgba(card_bg_hover()))) .active(|style| style.opacity(0.85)) .on_click(cx.listener(move |shell, _, window, cx| { shell.open_internal_tab(&detail_route, window, cx); @@ -317,7 +317,7 @@ fn render_plugin_card( .px(px(9.0)) .py(px(3.0)) .rounded(px(6.0)) - .bg(rgba(STATUS_BG)) + .bg(rgba(status_bg())) .text_size(px(11.0)) .font_weight(FontWeight(500.0)) .text_color(rgb(status_color)) @@ -378,11 +378,19 @@ fn sandbox_chip_label(snapshot: &BrowserSnapshot) -> &'static str { } } -const SEARCH_BG: u32 = 0xffffffd9; +fn search_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} const INSTALL_BG: u32 = 0x1d1c1aff; -const CARD_BG: u32 = 0xffffffc7; -const CARD_BG_HOVER: u32 = 0xffffffeb; -const STATUS_BG: u32 = 0xffffffd9; +fn card_bg() -> u32 { + colors::pick(0xffffffc7, 0x1f1d1bc7) +} +fn card_bg_hover() -> u32 { + colors::pick(0xffffffeb, 0x1f1d1beb) +} +fn status_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} /// Pick a deterministic gradient pair for a plugin cover. Mirrors the /// design's palette of 8 pastel-to-bold ramps used across `plugins.jsx`, diff --git a/crates/ely_app/src/shell/internal_pages/sync.rs b/crates/ely_app/src/shell/internal_pages/sync.rs index 4efda3d..aac985f 100644 --- a/crates/ely_app/src/shell/internal_pages/sync.rs +++ b/crates/ely_app/src/shell/internal_pages/sync.rs @@ -55,7 +55,7 @@ fn render_status_pill(snapshot: &BrowserSnapshot) -> AnyElement { .px(px(12.0)) .py(px(5.0)) .rounded(px(999.0)) - .bg(rgba(PILL_BG)) + .bg(rgba(pill_bg())) .text_size(px(11.0)) .text_color(rgb(colors::ink_3())) .child(div().text_color(rgb(colors::accent())).child(IconName::Globe)) @@ -93,7 +93,7 @@ fn render_metrics_card(snapshot: &BrowserSnapshot, cx: &mut Context) - .max_w(px(380.0)) .p(px(20.0)) .rounded(px(16.0)) - .bg(rgba(CARD_BG)) + .bg(rgba(card_bg())) .flex() .flex_col() .gap(px(16.0)) @@ -160,12 +160,12 @@ fn render_reset_button(cx: &mut Context) -> AnyElement { .px(px(12.0)) .py(px(7.0)) .rounded(px(8.0)) - .bg(rgba(BUTTON_BG)) + .bg(rgba(button_bg())) .text_size(px(12.0)) .font_weight(FontWeight(500.0)) .text_color(rgb(colors::ink_2())) .cursor_pointer() - .hover(|style| style.bg(rgba(BUTTON_BG_HOVER))) + .hover(|style| style.bg(rgba(button_bg_hover()))) .active(|style| style.opacity(0.85)) .on_click(cx.listener(|shell, _, _, cx| shell.reset_sync_settings(cx))) .child("Reset to defaults"), @@ -186,7 +186,7 @@ fn render_what_syncs_card(snapshot: &BrowserSnapshot, cx: &mut Context div() .p(px(18.0)) .rounded(px(16.0)) - .bg(rgba(CARD_BG)) + .bg(rgba(card_bg())) .flex() .flex_col() .gap(px(12.0)) @@ -370,7 +370,15 @@ fn sync_object_state_label(state: SyncObjectState) -> &'static str { } } -const PILL_BG: u32 = 0xffffffb3; -const CARD_BG: u32 = 0xffffffd9; -const BUTTON_BG: u32 = 0xffffffd9; -const BUTTON_BG_HOVER: u32 = 0xffffffeb; +fn pill_bg() -> u32 { + colors::pick(0xffffffb3, 0x1f1d1bb3) +} +fn card_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn button_bg() -> u32 { + colors::pick(0xffffffd9, 0x1f1d1bd9) +} +fn button_bg_hover() -> u32 { + colors::pick(0xffffffeb, 0x1f1d1beb) +} diff --git a/crates/ely_app/src/shell/render.rs b/crates/ely_app/src/shell/render.rs index 9452f50..9379668 100644 --- a/crates/ely_app/src/shell/render.rs +++ b/crates/ely_app/src/shell/render.rs @@ -192,7 +192,7 @@ impl ElyShell { .rounded(px(spacing::RADIUS_CARD)) .bg(rgba(panel_color)) .border_1() - .border_color(rgba(MAIN_PANE_HIGHLIGHT_BORDER)) + .border_color(rgba(main_pane_highlight_border())) .shadow(panel_shadow()) .overflow_hidden() .child(render_topbar_chrome(self, snapshot, active_tab, sidebar_collapsed, window, cx)) @@ -339,7 +339,9 @@ impl ElyShell { /// substitute for the design's `box-shadow: inset 0 0 0 1px rgba(255,255,255,0.5)`. /// Painted as the panel's own border so it stays part of the frame and /// never participates in hit testing. -const MAIN_PANE_HIGHLIGHT_BORDER: u32 = 0xffffff80; +fn main_pane_highlight_border() -> u32 { + colors::pick(0xffffff80, 0x1f1d1b80) +} /// Lower clamp for the live sidebar-resize drag. Stays a touch above /// the per-space DEFAULT so a casual flick doesn't snap the user to a diff --git a/crates/ely_app/src/shell/sidebar.rs b/crates/ely_app/src/shell/sidebar.rs index 65951a9..e8ef734 100644 --- a/crates/ely_app/src/shell/sidebar.rs +++ b/crates/ely_app/src/shell/sidebar.rs @@ -45,7 +45,7 @@ impl ElyShell { .rounded(px(spacing::RADIUS_CARD)) .bg(rgba(panel_color)) .border_1() - .border_color(rgba(HIGHLIGHT_BORDER)) + .border_color(rgba(highlight_border())) .shadow(panel_shadow()) .children(snapshot.favorites.iter().enumerate().map(|(index, tab)| { self.render_compact_tab_button( @@ -276,7 +276,9 @@ pub(super) fn collapsed_sidebar_active(sidebar_width: f32) -> bool { /// Painting it as the panel's own border keeps it part of the frame /// (no separate absolute overlay) so it never participates in hit /// testing and never blocks clicks on inner content. -const HIGHLIGHT_BORDER: u32 = 0xffffff80; +fn highlight_border() -> u32 { + colors::pick(0xffffff80, 0x1f1d1b80) +} fn panel_shadow() -> Vec { vec![ diff --git a/crates/ely_design_system/src/colors.rs b/crates/ely_design_system/src/colors.rs index 7f2faf3..475d73b 100644 --- a/crates/ely_design_system/src/colors.rs +++ b/crates/ely_design_system/src/colors.rs @@ -37,8 +37,13 @@ pub fn mode() -> Mode { CURRENT_MODE.with(Cell::get) } +/// Pick between a light- and dark-mode value based on the active +/// thread-local mode. Exposed so chrome-local constants that don't +/// belong in the global palette (component-internal translucent +/// glass, backdrops with bespoke alpha curves) can still flip with +/// the rest of the design system. #[must_use] -fn pick(light: u32, dark: u32) -> u32 { +pub fn pick(light: u32, dark: u32) -> u32 { match mode() { Mode::Light => light, Mode::Dark => dark,