claude-usage-dashboard 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,8 +9,8 @@ A self-hosted dashboard that visualizes your [Claude Code](https://claude.ai/cod
9
9
  ## Features
10
10
 
11
11
  - **Token tracking** — Total tokens with breakdown by input, output, cache read, and cache write
12
- - **Cost estimation** — API cost equivalent at standard pricing, compared against your subscription plan (Pro / Max 5x / Max 20x)
13
- - **Subscription quota** — Real-time utilization gauges (5-hour, 7-day, per-model) pulled from the Anthropic API with auto-detection of your plan tier. 7-day reset shows full date+time; all timestamps include timezone
12
+ - **Cost estimation** — API cost equivalent at standard pricing
13
+ - **Subscription quota** — Real-time utilization gauges (5-hour, 7-day, per-model) pulled from the Anthropic API with auto-detection of your plan tier. Includes projected API cost at 100% quota utilization (weekly and monthly). 7-day reset shows full date+time; all timestamps include timezone
14
14
  - **Token consumption trend** — Stacked bar chart with hourly, daily, weekly, or monthly granularity. Toggle between tokens and dollar view. Includes period summary with avg/min/max stats, active hours heatmap, and smart date range limits per granularity
15
15
  - **Model distribution** — Donut chart showing usage across Claude models
16
16
  - **Cache efficiency** — Visual breakdown of cache read, cache creation, and uncached requests
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-usage-dashboard",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "Dashboard that visualizes Claude Code usage from local session logs",
5
5
  "main": "server/index.js",
6
6
  "bin": {
@@ -37,7 +37,7 @@ body {
37
37
 
38
38
  .summary-cards {
39
39
  display: grid;
40
- grid-template-columns: repeat(4, 1fr);
40
+ grid-template-columns: repeat(3, 1fr);
41
41
  gap: 12px;
42
42
  margin-bottom: 20px;
43
43
  }
package/public/index.html CHANGED
@@ -34,12 +34,7 @@
34
34
  <div class="card-value" id="val-api-cost">—</div>
35
35
  <div class="card-sub" id="sub-api-cost">at standard API pricing</div>
36
36
  </div>
37
- <div class="card" id="card-savings">
38
- <div class="card-label">You Saved</div>
39
- <div class="card-value" id="val-savings">—</div>
40
- <div class="card-sub" id="sub-savings"></div>
41
- </div>
42
- <div class="card" id="card-cache">
37
+ <div class="card" id="card-cache">
43
38
  <div class="card-label">Cache Hit Rate</div>
44
39
  <div class="card-value" id="val-cache-rate">—</div>
45
40
  <div class="card-sub" id="sub-cache-rate">cache_read / total input</div>
package/public/js/app.js CHANGED
@@ -49,8 +49,16 @@ function getTimezoneAbbr() {
49
49
 
50
50
  async function loadQuota() {
51
51
  try {
52
- const data = await fetchQuota();
53
- renderQuotaGauges(document.getElementById('chart-quota'), data);
52
+ const today = new Date();
53
+ const sevenDaysAgo = new Date(today);
54
+ sevenDaysAgo.setDate(today.getDate() - 7);
55
+ const fmt = d => d.toISOString().slice(0, 10);
56
+
57
+ const [data, cost7d] = await Promise.all([
58
+ fetchQuota(),
59
+ fetchCost({ from: fmt(sevenDaysAgo), to: fmt(today), plan: state.plan.plan }),
60
+ ]);
61
+ renderQuotaGauges(document.getElementById('chart-quota'), data, { cost7d: cost7d.api_equivalent_cost_usd });
54
62
  const el = document.getElementById('quota-last-updated');
55
63
  if (el && data.lastFetched) el.textContent = `Updated ${new Date(data.lastFetched).toLocaleTimeString()} ${getTimezoneAbbr()}`;
56
64
  } catch { /* silently degrade */ }
@@ -106,12 +114,6 @@ async function loadAll() {
106
114
  `<span style="color:#f97316">out:${formatNumber(t.output_tokens)}</span>`;
107
115
  document.getElementById('val-api-cost').textContent = `$${cost.api_equivalent_cost_usd.toFixed(2)}`;
108
116
 
109
- const savings = cost.savings_usd;
110
- const savingsEl = document.getElementById('val-savings');
111
- savingsEl.textContent = `$${Math.abs(savings).toFixed(2)}`;
112
- savingsEl.style.color = savings >= 0 ? '#4ade80' : '#ef4444';
113
- document.getElementById('sub-savings').textContent = savings >= 0 ? 'subscription saved you this much!' : 'API would have been cheaper';
114
-
115
117
  document.getElementById('val-cache-rate').textContent = `${(cache.cache_read_rate * 100).toFixed(1)}%`;
116
118
 
117
119
  // Set active granularity button
@@ -1,4 +1,4 @@
1
- export function renderQuotaGauges(container, data) {
1
+ export function renderQuotaGauges(container, data, opts = {}) {
2
2
  container.innerHTML = '';
3
3
 
4
4
  if (!data || data.available === false) {
@@ -85,4 +85,20 @@ export function renderQuotaGauges(container, data) {
85
85
  }
86
86
 
87
87
  container.appendChild(wrapper);
88
+
89
+ // Project cost at full 7-day quota utilization
90
+ const sevenDay = data.seven_day;
91
+ if (sevenDay && sevenDay.utilization > 0 && opts.cost7d > 0) {
92
+ const pct = sevenDay.utilization / 100;
93
+ const projectedCost = opts.cost7d / pct;
94
+
95
+ const proj = document.createElement('div');
96
+ proj.style.cssText = 'margin-top:12px;padding:8px 12px;background:#1e293b;border-radius:6px;font-size:12px;color:#94a3b8';
97
+ const monthlyProjected = projectedCost * (30 / 7);
98
+ proj.innerHTML =
99
+ `7-day usage: <strong style="color:#e2e8f0">$${opts.cost7d.toFixed(2)}</strong> API cost at <strong style="color:#e2e8f0">${sevenDay.utilization.toFixed(1)}%</strong> quota` +
100
+ ` · Projected at 100%: <strong style="color:#fbbf24">$${projectedCost.toFixed(2)}</strong>/week` +
101
+ ` · <strong style="color:#fbbf24">$${monthlyProjected.toFixed(2)}</strong>/month`;
102
+ container.appendChild(proj);
103
+ }
88
104
  }