diff --git a/MyVoxtral/MyVoxtral/Views/TranscriptionWindow.swift b/MyVoxtral/MyVoxtral/Views/TranscriptionWindow.swift new file mode 100644 index 0000000..57b25c5 --- /dev/null +++ b/MyVoxtral/MyVoxtral/Views/TranscriptionWindow.swift @@ -0,0 +1,78 @@ +import SwiftUI +import AppKit + +struct TranscriptionContentView: View { + @ObservedObject var manager: TranscriptionManager + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack { + Circle() + .fill(manager.isRecording ? .red : .gray) + .frame(width: 8, height: 8) + Text(manager.isRecording ? "Recording..." : "Idle") + .font(.caption) + .foregroundStyle(.secondary) + Spacer() + Button { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(manager.currentText, forType: .string) + } label: { + Image(systemName: "doc.on.doc") + } + .buttonStyle(.borderless) + .disabled(manager.currentText.isEmpty) + } + + ScrollViewReader { proxy in + ScrollView { + Text(manager.currentText.isEmpty ? "Transcription will appear here..." : manager.currentText) + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundStyle(manager.currentText.isEmpty ? .secondary : .primary) + .textSelection(.enabled) + .id("bottom") + } + .onChange(of: manager.currentText) { + proxy.scrollTo("bottom", anchor: .bottom) + } + } + } + .padding() + .frame(width: 320, height: 200) + } +} + +final class TranscriptionPanel { + private var panel: NSPanel? + private let manager: TranscriptionManager + + init(manager: TranscriptionManager) { + self.manager = manager + } + + func show() { + if panel == nil { + let panel = NSPanel( + contentRect: NSRect(x: 0, y: 0, width: 320, height: 200), + styleMask: [.titled, .closable, .resizable, .nonactivatingPanel, .utilityWindow], + backing: .buffered, + defer: false + ) + panel.title = "MyVoxtral" + panel.isFloatingPanel = true + panel.level = .floating + panel.contentView = NSHostingView(rootView: TranscriptionContentView(manager: manager)) + panel.center() + self.panel = panel + } + panel?.orderFront(nil) + } + + func hide() { + panel?.orderOut(nil) + } + + var isVisible: Bool { + panel?.isVisible ?? false + } +}