Skip to content

Redesign Foreman settings as a sidebar and add launch-at-login#58

Merged
kyleve merged 4 commits into
mainfrom
foreman-settings-redesign
Jul 6, 2026
Merged

Redesign Foreman settings as a sidebar and add launch-at-login#58
kyleve merged 4 commits into
mainfrom
foreman-settings-redesign

Conversation

@kyleve

@kyleve kyleve commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • Reworks the Foreman Settings window from a single grouped Form into a macOS System-Settings-style NavigationSplitView: 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-agent path). Panes are modeled with a SettingsPane protocol (static title/icon + an init(session:) builder), discovered via a type-erased sidebar item keyed by ObjectIdentifier, rather than a central enum + detail switch.
  • General applies its toggle immediately (with the WindowVisibilityReader re-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.
  • Adds 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 it exposes a typed LoginItemStatus (enabled / requiresApproval / notRegistered), reads live status, and re-syncs after each change and whenever the window reappears. Register/unregister failures surface as a dedicated loginItemError in 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.
  • Restore of enabled workers on launch is unchanged — launching at login just triggers the existing applicationDidFinishLaunchingsession.start()repo.startIfEnabled() path, so no separate restore logic was needed.
  • Updates ForemanCore/Foreman README.md + AGENTS.md, adds a TODOs.md entry, and adds tests (LoginItemControllerTests + ForemanServicesTests cases covering status, error surfacing, and pending approval).

Notes / caveats

  • No Project.swift / entitlements change: SMAppService.mainApp self-registration needs no special entitlement.
  • Login-item launch is most reliable when the app is code-signed and in a stable location (e.g. /Applications); running from DerivedData during development may not auto-launch on boot.

Test plan

  • ./swiftformat --lint clean
  • tuist test Foreman-macOS-Tests --no-selective-testing -- -destination 'platform=macOS' passes (all suites green, incl. new LoginItemControllerTests and the new ForemanServicesTests cases)
  • tuist build Foreman succeeds
  • Manually verify the sidebar Settings panes, the Save/Cancel path sheets, and the "Launch Foreman at login" toggle (register/unregister, pending-approval hint, status reflects System Settings changes)

Made with Cursor

kyleve and others added 2 commits July 5, 2026 20:44
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>
Comment thread Foreman/Foreman/Sources/SettingsView.swift Outdated
kyleve and others added 2 commits July 5, 2026 21:18
… 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>
@kyleve kyleve enabled auto-merge (squash) July 6, 2026 01:27
@kyleve kyleve disabled auto-merge July 6, 2026 01:28
@kyleve kyleve enabled auto-merge (squash) July 6, 2026 01:30
@kyleve kyleve merged commit 7e20c41 into main Jul 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant