Redesign Foreman settings as a sidebar and add launch-at-login#58
Merged
Conversation
Rework the Foreman Settings window from a single grouped Form into a macOS System-Settings-style NavigationSplitView with three panes: General (launch at login), Repositories (scan directory), and Agent (cursor-agent path). Each editable pane keeps the settings-window "apply on commit, no Save button" behavior and the WindowVisibilityReader re-seed. Add a "Launch Foreman at login" toggle backed by a new ForemanCore LoginItemController that wraps SMAppService.mainApp behind an @_spi(Testing) backend. The OS owns the real state, so the toggle reads live status and re-syncs after each change; ForemanServices.startsAtLogin surfaces failures on issueMessage rather than swallowing them. Launching at login reuses the existing start() restore of enabled workers, so no separate restore path is needed. Co-authored-by: Cursor <cursoragent@cursor.com>
Pin the NavigationSplitView column visibility to .all and remove the default sidebar-toggle button so the settings panes can't be collapsed. Reduce the window's minimum width from 620 to 420pt. Co-authored-by: Cursor <cursoragent@cursor.com>
kyleve
commented
Jul 6, 2026
… edit paths via sheets Addresses code-review findings on the settings redesign: - Login-toggle failures now surface on a dedicated ForemanServices loginItemError (shown inline in the General settings pane) instead of the shared issueMessage, which the main-window rescan could clear before the user saw it. A successful toggle clears it. - LoginItemController now reads a typed LoginItemStatus; a registered-but- pending item (requiresApproval) reads as on with a needsApproval flag, and the General pane offers an "Open Login Items…" button (SMAppService.openSystemSettingsLoginItems) so it's no longer misreported as off. - Repositories and Agent panes show their value read-only with a Change… button that opens an explicit Save/Cancel sheet, so switching sidebar panes can't drop a half-typed path (removes the commit-on-blur reseed machinery). Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the parallel `Pane` enum (title/symbol properties plus a `detail` switch) with a `SettingsPane` protocol: each pane is a `View` that provides a static `title`, static `icon`, and an `init(session:)` builder. A small type-erased `SettingsPaneItem` (keyed by the pane type's `ObjectIdentifier`, so selection stays a typed token) drives the sidebar list and detail, so adding a pane is just conforming a new view and listing it — no central enum or switch to keep in lockstep. Addresses PR review feedback on SettingsView structure. Co-authored-by: Cursor <cursoragent@cursor.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.
Summary
Forminto a macOS System-Settings-styleNavigationSplitView: an always-open sidebar (collapse toggle removed) over a narrower window, split into three panes — General (launch at login), Repositories (scan directory), and Agent (cursor-agentpath). Panes are modeled with aSettingsPaneprotocol (statictitle/icon+ aninit(session:)builder), discovered via a type-erased sidebar item keyed byObjectIdentifier, rather than a central enum +detailswitch.WindowVisibilityReaderre-seed). Repositories and Agent show their paths read-only and edit through an explicit Save/Cancel sheet (PathEditorSheet), so switching panes can't drop a half-typed value.ForemanCoreLoginItemControllerthat wrapsSMAppService.mainAppbehind an@_spi(Testing)backend. The OS owns the real state, so it exposes a typedLoginItemStatus(enabled/requiresApproval/notRegistered), reads live status, and re-syncs after each change and whenever the window reappears. Register/unregister failures surface as a dedicatedloginItemErrorin the General pane (not swallowed, and not the main-window banner), and a pending-approval state shows a hint plus an "Open Login Items…" button into System Settings.applicationDidFinishLaunching→session.start()→repo.startIfEnabled()path, so no separate restore logic was needed.ForemanCore/ForemanREADME.md+AGENTS.md, adds aTODOs.mdentry, and adds tests (LoginItemControllerTests+ForemanServicesTestscases covering status, error surfacing, and pending approval).Notes / caveats
Project.swift/ entitlements change:SMAppService.mainAppself-registration needs no special entitlement./Applications); running from DerivedData during development may not auto-launch on boot.Test plan
./swiftformat --lintcleantuist test Foreman-macOS-Tests --no-selective-testing -- -destination 'platform=macOS'passes (all suites green, incl. newLoginItemControllerTestsand the newForemanServicesTestscases)tuist build ForemansucceedsMade with Cursor