Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
758f569
fix(ai-usagebar): drop rejected providers
FelipeArtur Sep 3, 2026
66d4ded
fix(ai-usagebar): quiet stale failures
FelipeArtur Sep 3, 2026
d6dee0b
fix(ai-usagebar): parse split HTTP sections
FelipeArtur Sep 3, 2026
b5df288
fix(ai-usagebar): rank working providers
FelipeArtur Sep 3, 2026
7309b30
fix(ai-usagebar): select bottleneck metric across windows
FelipeArtur Sep 4, 2026
2277f4d
fix(ai-usagebar): split antigravity readings by model
FelipeArtur Sep 4, 2026
e58be57
style(ai-usagebar): refine typography and modern card styling
FelipeArtur Sep 4, 2026
0bc2af9
fix(ai-usagebar): prioritize active session and streamline minimal bar
FelipeArtur Sep 4, 2026
76d8ea6
feat(ai-usagebar): display model brand glyphs and preserve active ses…
FelipeArtur Sep 4, 2026
53b6d59
feat(ai-usagebar): add paired dual progress bars to panel provider rows
FelipeArtur Sep 4, 2026
f04383c
feat(ai-usagebar): display 4 progress bars in 2 pairs for Antigravity
FelipeArtur Sep 4, 2026
ab9446f
style(ai-usagebar): separate model bar pairs and center glyph in prov…
FelipeArtur Sep 4, 2026
a5cad8f
feat(ai-usagebar): display dual session and weekly percentages for su…
FelipeArtur Sep 4, 2026
4043f79
refactor(ai-usagebar): expand panel layout, brand model cards, and ke…
FelipeArtur Sep 4, 2026
1163bee
feat(ai-usagebar): hide empty credits card when balance is zero
FelipeArtur Sep 4, 2026
166c8d6
feat(ai-usagebar): simplify bar tooltip to ultra-compact model rows
FelipeArtur Sep 5, 2026
df5f363
feat(ai-usagebar): use clean display names for submodels in tooltip
FelipeArtur Sep 5, 2026
1de40ab
feat(ai-usagebar): show reset countdown on bar and use fixed hours/mi…
FelipeArtur Sep 5, 2026
1265718
feat(ai-usagebar): rename Claude & GPT OSS to Gemini OSS and use robo…
FelipeArtur Sep 5, 2026
13d29b4
style(ai-usagebar): separate submodels with standard dot separator in…
FelipeArtur Sep 5, 2026
51fc499
fix(ai-usagebar): show blocking reset time
FelipeArtur Sep 5, 2026
da4ffd2
fix(ai-usagebar): show blocking window
FelipeArtur Sep 5, 2026
b9779ff
chore(ai-usagebar): release 2.2.0
FelipeArtur Sep 5, 2026
857be2c
fix(ai-usagebar): clarify quotas and reset times
FelipeArtur Sep 8, 2026
00f50ab
fix(ai-usagebar): redact sensitive report data
FelipeArtur Sep 8, 2026
5b22299
docs(ai-usagebar): update dashboard thumbnail
FelipeArtur Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 92 additions & 107 deletions ai-usagebar/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
# AI Usage

Your AI plan quota in the Noctalia bar: how much of the window is spent, when it
resets, and whether you are burning it faster than the clock.
Track AI plan usage, quota resets, and consumption pace in the Noctalia bar.

