Native-AOT, cross-platform AI plan-usage monitor (Avalonia) for
Anthropic Claude, OpenAI Codex/ChatGPT, GitHub Copilot and
Gemini CLI, living in the system tray. Runs on Windows and Linux,
compiled ahead-of-time to a self-contained native executable that needs no .NET
runtime on the target (it ships next to Avalonia's handful of native render
libraries — Skia, HarfBuzz, ANGLE) — an Avalonia port of the original
ai-usagebar (Rust).
It reuses the credentials the official CLIs already wrote to disk (claude,
codex, copilot / gh, gemini) and calls the same undocumented usage
endpoints, so there is no separate in-app login.
Built for Native AOT from the ground up. The entire stack is written to be reflection-free and trim-safe — source-generated JSON, compiled XAML bindings, and an AOT-compatible core — for near-instant startup, no JIT, and a low memory footprint. See Native AOT for the specifics.
Theme it to match your setup. A first-class focus: the app ships 59 built-in themes — its signature HUD dark default plus faithful takes on the palettes you already use elsewhere (One Dark, Dracula, Tokyo Night, Catppuccin, Gruvbox, Nord, Solarized, GitHub, Ayu, Rosé Pine, and more). Switch live from Settings: every theme previews instantly with color swatches and the whole window recolors on the fly, so the HUD blends into whatever editor/terminal/desktop you're working in.
Supported vendors:
- Anthropic Claude
- OpenAI Codex / ChatGPT
- GitHub Copilot
- Google Gemini (Gemini CLI / Code Assist)
| Windows | Linux (COSMIC) |
|---|---|
![]() |
![]() |
The main window shows one tab per vendor with usage gauges, reset countdowns and pacing, plus local Claude Code / Codex token usage for the active project and a per-day usage heatmap. The tray icon (bottom-right above) carries the active vendor's id tinted by severity, with a hover card summarizing every vendor.
- The app starts hidden in the system tray.
- Left-click the tray icon to open or focus the main window.
- The window shows one tab per enabled vendor with usage gauges, reset countdowns, pacing, credits, and related stats.
- Anthropic and OpenAI tabs also show local Claude Code / Codex token usage for the active project or workspace when those local logs are available.
- Pick from 59 built-in themes in Settings — previews are instant and the whole window recolors live; the default is the app's own HUD dark theme.
- Closing the window hides it back to the tray.
- Use Quit from the tray menu to exit the app.
- The tray icon shows the active vendor's short id, colored by severity, plus a tooltip with compact usage information.
- Install the .NET 10 SDK (no extra workload — the Avalonia head is cross-platform).
- Authenticate the vendors you want to use.
- Optionally create
%APPDATA%\ai-usage-hud\config.toml. - Run the app.
Authentication prerequisites:
- Anthropic: run
claudeand sign in. - OpenAI: run
codex login. - Copilot: sign in with the GitHub Copilot CLI or
gh auth login. - Gemini: run
geminiand sign in (the CLI writes~/.gemini/oauth_creds.json).
dotnet build
dotnet test
dotnet run --project src/AiUsageHud.AppThe app publishes as a self-contained Native-AOT executable — no .NET runtime on
the target — alongside Avalonia's native render libraries (libSkiaSharp,
libHarfBuzzSharp, av_libglesv2), which can't be linked into the managed AOT image.
Every AOT/trim/size setting lives in the app project, so a bare publish does the
right thing (no extra -p: flags):
dotnet publish -c Release -r win-x64 # Windows
dotnet publish -c Release -r linux-x64 # LinuxOnly the app head is published — the libraries are marked IsPublishable=false, so a
solution-level publish emits just the app (they are still built and bundled as its
dependencies). The output lands under src/AiUsageHud.App/bin/Release/net10.0/<rid>/publish/
as the AiUsageHud executable plus the three native render .dll/.so files above — to
distribute, ship that folder (the .pdb files are optional debug symbols).
See Native AOT for what's enabled and the toolchain it needs.
This publish mode is framework-dependent, so the target machine needs the .NET 10 Runtime installed. (For a self-contained build that needs no runtime on the target, use the Native AOT publish below.)
The Avalonia head sits on top of a platform-neutral core and shared view models:
src/
AiUsageHud.Core/ # platform-neutral core (no UI framework) — all logic + networking
AiUsageHud.Presentation/ # shared view models + abstractions (IUiDispatcher, IThemeService)
AiUsageHud.App/ # Avalonia head — UI + tray
AiUsageHud.Core.Tests/ # xUnit suite (ported from the Rust tests)
AiUsageHud.Core mirrors the Rust crate module-for-module: Models, Pacing
(pacing/countdown/severity), Config (+ AppPaths, ConfigWriter), Caching
(atomic write + TTL + lock + last-error), and one folder per vendor
(Vendors/Anthropic, OpenAi, Copilot, Gemini) with Types / Creds /
OAuth / Fetcher. UsageService is the orchestrator.
Core/Theming/PaletteColors.cs is the single source of truth for the 59 theme
palettes.
AiUsageHud.Presentation holds every view model (MainViewModel,
VendorTabViewModel, SettingsViewModel, the section/dashboard VMs,
ThemeOption) plus OpacityManager and the framework-agnostic abstractions the
head implements: IUiDispatcher (timer) and IThemeService (palette swap). The
head is thin — only XAML + framework interop (window chrome, animations, tray,
icon rendering).
Themes/Controls.axaml deliberately does not use a prebuilt Avalonia theme —
it re-templates every visible control by hand (borderless window, tabs, gauges,
dashboard/heatmap, settings overlay and palettes). Avalonia.Themes.Simple is
included only as structural plumbing for controls that aren't drawn directly
(ScrollViewer/ItemsControl), and is fully overridden.
The 59 palettes live in tools/palettes.json. Run the generator to (re)emit the
C# table — never hand-edit the generated files. The Avalonia head builds every
palette at runtime from PaletteColors.cs, so it needs only OneDark.axaml (the
compile-time default in App.axaml), which the generator also emits:
pwsh tools/Generate-Palettes.ps1 # PaletteColors.cs + Avalonia OneDark.axaml| Purpose | Windows | Linux |
|---|---|---|
| Config | %APPDATA%\ai-usage-hud\config.toml |
~/.config/ai-usage-hud/config.toml |
| Cache (per vendor) | %LOCALAPPDATA%\ai-usage-hud\<vendor>\ |
~/.local/share/ai-usage-hud/<vendor>/ |
| Anthropic creds | %USERPROFILE%\.claude\.credentials.json |
~/.claude/.credentials.json |
| OpenAI creds | %USERPROFILE%\.codex\auth.json |
~/.codex/auth.json |
| Copilot creds | Windows Credential Manager (copilot-cli / gh) |
~/.config/gh/hosts.yml (gh CLI) |
| Gemini creds | %USERPROFILE%\.gemini\oauth_creds.json |
~/.gemini/oauth_creds.json |
Optional config.toml (defaults enable all vendors). Same shape as the Rust
project:
[ui]
# primary = "anthropic" # anthropic | openai | copilot | gemini
[anthropic]
enabled = true
# credentials_path = "C:/Users/name/.claude/.credentials.json"
[openai]
enabled = true
# codex_auth_path = "C:/Users/name/.codex/auth.json"
[copilot]
enabled = true
# oauth_token = "gho_..." # explicit override; otherwise read from the OS (Credential
# # Manager on Windows, gh hosts.yml on Linux)
[gemini]
enabled = true
# credentials_path = "..." # override ~/.gemini/oauth_creds.json
# project_id = "..." # Code Assist project; otherwise resolved via loadCodeAssist
group_by_variant = true # collapse models into variants (Flash/Pro…), each showing its
# # highest usage; false lists every modelThe in-app Settings dialog edits [ui] (primary, theme, opacity, launch-at-login),
preserving the file's comments and unrelated fields. [gemini].group_by_variant is
file-only for now.
dotnet build
dotnet test # 108 tests
dotnet run --project src/AiUsageHud.App # Avalonia headRequires the .NET 10 SDK. The app builds the tray glyph at runtime and starts hidden to the tray.
The tray is split behind ITrayController:
- Windows (
WindowsTrayController) — a Win32Shell_NotifyIconwith a fully Avalonia-styled context menu and a rich hover card. - Linux (
LinuxTrayController) — Avalonia's cross-platformTrayIcon+NativeMenu. The StatusNotifierItem model (GNOME/KDE/waybar, over DBus) gives no per-icon hover/move events and lets the desktop shell own the menu, so the hover card and hand-styled menu are Windows-only; Linux gets the native menu (Open / Refresh all / vendor switch / Quit). The "ai" glyph is rendered with Skia (TrayGlyphRenderer), so noSystem.Drawingis needed there.
Native AOT compilation was a design constraint, not an afterthought — the code is written to compile cleanly with the ILC linker and run without a JIT or runtime reflection. Concretely:
- AOT enabled end to end. The app head sets
PublishAot=true;AiUsageHud.CoreandAiUsageHud.PresentationsetIsAotCompatible=true, which turns on the trim/AOT analyzers so any reflection or trim hazard surfaces as a build warning. - Reflection-free JSON. Every DTO is registered in a
System.Text.Jsonsource- generation context (Core/Json/AppJsonContext.cs) — serialization metadata is emitted at compile time, so there is noReflection.Emiton the wire paths. - Compiled XAML bindings.
AvaloniaUseCompiledBindingsByDefault=truemakes the UI bindings compile-time typed, avoiding the reflection-based binding fallback. - Explicit trimmer roots. The TOML parser (
Tomlyn) is used only through its document model, so it is kept viaTrimmerRootAssemblyto guarantee the linker never strips a path that is reached at runtime. - Publish settings in the project.
TrimMode=full,InvariantGlobalizationand the trimmed feature switches (DebuggerSupport/EventSourceSupport/StackTraceSupport) live in the app.csprojunder a RID-guarded property group — so they apply only to-r-targeted publishes, never todotnet run/ Debug or CI'sdotnet build.
A self-contained, AOT-compiled executable (no .NET runtime required on the target)
comes out of a bare publish — the native render libraries (libSkiaSharp,
libHarfBuzzSharp, av_libglesv2) sit beside it in the publish folder:
dotnet publish -c Release -r win-x64 # on Windows
dotnet publish -c Release -r linux-x64 # on LinuxNative AOT does not cross-compile between operating systems — each target must be published on that OS. On Windows the native link step needs the MSVC toolchain + Windows SDK, so run it from a Developer PowerShell for VS (or any shell where
vswhere/link.exeare onPATH). The Linux publish needsclangandzlib(e.g.apt install clang zlib1g-dev).
- Tray icon + window instead of the Waybar widget and terminal TUI.
- No Omarchy theme integration.
- The default theme is HUD (the app's own theme), and it ships 59 built-in palettes.
- No
SIGRTMINsignaling; the app auto-refreshes on a 60-secondDispatcherTimer. - File locking uses
FileShare.Noneretries instead offlock. - Config and cache live in per-user profile directories (Windows
%APPDATA%/%LOCALAPPDATA%, Linux XDG~/.config/~/.local/share).

