e1ac84ae44
- V4 cache-hit input prices cut to 1/10th per DeepSeek pricing update: Pro promo 0.03625→0.003625, Pro base 0.145→0.0145, Flash 0.028→0.0028 - Remove the 'light' theme variant (Variant::Light, Theme::light(), test) - Remove the theme setting entirely — hardcode UI_THEME to whale/dark, drop the theme field from Settings, ConfigView, and config command - Bump workspace version 0.6.0 → 0.6.1 (Cargo.toml, npm pkg, CHANGELOG) - De-cringe the README: drop emojis, marketing fluff, unverified claims
172 lines
5.8 KiB
Rust
172 lines
5.8 KiB
Rust
//! Whale/DeepSeek terminal theme tokens.
|
|
//!
|
|
//! A small, deliberately flat module that names the color, border, and
|
|
//! padding choices the TUI is already making. All values match the dark
|
|
//! palette previously hard-coded against [`crate::palette`]; a single
|
|
//! source-of-truth change here can swap the skin later. Visible output
|
|
//! is not changed by introducing this module.
|
|
//!
|
|
//! The only consumers today are the plan and tool cell renderers in
|
|
//! [`crate::tui::history`] and the sidebar section chrome in
|
|
//! [`crate::tui::ui`]. All other call sites continue to use [`crate::palette`]
|
|
//! directly until they are migrated in a later slice.
|
|
|
|
use ratatui::style::{Color, Modifier, Style};
|
|
use ratatui::widgets::{BorderType, Borders, Padding};
|
|
|
|
use crate::palette;
|
|
use crate::tui::history::ToolStatus;
|
|
|
|
/// Visual variant exposed by the theme.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum Variant {
|
|
Dark,
|
|
}
|
|
|
|
/// Centralized visual tokens for sidebar, plan, and tool rendering.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct Theme {
|
|
pub variant: Variant,
|
|
|
|
// Sidebar / section chrome
|
|
pub section_borders: Borders,
|
|
pub section_border_type: BorderType,
|
|
pub section_border_color: Color,
|
|
pub section_bg: Color,
|
|
pub section_title_color: Color,
|
|
pub section_padding: Padding,
|
|
|
|
// Tool cell color tokens
|
|
pub tool_title_color: Color,
|
|
pub tool_value_color: Color,
|
|
pub tool_label_color: Color,
|
|
pub tool_running_accent: Color,
|
|
pub tool_success_accent: Color,
|
|
pub tool_failed_accent: Color,
|
|
|
|
// Plan cell color tokens
|
|
pub plan_progress_color: Color,
|
|
pub plan_summary_color: Color,
|
|
pub plan_explanation_color: Color,
|
|
pub plan_pending_color: Color,
|
|
pub plan_in_progress_color: Color,
|
|
pub plan_completed_color: Color,
|
|
}
|
|
|
|
impl Theme {
|
|
/// The current dark theme. Visible output today uses these values.
|
|
#[must_use]
|
|
pub const fn dark() -> Self {
|
|
Self {
|
|
variant: Variant::Dark,
|
|
section_borders: Borders::ALL,
|
|
section_border_type: BorderType::Plain,
|
|
section_border_color: palette::BORDER_COLOR,
|
|
section_bg: palette::DEEPSEEK_INK,
|
|
section_title_color: palette::DEEPSEEK_BLUE,
|
|
section_padding: Padding::uniform(1),
|
|
tool_title_color: palette::TEXT_SOFT,
|
|
tool_value_color: palette::TEXT_MUTED,
|
|
tool_label_color: palette::TEXT_DIM,
|
|
tool_running_accent: palette::ACCENT_TOOL_LIVE,
|
|
tool_success_accent: palette::TEXT_DIM,
|
|
tool_failed_accent: palette::ACCENT_TOOL_ISSUE,
|
|
plan_progress_color: palette::STATUS_SUCCESS,
|
|
plan_summary_color: palette::TEXT_MUTED,
|
|
plan_explanation_color: palette::TEXT_DIM,
|
|
plan_pending_color: palette::TEXT_MUTED,
|
|
plan_in_progress_color: palette::STATUS_WARNING,
|
|
plan_completed_color: palette::STATUS_SUCCESS,
|
|
}
|
|
}
|
|
|
|
/// Pick the right tool accent for a given [`ToolStatus`].
|
|
#[must_use]
|
|
pub const fn tool_status_color(self, status: ToolStatus) -> Color {
|
|
match status {
|
|
ToolStatus::Running => self.tool_running_accent,
|
|
ToolStatus::Success => self.tool_success_accent,
|
|
ToolStatus::Failed => self.tool_failed_accent,
|
|
}
|
|
}
|
|
|
|
/// Bold tool title style (e.g. "Plan", "Shell").
|
|
#[must_use]
|
|
pub fn tool_title_style(self) -> Style {
|
|
Style::default()
|
|
.fg(self.tool_title_color)
|
|
.add_modifier(Modifier::BOLD)
|
|
}
|
|
|
|
/// Right-side status text ("running", "done", "issue") style.
|
|
#[must_use]
|
|
pub fn tool_status_style(self, status: ToolStatus) -> Style {
|
|
Style::default().fg(self.tool_status_color(status))
|
|
}
|
|
|
|
/// Detail label style ("command:", "time:", step markers).
|
|
#[must_use]
|
|
pub fn tool_label_style(self) -> Style {
|
|
Style::default().fg(self.tool_label_color)
|
|
}
|
|
|
|
/// Default value style for tool detail rows.
|
|
#[must_use]
|
|
pub fn tool_value_style(self) -> Style {
|
|
Style::default().fg(self.tool_value_color)
|
|
}
|
|
}
|
|
|
|
/// Returns the active theme used by the TUI today.
|
|
///
|
|
/// Today this is always `Theme::dark()`. A future PR can wire this to an
|
|
/// `App` field or a config setting in five lines.
|
|
#[must_use]
|
|
pub const fn active_theme() -> Theme {
|
|
Theme::dark()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{Theme, Variant, active_theme};
|
|
use crate::palette;
|
|
use crate::tui::history::ToolStatus;
|
|
|
|
#[test]
|
|
fn active_theme_returns_dark() {
|
|
assert_eq!(active_theme(), Theme::dark());
|
|
}
|
|
|
|
#[test]
|
|
fn dark_theme_matches_existing_palette_choices() {
|
|
let theme = Theme::dark();
|
|
assert_eq!(theme.variant, Variant::Dark);
|
|
assert_eq!(theme.section_border_color, palette::BORDER_COLOR);
|
|
assert_eq!(theme.section_bg, palette::DEEPSEEK_INK);
|
|
assert_eq!(theme.section_title_color, palette::DEEPSEEK_BLUE);
|
|
assert_eq!(theme.tool_title_color, palette::TEXT_SOFT);
|
|
assert_eq!(theme.tool_value_color, palette::TEXT_MUTED);
|
|
assert_eq!(theme.tool_label_color, palette::TEXT_DIM);
|
|
assert_eq!(theme.tool_running_accent, palette::ACCENT_TOOL_LIVE);
|
|
assert_eq!(theme.tool_success_accent, palette::TEXT_DIM);
|
|
assert_eq!(theme.tool_failed_accent, palette::ACCENT_TOOL_ISSUE);
|
|
}
|
|
|
|
#[test]
|
|
fn tool_status_color_maps_each_status() {
|
|
let theme = Theme::dark();
|
|
assert_eq!(
|
|
theme.tool_status_color(ToolStatus::Running),
|
|
theme.tool_running_accent
|
|
);
|
|
assert_eq!(
|
|
theme.tool_status_color(ToolStatus::Success),
|
|
theme.tool_success_accent
|
|
);
|
|
assert_eq!(
|
|
theme.tool_status_color(ToolStatus::Failed),
|
|
theme.tool_failed_accent
|
|
);
|
|
}
|
|
}
|