Editorial redesign: bilingual menu-bar app with stats, floating HUD & live quota - #1
Merged
Conversation
…, new panel Pivot the UI away from the cc-bar-style cards/rings to an editorial-minimal language (paper/ink, hairlines, tabular numbers, thin bars, no cards). Core (TDD): - StatusLevel: 4-state status (normal/warning/low/empty) by remaining quota. - HexColor: #RRGGBB parse/format. UI foundations: - DesignSystem: editorial tokens (light+dark), single statusColor source, adaptive Color(amLight:dark:) + Color(amHex:). - Components: Hairline, ServiceSwatch, ThinBar, MetricRow, HeroNumber, SegmentedPair ([5h|wk]). - LanguageStore + tr(en,zh): live manual zh/en switch, no restart. - ServiceColorStore: per-service identity colour (brand/mono presets). Panel rebuild (MenuContentView): - Hero number per service (Claude 5h default, [5h|wk] toggle to weekly; Codex shows context%), aligned mini-rows, today tokens + messages footer. - Status colour drives values/bars; identity colour drives the swatch only. 42 Core tests pass; app builds and launches. Cost ($) deferred to pricing phase. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… panel Core (TDD): - ModelKey: normalize model ids (strip dated -YYYYMMDD and [1m] suffixes, <synthetic>/empty -> unknown), vendor classification. - Pricing: ModelPricing (per-MTok, cache 0.1x/1.25x, reasoning=output), CostEstimate (amountUSD + isComplete), built-in Anthropic list prices, costEstimate(tokens,model) and costEstimate(byModel:). OpenAI/Codex models intentionally unpriced -> isComplete=false (renders "≈$X+"). - ToolUsage gains todayByModel; ClaudeCodeReader buckets today's tokens by normalized model key (parity test: buckets sum to flat today). UI: - Panel footer shows ≈$ from costEstimate(byModel:) when per-model data exists (Claude); hidden for Codex (no per-model attribution / no OpenAI pricing yet). 55 Core tests pass; app builds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…machine Core (TDD): - UsageHistory/DayBucket types + HistoryAccumulator (shared bucketing). - ClaudeCodeReader.history(range:): per-day, per-model buckets, dedup by (id,requestId), range-bounded; independent of read(now:) for safety. - CodexReader.history(range:): turn_context -> token_count model state machine (model attributed per-file, reset per file, .unknown before any turn_context); added payload.model decoding. Tests: Claude day/model bucketing + dedup + range bounds; Codex two-models-in-one-file attribution, no-leading-turn_context -> unknown, per-file state reset. 59 Core tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cker) - SettingsView rebuilt as a localized TabView: General (language 繁中/English, refresh interval, Claude hero metric 5h/weekly, launch-at-login), Appearance (per-service ColorPicker + brand/mono preset swatches, pricing-version note), Menu Bar (metrics multi-select + show toggles), Claude Quota (statusLine bridge). - Migrated hardcoded strings to tr(en,zh): MenuBarMetric titles, refresh-interval labels, all settings strings. Live language switch re-renders via LanguageStore. - ServiceColors: hexString(from: Color) pins sRGB so ColorPicker round-trips don't drift. App builds; bilingual + custom colors now fully operable from Settings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-model) - StatsWindowController: AppKit NSWindow hosting SwiftUI; opens from .accessory via ActivationPolicyCoordinator (ref-counted .regular↔.accessory, resets on app deactivation — replaces the old willResignActive revert). - StatsViewModel: runs ClaudeCodeReader/CodexReader.history(range:) off-main per time range (Today/7d/30d/All), publishes UsageHistory. - StatsRootView (editorial): custom sidebar (service filter + Overview/By-model), range tabs, KPIs (total tokens, ≈total cost, per-service), Swift Charts stacked daily bar chart (per-service identity colors), by-model table with ≈$ per model. - Panel header gains a chart.bar button to open it. App builds and launches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- FloatingPanel: NSPanel (.nonactivating/.borderless, .floating level, all-Spaces, movable-by-background) that snaps to the nearest screen edge on mouse-up. - FloatingHUDView: dual thin-line RingMeters — frame tinted with the identity colour, arc in the status colour, mono % centre; translucent material; dims to the configured idle opacity and brightens on hover. Live from UsageStore.shared. - FloatingPanelController: show/hide driven by the floatingEnabled default; refresh() called at launch to restore, and from the Settings toggle. - RingMeter component gains a configurable track colour. - Stores exposed as shared singletons so the HUD/launch get the live instances. - Settings gains a Floating tab (enable, per-service, idle-opacity slider). App builds and launches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uarantee - NetworkFeature (codexQuota, accurateCost): off by default; localized title + "this needs internet" explanation; isEnabled reads the defaults flag. - Advanced settings tab: each feature toggle asks for confirmation before enabling (alert with the explanation); offline-by-default statement. - CodexQuotaClient / BillingClient: consent-gated skeletons confined to Sources/AgentMeter/Network/. EXPERIMENTAL — return .unavailable rather than fabricate data; real OpenAI/billing endpoints intentionally not wired (unverified). - Scripts/check-offline.sh + offline-check CI workflow: fail if any networking (URLSession/Network/...) appears outside Network/ — keeps the guarantee verifiable. 59 Core tests pass; app builds and launches; offline check passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- R2.1 Cost: shared moneyString — plain "$X.XX", no ≈/+; "—" when a service/total is entirely unpriced (no misleading $0.00). Stats KPI label "≈ 總花費" → "總花費". - R2.2 Launch-at-login: detect unbundled/dev runs (not an installed .app) and disable the toggle with a clear note instead of the raw "Invalid argument" error. - R2.3 Merge Stats + Settings into one window with cc-bar-style top tabs (用量統計 | 設定): new MainWindowRootView + MainWindowModel; both the chart and gear buttons open it to the right tab; removed the separate SwiftUI Settings scene. App builds and launches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… endpoints)
Core (TDD, file-only — never the Keychain):
- Credentials.swift: JWT.payload (base64url), ClaudeCredentialParser
(.credentials.json + .claude.json email fallback), CodexCredentialParser
(auth.json + id_token JWT for email/plan), ServiceAccount, CredentialReader.
+6 tests (65 total).
Network (Network/, opt-in, gated):
- CodexQuotaClient: GET chatgpt.com/backend-api/wham/usage (Bearer + optional
ChatGPT-Account-Id), defensive window parsing, .unavailable on mismatch.
- CodexTokenRefresher: auth.openai.com/oauth/token refresh + write-back to auth.json.
- NetworkFeature: codexQuota (network) + showAccounts (reads credential files,
not network); dropped speculative accurateCost + BillingClient.
Wiring:
- UsageStore: codexFiveHour/codexWeekly + claudeAccount/codexAccount, populated
on refresh when the matching opt-in is on.
- Panel: service headers show plan (e.g. "Codex · Plus"); Codex hero uses real
5h when present, else context% + an "enable live quota" link.
- Settings Advanced: showAccounts ("reads credentials") + codexQuota ("needs
internet") toggles with confirmation; refresh on toggle.
Privacy: README updated to "offline by default + opt-in"; "never reads Keychain"
still holds (files only). offline check still green; 65 tests pass; app launches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ining/used - R3.8 Token counts now use total (incl cache reads) to match cc-bar; the ~36x gap was billable-vs-total. Cost unchanged (already weights cache reads). - R3.1 One window, one tab bar: 用量統計 is now the first tab alongside the settings panes (RootTabView + AppTab); dropped the separate pill switcher. - R3.5/R3.7 Accounts section in General: Claude/Codex email · plan · connected, so enabled accounts actually show. - R3.6 Default ON: codexQuota + showAccounts default enabled (NetworkFeature treats absent key as true) for accurate quota/accounts; README updated (still never reads Keychain; off switch in Advanced). - R3.4 Removed the offline-explainer captions in Advanced. - R3.3 Menu bar renders each metric as a stacked label/value (Stats-app style). - R3.2 New "Meters show used/remaining" setting, applied to panel + floating HUD. 65 tests pass; offline check green; app builds and launches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eychain The real macOS setup keeps the Claude token in the Keychain (which we never read), so ~/.claude/.credentials.json is absent and the account showed as "not detected". ~/.claude.json still has oauthAccount (email + organizationType), so: - ClaudeCredentialParser.account(fromClaudeConfigJSON:) derives email + plan (organizationType "claude_team" → "team"). - CredentialReader.claude() no longer requires .credentials.json — it returns an account from ~/.claude.json alone (token optional; Claude quota uses the bridge). Still file-only, never the Keychain. 66 Core tests pass; offline check green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Menu bar is ~22pt tall; 8/10pt stacked lines clipped the value. Shrink to 7/9pt with -1.5 spacing + fixedSize so both lines fit. - MenuBarMetric.parts now honors meterShowsRemaining (5h/weekly/context show remaining when set); MenuBarLabel re-renders on the setting via @AppStorage. App builds and launches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MenuBarExtra clips multi-line SwiftUI labels to the bar height, so the value line never showed. Render each metric as a 2-line (label/value) template NSImage at natural size; the menu bar scales it to fit and tints it for light/dark. App builds and launches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Menu bar - Draw each service's logo before its metrics, as a monochrome template glyph (new MenuBarIconPath.swift: embedded Claude/Codex SVG paths + a minimal absolute-command path parser → NSBezierPath). Adapts to light/dark like the text. - Add vertical (stacked) / horizontal (inline) layout option, and a "show agent icon" toggle. Horizontal uses one uniform font size with a shared baseline; logo sized to match the text height. - Add Codex 5h / weekly as menu-bar metrics (data already fetched via the Codex quota endpoint). - Drop the CC/CX text prefix — the per-service logo identifies the tool; only the combined total keeps a "Σ" marker. Settings - Consolidate connectivity onto General: a single "Live quota & connectivity" section with show-accounts, Claude live quota (statusLine bridge, local) and Codex live quota (needs internet). Remove the now redundant Advanced and Claude-Quota tabs. - Split menu-bar metrics into Claude / Codex / Combined sections. - Remove the Claude hero-metric picker (the panel's 5h|週 toggle owns it) and several explanatory captions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
A full redesign of AgentMeter into a polished, deliberately distinct editorial-minimal menu-bar app (paper/ink, hairlines, tabular numbers) — not a cc-bar look-alike. 14 commits across Phases 0–7 and Rounds 2–4.
Highlights
tr()store (no resource bundle, no restart).5h|週hero toggle.ModelIdentity+Pricing); plain$X.XX,—when unpriced.wham/usageendpoint.Menu bar (Round 4)
Privacy
Sources/AgentMeter/Network/, enforced byScripts/check-offline.sh+ a CI workflow.Verification
./Scripts/check-offline.shpasses — no networking outsideNetwork/.