Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
335ead5
ai-usagebar: widen the credential scrubber
FelipeArtur Aug 20, 2026
9ddfa48
ai-usagebar: rework the capsule and panel, v1.2.0
FelipeArtur Aug 20, 2026
32700dd
ai-usagebar: retake the thumbnail against the new panel
FelipeArtur Aug 20, 2026
7744b37
ai-usagebar: share the helpers, drop the busy hold
FelipeArtur Aug 20, 2026
18e6130
ai-usagebar: settings button, v1.3.0
FelipeArtur Aug 20, 2026
3652186
ai-usagebar: keep the redaction inside the callback's CPU budget
FelipeArtur Aug 21, 2026
4cf6a84
ai-usagebar: put a CPU budget under the redaction test
FelipeArtur Aug 21, 2026
bd7169d
ai-usagebar: rank mid severity, share the detail parser, cut the comm…
FelipeArtur Aug 21, 2026
8875f02
ai-usagebar: retake the thumbnail in the official generator
FelipeArtur Aug 21, 2026
ce38eaa
ai-usagebar: stamp the refresh command with a millisecond clock
FelipeArtur Aug 21, 2026
31c0a9c
ai-usagebar: keep the refresh request in one place
FelipeArtur Aug 21, 2026
87a3a04
ai-usagebar: make the right click a gesture binding
FelipeArtur Aug 21, 2026
7997ce0
feat(ai-usagebar): improve refresh and provider visuals
FelipeArtur Aug 24, 2026
6bb00f0
ai-usagebar: drop the catalog.toml edit, CI generates it
FelipeArtur Aug 25, 2026
93d6265
ai-usagebar: make the classify comments match the check
FelipeArtur Aug 25, 2026
404c3d0
ai-usagebar: stop the redaction from eating the readings
FelipeArtur Aug 25, 2026
b858348
ai-usagebar: only frame-tick while the panel is open
FelipeArtur Aug 25, 2026
8e9ff88
ai-usagebar: only hide providers the CLI says have no key
FelipeArtur Aug 25, 2026
f43a838
ai-usagebar: stop flagging things in the dimmest colour on screen
FelipeArtur Aug 25, 2026
e258ea9
ai-usagebar: name the detail pane after the provider, not the plan
FelipeArtur Aug 25, 2026
240bec2
ai-usagebar: leave the provider glyphs uncoloured
FelipeArtur Aug 25, 2026
43be9fc
ai-usagebar: keep the number exemption away from the real secrets
FelipeArtur Aug 25, 2026
b34812f
ai-usagebar: retake the thumbnail after the panel fixes
FelipeArtur Aug 25, 2026
7814176
ai-usagebar: drop the refresh pulse
FelipeArtur Aug 25, 2026
12a6a68
ai-usagebar: cut the skeletons and the guessed card icons
FelipeArtur Aug 25, 2026
11f3628
ai-usagebar: one capsule shape, v2.0.0
FelipeArtur Aug 25, 2026
b48f53c
ai-usagebar: configure the capsule the way sysmon does
FelipeArtur Aug 25, 2026
14ce9bc
ai-usagebar: drop the meter, default to no visualization, stop the dim
FelipeArtur Aug 25, 2026
6b13c15
ai-usagebar: rule between providers, plain provider marks
FelipeArtur Aug 25, 2026
1848947
ai-usagebar: redact secrets whose separator is padded
FelipeArtur Aug 25, 2026
50e0532
ai-usagebar: move the last duplicated parsing into shared
FelipeArtur Aug 25, 2026
4ec2adc
ai-usagebar: bring the README back in line with the plugin
FelipeArtur Aug 25, 2026
60a5e79
ai-usagebar: retake the thumbnail against the current panel
FelipeArtur Aug 25, 2026
0bb6c31
ai-usagebar: give the provider rule something to divide with
FelipeArtur Aug 25, 2026
852e12e
ai-usagebar: divide providers with a dot, not a rule
FelipeArtur Aug 25, 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
116 changes: 70 additions & 46 deletions ai-usagebar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,39 @@ 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.

