use ratatui::style::{Color, Modifier, Style};
#[derive(Debug, Clone)]
pub struct Theme {
pub text_primary: Style,
pub text_disabled: Style,
pub text_emphasis: Style,
pub effect_allow: Style,
pub effect_deny: Style,
pub effect_ask: Style,
pub selection: Style,
pub hint_key: Style,
pub hint_desc: Style,
pub cursor: Style,
pub border_focused: Style,
pub border_unfocused: Style,
pub border_active: Style,
pub tab_active: Style,
pub tab_inactive: Style,
pub tab_separator: Style,
pub bar_bg: Style,
pub flash_message: Style,
pub diff_add: Style,
pub diff_remove: Style,
pub diff_context: Style,
pub diff_header: Style,
pub modal_confirm_border: Style,
pub walkthrough_title: Style,
pub field_label_active: Style,
pub field_label_inactive: Style,
pub field_value_active: Style,
pub field_value_inactive: Style,
pub field_value_placeholder: Style,
pub field_option_selected: Style,
pub field_option_unselected: Style,
pub field_arrows_active: Style,
pub field_arrows_inactive: Style,
pub field_multi_cursor: Style,
pub field_multi_checked: Style,
pub field_multi_unchecked: Style,
pub test_input_active: Style,
pub test_input_inactive: Style,
pub test_error: Style,
pub test_changed_badge: Style,
pub scrollbar_thumb: Style,
pub scrollbar_track: Style,
pub provenance: Style,
pub section_header: Style,
pub detail_label: Style,
pub detail_value: Style,
}
impl Theme {
pub fn default_dark() -> Self {
Theme {
text_primary: Style::default().fg(Color::White),
text_disabled: Style::default().fg(Color::DarkGray),
text_emphasis: Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
effect_allow: Style::default().fg(Color::Green),
effect_deny: Style::default().fg(Color::Red),
effect_ask: Style::default().fg(Color::Yellow),
selection: Style::default()
.bg(Color::DarkGray)
.fg(Color::White)
.add_modifier(Modifier::BOLD),
hint_key: Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
hint_desc: Style::default().fg(Color::Gray),
cursor: Style::default().fg(Color::Black).bg(Color::White),
border_focused: Style::default().fg(Color::Cyan),
border_unfocused: Style::default().fg(Color::DarkGray),
border_active: Style::default().fg(Color::Blue),
tab_active: Style::default()
.fg(Color::White)
.bg(Color::Blue)
.add_modifier(Modifier::BOLD),
tab_inactive: Style::default().fg(Color::Gray),
tab_separator: Style::default().fg(Color::DarkGray),
bar_bg: Style::default().bg(Color::Black),
flash_message: Style::default()
.fg(Color::Green)
.add_modifier(Modifier::BOLD),
diff_add: Style::default().fg(Color::Green),
diff_remove: Style::default().fg(Color::Red),
diff_context: Style::default().fg(Color::Gray),
diff_header: Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
modal_confirm_border: Style::default().fg(Color::Yellow),
walkthrough_title: Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
field_label_active: Style::default()
.fg(Color::White)
.add_modifier(Modifier::BOLD),
field_label_inactive: Style::default().fg(Color::Gray),
field_value_active: Style::default().fg(Color::White),
field_value_inactive: Style::default().fg(Color::Cyan),
field_value_placeholder: Style::default().fg(Color::DarkGray),
field_option_selected: Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD)
.add_modifier(Modifier::UNDERLINED),
field_option_unselected: Style::default().fg(Color::DarkGray),
field_arrows_active: Style::default().fg(Color::Yellow),
field_arrows_inactive: Style::default().fg(Color::DarkGray),
field_multi_cursor: Style::default()
.fg(Color::White)
.bg(Color::DarkGray)
.add_modifier(Modifier::BOLD),
field_multi_checked: Style::default().fg(Color::Green),
field_multi_unchecked: Style::default().fg(Color::DarkGray),
test_input_active: Style::default().fg(Color::Cyan),
test_input_inactive: Style::default().fg(Color::DarkGray),
test_error: Style::default().fg(Color::Red),
test_changed_badge: Style::default()
.fg(Color::Magenta)
.add_modifier(Modifier::BOLD),
scrollbar_thumb: Style::default().fg(Color::Cyan),
scrollbar_track: Style::default().fg(Color::DarkGray),
provenance: Style::default().fg(Color::DarkGray),
detail_label: Style::default().fg(Color::DarkGray),
detail_value: Style::default().fg(Color::Cyan),
section_header: Style::default()
.fg(Color::White)
.add_modifier(Modifier::BOLD),
}
}
pub fn adaptive() -> Self {
Theme {
text_primary: Style::default(),
text_disabled: Style::default().add_modifier(Modifier::DIM),
text_emphasis: Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
effect_allow: Style::default().fg(Color::Green),
effect_deny: Style::default().fg(Color::Red),
effect_ask: Style::default().fg(Color::Yellow),
selection: Style::default()
.fg(Color::Black)
.bg(Color::White)
.add_modifier(Modifier::BOLD),
hint_key: Style::default()
.fg(Color::Yellow)
.add_modifier(Modifier::BOLD),
hint_desc: Style::default().add_modifier(Modifier::DIM),
cursor: Style::default().fg(Color::Black).bg(Color::White),
border_focused: Style::default().fg(Color::Cyan),
border_unfocused: Style::default().add_modifier(Modifier::DIM),
border_active: Style::default().fg(Color::Blue),
tab_active: Style::default()
.fg(Color::Black)
.bg(Color::Cyan)
.add_modifier(Modifier::BOLD),
tab_inactive: Style::default().add_modifier(Modifier::DIM),
tab_separator: Style::default().add_modifier(Modifier::DIM),
bar_bg: Style::default(),
flash_message: Style::default()
.fg(Color::Green)
.add_modifier(Modifier::BOLD),
diff_add: Style::default().fg(Color::Green),
diff_remove: Style::default().fg(Color::Red),
diff_context: Style::default().add_modifier(Modifier::DIM),
diff_header: Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
modal_confirm_border: Style::default().fg(Color::Yellow),
walkthrough_title: Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
field_label_active: Style::default().add_modifier(Modifier::BOLD),
field_label_inactive: Style::default().add_modifier(Modifier::DIM),
field_value_active: Style::default(),
field_value_inactive: Style::default().fg(Color::Cyan),
field_value_placeholder: Style::default().add_modifier(Modifier::DIM),
field_option_selected: Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD)
.add_modifier(Modifier::UNDERLINED),
field_option_unselected: Style::default().add_modifier(Modifier::DIM),
field_arrows_active: Style::default().fg(Color::Yellow),
field_arrows_inactive: Style::default().add_modifier(Modifier::DIM),
field_multi_cursor: Style::default()
.fg(Color::Black)
.bg(Color::White)
.add_modifier(Modifier::BOLD),
field_multi_checked: Style::default().fg(Color::Green),
field_multi_unchecked: Style::default().add_modifier(Modifier::DIM),
test_input_active: Style::default().fg(Color::Cyan),
test_input_inactive: Style::default().add_modifier(Modifier::DIM),
test_error: Style::default().fg(Color::Red),
test_changed_badge: Style::default()
.fg(Color::Magenta)
.add_modifier(Modifier::BOLD),
scrollbar_thumb: Style::default().fg(Color::Cyan),
scrollbar_track: Style::default().add_modifier(Modifier::DIM),
provenance: Style::default().add_modifier(Modifier::DIM),
detail_label: Style::default().add_modifier(Modifier::DIM),
detail_value: Style::default().fg(Color::Cyan),
section_header: Style::default().add_modifier(Modifier::BOLD),
}
}
pub fn from_env() -> Self {
match std::env::var("CLASH_THEME").as_deref() {
Ok("dark") => Self::default_dark(),
_ => Self::adaptive(),
}
}
pub fn effect(&self, decision: Option<&crate::policy::match_tree::Decision>) -> Style {
match decision {
Some(crate::policy::match_tree::Decision::Allow(_)) => self.effect_allow,
Some(crate::policy::match_tree::Decision::Deny) => self.effect_deny,
Some(crate::policy::match_tree::Decision::Ask(_)) => self.effect_ask,
None => self.text_primary,
}
}
pub fn policy_effect(&self, effect: crate::policy::Effect) -> Style {
match effect {
crate::policy::Effect::Allow => self.effect_allow,
crate::policy::Effect::Deny => self.effect_deny,
crate::policy::Effect::Ask => self.effect_ask,
}
}
pub fn sandbox_effect(&self, effect: crate::policy::sandbox_types::RuleEffect) -> Style {
match effect {
crate::policy::sandbox_types::RuleEffect::Allow => self.effect_allow,
crate::policy::sandbox_types::RuleEffect::Deny => self.effect_deny,
}
}
pub fn effect_read_only(
&self,
decision: Option<&crate::policy::match_tree::Decision>,
) -> Style {
self.effect(decision).add_modifier(Modifier::DIM)
}
}
pub struct ViewContext<'a> {
pub manifest: &'a crate::policy::match_tree::PolicyManifest,
pub theme: &'a Theme,
}