Skip to content

Commit 16a41bb

Browse files
committed
fix(waybar): omit alt/percentage sem dados + clamp 100
1 parent 9d3be62 commit 16a41bb

5 files changed

Lines changed: 41 additions & 20 deletions

File tree

docs/waybar-contract.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,13 @@ and adds provider-scoped classes such as `claude-ok`, `codex-warn`, or
132132
Single-provider modules (`--provider <id>`) emit, in addition to `text`,
133133
`tooltip`, and `class`:
134134

135-
- `alt` — the health state (`ok` / `low` / `warn` / `critical` / `disconnected`),
136-
for `format-icons` keyed by state.
135+
- `alt` — the health state (`ok` / `low` / `warn` / `critical`), or
136+
`disconnected`, for `format-icons` keyed by state. **Omitted** when the
137+
provider is available but has no quota data (so a missing window never reports
138+
`ok`).
137139
- `percentage` — the displayMode-aware quota value (the same number shown in
138-
`text`), for `{percentage}` in `format` or `format-icons` arrays. **Omitted**
139-
when there is no data or the provider is disconnected.
140+
`text`), clamped to `0..100`, for `{percentage}` in `format` or `format-icons`
141+
arrays. **Omitted** when there is no data or the provider is disconnected.
140142

141143
The aggregate module (no `--provider`) does **not** emit `alt`/`percentage`.
142144

src/formatters/waybar.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { APP_BASE_CLASS } from '../app-identity';
2-
import { getStatusForPercent } from '../config';
2+
import { getStatusForPercent, type HealthStatus } from '../config';
33
import type { AllQuotas, ProviderQuota } from '../providers/types';
44
import { type DisplayMode, loadSettingsSync } from '../settings';
55
import { ONE_DARK } from '../theme';
@@ -59,9 +59,9 @@ interface WaybarOutput {
5959
text: string;
6060
tooltip: string;
6161
class: string;
62-
/** Health state for `format-icons` (single-provider only): ok/low/warn/critical/disconnected. */
63-
alt?: string;
64-
/** displayMode-aware quota value (single-provider only), omitted when no data. */
62+
/** Health state for `format-icons` (single-provider only). Omitted when available but no data. */
63+
alt?: HealthStatus | 'disconnected';
64+
/** displayMode-aware quota value (single-provider only), clamped 0..100, omitted when no data. */
6565
percentage?: number;
6666
}
6767

@@ -229,7 +229,8 @@ export function formatProviderForWaybar(quota: ProviderQuota, mode: DisplayMode
229229
text: pctColored(disp, mode),
230230
tooltip: buildProviderTooltip(quota, undefined, mode),
231231
class: `${APP_BASE_CLASS}-${quota.provider} ${status}`,
232-
alt: status,
233-
...(disp !== null ? { percentage: Math.round(disp) } : {}),
232+
// alt/percentage only when there is real data: a missing window must not
233+
// report 'ok' (class keeps its pre-existing fallback for CSS).
234+
...(disp !== null ? { alt: status, percentage: Math.min(100, Math.round(disp)) } : {}),
234235
};
235236
}

src/waybar-contract.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ function copyDir(src: string, dest: string): void {
7373
}
7474
}
7575

76-
interface WaybarModuleConfig {
76+
export interface WaybarModuleConfig {
7777
exec: string;
7878
'return-type': 'json';
7979
interval: number;

tests/formatters.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,24 @@ describe('formatProviderForWaybar', () => {
251251
expect('alt' in result).toBe(false);
252252
expect('percentage' in result).toBe(false);
253253
});
254+
255+
it('omits alt and percentage when available but has no window data', () => {
256+
const quota: ProviderQuota = { provider: 'claude', displayName: 'Claude', available: true } as ProviderQuota;
257+
const out = formatProviderForWaybar(quota);
258+
expect('alt' in out).toBe(false);
259+
expect('percentage' in out).toBe(false);
260+
});
261+
262+
it('clamps percentage to 100 on overage (used > 100)', () => {
263+
const quota: ProviderQuota = {
264+
provider: 'codex',
265+
displayName: 'Codex',
266+
available: true,
267+
primary: { remaining: -7, used: 107, resetsAt: null },
268+
} as ProviderQuota;
269+
const out = formatProviderForWaybar(quota, 'used');
270+
expect(out.percentage).toBe(100);
271+
});
254272
});
255273

256274
describe('formatForTerminal displayMode=used', () => {

tests/settings.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,22 +142,22 @@ describe('settings', () => {
142142
expect(s.waybar.signal).toBeUndefined();
143143
});
144144

145-
it('preserves a valid signal (1..30)', async () => {
145+
it('preserves valid signals at the range boundaries (1, 8, 30)', async () => {
146146
const settingsDir = join(testRoot, 'agent-bar');
147147
await mkdir(settingsDir, { recursive: true });
148-
await writeFile(
149-
join(settingsDir, 'settings.json'),
150-
JSON.stringify({ version: 2, waybar: { providers: ['claude'], signal: 8 } }),
151-
);
152-
const s = await loadSettings();
153-
expect(s.waybar.signal).toBe(8);
148+
const settingsFile = join(settingsDir, 'settings.json');
149+
for (const ok of [1, 8, 30]) {
150+
await writeFile(settingsFile, JSON.stringify({ version: 2, waybar: { providers: ['claude'], signal: ok } }));
151+
const s = await loadSettings();
152+
expect(s.waybar.signal).toBe(ok);
153+
}
154154
});
155155

156-
it('drops an invalid signal (0, negative, non-integer, out of range, wrong type)', async () => {
156+
it('drops an invalid signal (0, negative, non-integer, above range, wrong type)', async () => {
157157
const settingsDir = join(testRoot, 'agent-bar');
158158
await mkdir(settingsDir, { recursive: true });
159159
const settingsFile = join(settingsDir, 'settings.json');
160-
for (const bad of [0, -1, 3.5, 99, 'x']) {
160+
for (const bad of [0, -1, 3.5, 31, 99, 'x']) {
161161
await writeFile(settingsFile, JSON.stringify({ version: 2, waybar: { providers: ['claude'], signal: bad } }));
162162
const s = await loadSettings();
163163
expect(s.waybar.signal).toBeUndefined();

0 commit comments

Comments
 (0)