refactor: gate balance status item behind DeepSeek provider
This commit is contained in:
+32
-10
@@ -800,7 +800,6 @@ impl StatusItem {
|
||||
StatusItem::ReasoningReplay,
|
||||
StatusItem::Cache,
|
||||
StatusItem::Tokens,
|
||||
StatusItem::Balance,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -821,11 +820,8 @@ impl StatusItem {
|
||||
StatusItem::GitBranch => "git_branch",
|
||||
StatusItem::LastToolElapsed => "last_tool_elapsed",
|
||||
StatusItem::RateLimit => "rate_limit",
|
||||
<<<<<<< HEAD
|
||||
StatusItem::Tokens => "tokens",
|
||||
=======
|
||||
StatusItem::Balance => "balance",
|
||||
>>>>>>> 4bc823e6 (feat: add account balance status bar item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,11 +842,8 @@ impl StatusItem {
|
||||
StatusItem::GitBranch => "Git branch",
|
||||
StatusItem::LastToolElapsed => "Last tool elapsed",
|
||||
StatusItem::RateLimit => "Rate-limit remaining",
|
||||
<<<<<<< HEAD
|
||||
StatusItem::Tokens => "Session tokens",
|
||||
=======
|
||||
StatusItem::Balance => "Account balance",
|
||||
>>>>>>> 4bc823e6 (feat: add account balance status bar item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,11 +865,8 @@ impl StatusItem {
|
||||
StatusItem::GitBranch => "current workspace branch",
|
||||
StatusItem::LastToolElapsed => "ms of the most recent tool call (placeholder)",
|
||||
StatusItem::RateLimit => "remaining requests in the budget (placeholder)",
|
||||
<<<<<<< HEAD
|
||||
StatusItem::Tokens => "input / cache-hit / output token totals",
|
||||
=======
|
||||
StatusItem::Balance => "topped-up + granted balance from DeepSeek",
|
||||
>>>>>>> 4bc823e6 (feat: add account balance status bar item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,6 +904,19 @@ impl StatusItem {
|
||||
| StatusItem::Balance
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether this item is relevant for `provider`. Provider-specific
|
||||
/// items return `false` for unsupported providers so the picker doesn't
|
||||
/// offer toggles that can never show useful data.
|
||||
#[must_use]
|
||||
pub fn is_available_for(self, provider: ApiProvider) -> bool {
|
||||
match self {
|
||||
StatusItem::Balance => {
|
||||
matches!(provider, ApiProvider::Deepseek | ApiProvider::DeepseekCN)
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolved retry policy with defaults applied.
|
||||
@@ -7555,4 +7558,23 @@ model = "deepseek-ai/deepseek-v4-pro"
|
||||
let deserialized: ProviderCapability = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(cap, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_item_balance_available_only_for_deepseek_providers() {
|
||||
// Balance item should only be offered for DeepSeek / DeepSeekCN.
|
||||
assert!(StatusItem::Balance.is_available_for(ApiProvider::Deepseek));
|
||||
assert!(StatusItem::Balance.is_available_for(ApiProvider::DeepseekCN));
|
||||
// Sanity: all other known providers should hide the Balance toggle.
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::Openrouter));
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::Novita));
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::NvidiaNim));
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::Fireworks));
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::Sglang));
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::Vllm));
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::Ollama));
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::Openai));
|
||||
assert!(!StatusItem::Balance.is_available_for(ApiProvider::Atlascloud));
|
||||
// Other StatusItem variants should be available everywhere.
|
||||
assert!(StatusItem::Mode.is_available_for(ApiProvider::Ollama));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4908,6 +4908,7 @@ async fn apply_command_result(
|
||||
app.view_stack
|
||||
.push(crate::tui::views::status_picker::StatusPickerView::new(
|
||||
&app.status_items,
|
||||
app.api_provider,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5683,13 +5683,17 @@ fn render_footer_from_with_default_items_renders_mode_and_model() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_footer_keeps_prefix_stability_opt_in() {
|
||||
fn default_footer_excludes_provider_specific_diagnostic_chips() {
|
||||
let items = crate::config::StatusItem::default_footer();
|
||||
|
||||
assert!(
|
||||
!items.contains(&crate::config::StatusItem::PrefixStability),
|
||||
"prefix stability is a diagnostic chip and should not crowd the default footer"
|
||||
);
|
||||
assert!(
|
||||
!items.contains(&crate::config::StatusItem::Balance),
|
||||
"balance is DeepSeek-only and should not crowd the default footer for non-DeepSeek users"
|
||||
);
|
||||
assert!(
|
||||
items.contains(&crate::config::StatusItem::Cache),
|
||||
"default footer should still include provider-reported cache hit rate"
|
||||
|
||||
@@ -20,7 +20,7 @@ use ratatui::{
|
||||
widgets::{Block, Borders, Clear, Padding, Paragraph, Widget},
|
||||
};
|
||||
|
||||
use crate::config::StatusItem;
|
||||
use crate::config::{ApiProvider, StatusItem};
|
||||
use crate::palette;
|
||||
use crate::tui::views::{ModalKind, ModalView, ViewAction, ViewEvent};
|
||||
|
||||
@@ -47,8 +47,12 @@ pub struct StatusPickerView {
|
||||
|
||||
impl StatusPickerView {
|
||||
#[must_use]
|
||||
pub fn new(active: &[StatusItem]) -> Self {
|
||||
let rows: Vec<StatusItem> = StatusItem::all().to_vec();
|
||||
pub fn new(active: &[StatusItem], provider: ApiProvider) -> Self {
|
||||
let rows: Vec<StatusItem> = StatusItem::all()
|
||||
.iter()
|
||||
.filter(|item| item.is_available_for(provider))
|
||||
.copied()
|
||||
.collect();
|
||||
let selected: Vec<bool> = rows.iter().map(|item| active.contains(item)).collect();
|
||||
Self {
|
||||
rows,
|
||||
@@ -297,14 +301,14 @@ mod tests {
|
||||
#[test]
|
||||
fn opens_with_active_items_pre_selected() {
|
||||
let active = StatusItem::default_footer();
|
||||
let view = StatusPickerView::new(&active);
|
||||
let view = StatusPickerView::new(&active, ApiProvider::Deepseek);
|
||||
assert_eq!(view.current_selection(), active);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn space_toggles_current_row_and_emits_live_preview() {
|
||||
let active = StatusItem::default_footer();
|
||||
let mut view = StatusPickerView::new(&active);
|
||||
let mut view = StatusPickerView::new(&active, ApiProvider::Deepseek);
|
||||
// Cursor starts at row 0 = StatusItem::Mode (currently checked).
|
||||
let action = view.handle_key(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE));
|
||||
match action {
|
||||
@@ -319,7 +323,7 @@ mod tests {
|
||||
#[test]
|
||||
fn enter_emits_final_save() {
|
||||
let active = StatusItem::default_footer();
|
||||
let mut view = StatusPickerView::new(&active);
|
||||
let mut view = StatusPickerView::new(&active, ApiProvider::Deepseek);
|
||||
let action = view.handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
match action {
|
||||
ViewAction::EmitAndClose(ViewEvent::StatusItemsUpdated { final_save, .. }) => {
|
||||
@@ -332,7 +336,7 @@ mod tests {
|
||||
#[test]
|
||||
fn esc_reverts_to_snapshot() {
|
||||
let active = StatusItem::default_footer();
|
||||
let mut view = StatusPickerView::new(&active);
|
||||
let mut view = StatusPickerView::new(&active, ApiProvider::Deepseek);
|
||||
// Toggle a few items off so the working set diverges from snapshot.
|
||||
view.handle_key(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE));
|
||||
view.move_down();
|
||||
@@ -350,7 +354,7 @@ mod tests {
|
||||
#[test]
|
||||
fn select_all_and_select_none_keys_work() {
|
||||
let active: Vec<StatusItem> = Vec::new();
|
||||
let mut view = StatusPickerView::new(&active);
|
||||
let mut view = StatusPickerView::new(&active, ApiProvider::Deepseek);
|
||||
let action = view.handle_key(KeyEvent::new(KeyCode::Char('a'), KeyModifiers::NONE));
|
||||
match action {
|
||||
ViewAction::Emit(ViewEvent::StatusItemsUpdated { items, .. }) => {
|
||||
@@ -370,7 +374,7 @@ mod tests {
|
||||
#[test]
|
||||
fn arrow_keys_move_cursor_within_bounds() {
|
||||
let active = StatusItem::default_footer();
|
||||
let mut view = StatusPickerView::new(&active);
|
||||
let mut view = StatusPickerView::new(&active, ApiProvider::Deepseek);
|
||||
assert_eq!(view.cursor, 0);
|
||||
view.handle_key(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE));
|
||||
assert_eq!(view.cursor, 1);
|
||||
@@ -382,4 +386,14 @@ mod tests {
|
||||
}
|
||||
assert_eq!(view.cursor, StatusItem::all().len() - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn balance_excluded_for_non_deepseek_provider() {
|
||||
let active = StatusItem::default_footer();
|
||||
let view = StatusPickerView::new(&active, ApiProvider::Openrouter);
|
||||
// Balance should not appear as a row for non-DeepSeek providers.
|
||||
assert!(!view.rows.contains(&StatusItem::Balance));
|
||||
// Mode should still be present.
|
||||
assert!(view.rows.contains(&StatusItem::Mode));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user