vOOice/VoiceInk/Views/Common/TranscriptionFallbackView.swift
2025-07-02 20:32:58 +05:45

101 lines
3.2 KiB
Swift

import SwiftUI
/// A view that provides a fallback UI to display transcribed text when it cannot be pasted automatically.
struct TranscriptionFallbackView: View {
let transcriptionText: String
let onCopy: () -> Void
let onClose: () -> Void
let onTextChange: ((String) -> Void)?
@State private var editableText: String = ""
@State private var isHoveringTitleBar = false
var body: some View {
VStack(spacing: 0) {
// Title Bar
HStack {
Spacer().frame(width: 20, height: 20)
Spacer()
Text("VoiceInk")
.font(.system(size: 13, weight: .medium, design: .rounded))
.foregroundColor(.secondary)
Spacer()
if isHoveringTitleBar {
Button(action: {
ClipboardManager.copyToClipboard(editableText)
onCopy()
}) {
Image(systemName: "doc.on.doc")
.font(.system(size: 9, weight: .semibold))
}
.buttonStyle(TitleBarButtonStyle(color: .blue))
} else {
Spacer().frame(width: 20, height: 20)
}
}
.padding(.horizontal, 8)
.padding(.vertical, 5)
.background(.ultraThinMaterial)
.onHover { hovering in
withAnimation(.easeInOut(duration: 0.2)) {
isHoveringTitleBar = hovering
}
}
// Text Editor
TextEditor(text: $editableText)
.font(.system(size: 14, weight: .regular, design: .rounded))
.scrollContentBackground(.hidden)
.background(Color.clear)
.padding(.horizontal, 16)
.onAppear {
editableText = transcriptionText
}
.onChange(of: editableText) { newValue in
onTextChange?(newValue)
}
}
.background(.regularMaterial)
.cornerRadius(16)
.background(
Button("", action: onClose)
.keyboardShortcut("w", modifiers: .command)
.hidden()
)
}
}
private struct TitleBarButtonStyle: ButtonStyle {
let color: Color
func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(.white)
.padding(3)
.background(Circle().fill(color))
.scaleEffect(configuration.isPressed ? 0.9 : 1.0)
}
}
#Preview {
VStack {
TranscriptionFallbackView(
transcriptionText: "Short text.",
onCopy: {},
onClose: {},
onTextChange: nil
)
TranscriptionFallbackView(
transcriptionText: "This is a much longer piece of transcription text to demonstrate how the view will adaptively resize to accommodate more content while still respecting the maximum constraints.",
onCopy: {},
onClose: {},
onTextChange: nil
)
}
.padding()
}