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(<light>, <dark>) }`
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.
223 lines
6.2 KiB
Rust
223 lines
6.2 KiB
Rust
use ely_browser_core::BrowserSnapshot;
|
|
use ely_design_system::colors;
|
|
use gpui::{
|
|
AnyElement, BoxShadow, Context, FontWeight, IntoElement, ParentElement, Styled, div, hsla,
|
|
point, px, rgb, rgba,
|
|
};
|
|
use gpui_component::IconName;
|
|
|
|
use crate::shell::ElyShell;
|
|
use crate::shell::chrome::animations::{blink, fade_in};
|
|
use crate::shell::chrome::command_footer::{render_command_footer, render_kbd};
|
|
use crate::shell::chrome::command_match::{
|
|
matching_actions, matching_bookmarks, matching_history, matching_tabs,
|
|
};
|
|
use crate::shell::chrome::command_rows::{
|
|
render_action_rows, render_bookmark_rows, render_history_rows, render_tab_rows,
|
|
};
|
|
|
|
const COMMAND_PREFIX: &str = ">";
|
|
|
|
pub(crate) fn render_command_overlay(
|
|
shell: &ElyShell,
|
|
snapshot: &BrowserSnapshot,
|
|
cx: &mut Context<ElyShell>,
|
|
) -> Option<AnyElement> {
|
|
let query = snapshot.command_query.as_str();
|
|
if !query.starts_with(COMMAND_PREFIX) {
|
|
return None;
|
|
}
|
|
let needle = query[COMMAND_PREFIX.len()..].trim().to_lowercase();
|
|
let selected_index = shell.command_selected_index;
|
|
|
|
Some(render_overlay(snapshot, &needle, selected_index, cx))
|
|
}
|
|
|
|
fn render_overlay(
|
|
snapshot: &BrowserSnapshot,
|
|
needle: &str,
|
|
selected_index: usize,
|
|
cx: &mut Context<ElyShell>,
|
|
) -> AnyElement {
|
|
fade_in(
|
|
"command-overlay-backdrop",
|
|
160,
|
|
div()
|
|
.absolute()
|
|
.inset_0()
|
|
.bg(rgba(backdrop_bg()))
|
|
.flex()
|
|
.flex_col()
|
|
.items_center()
|
|
.pt(px(80.0))
|
|
.child(render_panel(snapshot, needle, selected_index, cx)),
|
|
)
|
|
.into_any_element()
|
|
}
|
|
|
|
fn render_panel(
|
|
snapshot: &BrowserSnapshot,
|
|
needle: &str,
|
|
selected_index: usize,
|
|
cx: &mut Context<ElyShell>,
|
|
) -> AnyElement {
|
|
let query_label =
|
|
if needle.is_empty() { "Type to search…".to_string() } else { needle.to_string() };
|
|
|
|
div()
|
|
.w(px(640.0))
|
|
.rounded(px(16.0))
|
|
.bg(rgba(panel_bg()))
|
|
.border_1()
|
|
.border_color(rgba(panel_border()))
|
|
.shadow(panel_shadow())
|
|
.overflow_hidden()
|
|
.flex()
|
|
.flex_col()
|
|
.child(render_header(query_label.clone(), needle.is_empty()))
|
|
.child(render_results(snapshot, needle, selected_index, cx))
|
|
.child(render_command_footer())
|
|
.into_any_element()
|
|
}
|
|
|
|
fn render_header(query_label: String, is_empty: bool) -> AnyElement {
|
|
let label_color = if is_empty { colors::ink_4() } else { colors::ink() };
|
|
|
|
div()
|
|
.flex()
|
|
.items_center()
|
|
.gap(px(12.0))
|
|
.px(px(18.0))
|
|
.py(px(16.0))
|
|
.border_b_1()
|
|
.border_color(rgba(colors::divider()))
|
|
.child(div().text_color(rgb(colors::ink_3())).child(IconName::Search))
|
|
.child(
|
|
div()
|
|
.flex_1()
|
|
.flex()
|
|
.items_center()
|
|
.gap(px(2.0))
|
|
.text_size(px(16.0))
|
|
.text_color(rgb(label_color))
|
|
.child(query_label)
|
|
.child(blink(
|
|
"command-overlay-caret",
|
|
div().w(px(1.0)).h(px(18.0)).bg(rgb(colors::accent())),
|
|
)),
|
|
)
|
|
.child(
|
|
div()
|
|
.px(px(8.0))
|
|
.py(px(2.0))
|
|
.rounded(px(6.0))
|
|
.bg(rgba(badge_bg()))
|
|
.text_size(px(10.5))
|
|
.text_color(rgb(colors::ink_3()))
|
|
.child("Switcher"),
|
|
)
|
|
.child(render_kbd("esc"))
|
|
.into_any_element()
|
|
}
|
|
|
|
fn render_results(
|
|
snapshot: &BrowserSnapshot,
|
|
needle: &str,
|
|
selected_index: usize,
|
|
cx: &mut Context<ElyShell>,
|
|
) -> AnyElement {
|
|
let tabs = matching_tabs(snapshot, needle);
|
|
let history = matching_history(snapshot, needle);
|
|
let bookmarks = matching_bookmarks(snapshot, needle);
|
|
let actions = matching_actions(needle);
|
|
|
|
let mut offset = 0usize;
|
|
let mut sections: Vec<AnyElement> = Vec::new();
|
|
if !tabs.is_empty() {
|
|
let count = tabs.len();
|
|
sections
|
|
.push(render_section("Open tabs", render_tab_rows(tabs, offset, selected_index, cx)));
|
|
offset += count;
|
|
}
|
|
if !history.is_empty() {
|
|
let count = history.len();
|
|
sections.push(render_section(
|
|
"History",
|
|
render_history_rows(history, offset, selected_index, cx),
|
|
));
|
|
offset += count;
|
|
}
|
|
if !bookmarks.is_empty() {
|
|
let count = bookmarks.len();
|
|
sections.push(render_section(
|
|
"Bookmarks",
|
|
render_bookmark_rows(bookmarks, offset, selected_index, cx),
|
|
));
|
|
offset += count;
|
|
}
|
|
if !actions.is_empty() {
|
|
sections.push(render_section(
|
|
"Actions",
|
|
render_action_rows(actions, offset, selected_index, cx),
|
|
));
|
|
}
|
|
|
|
if sections.is_empty() {
|
|
sections.push(render_empty_state());
|
|
}
|
|
|
|
div().max_h(px(440.0)).py(px(8.0)).flex().flex_col().children(sections).into_any_element()
|
|
}
|
|
|
|
fn render_section(label: &'static str, body: AnyElement) -> AnyElement {
|
|
div()
|
|
.flex()
|
|
.flex_col()
|
|
.pb(px(4.0))
|
|
.pt(px(8.0))
|
|
.child(
|
|
div()
|
|
.px(px(16.0))
|
|
.pb(px(4.0))
|
|
.pt(px(6.0))
|
|
.text_size(px(10.0))
|
|
.font_weight(FontWeight(500.0))
|
|
.text_color(rgb(colors::ink_4()))
|
|
.child(label),
|
|
)
|
|
.child(body)
|
|
.into_any_element()
|
|
}
|
|
|
|
fn render_empty_state() -> AnyElement {
|
|
div()
|
|
.px(px(18.0))
|
|
.py(px(20.0))
|
|
.text_size(px(12.5))
|
|
.text_color(rgb(colors::ink_4()))
|
|
.child("No matches yet — keep typing.")
|
|
.into_any_element()
|
|
}
|
|
|
|
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<BoxShadow> {
|
|
vec![BoxShadow {
|
|
color: hsla(25.0 / 360.0, 0.33, 0.12, 0.45),
|
|
offset: point(px(0.0), px(30.0)),
|
|
blur_radius: px(80.0),
|
|
spread_radius: px(-20.0),
|
|
}]
|
|
}
|