pub mod colors;
pub mod compat;
pub mod semantic;
pub use colors::*;
pub use compat::*;
pub use semantic::*;
use ratatui::style::Color;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CosmicVariant {
#[default]
CosmicDark,
CosmicLight,
CosmicViolet,
}
impl CosmicVariant {
pub fn resolver(&self) -> TokenResolver {
match self {
Self::CosmicDark => TokenResolver::cosmic_dark(),
Self::CosmicLight => TokenResolver::cosmic_light(),
Self::CosmicViolet => TokenResolver::cosmic_violet(),
}
}
pub fn cycle(&self) -> Self {
match self {
Self::CosmicDark => Self::CosmicLight,
Self::CosmicLight => Self::CosmicViolet,
Self::CosmicViolet => Self::CosmicDark,
}
}
pub fn label(&self) -> &'static str {
match self {
Self::CosmicDark => "🌌 Cosmic Dark",
Self::CosmicLight => "🌅 Cosmic Light",
Self::CosmicViolet => "🦋 Cosmic Violet",
}
}
pub fn from_index(index: u8) -> Option<Self> {
match index {
0 => Some(Self::CosmicDark),
1 => Some(Self::CosmicLight),
2 => Some(Self::CosmicViolet),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct TokenResolver {
palette: ColorPalette,
semantic: SemanticColors,
variant: CosmicVariant,
}
impl Default for TokenResolver {
fn default() -> Self {
Self::cosmic_dark()
}
}
impl TokenResolver {
pub fn cosmic_dark() -> Self {
let palette = ColorPalette::tailwind();
let semantic = SemanticColors::cosmic_dark(&palette);
Self {
palette,
semantic,
variant: CosmicVariant::CosmicDark,
}
}
pub fn cosmic_light() -> Self {
let palette = ColorPalette::tailwind();
let semantic = SemanticColors::cosmic_light(&palette);
Self {
palette,
semantic,
variant: CosmicVariant::CosmicLight,
}
}
pub fn cosmic_violet() -> Self {
let palette = ColorPalette::tailwind();
let semantic = SemanticColors::cosmic_violet(&palette);
Self {
palette,
semantic,
variant: CosmicVariant::CosmicViolet,
}
}
pub fn bg_primary(&self) -> Color {
self.semantic.bg_primary
}
pub fn bg_secondary(&self) -> Color {
self.semantic.bg_secondary
}
pub fn bg_tertiary(&self) -> Color {
self.semantic.bg_tertiary
}
pub fn bg_hover(&self) -> Color {
self.semantic.bg_hover
}
pub fn bg_active(&self) -> Color {
self.semantic.bg_active
}
pub fn text_primary(&self) -> Color {
self.semantic.text_primary
}
pub fn text_secondary(&self) -> Color {
self.semantic.text_secondary
}
pub fn text_muted(&self) -> Color {
self.semantic.text_muted
}
pub fn text_disabled(&self) -> Color {
self.semantic.text_disabled
}
pub fn text_inverse(&self) -> Color {
self.semantic.text_inverse
}
pub fn border_default(&self) -> Color {
self.semantic.border_default
}
pub fn border_focused(&self) -> Color {
self.semantic.border_focused
}
pub fn border_subtle(&self) -> Color {
self.semantic.border_subtle
}
pub fn accent_primary(&self) -> Color {
self.semantic.accent_primary
}
pub fn accent_secondary(&self) -> Color {
self.semantic.accent_secondary
}
pub fn accent_tertiary(&self) -> Color {
self.semantic.accent_tertiary
}
pub fn status_success(&self) -> Color {
self.semantic.status_success
}
pub fn status_warning(&self) -> Color {
self.semantic.status_warning
}
pub fn status_error(&self) -> Color {
self.semantic.status_error
}
pub fn status_info(&self) -> Color {
self.semantic.status_info
}
pub fn verb_infer(&self) -> Color {
self.semantic.verb_infer
}
pub fn verb_exec(&self) -> Color {
self.semantic.verb_exec
}
pub fn verb_fetch(&self) -> Color {
self.semantic.verb_fetch
}
pub fn verb_invoke(&self) -> Color {
self.semantic.verb_invoke
}
pub fn verb_agent(&self) -> Color {
self.semantic.verb_agent
}
pub fn verb_color(&self, verb: &str) -> Color {
match verb.to_lowercase().as_str() {
"infer" => self.verb_infer(),
"exec" => self.verb_exec(),
"fetch" => self.verb_fetch(),
"invoke" => self.verb_invoke(),
"agent" => self.verb_agent(),
_ => self.text_muted(),
}
}
pub fn palette(&self) -> &ColorPalette {
&self.palette
}
pub fn semantic(&self) -> &SemanticColors {
&self.semantic
}
pub fn variant(&self) -> CosmicVariant {
self.variant
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cosmic_dark_resolver() {
let resolver = TokenResolver::cosmic_dark();
assert_eq!(resolver.variant(), CosmicVariant::CosmicDark);
assert_eq!(resolver.bg_primary(), Color::Rgb(15, 23, 42));
}
#[test]
fn test_cosmic_light_resolver() {
let resolver = TokenResolver::cosmic_light();
assert_eq!(resolver.variant(), CosmicVariant::CosmicLight);
assert_eq!(resolver.bg_primary(), Color::Rgb(248, 250, 252));
}
#[test]
fn test_cosmic_violet_resolver() {
let resolver = TokenResolver::cosmic_violet();
assert_eq!(resolver.variant(), CosmicVariant::CosmicViolet);
}
#[test]
fn test_verb_colors() {
let resolver = TokenResolver::cosmic_dark();
assert_eq!(resolver.verb_infer(), Color::Rgb(139, 92, 246));
assert_eq!(resolver.verb_exec(), Color::Rgb(245, 158, 11));
}
#[test]
fn test_variant_cycle() {
let v = CosmicVariant::CosmicDark;
assert_eq!(v.cycle(), CosmicVariant::CosmicLight);
assert_eq!(v.cycle().cycle(), CosmicVariant::CosmicViolet);
assert_eq!(v.cycle().cycle().cycle(), CosmicVariant::CosmicDark);
}
#[test]
fn test_variant_labels() {
assert_eq!(CosmicVariant::CosmicDark.label(), "🌌 Cosmic Dark");
assert_eq!(CosmicVariant::CosmicLight.label(), "🌅 Cosmic Light");
assert_eq!(CosmicVariant::CosmicViolet.label(), "🦋 Cosmic Violet");
}
#[test]
fn test_variant_from_index_valid() {
assert_eq!(
CosmicVariant::from_index(0),
Some(CosmicVariant::CosmicDark)
);
assert_eq!(
CosmicVariant::from_index(1),
Some(CosmicVariant::CosmicLight)
);
assert_eq!(
CosmicVariant::from_index(2),
Some(CosmicVariant::CosmicViolet)
);
}
#[test]
fn test_variant_from_index_invalid() {
assert_eq!(CosmicVariant::from_index(3), None);
assert_eq!(CosmicVariant::from_index(255), None);
}
}