Make every shell color token theme-aware

Dark mode was persistent in `AppearanceSettings` but never reached any
paint code: every call site in the shell read `colors::INK` etc. as a
`pub const u32`, so toggling `ThemeMode::Dark` mutated state nothing
sampled. Root-cause fix is to invert the contract — the design-system
exports functions that resolve through a thread-local `Mode`, and the
GPUI render impl sets that mode each frame.

What lands:
- `ely_design_system::colors::Mode` + thread-local + `set_mode` /
  `mode` accessors. Every ink shade, glass surface, stroke, divider,
  hairline, canvas, success / error chip now picks the warm-dark
  counterpart when the active mode is `Mode::Dark`.
- `ElyShell::render` resolves `ThemeMode::System` against
  `Window::appearance()` and pushes the mode before traversing the
  tree, so widgets lower down read the right shade without owning a
  `Mode` parameter.
- `render_wallpaper` + `panel_bg` now branch on `colors::mode()` so
  the gradient base, panel tint, and overlay highlights flip to
  warm-graphite when dark mode is active.
- Mechanical conversion across 687 call sites in 55 files from
  `colors::FOO` constants to `colors::foo()` accessors. The
  `Theme` / `ELY_THEME` const surface (unused outside the design
  system) is removed; the function surface is the new contract.
This commit is contained in:
2026-05-15 19:50:38 -04:00
parent 5548427df3
commit 6b2578c3a8
58 changed files with 1010 additions and 745 deletions
@@ -81,7 +81,7 @@ fn render_panel(
}
fn render_header(query_label: String, is_empty: bool) -> AnyElement {
let label_color = if is_empty { colors::INK_4 } else { colors::INK };
let label_color = if is_empty { colors::ink_4() } else { colors::ink() };
div()
.flex()
@@ -90,8 +90,8 @@ fn render_header(query_label: String, is_empty: bool) -> AnyElement {
.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))
.border_color(rgba(colors::divider()))
.child(div().text_color(rgb(colors::ink_3())).child(IconName::Search))
.child(
div()
.flex_1()
@@ -103,7 +103,7 @@ fn render_header(query_label: String, is_empty: bool) -> AnyElement {
.child(query_label)
.child(blink(
"command-overlay-caret",
div().w(px(1.0)).h(px(18.0)).bg(rgb(colors::ACCENT)),
div().w(px(1.0)).h(px(18.0)).bg(rgb(colors::accent())),
)),
)
.child(
@@ -113,7 +113,7 @@ fn render_header(query_label: String, is_empty: bool) -> AnyElement {
.rounded(px(6.0))
.bg(rgba(BADGE_BG))
.text_size(px(10.5))
.text_color(rgb(colors::INK_3))
.text_color(rgb(colors::ink_3()))
.child("Switcher"),
)
.child(render_kbd("esc"))
@@ -182,7 +182,7 @@ fn render_section(label: &'static str, body: AnyElement) -> AnyElement {
.pt(px(6.0))
.text_size(px(10.0))
.font_weight(FontWeight(500.0))
.text_color(rgb(colors::INK_4))
.text_color(rgb(colors::ink_4()))
.child(label),
)
.child(body)
@@ -194,7 +194,7 @@ fn render_empty_state() -> AnyElement {
.px(px(18.0))
.py(px(20.0))
.text_size(px(12.5))
.text_color(rgb(colors::INK_4))
.text_color(rgb(colors::ink_4()))
.child("No matches yet — keep typing.")
.into_any_element()
}