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() } } 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("Shotdeck") } .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("Shotdeck 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 AppModel( paths: paths, spool: try SpoolStore(paths: paths), composer: PDFComposer(), capturer: ScreenCapturer(), hotkeys: HotkeyCenter(), picker: RegionPickerController(), ledger: ledger, watcher: ReturnWatcher(paths: paths, ledger: ledger) ) } }