claude-usage-dashboard 1.5.4 → 1.5.6

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/js/app.js +47 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-usage-dashboard",
3
- "version": "1.5.4",
3
+ "version": "1.5.6",
4
4
  "description": "Claude Code usage dashboard — token costs, quota cycle tracking, cache efficiency, multi-machine sync across all your devices",
5
5
  "main": "server/index.js",
6
6
  "bin": {
package/public/js/app.js CHANGED
@@ -30,6 +30,32 @@ const state = {
30
30
  let datePicker, planSelector;
31
31
  let _cachedCycleData = null;
32
32
 
33
+ /**
34
+ * Find a quota cycle whose period matches the selected date range.
35
+ * Returns the cycle's overall metrics, or null if no match.
36
+ * Tolerance: 25 hours (handles hour-level normalization and timezone offsets).
37
+ */
38
+ function findMatchingCycle(dateRange, cycleData) {
39
+ if (!dateRange.from || !dateRange.to || !cycleData) return null;
40
+ const viewFrom = new Date(dateRange.from).getTime();
41
+ const viewTo = new Date(dateRange.to).getTime();
42
+ const tolerance = 25 * 60 * 60 * 1000;
43
+
44
+ const candidates = [];
45
+ if (cycleData.currentCycle) candidates.push(cycleData.currentCycle);
46
+ if (cycleData.history) candidates.push(...cycleData.history);
47
+
48
+ for (const c of candidates) {
49
+ if (!c.start || !c.resets_at || !c.overall?.tokens) continue;
50
+ const cStart = new Date(c.start).getTime();
51
+ const cEnd = new Date(c.resets_at).getTime();
52
+ if (Math.abs(viewFrom - cStart) < tolerance && Math.abs(viewTo - cEnd) < tolerance) {
53
+ return c.overall;
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+
33
59
  function formatNumber(n) {
34
60
  if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M';
35
61
  if (n >= 1_000) return (n / 1_000).toFixed(0) + 'K';
@@ -144,18 +170,30 @@ async function loadAll() {
144
170
  fetchCache(params),
145
171
  ]);
146
172
 
147
- // Summary cards
173
+ // Summary cards — use multi-machine cycle data when date range matches a cycle
148
174
  const t = usage.total;
149
- const totalAll = t.input_tokens + t.output_tokens + t.cache_read_tokens + t.cache_creation_tokens;
175
+ let tokIn = t.input_tokens, tokOut = t.output_tokens;
176
+ let tokCR = t.cache_read_tokens, tokCW = t.cache_creation_tokens;
177
+ let apiCost = cost.api_equivalent_cost_usd;
178
+ const matchedCycle = findMatchingCycle(state.dateRange, _cachedCycleData);
179
+ if (matchedCycle?.tokens) {
180
+ tokIn = matchedCycle.tokens.input; tokOut = matchedCycle.tokens.output;
181
+ tokCR = matchedCycle.tokens.cacheRead; tokCW = matchedCycle.tokens.cacheCreation;
182
+ apiCost = matchedCycle.actualCost;
183
+ }
184
+ const totalAll = tokIn + tokOut + tokCR + tokCW;
150
185
  document.getElementById('val-total-tokens').textContent = formatNumber(totalAll);
151
186
  document.getElementById('sub-total-tokens').innerHTML =
152
- `<span style="color:#4ade80">cache read:${formatNumber(t.cache_read_tokens)}</span> · ` +
153
- `<span style="color:#f59e0b">cache write:${formatNumber(t.cache_creation_tokens)}</span> · ` +
154
- `<span style="color:#60a5fa">in:${formatNumber(t.input_tokens)}</span> · ` +
155
- `<span style="color:#f97316">out:${formatNumber(t.output_tokens)}</span>`;
156
- document.getElementById('val-api-cost').textContent = `$${cost.api_equivalent_cost_usd.toFixed(2)}`;
157
-
158
- document.getElementById('val-cache-rate').textContent = `${(cache.cache_read_rate * 100).toFixed(1)}%`;
187
+ `<span style="color:#4ade80">cache read:${formatNumber(tokCR)}</span> · ` +
188
+ `<span style="color:#f59e0b">cache write:${formatNumber(tokCW)}</span> · ` +
189
+ `<span style="color:#60a5fa">in:${formatNumber(tokIn)}</span> · ` +
190
+ `<span style="color:#f97316">out:${formatNumber(tokOut)}</span>`;
191
+ document.getElementById('val-api-cost').textContent = `$${apiCost.toFixed(2)}`;
192
+
193
+ const totalInput = tokIn + tokCR + tokCW;
194
+ document.getElementById('val-cache-rate').textContent = totalInput > 0
195
+ ? `${((tokCR / totalInput) * 100).toFixed(1)}%`
196
+ : `${(cache.cache_read_rate * 100).toFixed(1)}%`;
159
197
 
160
198
  // Set active granularity button
161
199
  const activeGran = usage.granularity;