Skip to content
Open
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
2 changes: 2 additions & 0 deletions Overview/OverviewAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
The application delegate managing global state coordination and window management.
*/

import Defaults
import Sparkle
import SwiftUI

Expand Down Expand Up @@ -61,6 +62,7 @@ final class OverviewAppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
logger.debug("Application finished launching")
NSApp.setActivationPolicy(.accessory)
LoginItemService.shared.setLaunchAtLogin(enabled: Defaults[.launchAtLogin])

NotificationCenter.default.addObserver(
self,
Expand Down
12 changes: 12 additions & 0 deletions Overview/Settings/LaunchSettingsKeys.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
Settings/LaunchSettingsKeys.swift
Overview

Created by William Pierce on 3/19/25.
*/

import Defaults

extension Defaults.Keys {
static let launchAtLogin = Key<Bool>("launchAtLogin", default: false)
}
4 changes: 3 additions & 1 deletion Overview/Settings/Services/DiagnosticService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ final class DiagnosticService {
createOnLaunch: Defaults[.createOnLaunch],
closeWithSource: Defaults[.closeOnCaptureStop],
saveWindowsOnQuit: Defaults[.saveWindowsOnQuit],
restoreWindowsOnLaunch: Defaults[.restoreWindowsOnLaunch]
restoreWindowsOnLaunch: Defaults[.restoreWindowsOnLaunch],
launchAtLogin: Defaults[.launchAtLogin]
),
overlay: OverlaySettings(
focusBorder: FocusBorderSettings(
Expand Down Expand Up @@ -513,6 +514,7 @@ struct WindowSettings: Codable {
let closeWithSource: Bool
let saveWindowsOnQuit: Bool
let restoreWindowsOnLaunch: Bool
let launchAtLogin: Bool
}

struct OverlaySettings: Codable {
Expand Down
60 changes: 60 additions & 0 deletions Overview/Settings/Services/LoginItemService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Settings/Services/LoginItemService.swift
Overview

Created by William Pierce on 3/19/25.

Manages enabling or disabling Overview as a login item by
creating or removing a LaunchAgent in the user's Library.
*/

import Foundation

@MainActor
final class LoginItemService: ObservableObject {
static let shared = LoginItemService()
private let logger = AppLogger.settings

private init() {}

func setLaunchAtLogin(enabled: Bool) {
do {
if enabled {
try createLaunchAgent()
logger.info("Enabled launch at login")
} else {
try removeLaunchAgent()
logger.info("Disabled launch at login")
}
} catch {
logger.logError(error, context: "Updating launch at login")
}
}

private var agentURL: URL {
let bundleID = Bundle.main.bundleIdentifier ?? "com.overview"
return FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Library/LaunchAgents")
.appendingPathComponent("\(bundleID).launchAgent.plist")
}

private func createLaunchAgent() throws {
let directory = agentURL.deletingLastPathComponent()
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)

let contents: [String: Any] = [
"Label": Bundle.main.bundleIdentifier ?? "com.overview",
"ProgramArguments": [Bundle.main.bundlePath],
"RunAtLoad": true
]

let data = try PropertyListSerialization.data(fromPropertyList: contents, format: .xml, options: 0)
try data.write(to: agentURL, options: .atomic)
}

private func removeLaunchAgent() throws {
if FileManager.default.fileExists(atPath: agentURL.path) {
try FileManager.default.removeItem(at: agentURL)
}
}
}
5 changes: 4 additions & 1 deletion Overview/Settings/SettingsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ final class SettingsManager: ObservableObject {
.closeOnCaptureStop,
.assignPreviewsToAllDesktops,
.saveWindowsOnQuit,
.restoreWindowsOnLaunch
.restoreWindowsOnLaunch,
.launchAtLogin
)

/// Reset Overlay settings
Expand Down Expand Up @@ -89,6 +90,8 @@ final class SettingsManager: ObservableObject {
.appFilterNames
)

LoginItemService.shared.setLaunchAtLogin(enabled: Defaults[.launchAtLogin])

logger.info("Settings reset completed successfully")
}

Expand Down
5 changes: 5 additions & 0 deletions Overview/Window/Settings/WindowSettingsTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct WindowSettingsTab: View {
@Default(.windowOpacity) private var windowOpacity
@Default(.defaultWindowWidth) private var defaultWindowWidth
@Default(.defaultWindowHeight) private var defaultWindowHeight
@Default(.launchAtLogin) private var launchAtLogin

var body: some View {
Form {
Expand Down Expand Up @@ -126,6 +127,10 @@ struct WindowSettingsTab: View {
Defaults.Toggle("Save window positions on quit", key: .saveWindowsOnQuit)
Defaults.Toggle(
"Restore window positions on launch", key: .restoreWindowsOnLaunch)
Toggle("Launch Overview on login", isOn: $launchAtLogin)
.onChange(of: launchAtLogin) { newValue in
LoginItemService.shared.setLaunchAtLogin(enabled: newValue)
}
}
}
}
Expand Down