| 9 | import SwiftUI |
| 10 | |
| 11 | struct RecordingView: View { |
| 12 | @EnvironmentObject var appServices: AppServices |
| 13 | private var asr: ASRService { self.appServices.asr } |
| 14 | @Environment(\.theme) private var theme |
| 15 | @Binding var appear: Bool |
| 16 | |
| 17 | let stopAndProcessTranscription: () async -> Void |
| 18 | let startRecording: () -> Void |
| 19 | |
| 20 | var body: some View { |
| 21 | ScrollView(.vertical, showsIndicators: false) { |
| 22 | VStack(spacing: 14) { |
| 23 | // Hero Header Card |
| 24 | ThemedCard(style: .standard) { |
| 25 | VStack(spacing: 12) { |
| 26 | HStack { |
| 27 | Image(systemName: "waveform.circle.fill") |
| 28 | .font(.system(size: 32)) |
| 29 | .foregroundStyle(.white) |
| 30 | |
| 31 | VStack(alignment: .leading, spacing: 4) { |
| 32 | Text("Voice Dictation") |
| 33 | .font(.title2) |
| 34 | .fontWeight(.bold) |
| 35 | Text("AI-powered speech recognition") |
| 36 | .font(.subheadline) |
| 37 | .foregroundStyle(.secondary) |
| 38 | } |
| 39 | |
| 40 | Spacer() |
| 41 | } |
| 42 | |
| 43 | // Status and Recording Control |
| 44 | VStack(spacing: 10) { |
| 45 | // Status indicator |
| 46 | HStack { |
| 47 | Circle() |
| 48 | .fill(self.asr.isRunning ? .red : self.asr.isAsrReady ? Color.fluidGreen : .secondary) |
| 49 | .frame(width: 8, height: 8) |
| 50 | |
| 51 | Text(self.asr.isRunning ? "Recording..." : self.asr.isAsrReady ? "Ready to record" : "Model not ready") |
| 52 | .font(.subheadline) |
| 53 | .foregroundStyle(self.asr.isRunning ? .red : self.asr.isAsrReady ? Color.fluidGreen : .secondary) |
| 54 | } |
| 55 | |
| 56 | // Recording Control (Single Toggle Button) |
| 57 | Button(action: { |
| 58 | if self.asr.isRunning { |
| 59 | Task { |
| 60 | await self.stopAndProcessTranscription() |
| 61 | } |
| 62 | } else { |
| 63 | self.startRecording() |
| 64 | } |
| 65 | }) { |
| 66 | HStack { |
| 67 | Image(systemName: self.asr.isRunning ? "stop.fill" : "mic.fill") |
| 68 | .font(.system(size: 16, weight: .semibold)) |
no test coverage detected