claude-usage-widget/src-tauri/src/commands.rs

118 lines
3.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Tauri command surface — every JS-callable function lives here.
use std::path::PathBuf;
use crate::paths::{list_wsl_distros, resolve_roots, ResolvedRoots};
use crate::settings::{save as save_settings, Caps, Settings};
use crate::state::SharedState;
use crate::tier::{detect_in_with_diagnostics, PlanTier};
use crate::usage::{build_snapshot, UsageSnapshot};
use crate::watch::refresh_and_emit;
#[tauri::command]
pub async fn get_snapshot(state: tauri::State<'_, SharedState>) -> Result<UsageSnapshot, String> {
let mut events = state.collect_events();
events.sort_by_key(|e| e.ts);
let caps = state.settings.read().caps.clone();
Ok(build_snapshot(&events, &caps, chrono::Utc::now()))
}
#[tauri::command]
pub async fn get_settings(state: tauri::State<'_, SharedState>) -> Result<Settings, String> {
Ok(state.settings.read().clone())
}
#[tauri::command]
pub async fn set_settings(
state: tauri::State<'_, SharedState>,
app: tauri::AppHandle,
new: Settings,
) -> Result<(), String> {
{
*state.settings.write() = new.clone();
}
save_settings(&new).map_err(|e| e.to_string())?;
// If roots-related settings changed, force a re-resolve + rescan.
{
let mut roots = state.roots.write();
roots.clear();
}
refresh_and_emit(&app, state.inner(), None)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
#[tauri::command]
pub async fn list_distros() -> Result<Vec<String>, String> {
list_wsl_distros().map_err(|e| e.to_string())
}
#[tauri::command]
pub async fn get_roots(state: tauri::State<'_, SharedState>) -> Result<ResolvedRoots, String> {
let s = state.settings.read().clone();
Ok(resolve_roots(s.wsl_distro_override.as_deref(), s.include_native))
}
#[tauri::command]
pub async fn force_rescan(
state: tauri::State<'_, SharedState>,
app: tauri::AppHandle,
) -> Result<(), String> {
// Wipe caches so every file is reparsed from offset 0.
state.files.write().clear();
state.seen_ids.write().clear();
refresh_and_emit(&app, state.inner(), None)
.await
.map_err(|e| e.to_string())
}
#[tauri::command]
pub async fn quit_app(app: tauri::AppHandle) -> Result<(), String> {
app.exit(0);
Ok(())
}
#[derive(serde::Serialize)]
pub struct TierInfo {
/// The classified tier — `Pro`, `Max5x`, `Max20x`, etc.
pub tier: PlanTier,
/// Human-readable label for the UI ("Max 5×" / "Pro" / "Unknown").
pub label: String,
/// The caps that this tier maps to. The user's current Settings may
/// differ if they tuned manually.
pub recommended_caps: Caps,
/// Diagnostic: the candidate `.claude/` directories that were probed.
/// Useful for users on novel setups (multi-distro, custom $HOME, etc.).
pub searched: Vec<String>,
}
#[tauri::command]
pub async fn detect_plan_tier(
state: tauri::State<'_, SharedState>,
) -> Result<TierInfo, String> {
// Resolve roots fresh — `state.roots` may be empty if this command is
// invoked before the cold-start refresh finishes (which IS what happens
// when the user opens Settings shortly after launch).
let s = state.settings.read().clone();
let resolved =
resolve_roots(s.wsl_distro_override.as_deref(), s.include_native);
let mut candidates: Vec<PathBuf> = resolved
.roots
.iter()
.filter_map(|r| r.parent().map(PathBuf::from))
.collect();
if let Some(home) = dirs::home_dir() {
candidates.push(home.join(".claude"));
}
// Dedupe (canonicalize is unreliable on UNC paths, so compare raw).
candidates.sort();
candidates.dedup();
let (tier, diagnostics) = detect_in_with_diagnostics(&candidates);
let label = tier.label();
let recommended_caps = tier.caps();
Ok(TierInfo { tier, label, recommended_caps, searched: diagnostics })
}