Repository navigation
Conversation
Menu bar app for Anthropic API credit monitoring: OAuth token from Keychain (with manual override), auto-refresh, 3-section popover, Settings (token/interval/launch-at-login), Host Grotesk + status colors. Adds CI test workflow and a release workflow that builds a signed .dmg, publishes a GitHub Release, and bumps the Homebrew cask. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
.accessory apps never become frontmost automatically, so openWindow() could create the Settings window without focus (hidden behind other apps). Also documents the Keychain "Always Allow" prompt and why dev builds via swift run re-prompt after every rebuild. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
Root-caused two issues via console evidence and Apple Developer Forums (thread 726565, confirms font()/imageScale() are ignored on MenuBarExtra's label): NSStatusItem.button strips custom Shape fills and font modifiers from the label view. Workaround: pre-render MenuBarProgressLabel to an NSImage via ImageRenderer (isTemplate = false to keep the colors) instead of passing the SwiftUI view directly. Also fixes the "Unable to update Font Descriptor's weight" CoreText warning: chaining .fontWeight() on a custom static font is unreliable (confirmed via font metadata inspection - SemiBold isn't even grouped under the "Host Grotesk" legacy family). Each weight now references its own .ttf by exact PostScript name instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
Primary fix is `claude login` (picked up automatically). Documents the manual-token fallback in Settings for when that's not an option, including how to extract the current token from Keychain. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
Color.gray tint for the title/bottom sections didn't darken in dark mode, leaving white button text on a still-light background (illegible). Color.primary.opacity() adapts automatically (black in light mode, white in dark mode) - same pattern already used successfully in MenuBarProgressLabel's track. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
ProgressView's tint wasn't reliably applying in the popover (macOS native progress indicator limitation). Also .tint() only takes a flat color, so it couldn't express the requested green->orange->red gradient anyway. New UsageProgressBar draws the gradient across the full bar width then crops to the filled portion (rather than scaling it), keeping the exact threshold hex colors (48655D/F09E7F/D61B66) as gradient stops at 0%/50%/80%. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
release.yml now prefers .github/release-notes/vX.Y.Z.md when present (curated changelog) and falls back to --generate-notes otherwise. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
Self-hosted runner logs (Swift build/test output, absolute paths) were leaking the real macOS username/home path. ::add-mask:: redacts both wherever they appear in subsequent log output. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
Avoids using the Claude trademark in an unofficial app name, and doubles as a Night's Watch nod. Renames the Swift package/target/app struct, bundle identifier (com.lixeas.notchwatch), Keychain/UserDefaults storage keys, the Homebrew cask (notch-watch), and all docs/workflows. No functional changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133Kw22dMTxFkGLc8jCStQi
- VoiceOver labels on the menu bar pill and refresh button (P1) - Adaptive light/dark error color, fixes AA contrast of magenta in dark mode (P1) - Debounce Keychain writes for the manual token in Settings (P2) - Distinguish 401/403 in API errors (invalid/expired token) (P2) - Move pill rendering into UsageTracker, recomputed only when the value changes (P3) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
fix: address impeccable audit P1/P2/P3 findings
- Numeric values (used/limit, 5h, 7d) roll via .contentTransition(.numericText()) instead of snapping on refresh - UsageProgressBar fill eases to its new width instead of jumping - Error/disabled/normal states in the dropdown crossfade instead of hard-cutting - Refresh button crossfades between icon and spinner - All state-change motion respects accessibilityReduceMotion; the refresh icon/spinner crossfade stays (opacity-only, confirms the action) per HIG guidance to keep essential feedback under reduced motion - Extracted UsageDetailSection so the reduce-motion environment value is readable (Scene bodies can't read it reliably) Menu bar pill stays a static image; MenuBarExtra's label ignores live view updates outside the pre-rendered NSImage (existing AppKit constraint), so it is out of scope for this pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
feat: animate usage values like a cockpit instrument
- New Localization.swift: AppLanguage enum, LanguageManager (UserDefaults-backed,
defaults to English), Strings namespace with EN/FR pairs for every user-facing
string
- Segmented picker in Settings switches language app-wide (menu bar dropdown,
Settings labels, error messages, pill VoiceOver label)
- APIService/UsageTracker error messages now localized via LanguageManager.shared
(they aren't Views, so read the singleton directly instead of an environment
value)
Interval picker labels ("30 s", "1 min"...) stay shared between languages;
numeric + unit reads the same in both, not worth a second table.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
feat: add EN/FR language toggle in Settings
- Collapsed pill now shows "42% • $8.40" instead of "$15": percentage first, amount with cents, dot separator - Removed the proportional fill bar; the percentage number now carries the status color itself (green/orange/red), dot and amount stay neutral (Color.primary) - Pill keeps its neutral capsule background but is no longer fixed-width (content is longer and variable now) - Added greenText/orangeText alongside the existing redText: each status color needs a different light/dark variant to stay AA-contrast as bare text instead of white-on-fill Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
feat: replace menu bar fill bar with colored percentage text
Anthropic's usage API does not return real billing-period dates (extra_usage has only is_enabled/monthly_limit/used_credits/currency, no period_start/period_end) -- confirmed by searching public API discussions, no such field exists to read. This shows the current calendar month as a proxy instead, explicitly labeled "(estimate)" / "(estim.)" in both languages so it's never mistaken for Anthropic's actual cycle date. Recomputed on every render rather than cached, so a month rollover is picked up for free without a dedicated refresh path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
feat: show an estimated billing cycle range below the usage bar
- Label back to plain "Billing cycle:" / "Cycle facturation :" - Date range now day+month only (DateIntervalFormatter dateTemplate "MMMd" instead of dateStyle .medium, which included the year) The month-boundary approximation itself is unchanged (still no real period_start/period_end from the API), just no longer called out in the UI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
fix: drop year and estimate label from billing cycle
DateIntervalFormatter.string(from:to:) returns non-optional String, unlike DateFormatter.string(from:) -- the ?? "" was always dead code (compiler warning caught in a real Xcode build). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
Window("Reglages", id: "settings") was a hardcoded literal, never routed
through Strings when the EN/FR toggle was added -- title stayed "Reglages"
regardless of the selected language.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
fix: remove dead nil-coalescing on DateIntervalFormatter.string
fix: localize the Settings window title
- Package.swift bundles Resources/Images (mirrors the existing Fonts resource), loaded via Bundle.module same as AppFonts - BrandMark: loads the light/dark SVG (NSImage supports SVG natively on macOS 12+) matching the current colorScheme, cached per appearance; extracted as its own View since colorScheme isn't reliably readable from the App/Scene body - Dropped .foregroundColor(.secondary): the SVGs are already colored images, not a template glyph - README header now shows the 16-plate logo variant (has its own background, meant standalone) Not done: no .icns/.dmg icon pipeline in release.yml yet (there wasn't one before this change either) -- the app and .dmg still ship with no custom icon. Separate follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
feat: wire the custom brand mark, replace the cpu SF Symbol
- Logo + name side by side, centered heading - Test badge centered; added Release workflow badge and a live release version badge (shields.io) - Description, Features, Installation, Usage, Requirements and Development sections rewritten to match the current app (colored-text pill instead of a fill bar, estimated billing cycle, EN/FR toggle) - Features intentionally omits the 5h/7d window display (per request) -- the feature itself is untouched, just not called out in the README - LICENSE was referenced by name only (no file); added the actual MIT text Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vag2FZvTekVg34kUeb791Q
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LICENSEfile (was referenced by name only before, no file existed)Test plan
🤖 Generated with Claude Code