Import Coucou 0.1.9 (Louis-CFM/coucou@83708fe), Mac app only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
maksarsanjeev
2026-10-06 19:34:32 +03:00
commit 53b4f60da1
158 changed files with 29442 additions and 0 deletions
@@ -0,0 +1,89 @@
import Foundation
enum Outfit: String, CaseIterable {
case auto, none, partyHat, beanie, crown, sunglasses, roundGlasses,
bow, scarf, witchHat, pumpkin, santaHat, bunnyEars
var displayName: String {
switch self {
case .auto: return "Auto (seasons)"
case .none: return "None"
case .partyHat: return "Party hat"
case .beanie: return "Beanie"
case .crown: return "Crown"
case .sunglasses: return "Sunglasses"
case .roundGlasses: return "Round glasses"
case .bow: return "Bow"
case .scarf: return "Scarf"
case .witchHat: return "Witch hat"
case .pumpkin: return "Pumpkin"
case .santaHat: return "Santa hat"
case .bunnyEars: return "Bunny ears"
}
}
// Returns the seasonal outfit for the given date (user's local calendar).
// Priority: partyHat > santaHat > witchHat > bunnyEars > sunglasses > none
static func seasonal(for date: Date, calendar: Calendar) -> Outfit {
let day = calendar.component(.day, from: date)
let month = calendar.component(.month, from: date)
let year = calendar.component(.year, from: date)
// Dec 31 – Jan 2 → party hat
if (month == 12 && day == 31) || (month == 1 && day <= 2) { return .partyHat }
// Dec 1–26 → santa hat
if month == 12 && day <= 26 { return .santaHat }
// Oct 1 – Nov 1 → witch hat
if month == 10 || (month == 11 && day == 1) { return .witchHat }
// Easter −2 / +1 → bunny ears (Meeus/Jones/Butcher algorithm)
let (eMonth, eDay) = easterDate(year: year)
let eComps = DateComponents(year: year, month: eMonth, day: eDay)
if let easterDate = calendar.date(from: eComps) {
let delta = calendar.dateComponents([.day], from: easterDate, to: date).day ?? 0
if delta >= -2 && delta <= 1 { return .bunnyEars }
}
// Jun 21 – Aug 31 → sunglasses
if (month == 6 && day >= 21) || month == 7 || month == 8 { return .sunglasses }
return .none
}
// If selection == .auto → seasonal; otherwise selection itself.
static func resolved(selection: Outfit, date: Date, calendar: Calendar) -> Outfit {
selection == .auto ? seasonal(for: date, calendar: calendar) : selection
}
// UserDefaults key "mochiOutfit", default "auto", unknown value → .auto
static var stored: Outfit {
get {
let raw = UserDefaults.standard.string(forKey: "mochiOutfit") ?? "auto"
return Outfit(rawValue: raw) ?? .auto
}
set {
UserDefaults.standard.set(newValue.rawValue, forKey: "mochiOutfit")
}
}
}
// Meeus/Jones/Butcher algorithm — returns (month, day) of Easter Sunday for the given year.
private func easterDate(year: Int) -> (Int, Int) {
let a = year % 19
let b = year / 100
let c = year % 100
let d = b / 4
let e = b % 4
let f = (b + 8) / 25
let g = (b - f + 1) / 3
let h = (19 * a + b - d - g + 15) % 30
let i = c / 4
let k = c % 4
let l = (32 + 2 * e + 2 * i - h - k) % 7
let m = (a + 11 * h + 22 * l) / 451
let month = (h + l - 7 * m + 114) / 31
let day = (h + l - 7 * m + 114) % 31 + 1
return (month, day)
}