spb/focale Public
Swift 100%
1//2// Recipe.swift3// Focale4//5// Author: Simon-Pierre Boucher6// Contact: contact@spboucher.ai7//8// A recipe is a complete, named, one-gesture preset: manual settings,9// format, lens, shutter behavior, and the capture context to apply.10// Recipes are exportable files — they're meant to be shared (CLAUDE.md §6).11//1213import Foundation14import UniformTypeIdentifiers1516struct Recipe: Codable, Identifiable, Sendable, Hashable {17 typealias ID = UUID1819 let id: ID20 var name: String21 var settings: ManualSettings22 /// Project automatically applied while this recipe is active (by name,23 /// so an imported recipe stays meaningful on another device).24 var autoProjectName: String?25 /// Scene signal that *proposes* this recipe — never a silent switch.26 var sceneTrigger: SceneTrigger27 /// Overlay a visible timestamp on captures (e.g. "Chantier" recipe).28 var showsTimestamp: Bool29 /// Prioritize OCR indexing for photos taken with this recipe.30 var prioritizeOCR: Bool3132 init(33 id: ID = UUID(),34 name: String,35 settings: ManualSettings = .automatic,36 autoProjectName: String? = nil,37 sceneTrigger: SceneTrigger = .none,38 showsTimestamp: Bool = false,39 prioritizeOCR: Bool = false40 ) {41 self.id = id42 self.name = name43 self.settings = settings44 self.autoProjectName = autoProjectName45 self.sceneTrigger = sceneTrigger46 self.showsTimestamp = showsTimestamp47 self.prioritizeOCR = prioritizeOCR48 }4950 enum SceneTrigger: String, Codable, Sendable {51 case none52 case lowLight53 case documentDetected54 }5556 // MARK: - Import / export (shared as files — a free acquisition channel)5758 static let fileType = UTType(exportedAs: "ai.spboucher.focale.recipe")5960 func exportData() throws -> Data {61 let encoder = JSONEncoder()62 encoder.outputFormatting = [.prettyPrinted, .sortedKeys]63 return try encoder.encode(self)64 }6566 init(importing data: Data) throws {67 self = try JSONDecoder().decode(Recipe.self, from: data)68 }69}7071extension Recipe {72 /// Built-in starting recipes. User-facing names in French.73 static let builtIns: [Recipe] = [74 Recipe(75 name: "Documents",76 settings: {77 var s = ManualSettings.automatic78 s.format = .heic79 return s80 }(),81 sceneTrigger: .documentDetected,82 prioritizeOCR: true83 ),84 Recipe(85 name: "Nuit longue pose",86 settings: {87 var s = ManualSettings.automatic88 s.iso = 10089 s.shutterSeconds = 0.590 s.format = .proRAW91 return s92 }(),93 sceneTrigger: .lowLight94 ),95 Recipe(96 name: "Chantier",97 settings: {98 var s = ManualSettings.automatic99 s.lens = .ultraWide100 return s101 }(),102 autoProjectName: "Chantier",103 showsTimestamp: true104 ),105 ]106}107