clauddy 1.16.0 → 1.17.1

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
@@ -179,6 +179,8 @@ Switching is instant — no restart. (On Windows/Linux the icon lives in the sys
179
179
 
180
180
  Optional **macOS notifications** when your session or weekly usage crosses the thresholds you set (default **80%** and **95%**) — e.g. _"Your session is over 80% — now at 82%"_. They re-arm automatically once usage drops back below a threshold (after a reset). Toggle them and edit the thresholds in **⚙ Settings**.
181
181
 
182
+ The first alert asks macOS for permission; after that the app shows up in **System Settings > Notifications** like any other.
183
+
182
184
  ## Configure (`config.json`)
183
185
 
184
186
  Settings saved from the UI live in `~/.claude-usage-monitor/config.json`, so you can tweak them without rebuilding:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clauddy",
3
3
  "desktopName": "clauddy.desktop",
4
- "version": "1.16.0",
4
+ "version": "1.17.1",
5
5
  "description": "A cute desktop pet that tracks your Claude Code usage",
6
6
  "main": "main.js",
7
7
  "bin": {
@@ -61,6 +61,7 @@
61
61
  },
62
62
  "build": {
63
63
  "appId": "app.clauddy",
64
+ "afterPack": "scripts/adhoc-sign.js",
64
65
  "productName": "Clauddy",
65
66
  "directories": {
66
67
  "output": "dist"
package/renderer/pet.js CHANGED
@@ -162,6 +162,19 @@ function fmtReset(ms) {
162
162
  const m = Math.floor((ms % 3600000) / 60000)
163
163
  return h > 0 ? `${h}h ${m}m` : `${m}m`
164
164
  }
165
+ // wall-clock time of the reset, in the machine's own locale + timezone.
166
+ // Past 24h the day matters too, so prefix the weekday.
167
+ function fmtResetClock(ms) {
168
+ if (!ms || ms <= 0) return null
169
+ const at = new Date(Date.now() + ms)
170
+ const time = at.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
171
+ return ms >= 86400000 ? `${at.toLocaleDateString([], { weekday: 'short' })} ${time}` : time
172
+ }
173
+ // "2h 22m (14:35)" — countdown plus the clock time it lands on
174
+ function fmtResetIn(ms) {
175
+ const at = fmtResetClock(ms)
176
+ return at ? `${fmtReset(ms)} (${at})` : fmtReset(ms)
177
+ }
165
178
  // burn-rate projection lives in burn.js (shared with the tests)
166
179
  const burn = Burn.createBurnTracker()
167
180
 
@@ -420,7 +433,7 @@ function renderScoped(list, byModel) {
420
433
  m.innerHTML =
421
434
  `<div class="meter-top"><span>weekly · ${esc(key)}</span><span>${Math.round(s.pct)}%</span></div>` +
422
435
  `<div class="track"><div class="fill week${s.pct >= 80 ? ' high' : ''}" style="width:${s.pct}%"></div></div>` +
423
- `<div class="sub">${s.resetMs != null ? `resets in ${fmtReset(s.resetMs)} · ` : ''}${fmtTokens(tokens)} tokens</div>`
436
+ `<div class="sub">${s.resetMs != null ? `resets in ${fmtResetIn(s.resetMs)} · ` : ''}${fmtTokens(tokens)} tokens</div>`
424
437
  box.appendChild(m)
425
438
  }
426
439
  }
@@ -568,7 +581,7 @@ function render(d) {
568
581
  sf.style.width = `${sessPct}%`
569
582
  sf.classList.toggle('high', sessPct >= 80)
570
583
  el('session-sub').textContent = sessActive
571
- ? `resets in ${fmtReset(sessReset)} · ${fmtTokens(d.session.tokens)} tokens`
584
+ ? `resets in ${fmtResetIn(sessReset)} · ${fmtTokens(d.session.tokens)} tokens`
572
585
  : 'no active session'
573
586
 
574
587
  // where this pace is taking you — hidden until there's enough trail to tell
@@ -589,7 +602,7 @@ function render(d) {
589
602
  wf.classList.toggle('high', wkPct >= 80)
590
603
  el('week-sub').textContent =
591
604
  wkReset != null
592
- ? `resets in ${fmtReset(wkReset)} · ${fmtTokens(d.week.tokens)} tokens`
605
+ ? `resets in ${fmtResetIn(wkReset)} · ${fmtTokens(d.week.tokens)} tokens`
593
606
  : `${fmtTokens(d.week.tokens)} tokens · last 7 days`
594
607
 
595
608
  renderScoped(liveOn ? realUsage.scoped || [] : [], d.byModel || [])
@@ -1006,6 +1019,7 @@ if (typeof module === 'object' && module.exports) {
1006
1019
  render,
1007
1020
  fmtTokens,
1008
1021
  fmtReset,
1022
+ fmtResetIn,
1009
1023
  setState,
1010
1024
  renderModels,
1011
1025
  renderProjects,
@@ -1744,10 +1744,14 @@ body.state-tired #status-text {
1744
1744
 
1745
1745
  /* meter subtitle */
1746
1746
  .sub {
1747
- font-size: 9.5px;
1747
+ /* small enough that "resets in 69h 15m (Fri 7:01 AM) · 1.02B tokens" stays on one line */
1748
+ font-size: 9px;
1748
1749
  color: var(--muted);
1749
1750
  margin-top: 4px;
1750
- letter-spacing: 0.2px;
1751
+ letter-spacing: 0px;
1752
+ white-space: nowrap;
1753
+ overflow: hidden;
1754
+ text-overflow: ellipsis;
1751
1755
  }
1752
1756
 
1753
1757
  /* burn-rate projection — calm by default, warm when you'd run out first */