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.
Navigate to a Specific Tab
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
notificationsEnabledstate, persisted viaAppNotifications - Privacy: Manages
telemetryEnabledand 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:
- Extend the enum in
SettingsView.swift:
enum SettingsTab {
case account
case general
case models
case agent
case storage
case appearance // New case
}
- 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)
}
}
}
- 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:
Sources/PalmierPro/Settings/SettingsView.swift– Main UI,SettingsWindowController,SettingsTabenumSources/PalmierPro/Settings/AccountPane.swift– Account management and subscription UISources/PalmierPro/Settings/NotificationsPane.swift– System notification togglesSources/PalmierPro/Settings/PrivacyPane.swift– Telemetry and crash reportingSources/PalmierPro/Settings/ModelsPane.swift– LLM configuration and model selectionSources/PalmierPro/Settings/AgentPane.swift– Agent behavior preferencesSources/PalmierPro/Settings/StoragePane.swift– Cache limits and disk managementSources/PalmierPro/UI/AppTheme.swift– Design tokens for consistent spacing, colors, and typographySources/PalmierPro/App/AppDelegate.swift– Menu command integration for opening SettingsSources/PalmierPro/Editor/Tour/TourOverlay.swift– Example of programmatic Settings navigation
Summary
- Entry Point: Use
SettingsWindowController.shared.show(tab:)to open the preferences window from any context, withSettingsTabproviding type-safe navigation to Account, General, Models, Agent, or Storage panes. - Architecture: The Settings window is a native
NSWindowmanaged bySettingsWindowController, containing a SwiftUI hierarchy with a sidebar (SettingsSidebar) and detail view (SettingsDetail). - Customization: Each functional area is a separate pane (
AccountPane,NotificationsPane, etc.) using sharedAppThemeconstants andSettingsToggleRowcomponents for consistency. - Extension: Adding new preferences requires extending
SettingsTab, creating a SwiftUI view following the pane pattern, and adding a case toSettingsDetail. - State Management: Changes propagate via
@Bindablefor model objects and@Statefor local UI state, withonChangehandlers 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →