Theme · Editing: a theme laid open, and an owner's palette per mode

settings::ThemeEditor lays a theme open: density, corners, text size,
high contrast and reduced motion, then twelve colors of the mode shown,
each a swatch that opens a picker beside its hex; each change hands the
owner a whole draft. Theme::set_palette takes an owner's palette per
mode, the colors fading to it when the mode shows; Theme::palette names
the palette the shown mode fades to; Palette::token_mut and
Syntax::token_mut reach a color by its field's name. The gallery's
theme page applies the editor to the whole gallery live, with a way
back to Ely's theme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-09-27 16:31:33 -04:00
1 parent 5f2e6a5cda
commit 3d2bc0f4d6
13 files changed
+465 -11

No files matched your search

+61
View File
@@ -0,0 +1,61 @@
use ely_gpui_component::{
buttons::{Button, ButtonVariant},
settings::{ThemeDraft, ThemeEditor},
theme::{ActiveTheme, Density, Mode, Theme},
};
use gpui::{App, IntoElement, ParentElement, Styled, Window, div, px};
use crate::ui::section;
/// Applies a draft to the app's theme: its measures and switches, and its colors as the shown mode's own.
fn apply(draft: &ThemeDraft, cx: &mut App) {
let now = ThemeDraft::of(cx.theme());
Theme::update(cx, |theme| {
theme.density = draft.density;
theme.radius_scale = draft.radius_scale;
theme.font_scale = draft.font_scale;
theme.reduced_motion = draft.reduced_motion;
});
if draft.high_contrast != now.high_contrast {
Theme::set_high_contrast(draft.high_contrast, cx);
}
if draft.colors != now.colors {
Theme::set_palette(cx.theme().mode(), Some(draft.colors.clone()), cx);
}
}
/// Ely's own theme again, in both modes.
fn restore(cx: &mut App) {
Theme::update(cx, |theme| {
theme.density = Density::Standard;
theme.radius_scale = 1.0;
theme.font_scale = 1.0;
});
Theme::set_palette(Mode::Light, None, cx);
Theme::set_palette(Mode::Dark, None, cx);
Theme::set_high_contrast(false, cx);
}
pub fn editor(_: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
let draft = ThemeDraft::of(cx.theme());
section(
"ThemeEditor · DensityProvider · RadiusProvider · FontProvider · HighContrastMode · ReducedMotionProvider",
"The theme laid open: every change here applies to the whole gallery at once, the colors as the shown mode's own. The theme switch above is settings::ThemeSelector.",
cx,
)
.child(
div()
.w(px(520.))
.flex()
.flex_col()
.gap_4()
.child(ThemeEditor::new("theme-editor", draft).on_change(|draft, _, cx| apply(draft, cx)))
.child(
div().flex().child(
Button::new("theme-restore", "Back to Ely's theme")
.variant(ButtonVariant::Secondary)
.on_click(|_, _, cx| restore(cx)),
),
),
)
}
@@ -9,6 +9,8 @@ use gpui::{
IntoElement, ParentElement, SharedString, Styled, StyledText, Window, div, px,
};
mod editing;
use super::Page;
use crate::ui::{section, specimen, specimens};
@@ -21,7 +23,7 @@ pub const PAGE: Page = Page {
script: &[],
};
fn render(_: &mut Window, cx: &mut App) -> AnyElement {
fn render(window: &mut Window, cx: &mut App) -> AnyElement {
let colors = cx.theme().colors.clone();
let surfaces = [
("bg", colors.bg),
@@ -144,6 +146,7 @@ fn render(_: &mut Window, cx: &mut App) -> AnyElement {
))
.child(track("spring", "spring · damping 0.75", motion::spring, cx)),
)
.child(editing::editor(window, cx))
.into_any_element()
}