import Foundation /// How the lane and card lists order their rows. Raw values are persisted (`AppStorage`), so /// they are API. `.manual` is the board's own arrangement — the rank order the loader already /// delivered — and sorting here is display-only: it never rewrites ranks on disk. enum ItemSortOrder: String, CaseIterable, Sendable { case manual case name case recent } extension ItemSortOrder { /// Orders `items` for display without touching the manual arrangement they arrived in. /// /// **`.manual` is a no-op** — `items` is already in the loader's rank order, so this returns it /// unchanged. /// /// **`.name` sorts by `title`**, case/diacritic-insensitive (`localizedStandardCompare`). Callers /// pass the row's own display title — the "Untitled Lane"/"Untitled Card" fallback already /// applied — so there is no separate nil case to sort here. /// /// **`.recent` sorts by `modified` descending, with `nil` last.** Both sorts tie-break the same /// way: Swift's `sort` is not a stable sort, so ties — equal titles, equal or absent dates — keep /// the incoming order via each item's original offset, exactly as `BoardSortOrder.sorted` does. nonisolated func sorted( _ items: [Item], title: (Item) -> String, modified: (Item) -> Date? ) -> [Item] { switch self { case .manual: return items case .name: return items.enumerated().sorted { lhs, rhs in let comparison = title(lhs.element).localizedStandardCompare(title(rhs.element)) if comparison != .orderedSame { return comparison == .orderedAscending } return lhs.offset < rhs.offset }.map(\.element) case .recent: return items.enumerated().sorted { lhs, rhs in switch (modified(lhs.element), modified(rhs.element)) { case let (l?, r?) where l != r: return l > r case (nil, .some): return false case (.some, nil): return true default: return lhs.offset < rhs.offset } }.map(\.element) } } }