How to Configure Palmier Pro Settings: A Complete Guide to the macOS Preferences Window

To configure Palmier Pro settings programmatically, invoke SettingsWindowController.shared.show() to open the preferences window, or navigate directly to a specific tab using SettingsWindowController.shared.show(tab: .models).

The open-source Palmier Pro repository implements its preferences system as a native macOS window hosting a SwiftUI view hierarchy. Understanding how to configure Palmier Pro settings requires familiarity with the SettingsWindowController singleton and the modular pane architecture defined in Sources/PalmierPro/Settings/. Whether you are customizing the UI for a fork or adding new configuration options, the architecture provides a consistent pattern for extending preferences.

Settings Architecture Overview

Palmier Pro’s preferences window is built around a sidebar-detail pattern managed by three core components defined in SettingsView.swift.

SettingsTab Enum

The SettingsTab enum provides stable identifiers for each configuration pane:

enum SettingsTab {
    case account
    case general
    case models
    case agent
    case storage
}

This enum drives both the sidebar selection and the detail view routing, ensuring type-safe navigation between preference categories.

SettingsWindowController Singleton

The SettingsWindowController class inherits from NSWindowController and manages the window lifecycle as a singleton:

// Located in Sources/PalmierPro/Settings/SettingsView.swift
final class SettingsWindowController: NSWindowController {
    static let shared = SettingsWindowController()
    
    func show(tab: SettingsTab? = nil) {
        // Creates NSWindow with dark-aqua appearance
        // Injects SettingsView via NSHostingController
    }
}

When show() is called, it instantiates an NSWindow with a fixed content size and dark-aqua appearance, then injects the SettingsView SwiftUI hierarchy via an NSHostingController.

SettingsView Structure

The SettingsView struct contains two subviews: SettingsSidebar (rendering SidebarRowButton items) and SettingsDetail (wrapping the active pane in a ScrollView). The sidebar dynamically filters available tabs—for example, hiding the Account tab when account configuration is missing.

Opening the Settings Window Programmatically

Palmier Pro exposes a simple API for opening the Settings window from any part of the codebase, used in AppDelegate.swift and TourOverlay.swift.

Show the Default Tab

To open the Settings window on the default Account tab:

// From anywhere in the app
SettingsWindowController.shared.show()

This is the pattern used in AppDelegate.showSettings(_:) for the menu command.

To configure Palmier Pro settings by jumping directly to a functional area:

// Open directly on the General tab (Notifications & Privacy)
SettingsWindowController.shared.show(tab: .general)

// Open on the Models tab for LLM configuration
SettingsWindowController.shared.show(tab: .models)

// Open on the Storage tab to manage cache limits
SettingsWindowController.shared.show(tab: .storage)

These calls are utilized in the onboarding tour (TourOverlay.swift) to guide users to specific configuration screens contextually.

Observing Settings Changes

To react programmatically when a user modifies preferences, observe the underlying service via NotificationCenter:

// Example: React to notification preference changes
NotificationCenter.default.addObserver(
    forName: .AppNotificationsDidChange,
    object: nil,
    queue: .main
) { _ in
    // Update UI or reschedule background tasks
}

The individual panes use onChange handlers to wire UI controls (like SettingsToggleRow) back to services such as AppNotifications and Telemetry.

Individual Settings Panes

Each functional area is implemented as a separate SwiftUI view in Sources/PalmierPro/Settings/, following consistent theming from AppTheme.swift.

Account and Subscription

The AccountPane.swift file contains the subscription management interface and credit UI, binding to AccountService.shared via @Bindable to propagate account state changes.

Notifications and Privacy

NotificationsPane.swift and PrivacyPane.swift handle system-level toggles:

  • Notifications: Controls notificationsEnabled state, persisted via AppNotifications
  • Privacy: Manages telemetryEnabled and crash-reporting preferences

Both use the SettingsToggleRow component with consistent spacing defined in AppTheme.Spacing.

Models and Agent Configuration

The ModelsPane (ModelsPane.swift) provides LLM selection and API key configuration, while AgentPane (AgentPane.swift) contains agent-specific behavioral preferences. These panes interact with model configuration services and validate inputs before persisting to UserDefaults or secure storage.

Storage Management

StoragePane.swift implements disk cache limits and cleanup controls, allowing users to configure maximum cache size and manually purge temporary files downloaded by the editor.

Extending Settings with Custom Panes

To add a new Appearance pane with a dark-mode toggle, follow the established pattern in the Palmier Pro source code:

  1. Extend the enum in SettingsView.swift:
enum SettingsTab {
    case account
    case general
    case models
    case agent
    case storage
    case appearance  // New case
}
  1. Create the pane in AppearancePane.swift:
import SwiftUI

struct AppearancePane: View {
    @State private var darkMode = (NSApp.effectiveAppearance.name == .darkAqua)

    var body: some View {
        VStack(alignment: .leading, spacing: AppTheme.Spacing.md) {
            SettingsToggleRow(
                title: "Dark mode",
                subtitle: "Force the UI to use a dark appearance.",
                isOn: $darkMode
            )
            .onChange(of: darkMode) { _, newValue in
                NSApp.appearance = newValue 
                    ? NSAppearance(named: .darkAqua) 
                    : NSAppearance(named: .aqua)
            }

            Divider()
                .overlay(AppTheme.Border.subtleColor)
        }
    }
}
  1. Wire into SettingsDetail in SettingsView.swift:
switch selectedTab {
case .appearance:
    AppearancePane()
// existing cases...
}

The SettingsSidebar automatically displays the new tab without additional code, as it iterates over applicable SettingsTab cases.

Key Implementation Files

When configuring Palmier Pro settings or extending the preferences system, reference these source files:

Summary

  • Entry Point: Use SettingsWindowController.shared.show(tab:) to open the preferences window from any context, with SettingsTab providing type-safe navigation to Account, General, Models, Agent, or Storage panes.
  • Architecture: The Settings window is a native NSWindow managed by SettingsWindowController, containing a SwiftUI hierarchy with a sidebar (SettingsSidebar) and detail view (SettingsDetail).
  • Customization: Each functional area is a separate pane (AccountPane, NotificationsPane, etc.) using shared AppTheme constants and SettingsToggleRow components for consistency.
  • Extension: Adding new preferences requires extending SettingsTab, creating a SwiftUI view following the pane pattern, and adding a case to SettingsDetail.
  • State Management: Changes propagate via @Bindable for model objects and @State for local UI state, with onChange handlers persisting to underlying services.

Frequently Asked Questions

How do I open Palmier Pro settings from a menu item?

Use SettingsWindowController.shared.show() in your menu action handler. This is exactly how AppDelegate.swift implements the Preferences... menu command, creating a singleton window with dark-aqua appearance and injecting the SettingsView hierarchy.

Can I programmatically navigate to a specific settings tab?

Yes. Call SettingsWindowController.shared.show(tab: .models) (or .account, .general, .storage, .agent) to open the window directly on that pane. This pattern is used in the onboarding tour to guide users to configuration screens contextually.

Where are Palmier Pro settings stored?

Individual panes persist data to different backends: AccountService handles authentication state, AppNotifications and Telemetry manage boolean toggles, and StoragePane interacts with the file system for cache management. The UI uses @Bindable and @State to reflect these underlying stores in real-time.

How do I add a new toggle to the settings window?

Create a new pane file (e.g., CustomPane.swift), use the SettingsToggleRow component with AppTheme spacing, and wire it into SettingsDetail by adding a case to the SettingsTab enum. The SettingsSidebar automatically includes new tabs without additional code changes.

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 →