namespace ClaudeOverview.Models; /// /// Subscription usage pulled from the Claude /api/oauth/usage endpoint. /// Percentages are 0–100. /// public sealed class UsageInfo { /// Length of the rolling session-limit block (the API's "five_hour" bucket). public static readonly TimeSpan SessionWindow = TimeSpan.FromHours(5); public double SessionPercent { get; set; } public DateTime? SessionResetsAt { get; set; } /// UTC start of the current session-limit window (its reset time minus the 5-hour block). public DateTime? SessionWindowStartUtc => SessionResetsAt is { } r ? r.ToUniversalTime() - SessionWindow : null; public double WeeklyPercent { get; set; } public DateTime? WeeklyResetsAt { get; set; } public string? Error { get; set; } public bool Ok => Error is null; /// True when the last fetch was rejected with HTTP 429 (rate limited). public bool RateLimited { get; set; } /// Server-suggested wait before retrying (from the Retry-After header), if any. public TimeSpan? RetryAfter { get; set; } public static string ResetsIn(DateTime? resetsAtUtc) { if (resetsAtUtc is null) return ""; var span = resetsAtUtc.Value.ToUniversalTime() - DateTime.UtcNow; if (span <= TimeSpan.Zero) return "resetting…"; if (span < TimeSpan.FromHours(1)) return $"resets in {(int)span.TotalMinutes}m"; if (span < TimeSpan.FromDays(1)) return $"resets in {(int)span.TotalHours}h{span.Minutes:00}m"; return $"resets in {(int)span.TotalDays}d{span.Hours}h"; } }