feat: recording capsule visible in fullscreen and clicking doesnt focus main window (#46)

This commit is contained in:
Owen Gretzinger
2025-07-25 17:31:20 -04:00
committed by GitHub
parent c8f52cdb1f
commit 2136563428
2 changed files with 53 additions and 54 deletions
-27
View File
@@ -44,19 +44,7 @@ struct MeetingnotesApp: App {
CommandGroup(after: .appInfo) { CommandGroup(after: .appInfo) {
CheckForUpdatesView(updater: updaterController.updater) CheckForUpdatesView(updater: updaterController.updater)
} }
// Hidden command group that handles audio level window notifications
CommandGroup(after: .windowArrangement) {
ShowAudioLevelsCommand()
}
} }
// Floating Audio Level Window
Window("", id: "audio-levels") {
AudioLevelWindowView()
}
.windowStyle(.plain)
.windowResizability(.contentSize)
.defaultPosition(.trailing)
} }
} }
@@ -70,18 +58,3 @@ struct CheckForUpdatesView: View {
.keyboardShortcut("u", modifiers: .command) .keyboardShortcut("u", modifiers: .command)
} }
} }
struct ShowAudioLevelsCommand: View {
@Environment(\.openWindow) private var openWindow
var body: some View {
// Empty view - we only need this for the notification observer
EmptyView()
.onReceive(NotificationCenter.default.publisher(for: .openAudioLevelWindow)) { _ in
openWindow(id: "audio-levels")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
AudioLevelWindowManager.shared.showWindow()
}
}
}
}
+53 -27
View File
@@ -76,7 +76,7 @@ struct AudioLevelWindowView: View {
@StateObject private var audioLevelManager = AudioLevelManager.shared @StateObject private var audioLevelManager = AudioLevelManager.shared
var body: some View { var body: some View {
let micLevel = audioLevelManager.isRecording ? audioLevelManager.micAudioLevel * 40 : 0 let micLevel = audioLevelManager.isRecording ? audioLevelManager.micAudioLevel * 30 : 0
let systemLevel = audioLevelManager.isRecording ? audioLevelManager.systemAudioLevel * 5 : 0 let systemLevel = audioLevelManager.isRecording ? audioLevelManager.systemAudioLevel * 5 : 0
return DancingAudioBars( return DancingAudioBars(
micLevel: micLevel, micLevel: micLevel,
@@ -124,42 +124,68 @@ class AudioLevelManager: ObservableObject {
} }
} }
// MARK: - Audio Level Window Manager // MARK: - Audio Level Window Manager using NSPanel
@MainActor @MainActor
class AudioLevelWindowManager: ObservableObject { class AudioLevelWindowManager: ObservableObject {
static let shared = AudioLevelWindowManager() static let shared = AudioLevelWindowManager()
private init() {} private var audioLevelPanel: NSPanel?
private init() {
setupPanel()
}
private func setupPanel() {
let panelRect = NSRect(x: 0, y: 0, width: 60, height: 80)
audioLevelPanel = NSPanel(
contentRect: panelRect,
styleMask: [.nonactivatingPanel, .borderless],
backing: .buffered,
defer: true
)
guard let panel = audioLevelPanel else { return }
// Configure panel properties inspired by the provided code
panel.level = .mainMenu
panel.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
panel.isFloatingPanel = true
panel.isMovableByWindowBackground = true
panel.hasShadow = true
panel.backgroundColor = .clear
panel.isOpaque = false
panel.identifier = NSUserInterfaceItemIdentifier("audio-levels")
// Set up the SwiftUI hosting view
let hostingView = NSHostingView(rootView: AudioLevelWindowView())
hostingView.frame = panelRect
panel.contentView = hostingView
// Position the panel in the top-right corner of the screen
if let screen = NSScreen.main {
let screenFrame = screen.visibleFrame
let panelFrame = NSRect(
x: screenFrame.maxX - panelRect.width - 20,
y: screenFrame.maxY - panelRect.height - 20,
width: panelRect.width,
height: panelRect.height
)
panel.setFrame(panelFrame, display: false)
}
}
func showWindow() { func showWindow() {
// Find the audio levels window and bring it to front guard let panel = audioLevelPanel else {
for window in NSApplication.shared.windows { setupPanel()
if window.identifier?.rawValue == "audio-levels" { showWindow()
window.orderFront(nil) return
window.level = .floating
window.collectionBehavior = [.canJoinAllSpaces, .stationary, .ignoresCycle]
window.isMovableByWindowBackground = true
window.hasShadow = true
window.styleMask = [.borderless]
return
}
} }
// If window not found, post a notification to trigger opening panel.orderFrontRegardless()
NotificationCenter.default.post(name: .openAudioLevelWindow, object: nil)
} }
func hideWindow() { func hideWindow() {
// Find the audio levels window and hide it audioLevelPanel?.orderOut(nil)
for window in NSApplication.shared.windows {
if window.identifier?.rawValue == "audio-levels" {
window.orderOut(nil)
return
}
}
} }
} }
extension Notification.Name {
static let openAudioLevelWindow = Notification.Name("openAudioLevelWindow")
}