clauddy 1.20.0 → 1.21.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 +1 -1
- package/accounts.js +27 -0
- package/main.js +1 -0
- package/package.json +1 -1
- package/renderer/index.html +6 -0
- package/renderer/pet.js +10 -1
- package/renderer/style.css +95 -6
package/README.md
CHANGED
|
@@ -171,7 +171,7 @@ update-desktop-database ~/.local/share/applications 2>/dev/null
|
|
|
171
171
|
## Controls
|
|
172
172
|
|
|
173
173
|
- **Drag** the widget anywhere on screen
|
|
174
|
-
- **–** minimizes to just the pet
|
|
174
|
+
- **–** minimizes to just the pet, ringed by the live session % (the number sits inside the ring); the **⤢** button or a double-click on the pet expands it back
|
|
175
175
|
- **⚙** opens settings (log in, toggle alerts, set thresholds, pick the display mode)
|
|
176
176
|
- **↗** opens the official Usage page
|
|
177
177
|
- **×** quits
|
package/accounts.js
CHANGED
|
@@ -139,6 +139,32 @@ function remove(id) {
|
|
|
139
139
|
return true
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
// Folders left behind under `accounts/` by slots that are no longer listed —
|
|
143
|
+
// an add abandoned mid-login, mostly, since saving the alert state recreates
|
|
144
|
+
// the folder on its way out. Only ever drops a folder with no auth.json in it,
|
|
145
|
+
// so a token is never destroyed by a bad read of accounts.json.
|
|
146
|
+
function pruneOrphans() {
|
|
147
|
+
const root = path.join(BASE_DIR, 'accounts')
|
|
148
|
+
let dirs = []
|
|
149
|
+
try {
|
|
150
|
+
dirs = fs.readdirSync(root, { withFileTypes: true })
|
|
151
|
+
} catch {
|
|
152
|
+
return 0 // no extra accounts were ever added
|
|
153
|
+
}
|
|
154
|
+
const known = new Set(list().map((a) => a.id))
|
|
155
|
+
let n = 0
|
|
156
|
+
for (const d of dirs) {
|
|
157
|
+
if (!d.isDirectory() || known.has(d.name)) continue
|
|
158
|
+
const dir = path.join(root, d.name)
|
|
159
|
+
if (fs.existsSync(path.join(dir, 'auth.json'))) continue
|
|
160
|
+
try {
|
|
161
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
162
|
+
n++
|
|
163
|
+
} catch {}
|
|
164
|
+
}
|
|
165
|
+
return n
|
|
166
|
+
}
|
|
167
|
+
|
|
142
168
|
module.exports = {
|
|
143
169
|
BASE_DIR,
|
|
144
170
|
DEFAULT_ID,
|
|
@@ -151,4 +177,5 @@ module.exports = {
|
|
|
151
177
|
label,
|
|
152
178
|
setClaudeDir,
|
|
153
179
|
remove,
|
|
180
|
+
pruneOrphans,
|
|
154
181
|
}
|
package/main.js
CHANGED
|
@@ -334,6 +334,7 @@ function createWindow() {
|
|
|
334
334
|
if (fallback) accounts.setActive(fallback.id)
|
|
335
335
|
}
|
|
336
336
|
for (const a of accounts.list()) pruneEmpty(a.id)
|
|
337
|
+
accounts.pruneOrphans() // folders those slots left behind
|
|
337
338
|
applyAccount(accounts.active())
|
|
338
339
|
const { workAreaSize } = screen.getPrimaryDisplay()
|
|
339
340
|
const H = 480
|
package/package.json
CHANGED
package/renderer/index.html
CHANGED
|
@@ -112,6 +112,12 @@
|
|
|
112
112
|
<path class="dk-leaf" d="M198 103 q5 -12 3 -25 q-7 9 -3 25" />
|
|
113
113
|
<path class="dk-leaf-c" d="M198 103 q0 -14 0 -27 q3 13 0 27" />
|
|
114
114
|
</svg>
|
|
115
|
+
<!-- collapsed: a ring around the pet with the session % inside -->
|
|
116
|
+
<svg id="ring" viewBox="0 0 100 100" aria-hidden="true">
|
|
117
|
+
<circle class="ring-track" cx="50" cy="50" r="45" />
|
|
118
|
+
<circle id="ring-fill" cx="50" cy="50" r="45" />
|
|
119
|
+
</svg>
|
|
120
|
+
<span id="ring-pct">0%</span>
|
|
115
121
|
<div id="shadow"></div>
|
|
116
122
|
<div id="pet">
|
|
117
123
|
<svg id="claude" viewBox="0 0 100 90" width="86" height="77">
|
package/renderer/pet.js
CHANGED
|
@@ -455,6 +455,8 @@ function oneShot(cls, ms) {
|
|
|
455
455
|
setTimeout(() => document.body.classList.remove(cls), ms)
|
|
456
456
|
}
|
|
457
457
|
|
|
458
|
+
const RING_LEN = 2 * Math.PI * 45 // the collapsed ring's circumference (r=45)
|
|
459
|
+
|
|
458
460
|
// session-reset celebration: jump + a colorful confetti burst
|
|
459
461
|
const CONFETTI_COLORS = ['#ffd23f', '#ff5d86', '#7ec77d', '#6db3f2', '#e0805a', '#c89bff']
|
|
460
462
|
function spawnConfetti(i) {
|
|
@@ -577,6 +579,13 @@ function render(d) {
|
|
|
577
579
|
const mini = el('mini-pct')
|
|
578
580
|
mini.textContent = liveOn ? `${Math.round(sessPct)}%` : '—'
|
|
579
581
|
mini.classList.toggle('high', liveOn && sessPct >= 80)
|
|
582
|
+
// the collapsed ring: how much of the session is already spent
|
|
583
|
+
const rf = el('ring-fill')
|
|
584
|
+
rf.style.strokeDashoffset = `${RING_LEN * (1 - Math.min(sessPct, 100) / 100)}`
|
|
585
|
+
rf.classList.toggle('high', sessPct >= 80)
|
|
586
|
+
const rp = el('ring-pct')
|
|
587
|
+
rp.textContent = mini.textContent
|
|
588
|
+
rp.classList.toggle('high', liveOn && sessPct >= 80)
|
|
580
589
|
const sf = el('session-fill')
|
|
581
590
|
sf.style.width = `${sessPct}%`
|
|
582
591
|
sf.classList.toggle('high', sessPct >= 80)
|
|
@@ -632,7 +641,7 @@ function fitSize() {
|
|
|
632
641
|
requestAnimationFrame(() => {
|
|
633
642
|
const collapsed = document.body.classList.contains('collapsed')
|
|
634
643
|
const zoom = Number.parseFloat(document.body.style.zoom) || 1
|
|
635
|
-
const w = (collapsed ?
|
|
644
|
+
const w = (collapsed ? 168 : 276) * zoom
|
|
636
645
|
// offsetHeight never reflects a CSS zoom applied to an ancestor (confirmed
|
|
637
646
|
// empirically against this Electron build) — so measure the unzoomed content
|
|
638
647
|
// height, then scale the whole thing (content + margin) ourselves
|
package/renderer/style.css
CHANGED
|
@@ -314,6 +314,19 @@ body.one-account .ac-caret {
|
|
|
314
314
|
body.collapsed #account-chip {
|
|
315
315
|
display: none;
|
|
316
316
|
}
|
|
317
|
+
/* collapsed: the account rides up on the button line, like the chip does */
|
|
318
|
+
body.collapsed #mini-acct {
|
|
319
|
+
position: absolute;
|
|
320
|
+
top: 6px;
|
|
321
|
+
left: 10px;
|
|
322
|
+
z-index: 10;
|
|
323
|
+
height: 19px;
|
|
324
|
+
margin-bottom: 0;
|
|
325
|
+
justify-content: flex-start;
|
|
326
|
+
}
|
|
327
|
+
body.collapsed #mini-acct-name {
|
|
328
|
+
max-width: 62px;
|
|
329
|
+
}
|
|
317
330
|
|
|
318
331
|
/* settings panel */
|
|
319
332
|
#settings {
|
|
@@ -1076,7 +1089,7 @@ body.collapsed #mini {
|
|
|
1076
1089
|
display: block;
|
|
1077
1090
|
}
|
|
1078
1091
|
body.collapsed #card {
|
|
1079
|
-
width:
|
|
1092
|
+
width: 144px;
|
|
1080
1093
|
padding: 12px 12px 13px;
|
|
1081
1094
|
}
|
|
1082
1095
|
body.collapsed #stage {
|
|
@@ -1148,7 +1161,7 @@ body.settings-open #pet {
|
|
|
1148
1161
|
|
|
1149
1162
|
/* collapsed: just the face, on the warm card (like before) */
|
|
1150
1163
|
body.collapsed #stage {
|
|
1151
|
-
height:
|
|
1164
|
+
height: 120px;
|
|
1152
1165
|
margin-top: 9px;
|
|
1153
1166
|
background: transparent;
|
|
1154
1167
|
box-shadow: none;
|
|
@@ -1171,19 +1184,95 @@ body.collapsed #gears,
|
|
|
1171
1184
|
body.collapsed #glasses {
|
|
1172
1185
|
display: none;
|
|
1173
1186
|
}
|
|
1187
|
+
/* collapsed: the pet sits a bit high, so the % fits under it inside the ring */
|
|
1174
1188
|
body.collapsed #pet {
|
|
1175
1189
|
position: static;
|
|
1176
1190
|
left: auto;
|
|
1177
1191
|
bottom: auto;
|
|
1178
|
-
|
|
1192
|
+
transform: translateY(-8px);
|
|
1193
|
+
zoom: 0.86;
|
|
1194
|
+
}
|
|
1195
|
+
/* the ring is a fixed frame, so the usual bob reads as jumping in there */
|
|
1196
|
+
body.collapsed #claude {
|
|
1197
|
+
animation: bob-mini 3.6s ease-in-out infinite;
|
|
1198
|
+
}
|
|
1199
|
+
@keyframes bob-mini {
|
|
1200
|
+
0%,
|
|
1201
|
+
100% {
|
|
1202
|
+
transform: translateY(0);
|
|
1203
|
+
}
|
|
1204
|
+
50% {
|
|
1205
|
+
transform: translateY(-1.5px);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
/* the ring: session % drawn around the pet, only when there's a real % to show */
|
|
1209
|
+
#ring {
|
|
1210
|
+
display: none;
|
|
1211
|
+
}
|
|
1212
|
+
body.collapsed #ring {
|
|
1213
|
+
display: block;
|
|
1214
|
+
position: absolute;
|
|
1215
|
+
left: 50%;
|
|
1216
|
+
top: 50%;
|
|
1217
|
+
width: 118px;
|
|
1218
|
+
height: 118px;
|
|
1219
|
+
transform: translate(-50%, -50%);
|
|
1220
|
+
pointer-events: none;
|
|
1221
|
+
z-index: 1;
|
|
1222
|
+
}
|
|
1223
|
+
.ring-track {
|
|
1224
|
+
fill: none;
|
|
1225
|
+
stroke: var(--track);
|
|
1226
|
+
stroke-width: 3.5;
|
|
1227
|
+
}
|
|
1228
|
+
#ring-fill {
|
|
1229
|
+
fill: none;
|
|
1230
|
+
stroke: var(--green);
|
|
1231
|
+
stroke-width: 3.5;
|
|
1232
|
+
stroke-linecap: round;
|
|
1233
|
+
transform: rotate(-90deg);
|
|
1234
|
+
transform-origin: 50% 50%;
|
|
1235
|
+
stroke-dasharray: 282.7;
|
|
1236
|
+
stroke-dashoffset: 282.7; /* set from JS */
|
|
1237
|
+
transition:
|
|
1238
|
+
stroke-dashoffset 0.5s ease,
|
|
1239
|
+
stroke 0.3s ease;
|
|
1240
|
+
}
|
|
1241
|
+
#ring-fill.high {
|
|
1242
|
+
stroke: #e0553f;
|
|
1243
|
+
}
|
|
1244
|
+
#ring-pct {
|
|
1245
|
+
display: none;
|
|
1246
|
+
}
|
|
1247
|
+
body.collapsed #ring-pct {
|
|
1248
|
+
display: block;
|
|
1249
|
+
position: absolute;
|
|
1250
|
+
left: 0;
|
|
1251
|
+
right: 0;
|
|
1252
|
+
bottom: 12px;
|
|
1253
|
+
z-index: 3;
|
|
1254
|
+
text-align: center;
|
|
1255
|
+
font-size: 13px;
|
|
1256
|
+
font-weight: 700;
|
|
1257
|
+
color: var(--text);
|
|
1258
|
+
font-variant-numeric: tabular-nums;
|
|
1259
|
+
pointer-events: none;
|
|
1260
|
+
}
|
|
1261
|
+
#ring-pct.high {
|
|
1262
|
+
color: #e0553f;
|
|
1263
|
+
}
|
|
1264
|
+
/* the ring already says it — no need to repeat it under the pet */
|
|
1265
|
+
body.collapsed #mini-pct-row {
|
|
1266
|
+
display: none;
|
|
1179
1267
|
}
|
|
1180
1268
|
body.collapsed #shadow {
|
|
1181
1269
|
display: block;
|
|
1182
1270
|
position: absolute;
|
|
1183
|
-
|
|
1271
|
+
/* pinned under the pet's feet, which the ring lifted off the stage floor */
|
|
1272
|
+
top: calc(50% + 23px);
|
|
1184
1273
|
left: 50%;
|
|
1185
|
-
width:
|
|
1186
|
-
height:
|
|
1274
|
+
width: 48px;
|
|
1275
|
+
height: 8px;
|
|
1187
1276
|
background: radial-gradient(ellipse, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0));
|
|
1188
1277
|
transform: translateX(-50%);
|
|
1189
1278
|
}
|