Files

89 lines
3.2 KiB
Swift

// SPDX-FileCopyrightText: 2026 Kaede Akino
// SPDX-License-Identifier: GPL-3.0-only
import SwiftUI
/// Trailing controls inside an answer field. Both recording actions stay on
/// the right; neither the draft nor a waveform takes their place.
public struct UserInputSpeechControls: View {
private let isActive: Bool
private let isRecording: Bool
private let canStart: Bool
private let start: () -> Void
private let cancel: () -> Void
private let stop: () -> Void
public init(
isActive: Bool, isRecording: Bool, canStart: Bool,
start: @escaping () -> Void, cancel: @escaping () -> Void, stop: @escaping () -> Void
) {
self.isActive = isActive
self.isRecording = isRecording
self.canStart = canStart
self.start = start
self.cancel = cancel
self.stop = stop
}
private var controlSize: CGFloat {
#if os(iOS)
44
#else
32
#endif
}
public var body: some View {
HStack(spacing: 2) {
if isActive {
control(AppStrings.Assistant.cancelTranscription, symbol: "xmark", action: cancel)
.accessibilityIdentifier("UserInputSpeech.Cancel")
if isRecording {
control(AppStrings.Assistant.stopRecording, symbol: "stop.fill", action: stop)
.accessibilityIdentifier("UserInputSpeech.Stop")
} else {
ProgressView()
.controlSize(.small)
.frame(width: controlSize, height: controlSize)
.accessibilityLabel(Text(AppStrings.Assistant.transcribing))
}
} else {
Button(action: start) {
Image(systemName: "mic")
.frame(width: controlSize, height: controlSize)
.contentShape(.rect)
}
.buttonStyle(.plain)
.disabled(!canStart)
.accessibilityLabel(Text(AppStrings.Assistant.transcribe))
.help(Text(AppStrings.Assistant.transcribeWithAppleSpeech))
.accessibilityIdentifier("UserInputSpeech.Start")
}
}
.fixedSize()
}
private func control(_ title: LocalizedStringResource, symbol: String, action: @escaping () -> Void) -> some View {
Button(action: action) {
#if os(iOS)
Image(systemName: symbol)
.font(.title3.weight(.medium))
.symbolRenderingMode(.monochrome)
.foregroundStyle(Color.primary.opacity(0.75))
.frame(width: controlSize, height: controlSize)
.contentShape(.rect)
#else
Image(systemName: symbol)
.font(.system(size: 10, weight: .semibold))
.frame(width: 26, height: 26)
.background(.quaternary.opacity(0.55), in: .circle)
.frame(width: controlSize, height: controlSize)
.contentShape(.rect)
#endif
}
.buttonStyle(.plain)
.accessibilityLabel(Text(title))
.help(Text(title))
}
}