claude-usage-dashboard 1.5.5 → 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 +32 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-usage-dashboard",
3
- "version": "1.5.5",
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,21 +170,16 @@ async function loadAll() {
144
170
  fetchCache(params),
145
171
  ]);
146
172
 
147
- // Summary cards — use multi-machine cycle data when viewing current cycle
173
+ // Summary cards — use multi-machine cycle data when date range matches a cycle
148
174
  const t = usage.total;
149
175
  let tokIn = t.input_tokens, tokOut = t.output_tokens;
150
176
  let tokCR = t.cache_read_tokens, tokCW = t.cache_creation_tokens;
151
177
  let apiCost = cost.api_equivalent_cost_usd;
152
- const cc = _cachedCycleData?.currentCycle?.overall;
153
- if (cc?.tokens) {
154
- const cct = cc.tokens;
155
- const mergedAll = cct.input + cct.output + cct.cacheRead + cct.cacheCreation;
156
- const localAll = tokIn + tokOut + tokCR + tokCW;
157
- if (mergedAll > localAll) {
158
- tokIn = cct.input; tokOut = cct.output;
159
- tokCR = cct.cacheRead; tokCW = cct.cacheCreation;
160
- apiCost = cc.actualCost;
161
- }
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;
162
183
  }
163
184
  const totalAll = tokIn + tokOut + tokCR + tokCW;
164
185
  document.getElementById('val-total-tokens').textContent = formatNumber(totalAll);