How to Configure CodexBar Settings: A Complete Guide to SettingsStore and Config Persistence

You configure CodexBar settings through the centralized SettingsStore class, which synchronizes UI preferences to UserDefaults and provider-specific configurations to a JSON-backed CodexBarConfig file, automatically persisting changes with a 350ms debounce.

To configure CodexBar settings effectively, you need to understand how the open-source macOS menu-bar application steipete/CodexBar handles configuration persistence. The app uses a dual-layer storage system managed by the SettingsStore observable object, ensuring that both interface preferences and provider credentials remain synchronized between launches.

Understanding CodexBar's Dual-Layer Configuration System

CodexBar splits its configuration across two distinct storage mechanisms to separate transient UI state from structural provider data.

UserDefaults for UI Preferences

All user-facing interface settings—such as refresh frequency, menu bar display mode, and quota warning toggles—live in UserDefaults. The SettingsStore class bootstraps these values through the loadDefaultsState(userDefaults:) method in Sources/CodexBar/SettingsStore.swift (lines 73-124). This helper extracts values with fallbacks and performs migrations, such as converting legacy keys like menuBarMetricPreference into newer dictionary forms.

CodexBarConfig for Provider Settings

Provider-specific configuration—including enabled providers, token accounts, and provider ordering—resides in a typed CodexBarConfig model. This structure is persisted as JSON on disk and managed through Sources/CodexBar/SettingsStore+ConfigPersistence.swift (lines 36-84). The CodexBarConfigStore handles the actual file I/O, while SettingsStore maintains the runtime model.

Reading and Initializing Configuration Settings

When CodexBar launches, SettingsStore initializes its state by calling loadDefaultsState, which returns a SettingsDefaultsState struct containing concrete values for all UI preferences. This method runs migrations to ensure backward compatibility with older configuration formats.

For example, the refresh frequency setting is loaded with a fallback to five minutes:

let refreshFrequency = userDefaults.string(forKey: "refreshFrequency")
    .flatMap(RefreshFrequency.init(rawValue:))
    ?? .fiveMinutes

If you are running tests, this method also seeds UserDefaults with deterministic values to ensure consistent behavior across test runs.

How to Update CodexBar Settings Programmatically

All mutable settings funnel through the updateConfig(reason:mutate:) method. For provider-specific changes, use updateProviderConfig, which builds a new CodexBarConfig, updates the in-memory state, and triggers schedulePersistConfig.

Changing Refresh Frequency

To modify how often CodexBar polls for usage data, set the refreshFrequency property on the shared store:

let store = SettingsStore.shared
store.refreshFrequency = .twoMinutes

This writes directly to the UserDefaults key refreshFrequency and updates the polling timer immediately.

Enabling and Ordering Providers

To enable a specific provider like Claude or OpenAI, use setProviderEnabled(provider:metadata:enabled:), then reorder providers with setProviderOrder:

// Enable OpenAI
store.setProviderEnabled(
    provider: .openAI,
    metadata: ProviderDescriptorRegistry.metadata[.openAI]!,
    enabled: true
)

// Reorder to prioritize OpenAI first
store.setProviderOrder([.openAI, .claude, .gemini])

These changes modify the CodexBarConfig.providers array and persist to disk automatically.

Toggling UI Features

Menu bar display preferences are exposed through the defaultsState property:

// Disable the "highest usage" badge
store.defaultsState.menuBarShowsHighestUsage = false

// Change display mode to show percentage on the left
store.menuBarDisplayMode = .percentLeft

Persisting Configuration Changes to Disk

SettingsStore does not write to disk immediately on every change. Instead, it uses schedulePersistConfig to debounce writes by 350 milliseconds. This method cancels any pending write operation and schedules a new asynchronous save of the full CodexBarConfig to JSON.

As implemented in Sources/CodexBar/SettingsStore+ConfigPersistence.swift (lines 49-81), the persistence logic handles both asynchronous writes during normal operation and synchronous writes during test execution.

Reloading Configuration from External Sources

If an external process modifies the configuration file (such as during iCloud sync), you can force a reload without restarting the app:

store.reloadConfig(reason: "external-sync")

This method calls CodexBarConfigStore to load the JSON from disk, then uses applyExternalConfig to swap in the new model. The revision counter increments, but ConfigChangeContext.shouldBroadcast prevents unnecessary UI refresh broadcasts when the change originated externally.

Summary

  • CodexBar uses two storage layers: UserDefaults for UI preferences and a JSON-backed CodexBarConfig for provider settings.
  • SettingsStore in Sources/CodexBar/SettingsStore.swift serves as the central observable object that binds SwiftUI views to configuration state.
  • All mutations route through updateConfig or updateProviderConfig, ensuring type safety and automatic persistence.
  • Disk writes are debounced by 350ms via schedulePersistConfig to prevent excessive I/O during rapid setting changes.
  • Configuration is round-trippable: changes made programmatically or via the preferences pane reflect immediately in the UI and survive app restarts.

Frequently Asked Questions

Where does CodexBar store its settings on disk?

CodexBar stores UI preferences in UserDefaults under standard app suite identifiers, while provider configurations (tokens, enabled states, ordering) serialize to a JSON file managed by CodexBarConfigStore in Sources/CodexBar/SettingsStore+ConfigPersistence.swift. The exact file path depends on your app's container directory, typically within ~/Library/Application Support/ or the shared App Group container.

How do I change the refresh interval in CodexBar?

Set the refreshFrequency property on SettingsStore.shared to a valid RefreshFrequency enum case such as .oneMinute, .twoMinutes, or .fiveMinutes. This updates the UserDefaults key refreshFrequency immediately and reschedules the background polling timer without requiring an app restart.

What is the SettingsStore class in CodexBar?

SettingsStore is an @Observable singleton-style class defined in Sources/CodexBar/SettingsStore.swift that acts as the single source of truth for all configuration state. It exposes SwiftUI-friendly properties, handles legacy migrations, coordinates persistence through SettingsStore+ConfigPersistence.swift, and provides methods like setProviderEnabled and reloadConfig for programmatic configuration management.

How long does CodexBar wait before saving configuration changes?

CodexBar debounces configuration writes by 350 milliseconds using schedulePersistConfig. When you modify any setting, the store cancels any pending write, waits for 350ms of inactivity, then persists the full CodexBarConfig to disk asynchronously. This prevents excessive disk I/O when multiple settings change in rapid succession.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →