New features: - Offline Dictionary: reverse index of 175K verb forms + 200 common words, cached to disk, powers instant word lookups in Stories - Vocab SRS Review: spaced repetition for course vocabulary cards with due count badge and Again/Hard/Good/Easy rating - Cloze Practice: fill-in-the-blank using SentenceQuizEngine with distractor generation from vocabulary pool - Grammar Exercises: interactive quizzes for 5 grammar topics (ser/estar, por/para, preterite/imperfect, subjunctive, personal a) with "Practice This" button on grammar note detail - Listening Practice: listen-and-type + pronunciation check modes using Speech framework with word-by-word match scoring - Conversational Practice: AI chat partner via Foundation Models with 10 scenario types, saved to cloud container Other changes: - Add Conversation model to SharedModels and cloud container - Add Info.plist keys for speech recognition and microphone - Skip speech auth on simulator to prevent crash - Fix preparing data screen to only show during seed/migration - Extract courseDataVersion to static property on DataLoader - Add "How Features Work" reference page in Settings Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
253 lines
10 KiB
Swift
253 lines
10 KiB
Swift
import SwiftUI
|
|
|
|
struct FeatureReferenceView: View {
|
|
var body: some View {
|
|
List {
|
|
Section("Verb Conjugation Practice") {
|
|
featureRow(
|
|
icon: "rectangle.stack", color: .blue,
|
|
title: "Flashcard / Typing / MC / Handwriting / Sentence Builder",
|
|
details: [
|
|
"Pulls from verb conjugation database (1,750 verbs)",
|
|
"Filtered by your Level setting",
|
|
"Filtered by your Enabled Tenses",
|
|
"Respects Include Vosotros setting",
|
|
"Due cards (SRS) shown first, then random",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "tablecells", color: .blue,
|
|
title: "Full Table",
|
|
details: [
|
|
"Shows all 6 person forms for one verb + tense",
|
|
"Random verb from your Level",
|
|
"Random tense from your Enabled Tenses",
|
|
]
|
|
)
|
|
}
|
|
|
|
Section("Quick Actions") {
|
|
featureRow(
|
|
icon: "star.fill", color: .orange,
|
|
title: "Common Tenses",
|
|
details: [
|
|
"Restricts to 6 essential tenses: Present, Preterite, Imperfect, Future, Present Subjunctive, Imperative",
|
|
"Still filtered by your Level",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "exclamationmark.triangle", color: .red,
|
|
title: "Weak Verbs",
|
|
details: [
|
|
"Shows verbs you've struggled with (ease factor < 2.0)",
|
|
"Only includes verbs you've reviewed at least once",
|
|
"Weakest verbs shown first",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "wand.and.stars", color: .purple,
|
|
title: "Irregularity Drills",
|
|
details: [
|
|
"Spelling Changes: c->qu, g->gu, z->c patterns",
|
|
"Stem Changes: e->ie, o->ue, e->i patterns",
|
|
"Unique Irregulars: ser, ir, haber, etc.",
|
|
"Filtered by your Level and Enabled Tenses",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "rectangle.stack.fill", color: .teal,
|
|
title: "Vocab Review",
|
|
details: [
|
|
"Reviews vocabulary cards that are due (SRS scheduled)",
|
|
"Cards become due after you study them in Course quizzes",
|
|
"Rate Again/Hard/Good/Easy to schedule next review",
|
|
"Uses all course vocabulary, not filtered by level",
|
|
]
|
|
)
|
|
}
|
|
|
|
Section("Practice Activities") {
|
|
featureRow(
|
|
icon: "bubble.left.and.bubble.right.fill", color: .green,
|
|
title: "Conversation",
|
|
details: [
|
|
"AI chat partner using Apple Intelligence (on-device)",
|
|
"10 scenario types (restaurant, directions, etc.)",
|
|
"AI adapts vocabulary to your Level setting",
|
|
"Corrections provided inline when you make mistakes",
|
|
"Conversations saved to iCloud for revisiting",
|
|
"Requires Apple Intelligence-capable device",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "ear.fill", color: .blue,
|
|
title: "Listening",
|
|
details: [
|
|
"Listen & Type: hear a sentence, type what you heard",
|
|
"Pronunciation: read a sentence aloud, get scored on accuracy",
|
|
"Sentences pulled from course vocabulary examples",
|
|
"Uses all course vocab (not filtered by level)",
|
|
"Pronunciation requires microphone permission",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "text.badge.minus", color: .indigo,
|
|
title: "Cloze Practice",
|
|
details: [
|
|
"Fill in the missing word in a Spanish sentence",
|
|
"Sentences from course vocabulary examples",
|
|
"4 multiple-choice options (1 correct + 3 distractors)",
|
|
"Distractors are other vocabulary words from same pool",
|
|
"Uses all course vocab (not filtered by level)",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "music.note.list", color: .pink,
|
|
title: "Lyrics",
|
|
details: [
|
|
"Search and save Spanish song lyrics",
|
|
"Side-by-side Spanish and English translations",
|
|
"User-curated library, not filtered by level",
|
|
"Saved to iCloud for sync across devices",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "book.fill", color: .teal,
|
|
title: "Stories",
|
|
details: [
|
|
"AI-generated one-paragraph Spanish stories",
|
|
"Matched to your Level and Enabled Tenses",
|
|
"Every word is tappable for definition",
|
|
"Known words use offline dictionary (175K+ verb forms)",
|
|
"Unknown words looked up via on-device AI",
|
|
"English translation hidden by default (toggle to reveal)",
|
|
"3-question comprehension quiz at the end",
|
|
"Saved to iCloud for revisiting",
|
|
"Requires Apple Intelligence-capable device",
|
|
]
|
|
)
|
|
}
|
|
|
|
Section("Guide") {
|
|
featureRow(
|
|
icon: "book", color: .brown,
|
|
title: "Tense Guides",
|
|
details: [
|
|
"Detailed explanation of each of the 20 verb tenses",
|
|
"Conjugation ending tables for -ar, -er, -ir verbs",
|
|
"Usage patterns with example sentences",
|
|
"Essential tenses marked with orange badge",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "doc.text", color: .brown,
|
|
title: "Grammar Notes",
|
|
details: [
|
|
"23 grammar topics (ser vs estar, por vs para, etc.)",
|
|
"Interactive exercises available for 5 topics",
|
|
"Tap 'Practice This' on notes that have exercises",
|
|
"Content grouped by category with card-based layout",
|
|
]
|
|
)
|
|
}
|
|
|
|
Section("Course") {
|
|
featureRow(
|
|
icon: "list.clipboard", color: .orange,
|
|
title: "Course Quizzes",
|
|
details: [
|
|
"Vocabulary from specific course weeks",
|
|
"Multiple quiz types: MC, typing, handwriting, cloze",
|
|
"Focus Area mode for missed words",
|
|
"Not filtered by Level (uses course structure)",
|
|
]
|
|
)
|
|
|
|
featureRow(
|
|
icon: "checkmark.seal", color: .orange,
|
|
title: "Checkpoint Exams",
|
|
details: [
|
|
"Cumulative review across multiple weeks",
|
|
"Cards sampled evenly across all covered weeks",
|
|
"Choose 25, 50, or 100 questions",
|
|
]
|
|
)
|
|
}
|
|
|
|
Section("Dashboard") {
|
|
featureRow(
|
|
icon: "clock.fill", color: .mint,
|
|
title: "Study Time",
|
|
details: [
|
|
"Tracks time the app is in the foreground",
|
|
"Starts when app becomes active, stops on background",
|
|
"Shows today's time and all-time total",
|
|
"7-day bar chart of daily study time",
|
|
]
|
|
)
|
|
}
|
|
|
|
Section("Settings That Affect Practice") {
|
|
settingRow(name: "Level", affects: "Verb practice, Full Table, Quick Actions, Stories, Conversation")
|
|
settingRow(name: "Enabled Tenses", affects: "Verb practice, Full Table, Irregularity Drills, Stories")
|
|
settingRow(name: "Include Vosotros", affects: "Verb practice, Full Table, Quick Actions")
|
|
settingRow(name: "Daily Goal", affects: "Dashboard progress tracking only")
|
|
}
|
|
}
|
|
.navigationTitle("How Features Work")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
|
|
// MARK: - Components
|
|
|
|
@ViewBuilder
|
|
private func featureRow(icon: String, color: Color, title: String, details: [String]) -> some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
HStack(spacing: 10) {
|
|
Image(systemName: icon)
|
|
.font(.body)
|
|
.foregroundStyle(color)
|
|
.frame(width: 24)
|
|
Text(title)
|
|
.font(.subheadline.weight(.semibold))
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
ForEach(details, id: \.self) { detail in
|
|
HStack(alignment: .top, spacing: 6) {
|
|
Text("•")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
Text(detail)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
}
|
|
.padding(.leading, 34)
|
|
}
|
|
.padding(.vertical, 4)
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func settingRow(name: String, affects: String) -> some View {
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(name)
|
|
.font(.subheadline.weight(.semibold))
|
|
Text(affects)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
.padding(.vertical, 2)
|
|
}
|
|
}
|