Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Foreman/Foreman/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ formatting, global conventions) and ForemanCore's
- **Hiding the window does not cancel `.task`** — an ordered-out `NSWindow`
keeps its SwiftUI hierarchy alive and loops keep ticking (verified
empirically). Any periodic work in this window must gate on
`WindowVisibilityReader` (see `WorkerLogView`), and view drafts re-seed on
visibility (see `SettingsView`).
`WindowVisibilityReader` (see `WorkerLogView`), and the settings General
pane re-reads the login-item status on visibility (see `SettingsView`). The
settings path fields are edited in an explicit Save/Cancel sheet, so there
is no commit-on-blur to lose when switching panes.
- The target is an `LSUIElement` with a hand-written Info.plist in
[`Project.swift`](../../Project.swift) — don't switch to
`.extendingDefault`, which injects `NSMainStoryboardFile` on macOS.
Expand Down
22 changes: 18 additions & 4 deletions Foreman/Foreman/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,28 @@ closed. Closing the window just hides it — the app keeps running until Quit.
verbose startup logs. Editable while the worker is stopped — options apply
on the next start.
- **Toolbar** — a "Preventing sleep" badge while the sleep assertion is held,
Rescan, Settings (opens the standard settings window: the scan directory
and an explicit `cursor-agent` path, empty = auto-detect, applied as
fields commit), and Quit.
Rescan, Settings (opens the settings window — see below), and Quit.

## Settings

The settings window is a macOS System-Settings-style sidebar with three panes.
The *Launch at login* toggle applies immediately; the path settings open a
small editor sheet that commits only on **Save** (Cancel or Escape discards).

- **General** — *Launch Foreman at login*. Registers Foreman as a login item
via `SMAppService`, so it starts (and restores your enabled workers) when
you log in. If macOS needs you to approve the item first, the pane says so
and links straight to System Settings; a failed toggle shows its error here.
- **Repositories** — the directory scanned for git repositories (empty =
`~/Development`), edited via a sheet with a folder picker.
- **Agent** — an explicit `cursor-agent` executable path (empty = auto-detect),
edited via a sheet.

## Lifecycle

- On launch, Foreman restores the saved configuration and restarts the workers
that were enabled last time.
that were enabled last time. With *Launch Foreman at login* on, this happens
automatically after you log in.
- Quitting stops every worker (stop-on-quit: the app owns its processes and
never leaves orphans).
- The repo list refreshes every time the window is opened or focused, and on
Expand Down
30 changes: 30 additions & 0 deletions Foreman/Foreman/Sources/ForemanSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ final class ForemanSession {
services.isAnyWorkerLive
}

/// Whether Foreman launches at login. `SettingsView` binds this two-way;
/// the setter registers/unregisters the login item in Core (which logs and
/// surfaces any failure on `loginItemError`).
var startsAtLogin: Bool {
get { services.startsAtLogin }
set { services.startsAtLogin = newValue }
}

/// The login item is registered but awaiting approval in System Settings.
var loginItemNeedsApproval: Bool {
services.loginItemNeedsApproval
}

/// The most recent login-item failure (shown in the General settings pane).
var loginItemError: String? {
services.loginItemError
}

init(services: ForemanServices) {
self.services = services
}
Expand All @@ -63,4 +81,16 @@ final class ForemanSession {
func rescan() {
services.rescan()
}

/// Re-reads the login-item status from the OS (it can change in System
/// Settings while Foreman runs).
func refreshLoginItemStatus() {
services.refreshLoginItemStatus()
}

/// Opens System Settings › General › Login Items (to approve a pending
/// login item).
func openSystemSettingsLoginItems() {
services.openSystemSettingsLoginItems()
}
}
2 changes: 1 addition & 1 deletion Foreman/Foreman/Sources/MainWindowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ struct MainWindowView: View {
SettingsLink {
Label("Settings", systemImage: "gearshape")
}
.help("Change the scan directory or the cursor-agent executable.")
.help("Open Foreman's settings.")
}
ToolbarItem {
Button {
Expand Down
Loading
Loading