Mac extension (panel/pill/nub) to show LLM usage A new Mac utility called the LLM usage meter clips onto the screen edge to display real-time rate-limit usage for Claude Code and Codex, reading local CLI files without network calls or API keys. The tool shows a 5-hour ring, weekly meter, token counts, estimated spend, and reset countdowns, with optional Anthropic account checks for accurate Claude limits. An LLM usage meter that clips onto the edge of your Mac's screen. It shows how much of your Claude Code and Codex rate-limit windows you have burned, expands into a full panel on hover, and shrinks to a sliver "work mode" when you want it gone. | state | top edge | side edge | |---|---|---| | resting | || | work mode | Hovering either one opens the same panel: a 5-hour ring, a weekly meter, tokens, estimated spend and a reset countdown per provider. Everything below is in the menu-bar gauge icon, or in the right-click menu on the pill itself. Display — automatic prefers the notched built-in screen , or any connected display by name. Pick your external monitor here. Attach to — Top notch , Left edge or Right edge . Side-mounted, the pill turns into a slim vertical bar hugging the screen edge, and the panel opens inward. Position — only meaningful on the top edge: right of the notch default , left of the notch, or centred under it. On a display without a notch these read "right/left of center" and hang below the middle of the menu bar. Drag it. Press and drag the pill to slide it along its edge; drag it into another edge's band within 90pt and it re-attaches there, landing under the cursor. Position ▸ Reset to default spot undoes any nudging. The pill floats above the menu bar, but only over the pixels it actually draws: the window is deliberately larger than the pill so the panel can animate without the window ever resizing and stays mouse-transparent, opening up only while the cursor is over the pill itself. A window swallows every click inside its frame no matter what its views' hit tests return, so this is ignoresMouseEvents toggled from an event monitor — nothing else can hand a click to another application. Everything is read from files the two CLIs already write on this Mac. No API keys, no network calls, nothing leaves the machine unless you opt into the Anthropic account check below . Codex — reported, not guessed. Every token count event in ~/.codex/sessions/ /rollout- .jsonl carries the server's own answer: php payload.rate limits.primary used percent, window minutes 300 - 5-hour ring payload.rate limits.secondary used percent, window minutes 10080 - weekly meter The newest rollouts are tailed and the most recent event wins, so the ring matches what Codex itself would tell you as of your last Codex turn . Nothing writes to those files when you use the ChatGPT app, Codex on another machine, or the web, so the reading can trail your real usage until Codex next makes a request. Once a reading is more than five minutes old the panel says so "as of 14:32 · 20m old" , and opening the panel forces a re-read. Claude Code — estimated. ~/.claude/projects/ / .jsonl records per-message usage input, output, cache creation, cache read but no plan utilisation. The provider replays those records, de-duplicates by message + request id, and buckets them into rolling 5-hour blocks anchored to the top of the hour — the same shape as Anthropic's session window. The ring compares the live block against a ceiling: auto default : your busiest 5-hour block on record fixed : a number you set, in dollars or tokens Rows measured this way carry an est badge. Spend is priced from the published per-model list prices in Pricing.swift ; treat it as an estimate, not a bill. Optional: real Claude limits. Sources ▸ Use Claude account limits reads the Claude Code OAuth token from your login keychain and asks Anthropic for actual 5-hour and weekly utilisation. It is off by default. Enabling it explains itself first, then does one foreground read so the keychain prompt is visible; if access is denied it switches itself back off. After that the check runs on its own queue and never blocks the pill — the local estimate stays on screen if it fails. The transcript folder can run to hundreds of megabytes. Lines are filtered on raw bytes before any JSON is decoded, and the extracted usage plus per-file read offsets are cached in ~/Library/Application Support/UsageNotch/claude-cache.json . First run after install parses everything ~1s per 200MB ; later launches restore from the cache and refresh in single-digit milliseconds. Providers publish independently, so a slow one never holds up the others, and one that stops answering is parked rather than freezing the panel. Xcode is not required — SwiftPM plus a hand-assembled bundle is enough. ./run.sh That builds build/UsageNotch.app , replaces any running copy, and launches it. ./build.sh builds without launching. The app is an accessory LSUIElement , so it has no Dock icon; the menu-bar gauge icon carries the menu. Requires macOS 14+ and a Swift 5.9+ toolchain Command Line Tools are fine . ./build/UsageNotch.app/Contents/MacOS/UsageNotch --dump what the providers see ./build/UsageNotch.app/Contents/MacOS/UsageNotch --placement where the pill would land, per display ./build/UsageNotch.app/Contents/MacOS/UsageNotch --render ./docs re-render the screenshots USAGENOTCH DEBUG=1 ./build/UsageNotch.app/Contents/MacOS/UsageNotch --render snapshots the SwiftUI tree offscreen, which is also how the UI gets checked when Screen Recording permission is unavailable. USAGENOTCH DEBUG=1 traces placement, hit regions, mode changes and provider timings on stderr. hover — opens the panel and re-reads the sources; leaving collapses it after a short grace period click — refresh now, with the spin on the refresh glyph drag — slide the pill along its edge, or throw it at another edge to re-attach work mode — the chip in the panel or the menu collapses the pill to a nub; hovering the nub still peeks the full panel right-click — the full menu, same as the menu-bar icon main.swift entry point, --dump / --placement / --render modes UI/NotchController panel + status item + refresh loop + menu + click routing UI/NotchPanel borderless non-activating panel above the menu bar UI/NotchGeometry notch metrics and per-display, per-edge placement UI/Placement edge + anchor - alignment, content rect, corner radii UI/NotchState mini / pill / expanded, hover debounce, motion curves UI/Interaction hover tracking and rect reporting for AppKit hit routing UI/NotchRootView SwiftUI tree for the three states Model/UsageStore provider fan-out, deadlines, published snapshot Providers/ Claude Code, Codex, optional Anthropic account, cache, pricing Two decisions are load-bearing: The window never resizes. It is sized once for the largest state; the pill morphs inside it. Resizing a window per hover is what makes this kind of UI stutter. Clicks are routed in AppKit, not SwiftUI. The panel never becomes key, and SwiftUI gesture recognisers do not fire in a non-key panel. The hosting view dispatches presses itself: a press that travels more than 4pt is a reposition, anything shorter is a click, matched against the rects the controls report. - Claude percentages are an estimate until you enable the account check. - Codex numbers are as fresh as your last Codex turn; the panel labels a reading older than five minutes. Usage from the ChatGPT app is not visible until Codex itself makes another request. - Only Claude Code and Codex are wired up. Adding a provider means one file conforming to UsageProvider plus a line in UsageStore . - The app is ad-hoc signed, so "Open at login" may need approval in System Settings ▸ General ▸ Login Items. MIT — see LICENSE /abhayKashyap03/usage-notch/blob/main/LICENSE .