SPB Git

spb/focale Public

Swift 100%
3.1 KB · 107 lines swift
Raw Blame History
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