Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ version = "0.58"
features = [
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_System_LibraryLoader",
"Win32_UI_Shell",
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