Skip to content

Commit f95ecca

Browse files
author
LeoLin
committed
Merge remote-tracking branch 'upstream/main' into pr2524-work
# Conflicts: # CHANGELOG.md
2 parents 3544fe7 + 0c5255c commit f95ecca

92 files changed

Lines changed: 3356 additions & 228 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@
44

55
### Added
66
- z.ai: add 7-day and 30-day model-usage chart ranges with dataset-consistent legends, colors, and daily tooltips (#2524). Thanks @LeoLin990405!
7+
- Refresh: add a default-off global Low Power Mode that limits automatic provider, local usage, and storage work to once every 30 minutes while keeping manual refresh immediate (#2518). Thanks @Carl723000!
8+
- CLI: `codexbar hooks watch` continuously polls providers and fires hooks on real quota/status transitions for headless installs, with in-memory baselines, event rate limits, `--interval` (default 300s, minimum 60s), `--provider`, and JSON output (#2536). Thanks @OfficialAbhinavSingh!
79
- Claude: compact multi-account menu for claude-swap — with four or more accounts the active account keeps its full card while the others become one-line rows sorted by remaining headroom, constrained accounts surface in red/amber, the healthiest switch target gets a star, and the healthy tail folds behind a summary row. Click a row to expand its full card.
810
- Menu: the compact multi-account layout now covers every stacked multi-account list — token accounts on any provider and Codex accounts (flat lists; workspace-grouped Codex lists keep their sections).
11+
- Menu bar: Session/Weekly/Auto pace layout tokens that render the signed pace delta (`+11%`, `-8%`, `0%`), restoring the pre-0.45 "Both" display in the layout editor (#2540, fixes #2534). Thanks @kratocz!
912

1013
### Fixed
14+
- Doubao: show Agent Plan windows alongside Coding Plan usage for Volcengine AK/SK accounts that subscribe to both products (#2517). Thanks @Astro-Han!
15+
- Augment: store session cookies owner-only (0600), atomically publish updates, and repair permissions on legacy files (#2567).
16+
- Ollama: direct declined Chrome Keychain access recovery to the provider card's Refresh (⌘R) action instead of the ambiguous manual-cookie path (#2072).
17+
- Menu: switching from a tall Overview to a shorter provider now shrinks the menu instead of stretching the provider-height spacer into a large blank region.
1118
- Codex: persist and budget fork-parent discovery so missing parents quiesce between inventory changes instead of sweeping every rollout on each refresh (#2525, #2538). Thanks @xx205, and @Helmi and @kiranmagic7 for the investigation!
1219
- Claude: Auto cold boot with Keychain disabled loads without manual refresh (#2494, fixes #2493). Thanks @gmkbenjamin!
1320
- Menu: no more stray floating "Refresh" tooltip beside the menu when switching tabs with the cursor over the actions area.

Package.resolved

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ let useLocalSweetCookieKit =
88
let sweetCookieKitDependency: Package.Dependency =
99
useLocalSweetCookieKit && FileManager.default.fileExists(atPath: sweetCookieKitPath)
1010
? .package(path: sweetCookieKitPath)
11-
: .package(url: "https://github.com/steipete/SweetCookieKit", from: "0.4.1")
11+
: .package(url: "https://github.com/steipete/SweetCookieKit", from: "0.5.1")
1212

1313
let sqlite3LibDir = ProcessInfo.processInfo.environment["CODEXBAR_SQLITE3_LIB_DIR"]?
1414
.trimmingCharacters(in: .whitespacesAndNewlines)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Foundation
2+
3+
enum BackgroundWorkPowerPolicy {
4+
static let lowPowerMinimumInterval: TimeInterval = 30 * 60
5+
6+
static func automaticInterval(
7+
_ requested: TimeInterval?,
8+
lowPowerModeEnabled: Bool) -> TimeInterval?
9+
{
10+
guard let requested else { return nil }
11+
guard lowPowerModeEnabled else { return requested }
12+
return max(requested, self.lowPowerMinimumInterval)
13+
}
14+
}

Sources/CodexBar/MenuBarLayout.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ enum MenuBarLayoutToken: Codable, Hashable, Sendable {
1212
case providerName
1313
case accountLabel
1414
case percent(window: PercentWindow)
15+
/// Signed pace delta for a window, e.g. `+11%` when usage runs ahead of the sustainable rate.
16+
/// `runsOut` answers "when does this end"; this token answers "how far off the even rate am I".
17+
case pace(window: PercentWindow)
1518
case usageBar
1619
case resetCountdown
1720
case resetAbsolute

Sources/CodexBar/MenuBarLayoutEditor.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ struct MenuBarLayoutEditor: View {
246246
.percent(window: .weekly),
247247
.percent(window: .automatic),
248248
.usageBar,
249+
.pace(window: .session),
250+
.pace(window: .weekly),
251+
.pace(window: .automatic),
249252
],
250253
includesLineBreak: false),
251254
MenuBarLayoutPaletteGroup(
@@ -686,6 +689,9 @@ private struct MenuBarLayoutPreview: View {
686689
session: MenuBarLayoutRenderWindow(session),
687690
weekly: MenuBarLayoutRenderWindow(weekly),
688691
automatic: MenuBarLayoutRenderWindow(automatic),
692+
sessionPace: self.store.menuBarLayoutPaceText(provider: provider, window: session, now: now),
693+
weeklyPace: self.store.menuBarLayoutPaceText(provider: provider, window: weekly, now: now),
694+
automaticPace: self.store.menuBarLayoutPaceText(provider: provider, window: automatic, now: now),
689695
runsOut: runsOut,
690696
costToday: costToday.map {
691697
UsageFormatter.currencyString($0, currencyCode: cost?.currencyCode ?? "USD")
@@ -707,13 +713,21 @@ private struct MenuBarLayoutPreview: View {
707713
windowMinutes: 10080,
708714
resetsAt: now.addingTimeInterval(3 * 24 * 60 * 60),
709715
resetDescription: nil)
716+
// Sample pace comes straight from the pure calculation rather than the store, so the palette
717+
// preview stays deterministic before any snapshot has been fetched.
718+
let samplePace = { (window: RateWindow) -> String? in
719+
MenuBarDisplayText.paceText(pace: UsagePace.weekly(window: window, now: now))
720+
}
710721
return MenuBarLayoutRenderData(
711722
iconKey: "\(provider.rawValue)-representative",
712723
providerName: L(self.store.metadata(for: provider).displayName),
713724
accountLabel: self.settings.hidePersonalInfo ? nil : L("menu_bar_layout_sample_account"),
714725
session: MenuBarLayoutRenderWindow(session),
715726
weekly: MenuBarLayoutRenderWindow(weekly),
716727
automatic: MenuBarLayoutRenderWindow(session),
728+
sessionPace: samplePace(session),
729+
weeklyPace: samplePace(weekly),
730+
automaticPace: samplePace(session),
717731
runsOut: L("menu_bar_layout_sample_runs_out"),
718732
costToday: "$1.25",
719733
cost30d: "$20.00")
@@ -778,6 +792,9 @@ extension MenuBarLayoutToken {
778792
case .percent(window: .session): L("menu_bar_layout_token_session")
779793
case .percent(window: .weekly): L("menu_bar_layout_token_weekly")
780794
case .percent(window: .automatic): L("menu_bar_layout_token_auto")
795+
case .pace(window: .session): L("menu_bar_layout_token_session_pace")
796+
case .pace(window: .weekly): L("menu_bar_layout_token_weekly_pace")
797+
case .pace(window: .automatic): L("menu_bar_layout_token_auto_pace")
781798
case .usageBar: L("menu_bar_layout_token_bar")
782799
case .resetCountdown: L("menu_bar_layout_token_resets_in")
783800
case .resetAbsolute: L("menu_bar_layout_token_reset_at")
@@ -802,6 +819,7 @@ extension MenuBarLayoutToken {
802819
case .providerName: "textformat"
803820
case .accountLabel: "person.crop.circle"
804821
case .percent: "percent"
822+
case .pace: "speedometer"
805823
case .usageBar: "chart.bar.fill"
806824
case .resetCountdown: "timer"
807825
case .resetAbsolute: "clock"

Sources/CodexBar/MenuBarLayoutRenderer.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ struct MenuBarLayoutRenderData: Hashable {
2828
let session: MenuBarLayoutRenderWindow?
2929
let weekly: MenuBarLayoutRenderWindow?
3030
let automatic: MenuBarLayoutRenderWindow?
31+
/// Signed pace deltas per window, already formatted (`+11%`, `-8%`, `0%`). Pace needs the store's
32+
/// historical dataset and work-day setting, so it is resolved upstream like `runsOut` rather than
33+
/// derived from the render windows here.
34+
let sessionPace: String?
35+
let weeklyPace: String?
36+
let automaticPace: String?
3137
let runsOut: String?
3238
let costToday: String?
3339
let cost30d: String?
@@ -249,6 +255,12 @@ final class MenuBarLayoutRenderer {
249255
? L("%@ unavailable", accessibilityPrefix)
250256
: L("%@ %@", accessibilityPrefix, value)
251257
return self.textToken(display, accessibilityText: accessibility, attributes: style.attributes)
258+
case let .pace(window):
259+
return self.optionalTextToken(
260+
Self.pace(window, data: data),
261+
unavailableLabel: L("%@ unavailable", Self.paceAccessibilityPrefix(window)),
262+
accessibilityPrefix: Self.paceAccessibilityPrefix(window),
263+
attributes: style.attributes)
252264
case .usageBar:
253265
guard let window = data.automatic else {
254266
return self.textToken(
@@ -361,6 +373,26 @@ final class MenuBarLayoutRenderer {
361373
}
362374
}
363375

376+
private static func pace(
377+
_ percentWindow: PercentWindow,
378+
data: MenuBarLayoutRenderData)
379+
-> String?
380+
{
381+
switch percentWindow {
382+
case .session: data.sessionPace
383+
case .weekly: data.weeklyPace
384+
case .automatic: data.automaticPace
385+
}
386+
}
387+
388+
private static func paceAccessibilityPrefix(_ percentWindow: PercentWindow) -> String {
389+
switch percentWindow {
390+
case .session: L("menu_bar_layout_token_session_pace")
391+
case .weekly: L("menu_bar_layout_token_weekly_pace")
392+
case .automatic: L("menu_bar_layout_token_auto_pace")
393+
}
394+
}
395+
364396
private static func sessionPrefix(_ window: MenuBarLayoutRenderWindow?) -> String {
365397
guard let minutes = window?.windowMinutes, minutes > 0 else { return "S" }
366398
guard minutes.isMultiple(of: 60) else { return "\(minutes)m" }

Sources/CodexBar/PreferencesGeneralPane.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ struct GeneralPane: View {
174174

175175
Toggle(L("refresh_on_open_title"), isOn: self.$settings.refreshAllProvidersOnMenuOpen)
176176

177+
Toggle(isOn: self.$settings.backgroundWorkLowPowerModeEnabled) {
178+
SettingsRowLabel(
179+
L("Low Power Mode"),
180+
subtitle: L(
181+
"Runs automatic provider, local usage, and storage refreshes no more often than every " +
182+
"30 minutes. Manual refresh remains available."))
183+
}
184+
177185
Toggle(isOn: self.$settings.statusChecksEnabled) {
178186
SettingsRowLabel(
179187
L("check_provider_status_title"),

Sources/CodexBar/Providers/Codex/CodexProviderImplementation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ struct CodexProviderImplementation: ProviderImplementation {
134134
onAppearWhenEnabled: nil),
135135
ProviderSettingsToggleDescriptor(
136136
id: "codex-openai-web-battery-saver",
137-
title: "Battery Saver",
137+
title: "OpenAI web battery saver",
138138
subtitle: [
139139
"Limits background chatgpt.com refreshes to reduce battery and network usage.",
140140
"Dashboard extras may stay stale until you refresh them manually.",

Sources/CodexBar/Providers/Ollama/OllamaUIErrorMapper.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ struct OllamaUIErrorMapper {
1515
}
1616
if let browserName = self.browserName(
1717
in: trimmed,
18-
suffix: " cookie decryption was declined in Keychain; retry with a manual refresh.")
18+
suffix: " cookie decryption was declined in Keychain. " +
19+
"Open the provider card and click Refresh (⌘R) to request Keychain access again.")
1920
{
2021
return String(format: localize("ollama_browser_cookie_decryption_denied"), browserName)
2122
}

0 commit comments

Comments
 (0)