feat: introduce reusable app lists for rules

- AppList @Model with launch migration from legacy inline selections
  (rules with identical selections share one list; idempotent)
- rules point at one app list; Block/Allow Only belongs to the rule and
  is offered only in the Schedule editor (limit kinds sanitize to Block)
- app-list picker sheet (select / create / edit / delete with in-use
  protection) replaces the per-rule selection sheet
- Hard Mode locks app-list editing while any hard rule is blocking
- SwiftData stability: relationships are only wired between managed
  models, and unit tests share one in-memory container (fresh context +
  data wipe per test) — per-test container creation trapped
  intermittently inside SwiftData
This commit is contained in:
2026-06-12 20:02:35 -04:00
parent 05d48a9f34
commit 93f2eceb3a
22 changed files with 880 additions and 138 deletions

View File

@@ -123,7 +123,8 @@ struct RuleDetailSheet: View {
}
private var appCountLabel: String {
rule.selectionCount == 1 ? "1 App" : "\(rule.selectionCount) Apps"
guard let list = rule.appList else { return "No apps" }
return "\(list.name) · \(list.appCountLabel)"
}
private func row(_ label: String, _ value: String) -> some View {