feat: add floating transcription panel with auto-scroll and copy
This commit is contained in:
parent
285b833ba9
commit
b26995b15b
1 changed files with 78 additions and 0 deletions
78
MyVoxtral/MyVoxtral/Views/TranscriptionWindow.swift
Normal file
78
MyVoxtral/MyVoxtral/Views/TranscriptionWindow.swift
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue