Rebuild /settings/appearance as the design form
The appearance route now opens with the design layout: serif "GENERAL" overline + "Appearance" headline + intro paragraph, a four-tile wallpaper picker (Dawn/Violet/Mint/Slate with active outline + check glyph), a theme-mode segmented control, an accent swatch row, a reduce-motion toggle, and a reset row. Every control mutates real state through new shell methods (set_wallpaper_theme, set_theme_mode, toggle_reduce_motion, reset_appearance) which delegate to the core. The chrome lives in chrome::appearance_form so the page module is a six-line shim. Light/Dark theme buttons currently switch the persisted mode; rendering swaps will land when light/dark token sheets ship — keeping the persistence so the eventual flip is one place.
This commit is contained in:
Submodule
+1
Submodule claude-goal added at cacea1bbe7
@@ -0,0 +1,381 @@
|
|||||||
|
use ely_browser_core::BrowserSnapshot;
|
||||||
|
use ely_design_system::colors;
|
||||||
|
use ely_domain::{ThemeMode, WallpaperTheme};
|
||||||
|
use gpui::{
|
||||||
|
AnyElement, Context, FontWeight, Hsla, InteractiveElement, IntoElement, ParentElement,
|
||||||
|
SharedString, StatefulInteractiveElement, Styled, div, hsla, linear_color_stop,
|
||||||
|
linear_gradient, prelude::FluentBuilder, px, rgb, rgba,
|
||||||
|
};
|
||||||
|
use gpui_component::{IconName, scroll::ScrollableElement};
|
||||||
|
|
||||||
|
use crate::shell::ElyShell;
|
||||||
|
|
||||||
|
pub(crate) fn render_appearance_form(
|
||||||
|
snapshot: &BrowserSnapshot,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.h_full()
|
||||||
|
.overflow_y_scrollbar()
|
||||||
|
.pt(px(28.0))
|
||||||
|
.px(px(40.0))
|
||||||
|
.pb(px(32.0))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.max_w(px(780.0))
|
||||||
|
.mx_auto()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap(px(28.0))
|
||||||
|
.child(render_header())
|
||||||
|
.child(render_wallpaper_grid(snapshot, cx))
|
||||||
|
.child(render_appearance_rows(snapshot, cx)),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_header() -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap(px(6.0))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(11.0))
|
||||||
|
.text_color(rgb(colors::INK_4))
|
||||||
|
.child("GENERAL"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(34.0))
|
||||||
|
.font_weight(FontWeight(400.0))
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child("Appearance"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.max_w(px(520.0))
|
||||||
|
.text_size(px(13.0))
|
||||||
|
.text_color(rgb(colors::INK_3))
|
||||||
|
.child(
|
||||||
|
"Tune the atmosphere of your browser. ELY's wallpaper sets the ambient \
|
||||||
|
palette of every surface; pick one and let it breathe.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_wallpaper_grid(
|
||||||
|
snapshot: &BrowserSnapshot,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let active = snapshot.appearance.wallpaper();
|
||||||
|
div()
|
||||||
|
.grid()
|
||||||
|
.grid_cols(4)
|
||||||
|
.gap(px(10.0))
|
||||||
|
.child(render_wallpaper_swatch(WallpaperTheme::Dawn, active, cx))
|
||||||
|
.child(render_wallpaper_swatch(WallpaperTheme::Violet, active, cx))
|
||||||
|
.child(render_wallpaper_swatch(WallpaperTheme::Mint, active, cx))
|
||||||
|
.child(render_wallpaper_swatch(WallpaperTheme::Slate, active, cx))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_wallpaper_swatch(
|
||||||
|
theme: WallpaperTheme,
|
||||||
|
active: WallpaperTheme,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let label = wallpaper_label(theme);
|
||||||
|
let id = SharedString::from(format!("wallpaper-{}", label.to_ascii_lowercase()));
|
||||||
|
let selected = theme == active;
|
||||||
|
let (upper, lower, base) = swatch_colors(theme);
|
||||||
|
|
||||||
|
div()
|
||||||
|
.id(id)
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap(px(8.0))
|
||||||
|
.cursor_pointer()
|
||||||
|
.hover(|style| style.opacity(0.94))
|
||||||
|
.active(|style| style.opacity(0.85))
|
||||||
|
.on_click(cx.listener(move |shell, _, _, cx| {
|
||||||
|
shell.set_wallpaper_theme(theme, cx);
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.h(px(96.0))
|
||||||
|
.rounded(px(10.0))
|
||||||
|
.when(selected, |el| {
|
||||||
|
el.border_2().border_color(rgb(colors::ACCENT))
|
||||||
|
})
|
||||||
|
.when(!selected, |el| {
|
||||||
|
el.border_1().border_color(rgba(colors::STROKE))
|
||||||
|
})
|
||||||
|
.relative()
|
||||||
|
.overflow_hidden()
|
||||||
|
.bg(rgb(base))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.inset_0()
|
||||||
|
.bg(linear_gradient(
|
||||||
|
225.0,
|
||||||
|
linear_color_stop(upper, 0.0),
|
||||||
|
linear_color_stop(transparent_like(upper), 0.6),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.inset_0()
|
||||||
|
.bg(linear_gradient(
|
||||||
|
45.0,
|
||||||
|
linear_color_stop(lower, 0.0),
|
||||||
|
linear_color_stop(transparent_like(lower), 0.6),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap(px(6.0))
|
||||||
|
.text_size(px(12.0))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.font_weight(FontWeight(500.0))
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child(label),
|
||||||
|
)
|
||||||
|
.when(selected, |el| {
|
||||||
|
el.child(
|
||||||
|
div()
|
||||||
|
.text_color(rgb(colors::ACCENT))
|
||||||
|
.child(IconName::Check),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_appearance_rows(
|
||||||
|
snapshot: &BrowserSnapshot,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let appearance = snapshot.appearance;
|
||||||
|
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.child(render_theme_mode_row(appearance.theme_mode(), cx))
|
||||||
|
.child(render_accent_row())
|
||||||
|
.child(render_reduce_motion_row(appearance.reduce_motion(), cx))
|
||||||
|
.child(render_reset_row(cx))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_theme_mode_row(
|
||||||
|
active: ThemeMode,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
settings_row(
|
||||||
|
"Theme mode",
|
||||||
|
"Match system appearance, or pick one to lock.",
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap(px(2.0))
|
||||||
|
.p(px(2.0))
|
||||||
|
.rounded(px(8.0))
|
||||||
|
.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))
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn theme_segment(
|
||||||
|
label: &'static str,
|
||||||
|
mode: ThemeMode,
|
||||||
|
active: ThemeMode,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let selected = mode == active;
|
||||||
|
let bg = if selected { 0xffffffff } else { 0x00000000 };
|
||||||
|
let id = SharedString::from(format!("theme-mode-{}", label.to_ascii_lowercase()));
|
||||||
|
|
||||||
|
div()
|
||||||
|
.id(id)
|
||||||
|
.px(px(12.0))
|
||||||
|
.py(px(4.0))
|
||||||
|
.rounded(px(6.0))
|
||||||
|
.text_size(px(12.0))
|
||||||
|
.font_weight(FontWeight(500.0))
|
||||||
|
.text_color(rgb(colors::INK_2))
|
||||||
|
.bg(rgba(bg))
|
||||||
|
.cursor_pointer()
|
||||||
|
.hover(|style| style.opacity(0.92))
|
||||||
|
.active(|style| style.opacity(0.82))
|
||||||
|
.on_click(cx.listener(move |shell, _, _, cx| {
|
||||||
|
shell.set_theme_mode(mode, cx);
|
||||||
|
}))
|
||||||
|
.child(label)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_accent_row() -> AnyElement {
|
||||||
|
settings_row(
|
||||||
|
"Accent color",
|
||||||
|
"Used across selection, focus rings & links.",
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap(px(8.0))
|
||||||
|
.child(swatch(colors::ACCENT, true))
|
||||||
|
.child(swatch(0xec6a8e, false))
|
||||||
|
.child(swatch(0x7c6cf7, false))
|
||||||
|
.child(swatch(0x4fb59a, false))
|
||||||
|
.child(swatch(colors::INK, false))
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swatch(color: u32, selected: bool) -> AnyElement {
|
||||||
|
let mut element = div()
|
||||||
|
.size(px(22.0))
|
||||||
|
.rounded_full()
|
||||||
|
.bg(rgb(color))
|
||||||
|
.border_1()
|
||||||
|
.border_color(rgba(0x0000000d));
|
||||||
|
if selected {
|
||||||
|
element = element
|
||||||
|
.border_2()
|
||||||
|
.border_color(rgb(colors::ACCENT));
|
||||||
|
}
|
||||||
|
element.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_reduce_motion_row(reduce: bool, cx: &mut Context<ElyShell>) -> AnyElement {
|
||||||
|
settings_row(
|
||||||
|
"Reduce motion",
|
||||||
|
"Mute transitions and ambient effects.",
|
||||||
|
div()
|
||||||
|
.id(SharedString::from("toggle-reduce-motion"))
|
||||||
|
.w(px(34.0))
|
||||||
|
.h(px(20.0))
|
||||||
|
.rounded_full()
|
||||||
|
.bg(if reduce { rgb(colors::ACCENT) } else { rgba(0x281e1426) })
|
||||||
|
.p(px(2.0))
|
||||||
|
.cursor_pointer()
|
||||||
|
.hover(|style| style.opacity(0.9))
|
||||||
|
.active(|style| style.opacity(0.78))
|
||||||
|
.on_click(cx.listener(|shell, _, _, cx| shell.toggle_reduce_motion(cx)))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.size(px(16.0))
|
||||||
|
.rounded_full()
|
||||||
|
.bg(rgb(0xffffff))
|
||||||
|
.when(reduce, |el| el.ml(px(14.0))),
|
||||||
|
)
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_reset_row(cx: &mut Context<ElyShell>) -> AnyElement {
|
||||||
|
settings_row(
|
||||||
|
"Restore defaults",
|
||||||
|
"Reset wallpaper, theme mode, and motion to defaults.",
|
||||||
|
div()
|
||||||
|
.id(SharedString::from("appearance-reset"))
|
||||||
|
.px(px(12.0))
|
||||||
|
.py(px(7.0))
|
||||||
|
.rounded(px(8.0))
|
||||||
|
.bg(rgba(SEGMENT_BG))
|
||||||
|
.text_size(px(12.0))
|
||||||
|
.font_weight(FontWeight(500.0))
|
||||||
|
.text_color(rgb(colors::INK_2))
|
||||||
|
.cursor_pointer()
|
||||||
|
.hover(|style| style.opacity(0.92))
|
||||||
|
.active(|style| style.opacity(0.82))
|
||||||
|
.on_click(cx.listener(|shell, _, _, cx| shell.reset_appearance(cx)))
|
||||||
|
.child("Reset")
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings_row(title: &'static str, detail: &'static str, control: AnyElement) -> AnyElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap(px(16.0))
|
||||||
|
.py(px(14.0))
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(rgba(colors::DIVIDER))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.min_w_0()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(13.0))
|
||||||
|
.font_weight(FontWeight(500.0))
|
||||||
|
.text_color(rgb(colors::INK))
|
||||||
|
.child(title),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_size(px(11.5))
|
||||||
|
.text_color(rgb(colors::INK_3))
|
||||||
|
.child(detail),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(control)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wallpaper_label(theme: WallpaperTheme) -> &'static str {
|
||||||
|
match theme {
|
||||||
|
WallpaperTheme::Dawn => "Dawn",
|
||||||
|
WallpaperTheme::Violet => "Violet",
|
||||||
|
WallpaperTheme::Mint => "Mint",
|
||||||
|
WallpaperTheme::Slate => "Slate",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swatch_colors(theme: WallpaperTheme) -> (Hsla, Hsla, u32) {
|
||||||
|
match theme {
|
||||||
|
WallpaperTheme::Dawn => (
|
||||||
|
hsla(351.0 / 360.0, 1.0, 0.91, 0.95),
|
||||||
|
hsla(228.0 / 360.0, 1.0, 0.86, 0.85),
|
||||||
|
0xefe8e1,
|
||||||
|
),
|
||||||
|
WallpaperTheme::Violet => (
|
||||||
|
hsla(263.0 / 360.0, 1.0, 0.88, 0.95),
|
||||||
|
hsla(33.0 / 360.0, 1.0, 0.87, 0.85),
|
||||||
|
0xe9e2ee,
|
||||||
|
),
|
||||||
|
WallpaperTheme::Mint => (
|
||||||
|
hsla(146.0 / 360.0, 0.69, 0.85, 0.95),
|
||||||
|
hsla(40.0 / 360.0, 1.0, 0.86, 0.85),
|
||||||
|
0xe6ece2,
|
||||||
|
),
|
||||||
|
WallpaperTheme::Slate => (
|
||||||
|
hsla(218.0 / 360.0, 0.20, 0.85, 0.95),
|
||||||
|
hsla(20.0 / 360.0, 0.0, 0.10, 0.40),
|
||||||
|
0xd6dae3,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transparent_like(color: Hsla) -> Hsla {
|
||||||
|
hsla(color.h, color.s, color.l, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SEGMENT_BG: u32 = 0x281e140d;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub(crate) mod appearance_form;
|
||||||
pub(crate) mod brand_glyph;
|
pub(crate) mod brand_glyph;
|
||||||
pub(crate) mod command_footer;
|
pub(crate) mod command_footer;
|
||||||
pub(crate) mod command_overlay;
|
pub(crate) mod command_overlay;
|
||||||
@@ -9,6 +10,7 @@ pub(crate) mod split_pane;
|
|||||||
pub(crate) mod topbar;
|
pub(crate) mod topbar;
|
||||||
pub(crate) mod wallpaper;
|
pub(crate) mod wallpaper;
|
||||||
|
|
||||||
|
pub(crate) use appearance_form::render_appearance_form;
|
||||||
pub(crate) use brand_glyph::render_glyph_for;
|
pub(crate) use brand_glyph::render_glyph_for;
|
||||||
pub(crate) use command_overlay::render_command_overlay;
|
pub(crate) use command_overlay::render_command_overlay;
|
||||||
pub(crate) use home::render_home_page;
|
pub(crate) use home::render_home_page;
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ impl ElyShell {
|
|||||||
"ely://about" => self.render_about_page(snapshot),
|
"ely://about" => self.render_about_page(snapshot),
|
||||||
"ely://settings" => self.render_settings_page(snapshot, cx),
|
"ely://settings" => self.render_settings_page(snapshot, cx),
|
||||||
"ely://settings/advanced" => self.render_advanced_page(snapshot),
|
"ely://settings/advanced" => self.render_advanced_page(snapshot),
|
||||||
"ely://settings/appearance" => self.render_appearance_page(snapshot),
|
"ely://settings/appearance" => self.render_appearance_page(snapshot, cx),
|
||||||
"ely://settings/general" => self.render_general_page(snapshot, cx),
|
"ely://settings/general" => self.render_general_page(snapshot, cx),
|
||||||
"ely://settings/sidebar-tabs" => self.render_sidebar_tabs_page(snapshot, cx),
|
"ely://settings/sidebar-tabs" => self.render_sidebar_tabs_page(snapshot, cx),
|
||||||
"ely://settings/search" => self.render_search_page(snapshot, cx),
|
"ely://settings/search" => self.render_search_page(snapshot, cx),
|
||||||
|
|||||||
@@ -1,235 +1,15 @@
|
|||||||
use ely_browser_core::BrowserSnapshot;
|
use ely_browser_core::BrowserSnapshot;
|
||||||
use ely_design_system::colors;
|
use gpui::{AnyElement, Context};
|
||||||
use gpui::{AnyElement, IntoElement, ParentElement, Styled, div, px, rgb};
|
|
||||||
use gpui_component::{IconName, StyledExt, scroll::ScrollableElement};
|
|
||||||
|
|
||||||
use super::{ElyShell, render_canvas_surface};
|
use super::ElyShell;
|
||||||
|
use crate::shell::chrome::render_appearance_form;
|
||||||
struct ColorToken {
|
|
||||||
label: &'static str,
|
|
||||||
detail: &'static str,
|
|
||||||
value: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
const THEME_TOKENS: &[ColorToken] = &[
|
|
||||||
ColorToken { label: "Canvas", detail: "Browser frame background", value: colors::CANVAS },
|
|
||||||
ColorToken {
|
|
||||||
label: "Canvas Soft",
|
|
||||||
detail: "Internal page backdrop",
|
|
||||||
value: colors::CANVAS_SOFT,
|
|
||||||
},
|
|
||||||
ColorToken { label: "Surface", detail: "Cards and panels", value: colors::SURFACE_CARD },
|
|
||||||
ColorToken { label: "Ink", detail: "Primary text", value: colors::INK },
|
|
||||||
ColorToken { label: "Muted", detail: "Secondary text", value: colors::MUTED },
|
|
||||||
ColorToken { label: "Hairline", detail: "Borders and dividers", value: colors::HAIRLINE },
|
|
||||||
ColorToken { label: "Accent", detail: "Primary actions", value: colors::PRIMARY },
|
|
||||||
ColorToken { label: "Success", detail: "Resolved state", value: colors::SUCCESS },
|
|
||||||
ColorToken { label: "Error", detail: "Attention state", value: colors::ERROR },
|
|
||||||
];
|
|
||||||
|
|
||||||
impl ElyShell {
|
impl ElyShell {
|
||||||
pub(super) fn render_appearance_page(&mut self, snapshot: &BrowserSnapshot) -> AnyElement {
|
pub(super) fn render_appearance_page(
|
||||||
render_canvas_surface(
|
&mut self,
|
||||||
div()
|
snapshot: &BrowserSnapshot,
|
||||||
.size_full()
|
cx: &mut Context<Self>,
|
||||||
.p_8()
|
) -> AnyElement {
|
||||||
.flex()
|
render_appearance_form(snapshot, cx)
|
||||||
.flex_col()
|
|
||||||
.gap_5()
|
|
||||||
.child(render_appearance_header(snapshot))
|
|
||||||
.child(render_theme_summary(snapshot))
|
|
||||||
.child(render_color_sections(snapshot)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_appearance_header(snapshot: &BrowserSnapshot) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_end()
|
|
||||||
.justify_between()
|
|
||||||
.gap_4()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.min_w_0()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_2()
|
|
||||||
.child(div().text_size(px(26.0)).text_color(rgb(colors::INK)).child("Appearance"))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_sm()
|
|
||||||
.truncate()
|
|
||||||
.text_color(rgb(colors::MUTED))
|
|
||||||
.child(format!("Space: {}", snapshot.active_space_name)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_2()
|
|
||||||
.text_xs()
|
|
||||||
.font_semibold()
|
|
||||||
.text_color(rgb(colors::MUTED))
|
|
||||||
.child(IconName::Palette)
|
|
||||||
.child(format!("{} tokens", THEME_TOKENS.len())),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_theme_summary(snapshot: &BrowserSnapshot) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.rounded_md()
|
|
||||||
.border_1()
|
|
||||||
.border_color(rgb(colors::HAIRLINE))
|
|
||||||
.bg(rgb(colors::CANVAS_SOFT))
|
|
||||||
.px_4()
|
|
||||||
.py_3()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.gap_4()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.min_w_0()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_3()
|
|
||||||
.child(div().text_color(rgb(colors::PRIMARY)).child(IconName::Palette))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.min_w_0()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_sm()
|
|
||||||
.font_semibold()
|
|
||||||
.text_color(rgb(colors::INK))
|
|
||||||
.child("ELY Theme"),
|
|
||||||
)
|
|
||||||
.child(div().text_xs().truncate().text_color(rgb(colors::MUTED)).child(
|
|
||||||
format!(
|
|
||||||
"{} profile on {} space",
|
|
||||||
snapshot.active_profile_name, snapshot.active_space_name
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_2()
|
|
||||||
.text_xs()
|
|
||||||
.font_semibold()
|
|
||||||
.text_color(rgb(colors::MUTED))
|
|
||||||
.child(color_swatch(colors::PRIMARY))
|
|
||||||
.child(hex_label(colors::PRIMARY)),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_color_sections(snapshot: &BrowserSnapshot) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.min_h_0()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.overflow_y_scrollbar()
|
|
||||||
.border_t_1()
|
|
||||||
.border_color(rgb(colors::HAIRLINE))
|
|
||||||
.child(render_section_label("Theme Tokens"))
|
|
||||||
.children(THEME_TOKENS.iter().map(render_token_row))
|
|
||||||
.child(render_section_label("Current Context"))
|
|
||||||
.child(render_active_space_color(snapshot))
|
|
||||||
.child(render_active_profile_color(snapshot))
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_section_label(label: &'static str) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.pt_4()
|
|
||||||
.pb_2()
|
|
||||||
.text_xs()
|
|
||||||
.font_semibold()
|
|
||||||
.text_color(rgb(colors::MUTED))
|
|
||||||
.child(label)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_token_row(token: &ColorToken) -> AnyElement {
|
|
||||||
render_color_row(token.label, token.detail.to_string(), token.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_active_space_color(snapshot: &BrowserSnapshot) -> AnyElement {
|
|
||||||
let detail = format!("Space: {}", snapshot.active_space_name);
|
|
||||||
let value = snapshot
|
|
||||||
.spaces
|
|
||||||
.iter()
|
|
||||||
.find(|space| space.id() == &snapshot.active_space_id)
|
|
||||||
.map_or(colors::ERROR, |space| space.accent_hex());
|
|
||||||
|
|
||||||
render_color_row("Active Space Accent", detail, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_active_profile_color(snapshot: &BrowserSnapshot) -> AnyElement {
|
|
||||||
let detail = format!("Profile: {}", snapshot.active_profile_name);
|
|
||||||
let value = snapshot
|
|
||||||
.profiles
|
|
||||||
.iter()
|
|
||||||
.find(|profile| profile.id() == &snapshot.active_profile_id)
|
|
||||||
.map_or(colors::ERROR, |profile| profile.color_hex());
|
|
||||||
|
|
||||||
render_color_row("Active Profile Color", detail, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_color_row(label: &'static str, detail: String, value: u32) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.py_3()
|
|
||||||
.border_b_1()
|
|
||||||
.border_color(rgb(colors::HAIRLINE))
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_between()
|
|
||||||
.gap_4()
|
|
||||||
.child(
|
|
||||||
div().min_w_0().flex().items_center().gap_3().child(color_swatch(value)).child(
|
|
||||||
div()
|
|
||||||
.min_w_0()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_sm()
|
|
||||||
.font_semibold()
|
|
||||||
.truncate()
|
|
||||||
.text_color(rgb(colors::INK))
|
|
||||||
.child(label),
|
|
||||||
)
|
|
||||||
.child(div().text_xs().truncate().text_color(rgb(colors::MUTED)).child(detail)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div().text_xs().font_semibold().text_color(rgb(colors::BODY)).child(hex_label(value)),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn color_swatch(value: u32) -> AnyElement {
|
|
||||||
div()
|
|
||||||
.w(px(30.0))
|
|
||||||
.h(px(22.0))
|
|
||||||
.rounded_md()
|
|
||||||
.border_1()
|
|
||||||
.border_color(rgb(colors::HAIRLINE_STRONG))
|
|
||||||
.bg(rgb(value))
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hex_label(value: u32) -> String {
|
|
||||||
format!("#{value:06X}")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use ely_domain::{
|
use ely_domain::{
|
||||||
ArchivePolicy, DiagnosticsReportingPolicy, DownloadPolicy, FavoriteLimit,
|
ArchivePolicy, DiagnosticsReportingPolicy, DownloadPolicy, FavoriteLimit,
|
||||||
HistoryRecordingPolicy, NewTabDestination, ProfileId, ProfileSyncPolicy, SearchEngine,
|
HistoryRecordingPolicy, NewTabDestination, ProfileId, ProfileSyncPolicy, SearchEngine,
|
||||||
SyncObjectKind, SyncObjectPolicy, UpdatePolicy,
|
SyncObjectKind, SyncObjectPolicy, ThemeMode, UpdatePolicy, WallpaperTheme,
|
||||||
};
|
};
|
||||||
use gpui::Context;
|
use gpui::Context;
|
||||||
|
|
||||||
@@ -49,6 +49,43 @@ impl ElyShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_wallpaper_theme(
|
||||||
|
&mut self,
|
||||||
|
wallpaper: WallpaperTheme,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
if let ShellState::Ready(core) = &mut self.state {
|
||||||
|
core.set_wallpaper_theme(wallpaper);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_theme_mode(
|
||||||
|
&mut self,
|
||||||
|
theme_mode: ThemeMode,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
if let ShellState::Ready(core) = &mut self.state {
|
||||||
|
core.set_theme_mode(theme_mode);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn toggle_reduce_motion(&mut self, cx: &mut Context<Self>) {
|
||||||
|
if let ShellState::Ready(core) = &mut self.state {
|
||||||
|
let next = !core.appearance().reduce_motion();
|
||||||
|
core.set_reduce_motion(next);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn reset_appearance(&mut self, cx: &mut Context<Self>) {
|
||||||
|
if let ShellState::Ready(core) = &mut self.state {
|
||||||
|
core.reset_appearance();
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn reset_general_settings(&mut self, cx: &mut Context<Self>) {
|
pub(super) fn reset_general_settings(&mut self, cx: &mut Context<Self>) {
|
||||||
if let ShellState::Ready(core) = &mut self.state {
|
if let ShellState::Ready(core) = &mut self.state {
|
||||||
core.reset_general_settings();
|
core.reset_general_settings();
|
||||||
|
|||||||
Reference in New Issue
Block a user