Merge remote-tracking branch 'origin/feat/shotdeck-20260830' into fix/send-truth-20260902

# Conflicts:
#	Sources/Shotdeck/PickerSelfTest.swift
This commit is contained in:
2026-09-02 09:17:09 +04:00
6 changed files with 791 additions and 4 deletions
+25
View File
@@ -41,6 +41,8 @@ public final class AppModel {
/// Currently bound capture combo (the last one Carbon accepted, or the preferred load).
private(set) var captureHotkey: HotkeyPreference
var hotkeyDisplayString: String { captureHotkey.displayString }
/// Staged update offered in the menu. Set only after checksum + payload validation.
public private(set) var updateAvailable: (version: String, notes: String)?
let paths: AppSupportPaths
let spool: SpoolStore
@@ -50,6 +52,7 @@ public final class AppModel {
let picker: RegionPickerController
let ledger: ReturnLedger
let watcher: ReturnWatcher
let updateChecker: UpdateChecker
public init(
paths: AppSupportPaths,
@@ -85,6 +88,14 @@ public final class AppModel {
self.outboxDisplayName = folders.outbox.lastPathComponent
self.watchFolderDisplayName = folders.watch.lastPathComponent
self.captureHotkey = HotkeyPreference.load()
self.updateChecker = UpdateChecker()
self.updateChecker.onChecked = { [weak self] in
guard let self else { return }
self.updateAvailable = self.updateChecker.availableUpdate
if let message = self.updateChecker.statusMessage {
self.setStatus(message)
}
}
}
// MARK: Seam mutators the only way a WP-4b/4c extension changes state.
@@ -192,6 +203,20 @@ public final class AppModel {
if !bindCaptureHotkey(pref) {
setStatus("\(pref.displayString) is already used by another app — capture only works from the menu.")
}
let skipSchedule =
ProcessInfo.processInfo.environment["SHOTDECK_PICKER_SELFTEST"] != nil
|| ProcessInfo.processInfo.environment["SHOTDECK_SNAPSHOT_DIR"] != nil
|| ProcessInfo.processInfo.environment["SHOTDECK_UPDATE_SELFTEST"] != nil
if !skipSchedule {
updateChecker.startSchedule()
}
}
/// Installs the staged update over `/Applications/Redline.app` and relaunches.
/// Does nothing unless the user clicked the menu row.
public func installUpdate() {
updateChecker.installStaged()
}
/// Unregisters `capture` and binds `HotkeyPreference.load()`. If Carbon rejects the new