claude-dashboard 0.2.2

A terminal TUI dashboard that renders Claude Code usage reports with token stats, project breakdowns, and language distribution.
Documentation
use std::collections::HashMap;
use std::fs;
use std::path::Path;

use serde::Deserialize;

// ── API config from settings.json ────────────────────────

pub struct LlmApiConfig {
    pub auth_token: String,
    pub base_url: String,
    pub haiku_model: String,
}

#[derive(Deserialize, Default)]
struct SettingsFile {
    #[serde(default)]
    env: Option<HashMap<String, String>>,
}

const DEFAULT_HAIKU: &str = "deepseek-v4-flash";

/// Load LLM API configuration from `{claude_dir}/settings.json` env block.
pub fn load_api_config(claude_dir: &Path) -> LlmApiConfig {
    let settings_path = claude_dir.join("settings.json");
    let env = fs::read_to_string(&settings_path)
        .ok()
        .and_then(|content| serde_json::from_str::<SettingsFile>(&content).ok())
        .and_then(|s| s.env)
        .unwrap_or_default();

    let auth_token = env
        .get("ANTHROPIC_AUTH_TOKEN")
        .cloned()
        .unwrap_or_default();
    let base_url = env
        .get("ANTHROPIC_BASE_URL")
        .cloned()
        .unwrap_or_else(|| "https://api.anthropic.com".to_string());
    let haiku_model = env
        .get("ANTHROPIC_DEFAULT_HAIKU_MODEL")
        .cloned()
        .filter(|m| !m.is_empty())
        .unwrap_or_else(|| DEFAULT_HAIKU.to_string());

    LlmApiConfig {
        auth_token,
        base_url,
        haiku_model,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_load_api_config_defaults() {
        // When settings file doesn't exist, should get defaults
        let cfg = load_api_config(Path::new("/nonexistent/path"));
        assert!(!cfg.haiku_model.is_empty());
    }

    #[test]
    fn test_load_api_config_from_file() {
        // Create a temp settings.json and verify parsing
        let tmp = std::env::temp_dir().join("__llm_test_settings");
        let _ = std::fs::remove_dir_all(&tmp);
        std::fs::create_dir_all(&tmp).unwrap();
        let settings = tmp.join("settings.json");
        std::fs::write(&settings, r#"{
            "env": {
                "ANTHROPIC_AUTH_TOKEN": "test-token",
                "ANTHROPIC_BASE_URL": "https://test.api.com",
                "ANTHROPIC_DEFAULT_HAIKU_MODEL": "test-haiku-model"
            }
        }"#).unwrap();

        let cfg = load_api_config(&tmp);
        assert_eq!(cfg.auth_token, "test-token");
        assert_eq!(cfg.base_url, "https://test.api.com");
        assert_eq!(cfg.haiku_model, "test-haiku-model");

        let _ = std::fs::remove_dir_all(&tmp);
    }
}