Add AI-powered mental wellness features: Reflection Companion, Pattern Tags, Weekly Digest

Three new Foundation Models features to deepen user engagement with mental wellness:

1. AI Reflection Companion — personalized feedback after completing guided reflections,
   referencing the user's actual words with personality-pack-adapted tone
2. Mood Pattern Tags — auto-extracts theme tags (work, family, stress, etc.) from notes
   and reflections, displayed as colored pills on entries
3. Weekly Emotional Digest — BGTask-scheduled Sunday digest with headline, summary,
   highlight, and intention; shown as card in Insights tab with notification

All features: on-device (zero cost), premium-gated, iOS 26+ with graceful degradation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-04 00:47:28 -05:00
parent 43ff239781
commit ab8d8fbdc0
18 changed files with 1076 additions and 3 deletions

View File

@@ -129,6 +129,12 @@ struct NoteEditorView: View {
let success = DataController.shared.updateNotes(forDate: entry.forDate, notes: noteToSave)
if success {
// Fire-and-forget tag extraction after saving a note
if #available(iOS 26, *), !IAPManager.shared.shouldShowPaywall, noteToSave != nil {
Task {
await FoundationModelsTagService.shared.extractAndSaveTags(for: entry)
}
}
dismiss()
} else {
isSaving = false
@@ -186,6 +192,11 @@ struct EntryDetailView: View {
// Mood section
moodSection
// Tags section
if entry.hasTags {
tagsSection
}
// Guided reflection section
if currentMood != .missing && currentMood != .placeholder {
reflectionSection
@@ -389,6 +400,35 @@ struct EntryDetailView: View {
}
}
private var tagsSection: some View {
VStack(alignment: .leading, spacing: 12) {
Text(String(localized: "Themes"))
.font(.headline)
.foregroundColor(textColor)
FlowLayout(spacing: 8) {
ForEach(entry.tags, id: \.self) { tag in
Text(tag.capitalized)
.font(.caption)
.fontWeight(.medium)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(
Capsule()
.fill(moodColor.opacity(0.15))
)
.foregroundColor(moodColor)
}
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color(.systemBackground))
)
}
}
private var notesSection: some View {
VStack(alignment: .leading, spacing: 12) {
HStack {