Files
shotdeck/Sources/Shotdeck/main.swift
T

84 lines
3.0 KiB
Swift

import AppKit
import SwiftUI
import ShotdeckCore
// SwiftPM treats a file named main.swift as top-level code, which forbids `@main`.
// App.main() is the equivalent entry point.
if ProcessInfo.processInfo.environment["SHOTDECK_SNAPSHOT_DIR"] != nil {
MainActor.assumeIsolated { PanelSnapshot.runIfRequested() }
}
if ProcessInfo.processInfo.environment["SHOTDECK_PICKER_SELFTEST"] != nil {
MainActor.assumeIsolated { PickerSelfTest.runIfRequested() }
} else if ProcessInfo.processInfo.environment["SHOTDECK_HISTORY_SELFTEST"] != nil {
MainActor.assumeIsolated { PickerSelfTest.runHistoryIfRequested() }
}
ShotdeckApp.main()
struct ShotdeckApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
var body: some Scene {
MenuBarExtra {
MenuBarView()
.environment(appDelegate.model)
} label: {
let state = appDelegate.model.iconState
HStack(spacing: 4) {
Image(systemName: state.symbolName)
if let count = state.countText {
Text(count).font(.system(size: 11, weight: .semibold))
}
}
.accessibilityLabel("Redline")
}
.menuBarExtraStyle(.window)
}
}
@MainActor
final class AppDelegate: NSObject, NSApplicationDelegate {
let model: AppModel
override init() {
NSApplication.shared.setActivationPolicy(.accessory)
model = AppDelegate.makeLaunchModel()
super.init()
}
func applicationDidFinishLaunching(_ notification: Notification) {
Task { await model.bootstrap() }
}
private static func makeLaunchModel() -> AppModel {
do {
let paths = try FolderSettings.resolvedAppSupportPaths()
return try makeModel(paths: paths)
} catch {
Log.ui.critical(
"AppModel init failed: \(String(describing: error), privacy: .public)"
)
let tmp = FileManager.default.temporaryDirectory
let fallbackRoot = tmp.appendingPathComponent("Shotdeck-fallback", isDirectory: true)
// Safe: temp-dir creation for a path this process controls cannot legitimately fail.
let fallback = try! AppSupportPaths(root: fallbackRoot, outbox: tmp, watchFolder: tmp)
let model = try! makeModel(paths: fallback)
model.setStatus("Redline could not access its storage folder. Captures will not persist.")
return model
}
}
private static func makeModel(paths: AppSupportPaths) throws -> AppModel {
let ledger = try ReturnLedger(paths: paths)
return try AppModel(
paths: paths,
spool: try SpoolStore(paths: paths),
composer: PDFComposer(),
capturer: ScreenCapturer(),
hotkeys: HotkeyCenter(),
picker: RegionPickerController(),
ledger: ledger,
watcher: ReturnWatcher(paths: paths, ledger: ledger)
)
}
}