add explain module

This commit is contained in:
2026-09-06 07:38:56 +08:00
parent 7d843e8ff1
commit 701148e00e
20 changed files with 775 additions and 8 deletions

View File

@@ -10,6 +10,7 @@ struct PlayerSectionView: View {
@State private var progressDragging = false
@State private var dragPositionMs: Int64 = 0
@State private var explanationSentence: SentenceBoundary?
var body: some View {
VStack(spacing: 0) {
@@ -19,6 +20,12 @@ struct PlayerSectionView: View {
}
}
.padding(.horizontal, 16)
.sheet(item: $explanationSentence) { sentence in
SentenceExplanationSheet(
courseTitle: controller.currentItem?.title ?? "口语训练课程",
sentence: sentence
)
}
}
private var playerSurface: some View {
@@ -85,10 +92,33 @@ struct PlayerSectionView: View {
Text(sentenceMetaText)
.font(.system(size: 13))
.foregroundColor(Theme.textSubtle)
Text(sentenceTextText)
HStack(alignment: .center, spacing: 8) {
Text(sentenceTextText)
.font(.system(size: 15))
.foregroundColor(.white)
.lineLimit(2)
.foregroundColor(.white)
.lineLimit(2)
Spacer()
Button {
guard let sentence = controller.currentSentence, !(sentence.text ?? "").isEmpty else {
return
}
controller.pause()
explanationSentence = sentence
} label: {
Image(systemName: "lightbulb.circle.fill")
.font(.system(size: 23, weight: .semibold))
.foregroundColor(
controller.currentSentence?.text?.isEmpty == false
? Theme.accentLight
: Theme.textSubtle
)
.frame(width: 32, height: 32)
.background(Theme.overlay)
.clipShape(Circle())
.accessibilityLabel("AI 讲解这句话")
}
.buttonStyle(.plain)
}
progressRow
Text(statusText)
.font(.system(size: 13))
@@ -195,3 +225,53 @@ struct PlayerSectionView: View {
controller.setPlaybackSpeed(next)
}
}
struct SentenceExplanationSheet: View {
let courseTitle: String
let sentence: SentenceBoundary
@State private var explanation = ""
@State private var errorMessage: String?
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading, spacing: 14) {
Text(sentence.text ?? "")
.font(.system(size: 18, weight: .bold))
.foregroundColor(Theme.textDark)
if let errorMessage {
Text(errorMessage)
.font(.system(size: 15))
.foregroundColor(Theme.danger)
} else if explanation.isEmpty {
Text("AI 正在讲解这句话…")
.font(.system(size: 15))
.foregroundColor(Theme.textSubtle)
} else {
Text(explanation)
.font(.system(size: 16))
.foregroundColor(Theme.textDark)
.lineSpacing(5)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(20)
}
.background(Color.white.ignoresSafeArea())
.navigationTitle("句子讲解")
.navigationBarTitleDisplayMode(.inline)
}
.task {
guard explanation.isEmpty, errorMessage == nil else { return }
do {
explanation = try await SentenceBoundaryAPI.explain(
sentence,
courseTitle: courseTitle
)
} catch {
errorMessage = "讲解失败:\(error.localizedDescription)"
}
}
}
}