scaffold
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "gpui-native-theme"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
description = "Native theme engine: reads the OS theme and resolves it into semantic tokens"
|
||||
|
||||
[dependencies]
|
||||
gpui = { workspace = true }
|
||||
@@ -0,0 +1,81 @@
|
||||
//! Semantic color roles. This module is the only place in the workspace
|
||||
//! allowed to hold color literals.
|
||||
|
||||
use gpui::{Hsla, rgb};
|
||||
|
||||
fn hex(hex: u32) -> Hsla {
|
||||
rgb(hex).into()
|
||||
}
|
||||
|
||||
/// The semantic color roles components paint from.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct ColorTokens {
|
||||
pub background: Hsla,
|
||||
pub foreground: Hsla,
|
||||
pub surface: Hsla,
|
||||
pub surface_foreground: Hsla,
|
||||
pub primary: Hsla,
|
||||
pub primary_foreground: Hsla,
|
||||
pub secondary: Hsla,
|
||||
pub secondary_foreground: Hsla,
|
||||
pub muted: Hsla,
|
||||
pub muted_foreground: Hsla,
|
||||
pub accent: Hsla,
|
||||
pub accent_foreground: Hsla,
|
||||
pub destructive: Hsla,
|
||||
pub destructive_foreground: Hsla,
|
||||
pub border: Hsla,
|
||||
pub input: Hsla,
|
||||
pub ring: Hsla,
|
||||
pub selection: Hsla,
|
||||
}
|
||||
|
||||
impl ColorTokens {
|
||||
/// Neutral light palette; the fallback when no system theme applies.
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
background: hex(0xffffff),
|
||||
foreground: hex(0x18181b),
|
||||
surface: hex(0xfafafa),
|
||||
surface_foreground: hex(0x18181b),
|
||||
primary: hex(0x2563eb),
|
||||
primary_foreground: hex(0xffffff),
|
||||
secondary: hex(0xf4f4f5),
|
||||
secondary_foreground: hex(0x18181b),
|
||||
muted: hex(0xf4f4f5),
|
||||
muted_foreground: hex(0x71717a),
|
||||
accent: hex(0x2563eb),
|
||||
accent_foreground: hex(0xffffff),
|
||||
destructive: hex(0xdc2626),
|
||||
destructive_foreground: hex(0xffffff),
|
||||
border: hex(0xe4e4e7),
|
||||
input: hex(0xe4e4e7),
|
||||
ring: hex(0x93c5fd),
|
||||
selection: hex(0xdbeafe),
|
||||
}
|
||||
}
|
||||
|
||||
/// Neutral dark palette; the fallback when no system theme applies.
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
background: hex(0x09090b),
|
||||
foreground: hex(0xfafafa),
|
||||
surface: hex(0x18181b),
|
||||
surface_foreground: hex(0xfafafa),
|
||||
primary: hex(0x3b82f6),
|
||||
primary_foreground: hex(0xffffff),
|
||||
secondary: hex(0x27272a),
|
||||
secondary_foreground: hex(0xfafafa),
|
||||
muted: hex(0x27272a),
|
||||
muted_foreground: hex(0xa1a1aa),
|
||||
accent: hex(0x3b82f6),
|
||||
accent_foreground: hex(0xffffff),
|
||||
destructive: hex(0xef4444),
|
||||
destructive_foreground: hex(0xffffff),
|
||||
border: hex(0x27272a),
|
||||
input: hex(0x27272a),
|
||||
ring: hex(0x1d4ed8),
|
||||
selection: hex(0x1e3a8a),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//! The theme engine: reads the running system theme and resolves it into the
|
||||
//! semantic tokens every component paints from.
|
||||
//!
|
||||
//! Phase B replaces the bundled light/dark presets with platform adapters
|
||||
//! (`NSColor` / `UISettings` / portal / Omarchy) behind a `ThemeSource` seam.
|
||||
|
||||
mod colors;
|
||||
|
||||
pub use colors::ColorTokens;
|
||||
|
||||
use gpui::{App, Global, WindowAppearance};
|
||||
|
||||
/// Light or dark, resolved from the system appearance.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ThemeVariant {
|
||||
#[default]
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
impl ThemeVariant {
|
||||
fn from_appearance(appearance: WindowAppearance) -> Self {
|
||||
match appearance {
|
||||
WindowAppearance::Light | WindowAppearance::VibrantLight => Self::Light,
|
||||
WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::Dark,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The resolved theme, installed as the app-wide global.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Theme {
|
||||
pub variant: ThemeVariant,
|
||||
pub colors: ColorTokens,
|
||||
}
|
||||
|
||||
impl Global for Theme {}
|
||||
|
||||
impl Theme {
|
||||
/// Reads the theme global. Panics before `init`.
|
||||
pub fn global(cx: &App) -> &Self {
|
||||
cx.global::<Self>()
|
||||
}
|
||||
|
||||
pub fn for_variant(variant: ThemeVariant) -> Self {
|
||||
Self {
|
||||
variant,
|
||||
colors: match variant {
|
||||
ThemeVariant::Light => ColorTokens::light(),
|
||||
ThemeVariant::Dark => ColorTokens::dark(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Installs the theme global for the current system appearance.
|
||||
pub fn init(cx: &mut App) {
|
||||
let variant = ThemeVariant::from_appearance(cx.window_appearance());
|
||||
cx.set_global(Self::for_variant(variant));
|
||||
}
|
||||
|
||||
/// Re-installs the global when a window's appearance differs from it.
|
||||
pub fn sync_from_window(window: &gpui::Window, cx: &mut App) {
|
||||
let variant = ThemeVariant::from_appearance(window.appearance());
|
||||
if cx.global::<Self>().variant != variant {
|
||||
cx.set_global(Self::for_variant(variant));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user