Add 6 new practice features, offline dictionary, and feature reference
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>
This commit is contained in:
48
Conjuga/SharedModels/Sources/SharedModels/Conversation.swift
Normal file
48
Conjuga/SharedModels/Sources/SharedModels/Conversation.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
import SwiftData
|
||||
import Foundation
|
||||
|
||||
@Model
|
||||
public final class Conversation {
|
||||
public var id: String = ""
|
||||
public var scenario: String = ""
|
||||
public var level: String = ""
|
||||
public var messages: String = "[]"
|
||||
public var createdDate: Date = Date()
|
||||
|
||||
public init(scenario: String, level: String) {
|
||||
self.id = UUID().uuidString
|
||||
self.scenario = scenario
|
||||
self.level = level
|
||||
self.messages = "[]"
|
||||
self.createdDate = Date()
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChatMessage: Codable, Identifiable, Hashable {
|
||||
public var id: String
|
||||
public let role: String // "assistant" or "user"
|
||||
public let content: String
|
||||
public let correction: String?
|
||||
|
||||
public init(role: String, content: String, correction: String? = nil) {
|
||||
self.id = UUID().uuidString
|
||||
self.role = role
|
||||
self.content = content
|
||||
self.correction = correction
|
||||
}
|
||||
}
|
||||
|
||||
extension Conversation {
|
||||
public var decodedMessages: [ChatMessage] {
|
||||
guard let data = messages.data(using: .utf8) else { return [] }
|
||||
return (try? JSONDecoder().decode([ChatMessage].self, from: data)) ?? []
|
||||
}
|
||||
|
||||
public func appendMessage(_ message: ChatMessage) {
|
||||
var msgs = decodedMessages
|
||||
msgs.append(message)
|
||||
if let data = try? JSONEncoder().encode(msgs), let str = String(data: data, encoding: .utf8) {
|
||||
messages = str
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user