diff --git a/Conjuga/Conjuga/Services/VerbExampleGenerator.swift b/Conjuga/Conjuga/Services/VerbExampleGenerator.swift index 1a87187..7ca3c7a 100644 --- a/Conjuga/Conjuga/Services/VerbExampleGenerator.swift +++ b/Conjuga/Conjuga/Services/VerbExampleGenerator.swift @@ -66,9 +66,16 @@ struct VerbExampleGenerator { // Map by tenseId and return in the caller's requested order so the UI // renders a predictable sequence even if the model shuffles its output. - let byTense = Dictionary(uniqueKeysWithValues: response.content.examples.map { - ($0.tenseId, VerbExample(tenseId: $0.tenseId, spanish: $0.spanish, english: $0.english)) - }) + // Use `uniquingKeysWith` defensively — the @Generable schema requires + // exactly 6 examples, but if the model duplicates a tenseId (it does + // happen when the caller passes fewer than 6 distinct tenses), the + // strict `uniqueKeysWithValues:` initializer would trap. + let byTense = Dictionary( + response.content.examples.map { + ($0.tenseId, VerbExample(tenseId: $0.tenseId, spanish: $0.spanish, english: $0.english)) + }, + uniquingKeysWith: { first, _ in first } + ) return tenseIds.compactMap { byTense[$0] } } diff --git a/Conjuga/Conjuga/Views/Practice/Vocab/VocabFlashcardPracticeView.swift b/Conjuga/Conjuga/Views/Practice/Vocab/VocabFlashcardPracticeView.swift index 04efcd5..9b558c1 100644 --- a/Conjuga/Conjuga/Views/Practice/Vocab/VocabFlashcardPracticeView.swift +++ b/Conjuga/Conjuga/Views/Practice/Vocab/VocabFlashcardPracticeView.swift @@ -207,16 +207,19 @@ struct VocabFlashcardPracticeView: View { let english = verb.english Task { do { + // The generator's @Generable schema requires exactly 6 + // examples; pass the canonical 6-tense set used by + // VerbDetailView, then pick the present-tense one to + // show on the card. let examples = try await VerbExampleGenerator.generate( verbInfinitive: infinitive, verbEnglish: english, - tenseIds: ["ind_presente"] + tenseIds: VocabExampleTenseIds.canonical ) - if let first = examples.first { - exampleCache.setExamples(examples, for: verbId) - if currentVerb?.id == verbId { - exampleByVerbId[verbId] = first - } + exampleCache.setExamples(examples, for: verbId) + let pick = examples.first { $0.tenseId == "ind_presente" } ?? examples.first + if let pick, currentVerb?.id == verbId { + exampleByVerbId[verbId] = pick } } catch { // Silent — the example block just stays hidden. @@ -238,6 +241,20 @@ struct VocabFlashcardPracticeView: View { } } +/// Canonical 6-tense set used by `VerbExampleGenerator`. Its `@Generable` +/// schema requires exactly 6 examples; callers must pass 6 distinct tense +/// IDs so the model has a unique slot for each generated example. +enum VocabExampleTenseIds { + static let canonical: [String] = [ + TenseID.ind_presente.rawValue, + TenseID.ind_preterito.rawValue, + TenseID.ind_imperfecto.rawValue, + TenseID.ind_futuro.rawValue, + TenseID.subj_presente.rawValue, + TenseID.imp_afirmativo.rawValue, + ] +} + // MARK: - Pool helper /// Shared verb-pool fetch used by both vocab flashcard and vocab MC. diff --git a/Conjuga/Conjuga/Views/Practice/Vocab/VocabMultipleChoicePracticeView.swift b/Conjuga/Conjuga/Views/Practice/Vocab/VocabMultipleChoicePracticeView.swift index 84abff3..334a726 100644 --- a/Conjuga/Conjuga/Views/Practice/Vocab/VocabMultipleChoicePracticeView.swift +++ b/Conjuga/Conjuga/Views/Practice/Vocab/VocabMultipleChoicePracticeView.swift @@ -219,13 +219,12 @@ struct VocabMultipleChoicePracticeView: View { let examples = try await VerbExampleGenerator.generate( verbInfinitive: infinitive, verbEnglish: english, - tenseIds: ["ind_presente"] + tenseIds: VocabExampleTenseIds.canonical ) - if let first = examples.first { - exampleCache.setExamples(examples, for: verbId) - if currentVerb?.id == verbId { - exampleByVerbId[verbId] = first - } + exampleCache.setExamples(examples, for: verbId) + let pick = examples.first { $0.tenseId == "ind_presente" } ?? examples.first + if let pick, currentVerb?.id == verbId { + exampleByVerbId[verbId] = pick } } catch {} if generatingExampleForVerbId == verbId { diff --git a/Conjuga/Conjuga/Views/Verbs/VerbListView.swift b/Conjuga/Conjuga/Views/Verbs/VerbListView.swift index 8c5d4bd..c85c61c 100644 --- a/Conjuga/Conjuga/Views/Verbs/VerbListView.swift +++ b/Conjuga/Conjuga/Views/Verbs/VerbListView.swift @@ -97,13 +97,13 @@ struct VerbListView: View { Button { setAllLevels(enabled: true) } label: { - Label("All Levels", systemImage: allLevelsActive ? "checkmark" : "") + Label("All Levels", systemImage: allLevelsActive ? "checkmark" : "circle") } ForEach(levels, id: \.self) { level in Button { toggleLevel(level) } label: { - Label(level.displayName, systemImage: selectedLevels.contains(level) ? "checkmark" : "") + Label(level.displayName, systemImage: selectedLevels.contains(level) ? "checkmark" : "circle") } } } @@ -112,7 +112,7 @@ struct VerbListView: View { Button { selectedIrregularity = nil } label: { - Label("All Verbs", systemImage: selectedIrregularity == nil ? "checkmark" : "") + Label("All Verbs", systemImage: selectedIrregularity == nil ? "checkmark" : "circle") } ForEach(IrregularityCategory.allCases) { category in Button {