clauddy 1.9.0 → 1.10.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.
Files changed (4) hide show
  1. package/README.md +18 -3
  2. package/main.js +45 -3
  3. package/package.json +8 -3
  4. package/usage.js +7 -4
package/README.md CHANGED
@@ -71,7 +71,7 @@ back to plain **working** / **idle**.
71
71
 
72
72
  ## Install
73
73
 
74
- **macOS (Apple Silicon)** is the first-class build. **Windows (x64)** works too, and **Linux** is next. The `bunx`/`npx` route below runs on all of them today.
74
+ **macOS (Apple Silicon)** is the first-class build. **Windows (x64)** and **Linux (x64)** work too. The `bunx`/`npx` route below runs on all of them today.
75
75
 
76
76
  ### macOS
77
77
 
@@ -111,9 +111,24 @@ Prefer a standalone app with no Node/Bun? Grab the **portable zip** (`Clauddy-<v
111
111
 
112
112
  > Windows builds are produced by the **Build** workflow (Actions ▸ Build) — attaching them to every release automatically is on the roadmap.
113
113
 
114
- ### Linux
114
+ ### Linux (x64)
115
115
 
116
- Coming soon the build is already wired for `tar.gz` and AppImage. In the meantime, `bunx clauddy` / `npx clauddy` works today.
116
+ The quickest path works the same as macOS with [Bun](https://bun.sh) or Node 24 installed:
117
+
118
+ ```bash
119
+ bunx clauddy # or: npx clauddy
120
+ ```
121
+
122
+ Prefer a standalone app? Grab the **AppImage** or **tar.gz** (`Clauddy-<version>.AppImage` / `clauddy-<version>.tar.gz`) from the [latest release](https://github.com/renatoaug/claude-usage-monitor/releases), then:
123
+
124
+ ```bash
125
+ chmod +x Clauddy-*.AppImage
126
+ ./Clauddy-*.AppImage
127
+ ```
128
+
129
+ > Linux builds are produced by the **Build** workflow (Actions ▸ Build) — attaching them to every release automatically is on the roadmap.
130
+
131
+ > 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/`.
117
132
 
118
133
  > The app keeps its data in `~/.claude-usage-monitor`, regardless of platform or how you run it.
119
134
 
package/main.js CHANGED
@@ -196,8 +196,14 @@ function applyMode(mode) {
196
196
 
197
197
  function ensureTray() {
198
198
  if (tray) return
199
- const img = nativeImage.createFromPath(path.join(__dirname, 'build', 'trayTemplate.png'))
200
- img.setTemplateImage(true) // let macOS recolor it for the light/dark menu bar
199
+ // macOS recolors a "template" (black+alpha) image for the light/dark menu
200
+ // bar; Linux/Windows tray icons get no such recoloring, so they get the
201
+ // pre-colored terracotta variant instead — a plain black icon disappears
202
+ // on dark panels (e.g. Linux Mint's default Cinnamon taskbar).
203
+ const isMac = process.platform === 'darwin'
204
+ const iconFile = isMac ? 'trayTemplate.png' : 'trayColor.png'
205
+ const img = nativeImage.createFromPath(path.join(__dirname, 'build', iconFile))
206
+ if (isMac) img.setTemplateImage(true)
201
207
  tray = new Tray(img)
202
208
  tray.setToolTip('Clauddy')
203
209
  tray.on('click', (_e, bounds) => {
@@ -408,13 +414,49 @@ ipcMain.on('save-config', (_e, patch) => {
408
414
 
409
415
  ipcMain.on('quit', () => app.quit())
410
416
 
417
+ // Electron's login-item API only covers macOS (SMAppService) and Windows (the
418
+ // registry Run key) — @platform darwin,win32 in electron.d.ts, a no-op on
419
+ // Linux. The real "start with the system" mechanism there is the XDG
420
+ // Autostart spec: a .desktop file in ~/.config/autostart, read by every major
421
+ // desktop environment's session manager at login (GNOME, KDE, XFCE, Cinnamon,
422
+ // MATE) — same role as the registry key or SMAppService, just file-based.
423
+ function enableLinuxAutostart() {
424
+ const exec = process.env.APPIMAGE || process.execPath
425
+ const autostartDir = path.join(os.homedir(), '.config', 'autostart')
426
+ const desktopFile = path.join(autostartDir, 'clauddy.desktop')
427
+ // AppImage isn't registered in the system's hicolor icon theme, so a bare
428
+ // "Icon=clauddy" name won't resolve — copy the icon to a path that outlives
429
+ // the AppImage's temp mount and reference it absolutely instead.
430
+ const iconFile = path.join(DATA_DIR, 'icon.png')
431
+ fs.mkdirSync(DATA_DIR, { recursive: true })
432
+ fs.copyFileSync(path.join(__dirname, 'build', 'icon.png'), iconFile)
433
+ const entry = [
434
+ '[Desktop Entry]',
435
+ 'Type=Application',
436
+ 'Name=Clauddy',
437
+ 'Comment=A cute pixel-art desktop pet that tracks your Claude Code usage',
438
+ `Exec="${exec}"`,
439
+ `Icon=${iconFile}`,
440
+ 'Terminal=false',
441
+ 'X-GNOME-Autostart-enabled=true',
442
+ '',
443
+ ].join('\n')
444
+ if (fs.existsSync(desktopFile) && fs.readFileSync(desktopFile, 'utf8') === entry) return
445
+ fs.mkdirSync(autostartDir, { recursive: true })
446
+ fs.writeFileSync(desktopFile, entry)
447
+ }
448
+
411
449
  app.whenReady().then(() => {
412
450
  // Windows toast notifications need an explicit AppUserModelID to show reliably
413
451
  if (process.platform === 'win32') app.setAppUserModelId('app.clauddy')
414
452
  createWindow()
415
453
  // open at login (packaged app only)
416
454
  if (app.isPackaged) {
417
- app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false })
455
+ if (process.platform === 'linux') {
456
+ enableLinuxAutostart()
457
+ } else {
458
+ app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false })
459
+ }
418
460
  }
419
461
  })
420
462
 
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "clauddy",
3
- "version": "1.9.0",
3
+ "desktopName": "clauddy.desktop",
4
+ "version": "1.10.0",
4
5
  "description": "A cute desktop pet that tracks your Claude Code usage",
5
6
  "main": "main.js",
6
7
  "bin": {
@@ -70,7 +71,10 @@
70
71
  "config.json",
71
72
  "renderer/**",
72
73
  "build/trayTemplate.png",
73
- "build/trayTemplate@2x.png"
74
+ "build/trayTemplate@2x.png",
75
+ "build/trayColor.png",
76
+ "build/trayColor@2x.png",
77
+ "build/icon.png"
74
78
  ],
75
79
  "mac": {
76
80
  "category": "public.app-category.developer-tools",
@@ -88,7 +92,8 @@
88
92
  "AppImage"
89
93
  ],
90
94
  "category": "Utility",
91
- "icon": "build/icon.png"
95
+ "icon": "build/icon.png",
96
+ "syncDesktopName": true
92
97
  }
93
98
  },
94
99
  "dependencies": {
package/usage.js CHANGED
@@ -19,10 +19,13 @@ function labelFor(model) {
19
19
  : m.includes('mythos')
20
20
  ? 'Mythos'
21
21
  : null
22
- const ver = m.match(/-(\d)-(\d+)/)
23
- if (fam && ver) return `${fam} ${ver[1]}.${ver[2]}`
24
- if (fam) return fam
25
- return model
22
+ if (!fam) return model
23
+ // version after the family: opus-5, opus-4-8, haiku-4-5-<date>
24
+ let ver = m.match(/(?:opus|sonnet|haiku|fable|mythos)-(\d{1,2})(?:-(\d{1,2}))?(?!\d)/)
25
+ // legacy ids put the version before the family: claude-3-5-sonnet
26
+ if (!ver) ver = m.match(/-(\d)-(\d+)/)
27
+ if (ver) return ver[2] ? `${fam} ${ver[1]}.${ver[2]}` : `${fam} ${ver[1]}`
28
+ return fam
26
29
  }
27
30
 
28
31
  function tokensOf(entry) {