theme: build out the theme engine core with resolve pipeline
This commit is contained in:
+152
-16
@@ -1,6 +1,8 @@
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
App, AppContext as _, Bounds, Context, IntoElement, ParentElement as _, Render, Styled as _,
|
||||
Window, WindowBounds, WindowOptions, div, px, size,
|
||||
App, AppContext as _, Bounds, ClickEvent, Context, InteractiveElement as _, IntoElement,
|
||||
ParentElement as _, Render, SharedString, StatefulInteractiveElement as _, Styled as _, Window,
|
||||
WindowBounds, WindowOptions, div, px, size,
|
||||
};
|
||||
use gpui_native_icons::{IconAssets, IconRole, icon};
|
||||
use gpui_native_theme::Theme;
|
||||
@@ -13,6 +15,9 @@ impl Render for Gallery {
|
||||
let theme = Theme::global(cx);
|
||||
let colors = theme.colors;
|
||||
let variant = theme.variant;
|
||||
let source = gpui_native_theme::detected_source(cx);
|
||||
let mode_override = gpui_native_theme::mode_override(cx);
|
||||
let active_preset = gpui_native_theme::active_preset(cx);
|
||||
|
||||
let swatches: [(&str, gpui::Hsla); 18] = [
|
||||
("background", colors.background),
|
||||
@@ -34,6 +39,34 @@ impl Render for Gallery {
|
||||
("ring", colors.ring),
|
||||
("selection", colors.selection),
|
||||
];
|
||||
let shades = [
|
||||
("base", theme.ramps.primary.base),
|
||||
("hover", theme.ramps.primary.hover),
|
||||
("pressed", theme.ramps.primary.pressed),
|
||||
("subtle", theme.ramps.primary.subtle),
|
||||
];
|
||||
|
||||
let modes: [(SharedString, Option<gpui_native_theme::ThemeVariant>, bool); 3] = [
|
||||
("System".into(), None, mode_override.is_none()),
|
||||
(
|
||||
"Light".into(),
|
||||
Some(gpui_native_theme::ThemeVariant::Light),
|
||||
mode_override == Some(gpui_native_theme::ThemeVariant::Light),
|
||||
),
|
||||
(
|
||||
"Dark".into(),
|
||||
Some(gpui_native_theme::ThemeVariant::Dark),
|
||||
mode_override == Some(gpui_native_theme::ThemeVariant::Dark),
|
||||
),
|
||||
];
|
||||
let presets: [(SharedString, Option<&'static str>, bool); 2] = [
|
||||
("Follow system".into(), None, active_preset.is_none()),
|
||||
(
|
||||
"Tokyo Night".into(),
|
||||
Some("tokyo-night"),
|
||||
active_preset == Some("tokyo-night"),
|
||||
),
|
||||
];
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
@@ -60,7 +93,7 @@ impl Render for Gallery {
|
||||
.text_sm()
|
||||
.text_color(colors.muted_foreground)
|
||||
.child(format!(
|
||||
"phase A scaffold — variant: {variant:?} (system-resolved)"
|
||||
"phase B — theme engine · source: {source} · variant: {variant:?}"
|
||||
)),
|
||||
),
|
||||
)
|
||||
@@ -69,13 +102,78 @@ impl Render for Gallery {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(section_label("mode", colors))
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_weight(gpui::FontWeight::MEDIUM)
|
||||
.text_color(colors.muted_foreground)
|
||||
.child("color tokens"),
|
||||
)
|
||||
.flex()
|
||||
.gap_2()
|
||||
.children(modes.map(|(label, value, active)| {
|
||||
chip(
|
||||
format!("mode-{label}"),
|
||||
label,
|
||||
active,
|
||||
colors,
|
||||
move |_, _, cx| gpui_native_theme::set_mode_override(value, cx),
|
||||
)
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(section_label("preset", colors))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_2()
|
||||
.children(presets.map(|(label, value, active)| {
|
||||
chip(
|
||||
format!("preset-{label}"),
|
||||
label,
|
||||
active,
|
||||
colors,
|
||||
move |_, _, cx| {
|
||||
let _ = gpui_native_theme::set_preset(value, cx);
|
||||
},
|
||||
)
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(section_label("accent shades (derived, OKLCH)", colors))
|
||||
.child(div().flex().gap_2().children(shades.map(|(name, color)| {
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
.size_6()
|
||||
.rounded_full()
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
.bg(color),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(colors.muted_foreground)
|
||||
.child(name),
|
||||
)
|
||||
}))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(section_label("color tokens", colors))
|
||||
.child(div().flex().flex_wrap().gap_2().children(swatches.map(
|
||||
|(name, color)| {
|
||||
div()
|
||||
@@ -104,13 +202,10 @@ impl Render for Gallery {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_weight(gpui::FontWeight::MEDIUM)
|
||||
.text_color(colors.muted_foreground)
|
||||
.child("icons (Lucide fallback; platform resolvers land later)"),
|
||||
)
|
||||
.child(section_label(
|
||||
"icons (Lucide fallback; platform resolvers land later)",
|
||||
colors,
|
||||
))
|
||||
.child(
|
||||
div().flex().items_center().gap_4().children(
|
||||
IconRole::all()
|
||||
@@ -122,10 +217,51 @@ impl Render for Gallery {
|
||||
}
|
||||
}
|
||||
|
||||
fn section_label(text: &str, colors: gpui_native_theme::ColorTokens) -> impl IntoElement + use<> {
|
||||
div()
|
||||
.text_sm()
|
||||
.font_weight(gpui::FontWeight::MEDIUM)
|
||||
.text_color(colors.muted_foreground)
|
||||
.child(text.to_string())
|
||||
}
|
||||
|
||||
fn chip<F>(
|
||||
id: String,
|
||||
label: SharedString,
|
||||
active: bool,
|
||||
colors: gpui_native_theme::ColorTokens,
|
||||
on_click: F,
|
||||
) -> impl IntoElement + use<F>
|
||||
where
|
||||
F: Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
||||
{
|
||||
div()
|
||||
.id(SharedString::from(id))
|
||||
.px_3()
|
||||
.py_1()
|
||||
.rounded_full()
|
||||
.border_1()
|
||||
.text_xs()
|
||||
.cursor_pointer()
|
||||
.when(active, |el| {
|
||||
el.bg(colors.primary)
|
||||
.border_color(colors.primary)
|
||||
.text_color(colors.primary_foreground)
|
||||
})
|
||||
.when(!active, |el| {
|
||||
el.bg(colors.secondary)
|
||||
.border_color(colors.border)
|
||||
.text_color(colors.secondary_foreground)
|
||||
.hover(|el| el.bg(colors.muted))
|
||||
})
|
||||
.on_click(move |event, window, cx| on_click(event, window, cx))
|
||||
.child(label)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
application().with_assets(IconAssets).run(|cx: &mut App| {
|
||||
Theme::init(cx);
|
||||
let bounds = Bounds::centered(None, size(px(720.0), px(480.0)), cx);
|
||||
let bounds = Bounds::centered(None, size(px(720.0), px(560.0)), cx);
|
||||
if let Err(err) = cx.open_window(
|
||||
WindowOptions {
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
|
||||
Reference in New Issue
Block a user