Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a8be4f8
feat: display enterprise account credit and spend usage rows
oyardena Jun 20, 2026
9bc92c8
fix: embedded-mode drag and taskbar-recovery improvements
oyardena Jun 29, 2026
5f0f1bb
fix: self-recover stuck usage widget after auth failures
oyardena Jul 14, 2026
eacd4eb
fix: stabilize taskbar widget visibility and usage refresh on Windows
oyardena Jul 21, 2026
9100128
fix: place leftmost widget at taskbar physical left edge
oyardena Jul 21, 2026
cde7ef0
fix: widen taskbar text column and hide widget over fullscreen apps
oyardena Aug 17, 2026
23675c7
fix: cache poll results and skip redundant repaints
oyardena Aug 26, 2026
7c4056e
fix: widget vanishing during peek, off-monitor fullscreen suppress, a…
oyardena Sep 7, 2026
cb740c1
fix: deadlock in sync_fullscreen_visibility and position_at_taskbar
oyardena Sep 7, 2026
855536b
fix: react to foreground-window changes immediately instead of on a 5…
oyardena Sep 10, 2026
af571ff
Fix widget briefly disappearing after unlock (false fullscreen-suppress)
oyardena Sep 12, 2026
e79e3db
Fix widget jumping off-screen: EnumWindows misses live Shell_TrayWnd
oyardena Sep 12, 2026
6fbcdbb
Fix self-inflicted z-order flicker on every widget repaint
oyardena Sep 12, 2026
d7aba6b
Simplify position_above_taskbar to always use single-call z-order
oyardena Sep 12, 2026
2f3f3bb
Revert d7aba6b: it hid the widget entirely behind the real taskbar
oyardena Sep 13, 2026
49f5122
Exclude XamlWindow class from fullscreen-suppress false positives
oyardena Sep 13, 2026
e288e1f
Log foreground window class name on every foreground change
oyardena Sep 13, 2026
419b118
Fix silent no-op in the widget-invisible recovery path
oyardena Sep 13, 2026
f68180a
Exclude LockScreenControllerProxyWindow from fullscreen-suppress
oyardena Sep 13, 2026
ac23ccb
Arm a settle-repaint timer specifically for Start menu opening
oyardena Sep 13, 2026
8c4293f
Fix monitor-selection heuristic and a debounce gap in the Start-menu fix
oyardena Sep 13, 2026
33c4b11
Log UpdateLayeredWindow failures; document unresolved Start-menu bug
oyardena Sep 14, 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
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[target.x86_64-pc-windows-gnu]
linker = "C:/msys64/mingw64/bin/gcc.exe"
ar = "C:/msys64/mingw64/bin/ar.exe"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ AGENTS.md

# Local WinGet manifest generation output
/manifests/

# Cursor IDE agent rules - hard-linked, not repo content
.cursor/
24 changes: 24 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Screen capture

Capture the screen yourself: PowerShell + Win32 P/Invoke — `user32.dll PrintWindow` (by window class name) or `Graphics.CopyFromScreen` (screen region). Examples: `.cr-tmp/HANDOFF.md`.

## Widget default position: x=0, no exceptions

The widget's default/leftmost taskbar position is **screen x=0** — the taskbar's own
physical left edge. Literally zero. Not "clear of the Start button," not "left of the
search box," not "the left edge of the icon cluster." x=0.

This has been re-litigated with more than one agent session already. Every time, an
agent reasoned its way into a *smaller* definition of "leftmost" — usually "as far
left as possible without overlapping existing taskbar chrome/icons" — and presented
that as if it satisfied the requirement. It does not. The owner has explicitly and
repeatedly rejected that substitution. Do not re-derive it, do not re-ask what
"leftmost" means, do not propose it as an "honest tradeoff." If x=0 seems to conflict
with something else (Start button overlap, icon cluster, etc.), that is a bug in the
positioning code to fix (see `native_interop::taskbar_placement_band_left`, which is
hardcoded to return 0 for exactly this reason) — it is never a reason to move the
widget off x=0.

If a genuinely new constraint makes x=0 impossible on some configuration, stop and
ask the owner directly with the specific conflict — do not silently pick a different
default.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ version = "0.58"
features = [
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_System_LibraryLoader",
"Win32_System_RemoteDesktop",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
"Win32_UI_Accessibility",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ What it does **not** do:

Notes:

- If your Claude Code token is expired, the app may ask the local Claude CLI to refresh it in the background
- If your Claude Code token is expired, the app refreshes OAuth directly against `platform.claude.com/v1/oauth/token` and updates `~/.claude/.credentials.json` (no Claude CLI session / no model spend on Windows)
- If your Codex token is expired, the app may ask the local Codex CLI to refresh it in the background. The monitor does not write `auth.json` itself; any credential update is handled by the Codex CLI.
- If your Antigravity token is expired, open Antigravity and sign in again. The monitor does not write Windows Credential Manager entries itself.
- Portable installs can update themselves by downloading the latest release from this repository
Expand Down
2 changes: 1 addition & 1 deletion src/localization/english.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub(super) const STRINGS: Strings = Strings {
hour_suffix: "h",
minute_suffix: "m",
token_expired_title: "Claude Code Auth Error",
token_expired_body: "Run 'claude' in a terminal, then use '/login' and follow the prompts. After that, refresh or restart this app.",
token_expired_body: "Run 'claude auth login' in a terminal and complete sign-in. After that, refresh or restart this app.",
codex_token_expired_title: "Codex Auth Error",
codex_token_expired_body: "Run 'codex' in a terminal and follow the sign-in prompts. After that, refresh or restart this app.",
antigravity_token_expired_title: "Antigravity Auth Error",
Expand Down
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![windows_subsystem = "windows"]

mod diagnose;
mod proof_capture;
mod localization;
mod models;
mod native_interop;
mod poller;
mod spend_pace;
mod theme;
mod tray_icon;
mod updater;
Expand All @@ -23,7 +25,9 @@ fn main() {
}
}

if let Some(exit_code) = updater::handle_cli_mode(&args) {
if let Some(exit_code) = proof_capture::handle_cli(&args)
.or_else(|| updater::handle_cli_mode(&args))
{
if diagnose_enabled {
diagnose::log(format!("cli mode exited with code {exit_code}"));
}
Expand Down
40 changes: 37 additions & 3 deletions src/models.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,54 @@
use std::time::SystemTime;

#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct UsageSection {
pub percentage: f64,
pub resets_at: Option<SystemTime>,
pub has_bucket: bool,
}

#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct UsageData {
pub session: UsageSection,
pub weekly: UsageSection,
}

#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AccountUsage {
pub credit_pct: f64,
pub credit_expiry: Option<SystemTime>,
pub spend_used: f64,
pub spend_limit: f64,
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct SpendPaceSlots {
pub month_actual: f64,
pub month_cap: f64,
pub month_expected: f64,
pub month_level: u8,
pub week_actual: f64,
pub week_cap: f64,
pub week_expected: f64,
pub week_level: u8,
pub day_actual: f64,
pub day_cap: f64,
pub day_expected: f64,
pub day_level: u8,
}

#[derive(Clone, Debug, PartialEq)]
pub struct SpendPaceView {
pub credit_pct: f64,
pub credit_expiry: Option<SystemTime>,
pub slots: SpendPaceSlots,
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct AppUsageData {
pub claude_code: Option<UsageData>,
pub codex: Option<UsageData>,
pub antigravity: Option<UsageData>,
pub account: Option<AccountUsage>,
pub spend_pace: Option<SpendPaceView>,
}
Loading