Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 149 additions & 2 deletions crates/tui/src/tui/views/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ pub struct ConfigView {
filter: String,
status: Option<String>,
locale: Locale,
effective_cost_currency: String,
last_visible_rows: Cell<usize>,
last_row_hitboxes: RefCell<Vec<(u16, usize)>>,
}
Expand Down Expand Up @@ -819,6 +820,7 @@ impl ConfigView {
filter: String::new(),
status: None,
locale: app.ui_locale,
effective_cost_currency: cost_currency_config_value(app),
last_visible_rows: Cell::new(0),
last_row_hitboxes: RefCell::new(Vec::new()),
}
Expand All @@ -841,7 +843,7 @@ impl ConfigView {

let section = row.section.label().to_lowercase();
let key = row.key.to_lowercase();
let value = row.value.to_lowercase();
let value = self.row_display_value(row).to_lowercase();
let scope = row.scope.label().to_lowercase();

filter.split_whitespace().all(|term| {
Expand Down Expand Up @@ -1120,6 +1122,27 @@ impl ConfigView {

self.update_filter(|filter| filter.clear());
}

fn row_display_value(&self, row: &ConfigRow) -> String {
if row.key == "cost_currency" && row.scope == ConfigScope::Saved {
let saved_cost_currency = crate::pricing::CostCurrency::from_setting(&row.value);
let effective_cost_currency =
crate::pricing::CostCurrency::from_setting(&self.effective_cost_currency);
if saved_cost_currency != effective_cost_currency {
return format!("{} (effective {})", row.value, self.effective_cost_currency);
}
}

row.value.clone()
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}

fn cost_currency_config_value(app: &App) -> String {
match app.cost_currency {
crate::pricing::CostCurrency::Usd => "usd",
crate::pricing::CostCurrency::Cny => "cny",
}
.to_string()
}

fn config_hint_for_key(key: &str) -> &'static str {
Expand All @@ -1137,6 +1160,7 @@ fn config_hint_for_key(key: &str) -> &'static str {
"theme" => "system | dark | light | grayscale",
"locale" => "auto | en | ja | zh-Hans | pt-BR",
"background_color" => "#RRGGBB | default",
"cost_currency" => "usd | cny",
"default_mode" => "agent | plan | yolo",
"sidebar_width" => "10..=50",
"sidebar_focus" => "auto | work | tasks | agents | context | hidden",
Expand Down Expand Up @@ -1444,7 +1468,10 @@ impl ModalView for ConfigView {
} else {
Style::default().fg(palette::TEXT_PRIMARY)
};
let value = truncate_view_text(&row.value, CONFIG_VALUE_COLUMN_WIDTH);
let value = truncate_view_text(
&self.row_display_value(row),
CONFIG_VALUE_COLUMN_WIDTH,
);
let mut line = Line::from(format!(
" {:<key_width$} {:<value_width$} {}",
row.key,
Expand Down Expand Up @@ -2023,8 +2050,52 @@ mod tests {
KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
};
use ratatui::{buffer::Buffer, layout::Rect};
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;
use std::sync::MutexGuard;
use tempfile::TempDir;

struct ConfigSettingsEnvGuard {
_tmp: TempDir,
previous_config_path: Option<OsString>,
_lock: MutexGuard<'static, ()>,
}

impl ConfigSettingsEnvGuard {
fn new(settings_toml: &str) -> Self {
let lock = crate::test_support::lock_test_env();
let tmp = TempDir::new().expect("settings tempdir");
let config_path = tmp.path().join(".deepseek").join("config.toml");
let settings_path = config_path
.parent()
.expect("settings parent")
.join("settings.toml");
std::fs::create_dir_all(config_path.parent().expect("config parent"))
.expect("config dir");
std::fs::write(&settings_path, settings_toml).expect("settings file");
let previous_config_path = std::env::var_os("DEEPSEEK_CONFIG_PATH");
unsafe {
std::env::set_var("DEEPSEEK_CONFIG_PATH", &config_path);
}
Self {
_tmp: tmp,
previous_config_path,
_lock: lock,
}
}
}

impl Drop for ConfigSettingsEnvGuard {
fn drop(&mut self) {
unsafe {
match self.previous_config_path.take() {
Some(previous) => std::env::set_var("DEEPSEEK_CONFIG_PATH", previous),
None => std::env::remove_var("DEEPSEEK_CONFIG_PATH"),
}
}
}
}

fn create_test_app() -> App {
let options = TuiOptions {
Expand All @@ -2051,6 +2122,26 @@ mod tests {
App::new(options, &Config::default())
}

fn cost_currency_row_for_settings(
settings_toml: &str,
) -> (String, String, crate::pricing::CostCurrency, Locale) {
let _guard = ConfigSettingsEnvGuard::new(settings_toml);
let app = create_test_app();
let view = ConfigView::new_for_app(&app);
let row = view
.rows
.iter()
.find(|row| row.key == "cost_currency")
.expect("cost_currency row");

(
row.value.clone(),
view.row_display_value(row),
app.cost_currency,
app.ui_locale,
)
}

fn type_filter(view: &mut ConfigView, text: &str) {
for ch in text.chars() {
let action = view.handle_key(KeyEvent::new(KeyCode::Char(ch), KeyModifiers::NONE));
Expand Down Expand Up @@ -2231,6 +2322,62 @@ mod tests {
assert_eq!(row.value, "https://ui-config-view.local/v1");
}

#[test]
fn config_view_cost_currency_shows_saved_and_effective_runtime_currency() {
let _guard = ConfigSettingsEnvGuard::new("locale = \"zh-Hans\"\ncost_currency = \"usd\"\n");
let app = create_test_app();
assert_eq!(app.ui_locale, Locale::ZhHans);
assert_eq!(app.cost_currency, crate::pricing::CostCurrency::Cny);

let view = ConfigView::new_for_app(&app);
let row = view
.rows
.iter()
.find(|row| row.key == "cost_currency")
.expect("cost_currency row");

assert_eq!(row.value, "usd");
assert_eq!(view.row_display_value(row), "usd (effective cny)");
assert_eq!(Settings::load().expect("settings").cost_currency, "usd");
}

#[test]
fn config_view_cost_currency_aliases_matching_effective_currency_are_silent() {
for alias in ["rmb", "yuan", "¥"] {
let (saved_value, display_value, effective_currency, locale) =
cost_currency_row_for_settings(&format!(
"locale = \"zh-Hans\"\ncost_currency = \"{alias}\"\n"
));

assert_eq!(locale, Locale::ZhHans);
assert_eq!(effective_currency, crate::pricing::CostCurrency::Cny);
assert_eq!(saved_value, alias);
assert_eq!(display_value, alias);
}
}

#[test]
fn config_view_cost_currency_matching_cny_setting_is_silent() {
let (saved_value, display_value, effective_currency, locale) =
cost_currency_row_for_settings("locale = \"zh-Hans\"\ncost_currency = \"cny\"\n");

assert_eq!(locale, Locale::ZhHans);
assert_eq!(effective_currency, crate::pricing::CostCurrency::Cny);
assert_eq!(saved_value, "cny");
assert_eq!(display_value, "cny");
}

#[test]
fn config_view_cost_currency_non_zh_hans_locale_uses_saved_currency() {
let (saved_value, display_value, effective_currency, locale) =
cost_currency_row_for_settings("locale = \"en\"\ncost_currency = \"cny\"\n");

assert_eq!(locale, Locale::En);
assert_eq!(effective_currency, crate::pricing::CostCurrency::Cny);
assert_eq!(saved_value, "cny");
assert_eq!(display_value, "cny");
}

#[test]
fn config_view_exposes_all_available_saved_settings() {
let app = create_test_app();
Expand Down
Loading