rename: user-facing product name Shotdeck -> Redline; legacy PDFs still recognized

This commit is contained in:
2026-09-01 22:25:15 +04:00
parent 74606e5046
commit d05bd735b5
15 changed files with 158 additions and 26 deletions
@@ -16,7 +16,7 @@ public enum ShotdeckError: Error, LocalizedError, Sendable {
case .screenRecordingNotGranted:
return "Screen Recording is turned off. Grant it in System Settings to capture."
case .noRegionRemembered:
return "No capture region is set. Choose 'Re-select area' from the Shotdeck menu."
return "No capture region is set. Choose 'Re-select area' from the Redline menu."
case .displayNoLongerConnected:
return "The display used for capture is no longer connected."
case .captureFailed(let underlying):
+2 -2
View File
@@ -71,7 +71,7 @@ public struct PDFComposer: Sendable {
}
public static func fileName(for session: CaptureSession) -> String {
"Shotdeck-\(DubaiTime.fileStamp(session.createdAt)).pdf"
"Redline-\(DubaiTime.fileStamp(session.createdAt)).pdf"
}
private static func writePDF(
@@ -86,7 +86,7 @@ public struct PDFComposer: Sendable {
}
let auxiliaryInfo: [String: Any] = [
kCGPDFContextCreator as String: "Shotdeck",
kCGPDFContextCreator as String: "Redline",
kCGPDFContextTitle as String: title,
kCGPDFContextSubject as String: sessionID.uuidString.lowercased(),
]
@@ -93,16 +93,19 @@ public enum AnnotationInspector {
)
}
/// True when this PDF was produced by Shotdeck. Creator attribute is authoritative;
/// True when this PDF was produced by this app. Creator attribute is authoritative;
/// the filename fallback applies ONLY when the creator attribute is absent.
/// Accepts both the current product name ("Redline") and the legacy name ("Shotdeck")
/// so PDFs already on the iPad or in Downloads are still detected.
public static func isShotdeckDocument(_ document: PDFDocument) -> Bool {
if let creator = document.documentAttributes?[PDFDocumentAttribute.creatorAttribute] as? String {
return creator == "Shotdeck" // present creator is authoritative, full stop
// Present creator is authoritative, full stop.
return creator == "Redline" || creator == "Shotdeck"
}
// Creator ABSENT (some apps rewrite metadata on save) -> filename fallback only here.
guard let name = document.documentURL?.lastPathComponent else { return false }
// .lastPathComponent on a file URL is already percent-decoded; do not use .absoluteString.
return name.wholeMatch(of: /^Shotdeck-\d{8}-\d{6}( \d+)?\.pdf$/) != nil
return name.wholeMatch(of: /^(Redline|Shotdeck)-\d{8}-\d{6}( \d+)?\.pdf$/) != nil
// Case-sensitive by construction (Swift Regex literals are case-sensitive by default).
// The optional "( \d+)?" is macOS's duplicate-name suffix AirDrop adds when a file of
// the same name already exists in the watch folder the normal case for a return.
@@ -60,7 +60,7 @@ public actor ReturnWatcher {
}
/// Scans the watch folder once, immediately, without waiting for an event. Every
/// recognized, stable, openable Shotdeck PDF present is (re-)inspected and (re-)recorded
/// recognized, stable, openable Redline/Shotdeck PDF present is (re-)inspected and (re-)recorded
/// into the ledger; returns exactly the documents processed in this call.
@discardableResult
public func scanNow() async throws -> [ReturnedDocument] {