The numbers come from [ai-usagebar](https://github.com/akitaonrails/ai-usagebar),
a Rust CLI that reads Claude, Codex, Cursor, Antigravity, Kiro, Z.AI, Nous
Research, OpenCode Go, Command Code, OpenRouter, DeepSeek, Kimi and Grok, among
others. This plugin never talks to a
provider, holds a token, or reads a credential file. It runs
`ai-usagebar usage --json` and draws the answer.
others. The plugin runs `ai-usagebar usage --json` and displays the report.
It does not call provider APIs or read credential files.

## Plugin

Expand All @@ -19,53 +17,47 @@ provider, holds a token, or reads a credential file. It runs

## Requirements

Install `ai-usagebar` on `PATH`. The plugin runs it by name, so there is no path
setting to fill in. It ships as `ai-usagebar-bin` on the AUR, and as release
Install `ai-usagebar` on `PATH`. The plugin runs it by name and has no separate
path setting. It ships as `ai-usagebar-bin` on the AUR, and as release
tarballs on the project's GitHub Releases page. Configure your providers once in
`~/.config/ai-usagebar/config.toml`; the CLI owns the credentials and the
endpoints, and this plugin never sees them.
`~/.config/ai-usagebar/config.toml`; the CLI manages credentials and provider
connections.

`xdg-open` is optional. The panel spawns it for one button, the link to the
CLI's project page it offers when `ai-usagebar` is not on `PATH`. Without
xdg-utils the panel leaves that button out and nothing else changes.
When the CLI is missing, the panel displays its project address for installation
instructions. No browser-opening command or additional dependency is needed.

The plugin asks for **plugin API 22**, which is where Noctalia gained
`require()`. On a shell older than that it will not install. Version 1.1.0 asked
for API 9 and still runs there.
The plugin requires plugin API 22 for `require()`. It will not install on an
older shell. Plugin version 1.1.0 remains available for API 9.

## Usage

Add `felipeartur/ai-usagebar:bar` to a bar in Settings, Bar. The capsule shows
one provider's headline percentage next to that provider's mark. The reading
sits in the bar's own colour while there is room, picks up the theme's
`secondary` when the CLI calls the window high, and `error` when it calls it
critical. The mark itself never changes colour: it says which provider, not how
full the plan is.

Left on `Automatic`, the capsule follows the busiest provider, so what sits in
the bar is the plan closest to running out. Raise `provider_limit` and it
carries the next busiest ones too, with a `+N` for whatever did not fit. Pin a
provider instead, or add the widget twice, when you want two fixed plans side by
side.
one provider's headline percentage beside its icon. Readings use the bar's text
color, the theme's `secondary` color for high usage, and `error` for critical
usage. Icons keep their normal color unless a read fails.

`Automatic` selects by headline severity, then usage. Raise `provider_limit` to
show more providers; limits above one also show `+N` for providers that do not
fit. Pin a provider or add another widget instance to keep particular plans
visible.

Named accounts use the label from the CLI config. Pick the provider and put the
label in `account`: `vendor = "openai"` with `account = "work"` follows the
`openai@work` report entry. Leave it empty for the provider's default account.

The capsule is put together the way the core `sysmon` widget is, with the same
key names, so the CPU reading beside it is configured with the same vocabulary.
`visualization` draws a `gauge`, a quota bar over a thinner bar for how much of
the window has gone, so a longer fill than clock is spend running ahead, or
`none`. `show_value`, `show_glyph` and `glyph_position` decide whether the
percentage and the icon are there and which side the icon sits on.
Settings use the same names as the core `sysmon` widget. Set `visualization` to
`gauge` for a usage bar above a thinner elapsed-time bar, or `none` to hide it.
Usage ahead of elapsed time means consumption is ahead of pace. `show_value`,
`show_glyph` and `glyph_position` control the percentage and icon placement,
including Antigravity's individual model readings.

`extras` puts the time left in the window (`3h 51m`), the pace against the
clock (`↑3` is three points ahead of where the window says you should be, `↓3`
is three under), both, or neither.
`extras` adds the reset countdown (`3h 51m`), consumption pace (`↑3` means three
percentage points ahead of elapsed time; `↓3` means three behind), both, or
neither. Countdowns show days and hours from 24 hours onward, and hours and
minutes below that.

If you add the widget by hand in `config.toml`, give it a name. A bar list entry
that is a raw widget id becomes an anonymous instance, and an anonymous instance
has no settings of its own, so the gear opens empty:
When editing `config.toml` by hand, create a named instance. A raw widget id in
the bar list creates an anonymous instance with no settings of its own:

```toml
[widget.ai_usage]
Expand All @@ -77,59 +69,55 @@ provider_limit = 2
start = [ "clock", "ai_usage" ]
```

- **Hover** lists every window that provider reports: value, time left, and the
clock time the reset lands on.
- **Left click** opens the `AI Usage` panel for the provider that capsule
- Hover shows session and long-window values with a reset countdown per model.
- When a long window is exhausted, the capsule replaces the short-window value
with `100%` and counts down to the blocking window's reset. If several windows
are exhausted, it uses the latest reset. A critical warning below 100% does
not count as exhaustion.
- Left click opens the `AI Usage` panel for the provider that capsule
tracks.
- **Right click** asks the poller for a read. One process serves every capsule,
and it coalesces repeated clicks into at most one pending read, so holding the
button down does not spawn a queue of processes.
- **Middle click** opens the widget's settings, as everywhere else in the shell.

Left and middle are the script's; right is a gesture binding, so it is listed in
the widget's settings and can be pointed at any other action, or at `none`.

The panel is a two-pane view. On the left is every provider you have set up,
with its headline percentage. On the right is the selected one in detail: one
card per reported metric, with a quota bar over a thinner "window elapsed" bar,
so a fill that outruns the clock bar means quota is burning ahead of pace.
Credit balances and free text rows the CLI reports get rendered as well.
Opening the panel asks the CLI for fresh numbers, and the detail pane says how
old the reading is. The refresh button in the header asks again; it turns into
a spinner while the CLI is answering. The gear beside it opens this plugin's
settings. There is no close button: the panel closes when you click away from
it or press the same widget again.

The list follows the CLI. A provider the CLI reports no API key for never
appears, because it was never set up. One that is set up and unreachable keeps
its row, marked unavailable, and shows the CLI's own words, so Antigravity with
its local server down says to open Antigravity rather than vanishing.

A provider's readings share one card, so its session and its week are read
together. Antigravity's plan spans two models and reports each of them once per
window, so its cards are keyed by model, titled with it, and each reading is
headed by the window it covers. Every other provider reports one subject, which
the pane's own header already names, so its readings head themselves and the
card goes untitled.

The detail pane spells out what the CLI reports for that provider instead of
implying it: the plan and account name, when it was fetched, and a stale flag
when the reading is old. Each window gets its label, the percentage, the raw
value string when that says more than the percentage, how much of the window has
elapsed, the time left with the clock time (or date) its reset lands on, the
pace line, and the severity as a word whenever the CLI calls the window high or
critical. Credit blocks and free text rows appear as the CLI writes them.

A provider that is down draws no gauges. A bar reads as a live reading, and
nothing is reading it: the warning takes their place, and the numbers it was
last seen with follow as dated text. If the plan changed since, they are dropped
instead -- a plan carries the limits every percentage is measured against, so a
reading taken under the old one says nothing about the new one.

The plan a provider was last seen on is remembered only for as long as the panel
process lives, and only for providers the current report still carries. After a
shell reload there is no previous plan to compare against, so the first reading
that follows is shown whatever the plan says.
- Right click requests a refresh. One poller serves every capsule and keeps at
most one pending refresh when requests arrive during a read.
- Middle click opens the widget's settings, as everywhere else in the shell.

The script handles left and middle clicks. Right click is a gesture binding in
the widget settings, where you can assign another action or choose `none`.

The panel lists providers on the left and shows the selected provider's limits
on the right. Session and weekly limits share a card; Antigravity has a separate
card for each model. Each window shows usage above a thinner elapsed-time bar.
A longer usage bar means consumption is ahead of the window's pace.
The shared `Claude & GPT OSS` quota keeps the name supplied by the CLI.

Exhausted quotas get a compact notice with the model name and reset countdown.
The notice uses text and theme colors. The panel displays quota readings, not
agent process health.

Opening the panel requests fresh data. The header shows when the last reading
arrived, a refresh button, and plugin settings. Click outside or click the widget
again to close the panel.

The list contains only providers with a usable reading. A provider the CLI
reports no API key for never appears, because it was never set up. A configured
provider with its own refresh failure also leaves the bar and panel, then
returns automatically after a healthy read. The panel sorts providers by headline
severity, then usage; equal readings keep the CLI's order. Automatic bar selection
uses the same priorities, then the configured primary provider and account id.

Parser errors are an exception: the provider remains visible with missing usage
and the CLI error, because a response-format failure does not establish that the
account is unavailable.

Details include the plan and account name, reading age, stale status, window
labels, percentages, elapsed time, reset countdowns and local reset times, pace,
and high or critical severity labels. Raw values appear when they add information
to the percentage. Credit blocks and free text are also shown; zero credit
balances are hidden. Codex reset credits show the reset type and expiry on
separate lines, with wrapping for longer descriptions.

A whole-report failure is different: it cannot identify one broken provider,
so it keeps the last list and marks those readings as old instead of blanking
the panel during a network interruption.

To open the panel from a terminal:

Expand All @@ -156,9 +144,9 @@ Per widget instance, so two capsules can follow two providers:
| `show_glyph` | `bool` | `true` | Show the provider's icon. |
| `glyph_position` | `select` | `before` | `before` or `after` the reading. |
| `provider_limit` | `int` | `1` | How many providers one capsule carries, busiest first, from 1 to 4. Only applies on `auto`. |
| `extras` | `select` | `countdown` | What rides beside the percentage: `countdown`, `pace`, `both` or `none`. |
| `show_name` | `bool` | `false` | Adds the product name, so two capsules do not look alike. |
| `color_by_usage` | `bool` | `true` | Off drops the high and critical tint, so the capsule never changes colour. |
| `extras` | `select` | `countdown` | Information beside the percentage: `countdown`, `pace`, `both` or `none`. |
| `show_name` | `bool` | `false` | Show the provider name beside the reading. |
| `color_by_usage` | `bool` | `true` | Color readings by quota severity. Turning it off does not hide error indicators. |

## IPC

Expand All @@ -183,23 +171,20 @@ noctalia msg plugin felipeartur/ai-usagebar:poller all select anthropic
process.
- The plugin makes no network calls and writes no files of its own. Everything
it knows arrives on that command's stdout.
- A provider that fails still comes back as an entry with `status = "error"`, so
one broken provider does not blank the others. A reading the CLI marks stale
keeps showing, flagged by an icon in the list and the panel's detail pane.
- A provider whose service is down leaves the bar on the first report that says
so, rather than sitting in the capsule with a number nothing is refreshing. It
is not counted behind the `+n`: that count is what the panel has more of, and
this one has nothing to show. It comes back the moment a report carries a
reading for it again.
- A failed read does not erase the last one. The failure is said once, as a
banner in the panel and a flag on the capsule, over readings that are simply
older than they should be; the panel only gives itself over to the failure
when there is no report behind it at all.
- A provider whose service is down leaves the bar and panel on the first report
that says so. It is not counted behind the `+n`, and returns when a report
carries a healthy reading for it again.
- A failed whole-report read keeps the previous report and marks it as stale.
Without a previous report, the panel shows the failure and its scrubbed
diagnostic. Raw HTTP details stay out of the reading cards.
- Report text is scrubbed for common credential patterns before publication to
plugin state. This is a precaution, not a guarantee that arbitrary CLI output
contains no sensitive data. Review account labels and diagnostics before
sharing screenshots.

## Tests

Everything the CLI prints is redacted on its way to the screen, and that is the
part worth a test. From the `ai-usagebar` directory:
Run the tests from the `ai-usagebar` directory:

```sh
lua tests/scrub_test.lua
Expand Down
Loading