clauddy 1.13.1 → 1.14.0
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 +13 -1
- package/config.json +1 -0
- package/main.js +3 -0
- package/package.json +1 -1
- package/renderer/index.html +16 -0
- package/renderer/pet.js +21 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A cute pixel-art desktop pet for macOS that tracks your Claude Code usage — mi
|
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
|
|
7
|
-
https://github.com/user-attachments/assets/
|
|
7
|
+
https://github.com/user-attachments/assets/dbe00d9a-b49c-48ea-941c-76c517dec358
|
|
8
8
|
|
|
9
9
|
<em>A little terracotta creature that lives in the corner of your screen, eats your tokens, and naps when you're idle.</em>
|
|
10
10
|
</p>
|
|
@@ -142,6 +142,18 @@ chmod +x Clauddy-*.AppImage
|
|
|
142
142
|
./Clauddy-*.AppImage
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
+
Want the pet icon to show up in your app menu / taskbar too, instead of a generic icon? Running the AppImage directly doesn't register it anywhere — GNOME (and most Wayland desktops) only pick up an app's icon from an installed `.desktop` entry. One-time setup, right next to the AppImage:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
./Clauddy-*.AppImage --appimage-extract clauddy.desktop >/dev/null
|
|
149
|
+
./Clauddy-*.AppImage --appimage-extract usr/share/icons/hicolor/512x512/apps/clauddy.png >/dev/null
|
|
150
|
+
mkdir -p ~/.local/share/applications ~/.local/share/icons/hicolor/512x512/apps
|
|
151
|
+
cp squashfs-root/usr/share/icons/hicolor/512x512/apps/clauddy.png ~/.local/share/icons/hicolor/512x512/apps/clauddy.png
|
|
152
|
+
sed "s|^Exec=.*|Exec=$(readlink -f Clauddy-*.AppImage) --no-sandbox %U|" squashfs-root/clauddy.desktop > ~/.local/share/applications/clauddy.desktop
|
|
153
|
+
rm -rf squashfs-root
|
|
154
|
+
update-desktop-database ~/.local/share/applications 2>/dev/null
|
|
155
|
+
```
|
|
156
|
+
|
|
145
157
|
> The system tray icon needs an indicator extension on vanilla GNOME (e.g. "AppIndicator and KStatusNotifier Item Support") — it works out of the box on Cinnamon, KDE, and XFCE. Autostart-at-login is wired up via an XDG `.desktop` entry in `~/.config/autostart/`.
|
|
146
158
|
|
|
147
159
|
> The app keeps its data in `~/.claude-usage-monitor`, regardless of platform or how you run it.
|
package/config.json
CHANGED
package/main.js
CHANGED
|
@@ -65,6 +65,7 @@ function publicConfig(c) {
|
|
|
65
65
|
alerts: c.alerts,
|
|
66
66
|
alertThresholds: c.alertThresholds,
|
|
67
67
|
fireThreshold: c.fireThreshold,
|
|
68
|
+
zoom: c.zoom,
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -78,6 +79,7 @@ function loadConfig() {
|
|
|
78
79
|
alerts: true,
|
|
79
80
|
alertThresholds: [80, 95],
|
|
80
81
|
fireThreshold: 90, // session % at which the pet catches fire (tired still fixed at 100)
|
|
82
|
+
zoom: 100, // widget scale %, 100-200
|
|
81
83
|
pollIntervalMs: 4000,
|
|
82
84
|
activeThresholdMs: 20000,
|
|
83
85
|
sleepThresholdMs: 300000,
|
|
@@ -137,6 +139,7 @@ function createWindow() {
|
|
|
137
139
|
skipTaskbar: true,
|
|
138
140
|
hasShadow: false,
|
|
139
141
|
fullscreenable: false,
|
|
142
|
+
icon: path.join(__dirname, 'build', 'icon.png'),
|
|
140
143
|
webPreferences: {
|
|
141
144
|
preload: path.join(__dirname, 'preload.js'),
|
|
142
145
|
contextIsolation: true,
|
package/package.json
CHANGED
package/renderer/index.html
CHANGED
|
@@ -328,6 +328,22 @@
|
|
|
328
328
|
</span>
|
|
329
329
|
</div>
|
|
330
330
|
|
|
331
|
+
<div class="set-section">
|
|
332
|
+
<div class="set-sec-title">Zoom</div>
|
|
333
|
+
<div class="set-thresholds">
|
|
334
|
+
<span class="thr">
|
|
335
|
+
<span class="thr-cap">size</span>
|
|
336
|
+
<span class="num">
|
|
337
|
+
<input id="set-zoom" type="number" min="100" max="200" step="25" />
|
|
338
|
+
<span class="num-spin">
|
|
339
|
+
<button type="button" class="num-btn" data-for="set-zoom" data-step="25">▲</button>
|
|
340
|
+
<button type="button" class="num-btn" data-for="set-zoom" data-step="-25">▼</button>
|
|
341
|
+
</span>
|
|
342
|
+
</span>
|
|
343
|
+
</span>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
331
347
|
<div class="set-section">
|
|
332
348
|
<div class="set-sec-title">
|
|
333
349
|
Alerts
|
package/renderer/pet.js
CHANGED
|
@@ -596,8 +596,12 @@ let lastW = 0
|
|
|
596
596
|
function fitSize() {
|
|
597
597
|
requestAnimationFrame(() => {
|
|
598
598
|
const collapsed = document.body.classList.contains('collapsed')
|
|
599
|
-
const
|
|
600
|
-
const
|
|
599
|
+
const zoom = Number.parseFloat(document.body.style.zoom) || 1
|
|
600
|
+
const w = (collapsed ? 140 : 276) * zoom
|
|
601
|
+
// offsetHeight never reflects a CSS zoom applied to an ancestor (confirmed
|
|
602
|
+
// empirically against this Electron build) — so measure the unzoomed content
|
|
603
|
+
// height, then scale the whole thing (content + margin) ourselves
|
|
604
|
+
const h = (el('card').offsetHeight + 24) * zoom // 12px margin top + bottom
|
|
601
605
|
if (Math.abs(h - lastH) > 2 || w !== lastW) {
|
|
602
606
|
lastH = h
|
|
603
607
|
lastW = w
|
|
@@ -606,6 +610,12 @@ function fitSize() {
|
|
|
606
610
|
})
|
|
607
611
|
}
|
|
608
612
|
|
|
613
|
+
// widget scale, applied as a CSS zoom (not transform) so offsetWidth/Height
|
|
614
|
+
// keep reflecting it — see fitNames()'s comment on why transform can't be used here
|
|
615
|
+
function applyZoom(z) {
|
|
616
|
+
document.body.style.zoom = (z || 100) / 100
|
|
617
|
+
}
|
|
618
|
+
|
|
609
619
|
let currentConfig = {}
|
|
610
620
|
let realUsage = null
|
|
611
621
|
let debugState = null
|
|
@@ -624,6 +634,8 @@ window.api.onError((msg) => {
|
|
|
624
634
|
window.api.onConfig((cfg) => {
|
|
625
635
|
currentConfig = cfg || {}
|
|
626
636
|
document.body.classList.toggle('is-menubar', currentConfig.mode === 'menubar')
|
|
637
|
+
applyZoom(currentConfig.zoom)
|
|
638
|
+
fitSize()
|
|
627
639
|
})
|
|
628
640
|
window.api.onRealUsage((u) => {
|
|
629
641
|
realUsage = u || null
|
|
@@ -809,6 +821,7 @@ function snapshotSettings() {
|
|
|
809
821
|
t1: el('set-t1').value,
|
|
810
822
|
t2: el('set-t2').value,
|
|
811
823
|
fire: el('set-fire').value,
|
|
824
|
+
zoom: el('set-zoom').value,
|
|
812
825
|
})
|
|
813
826
|
}
|
|
814
827
|
function refreshSaveDirty() {
|
|
@@ -827,6 +840,7 @@ function populateSettings() {
|
|
|
827
840
|
el('set-t1').value = th[0] != null ? th[0] : 80
|
|
828
841
|
el('set-t2').value = th[1] != null ? th[1] : 95
|
|
829
842
|
el('set-fire').value = c.fireThreshold != null ? c.fireThreshold : 90
|
|
843
|
+
el('set-zoom').value = c.zoom != null ? c.zoom : 100
|
|
830
844
|
clearSaveDirty() // fields now match the saved config
|
|
831
845
|
}
|
|
832
846
|
function openSettings() {
|
|
@@ -850,7 +864,7 @@ for (const b of document.querySelectorAll('.num-btn')) {
|
|
|
850
864
|
})
|
|
851
865
|
}
|
|
852
866
|
// light up Save whenever an editable field changes
|
|
853
|
-
for (const id of ['set-alerts', 'set-t1', 'set-t2', 'set-fire']) {
|
|
867
|
+
for (const id of ['set-alerts', 'set-t1', 'set-t2', 'set-fire', 'set-zoom']) {
|
|
854
868
|
el(id).addEventListener('input', refreshSaveDirty)
|
|
855
869
|
el(id).addEventListener('change', refreshSaveDirty)
|
|
856
870
|
}
|
|
@@ -869,6 +883,9 @@ el('set-cancel').addEventListener('click', () => {
|
|
|
869
883
|
el('set-save').addEventListener('click', () => {
|
|
870
884
|
const num = (id) => parseFloat(el(id).value)
|
|
871
885
|
const fire = num('set-fire')
|
|
886
|
+
const zoomRaw = num('set-zoom')
|
|
887
|
+
const zoom = zoomRaw >= 100 && zoomRaw <= 200 ? Math.round(zoomRaw) : 100
|
|
888
|
+
applyZoom(zoom) // apply immediately so the fitSize() below measures the new size
|
|
872
889
|
window.api.saveConfig({
|
|
873
890
|
mode: selectedMode,
|
|
874
891
|
alerts: el('set-alerts').checked,
|
|
@@ -876,6 +893,7 @@ el('set-save').addEventListener('click', () => {
|
|
|
876
893
|
.filter((n) => n >= 1 && n <= 100)
|
|
877
894
|
.sort((a, b) => a - b),
|
|
878
895
|
fireThreshold: fire >= 1 && fire <= 99 ? fire : 90,
|
|
896
|
+
zoom,
|
|
879
897
|
})
|
|
880
898
|
clearSaveDirty()
|
|
881
899
|
document.body.classList.remove('settings-open')
|