`xdg-open` is optional. It is spawned by one row in the panel, the link to the
CLI's project page offered when `ai-usagebar` is not on `PATH`. Without
xdg-utils that row does nothing and the rest of the plugin is unaffected.
`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.

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.

## Usage

Add `felipeartur/ai-usagebar:bar` to a bar in Settings, Bar. The capsule shows
the headline percentage of a provider, behind that provider's icon. It reads in
the bar's own colour while there is room, picks up the theme's `tertiary` when
the CLI calls the window high, and `error` when it calls it critical. The accent
stays on the gauge fill, so a calm capsule looks like the widgets beside it.
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.

Four styles, all with the same reading:

| Style | Shape |
| --- | --- |
| `pill` | Icon and percentage. The compact one. |
| `gauge` | Icon, a small quota bar over a thinner "window elapsed" bar, percentage. |
| `meter` | Icon and five segments, filled in twenties, with no percentage. |
| `label` | Icon, provider name and percentage stacked over the bars. |
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.

Next to that, `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` 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.

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
Expand All @@ -62,7 +65,7 @@ has no settings of its own, so the gear opens empty:
```toml
[widget.ai_usage]
type = "felipeartur/ai-usagebar:bar"
style = "gauge"
visualization = "gauge"
provider_limit = 2

[bar.default]
Expand All @@ -73,29 +76,37 @@ start = [ "clock", "ai_usage" ]
clock time the reset lands on.
- **Left click** opens the `AI Usage` panel for the provider that capsule
tracks.
- **Right click** refreshes immediately.
- **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.

The panel is a two pane view. On the left is every provider you have set up,
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 header says how old
the reading is. There is no refresh button and no close button: the read
happens on open, and the panel closes when you click away from it or press the
same widget again.

The list follows the CLI. A provider that `ai-usagebar` has no credential for
never appears, while one that is set up and failing keeps its row and shows the
error.

The detail pane spells out everything the CLI reports for that provider instead
of implying it: the plan and account name, the provider id, its status, a stale
flag when the reading is old, and when it was fetched. Each window gets its
label, the severity the CLI assigned it, 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, and the pace line.
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 and shows the CLI's own words, so Antigravity with its local server
down says to open Antigravity rather than vanishing.

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, a stale flag when
the reading is old, and the status when it is anything other than a healthy
read. 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.

To open the panel from a terminal:
Expand All @@ -117,7 +128,10 @@ Per widget instance, so two capsules can follow two providers:
| Setting | Type | Default | Description |
| --- | --- | --- | --- |
| `vendor` | `select` | `auto` | Which plan this capsule tracks. `auto` follows the busiest provider, with the CLI's own `[ui] primary` breaking ties. |
| `style` | `select` | `pill` | `pill`, `gauge`, `meter` or `label`, as described in the table above. |
| `visualization` | `select` | `none` | `gauge` or `none`, as described above. |
| `show_value` | `bool` | `true` | Show the percentage as text. |
| `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. |
Expand Down Expand Up @@ -148,12 +162,22 @@ noctalia msg plugin felipeartur/ai-usagebar:poller all select anthropic
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 in the capsule and in the panel header.
- The file watcher follows the `.luau` entries only, so the files in
`translations/` are read once, when the plugin loads. Editing a string takes
a reload before the new text shows up:

```sh
noctalia msg plugins disable felipeartur/ai-usagebar
noctalia msg plugins enable felipeartur/ai-usagebar
```
keeps showing, flagged by an icon in the list, the capsule, and the panel's
detail pane.

## 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:

```sh
lua tests/scrub_test.lua
lua tests/refresh_test.lua
```

The first test reads `safeText` and `scrub` out of `service.luau` rather than
copying them, then checks that real credential shapes never survive, that ordinary
readings pass through unchanged, and that scrubbing a four-vendor report stays
inside the CPU budget the poller's async callback is given. The second exercises
the coalesced refresh state and checks that every provider it knows about has a
glyph of its own rather than the fallback. An overrun in the first test loses the whole reading, not just time.
Loading