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 +2 -2
- package/package.json +1 -1
- package/public/css/style.css +1 -1
- package/public/index.html +1 -6
- package/public/js/app.js +10 -8
- package/public/js/charts/quota-gauge.js +17 -1
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
|
|
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
package/public/css/style.css
CHANGED
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
|
-
|
|
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
|
|
53
|
-
|
|
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
|
}
|