Fix issues with button popovers
This commit is contained in:
parent
154748c1aa
commit
8abd8247af
@ -92,7 +92,7 @@ import Foundation
|
||||
name: "parakeet-tdt-0.6b",
|
||||
displayName: "Parakeet V3",
|
||||
description: "NVIDIA's ASR model V3 for lightning-fast transcription with multi-lingual(English + European) support.",
|
||||
size: "500 MB",
|
||||
size: "630 MB",
|
||||
speed: 0.99,
|
||||
accuracy: 0.94,
|
||||
ramUsage: 0.8,
|
||||
|
||||
@ -77,12 +77,12 @@ struct PowerModeRow: View {
|
||||
HStack(spacing: 8) {
|
||||
Text(config.emoji)
|
||||
.font(.system(size: 14))
|
||||
|
||||
|
||||
Text(config.name)
|
||||
.foregroundColor(.white.opacity(0.9))
|
||||
.font(.system(size: 13))
|
||||
.lineLimit(1)
|
||||
|
||||
|
||||
if isSelected {
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
@ -90,9 +90,10 @@ struct PowerModeRow: View {
|
||||
.font(.system(size: 10))
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.vertical, 4)
|
||||
.padding(.horizontal, 8)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.background(isSelected ? Color.white.opacity(0.1) : Color.clear)
|
||||
|
||||
@ -73,12 +73,12 @@ struct EnhancementPromptRow: View {
|
||||
Image(systemName: prompt.icon.rawValue)
|
||||
.font(.system(size: 14))
|
||||
.foregroundColor(isDisabled ? .white.opacity(0.4) : .white.opacity(0.7))
|
||||
|
||||
|
||||
Text(prompt.title)
|
||||
.foregroundColor(isDisabled ? .white.opacity(0.4) : .white.opacity(0.9))
|
||||
.font(.system(size: 13))
|
||||
.lineLimit(1)
|
||||
|
||||
|
||||
if isSelected {
|
||||
Spacer()
|
||||
Image(systemName: "checkmark")
|
||||
@ -86,9 +86,10 @@ struct EnhancementPromptRow: View {
|
||||
.font(.system(size: 10))
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.vertical, 4)
|
||||
.padding(.horizontal, 8)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.background(isSelected ? Color.white.opacity(0.1) : Color.clear)
|
||||
|
||||
@ -6,8 +6,7 @@ struct MiniRecorderView: View {
|
||||
@EnvironmentObject var windowManager: MiniWindowManager
|
||||
@EnvironmentObject private var enhancementService: AIEnhancementService
|
||||
|
||||
@State private var showPowerModePopover = false
|
||||
@State private var showEnhancementPromptPopover = false
|
||||
@State private var activePopover: ActivePopoverState = .none
|
||||
|
||||
private var backgroundView: some View {
|
||||
ZStack {
|
||||
@ -36,19 +35,19 @@ struct MiniRecorderView: View {
|
||||
private var contentLayout: some View {
|
||||
HStack(spacing: 0) {
|
||||
// Left button zone - always visible
|
||||
RecorderPromptButton(showPopover: $showEnhancementPromptPopover)
|
||||
RecorderPromptButton(activePopover: $activePopover)
|
||||
.padding(.leading, 7)
|
||||
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
// Fixed visualizer zone
|
||||
statusView
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
// Right button zone - always visible
|
||||
RecorderPowerModeButton(showPopover: $showPowerModePopover)
|
||||
RecorderPowerModeButton(activePopover: $activePopover)
|
||||
.padding(.trailing, 7)
|
||||
}
|
||||
.padding(.vertical, 9)
|
||||
|
||||
@ -5,8 +5,7 @@ struct NotchRecorderView: View {
|
||||
@ObservedObject var recorder: Recorder
|
||||
@EnvironmentObject var windowManager: NotchWindowManager
|
||||
@State private var isHovering = false
|
||||
@State private var showPowerModePopover = false
|
||||
@State private var showEnhancementPromptPopover = false
|
||||
@State private var activePopover: ActivePopoverState = .none
|
||||
@ObservedObject private var powerModeManager = PowerModeManager.shared
|
||||
|
||||
@EnvironmentObject private var enhancementService: AIEnhancementService
|
||||
@ -32,19 +31,19 @@ struct NotchRecorderView: View {
|
||||
}
|
||||
|
||||
private var leftSection: some View {
|
||||
HStack(spacing: 8) {
|
||||
HStack(spacing: 12) {
|
||||
RecorderPromptButton(
|
||||
showPopover: $showEnhancementPromptPopover,
|
||||
activePopover: $activePopover,
|
||||
buttonSize: 22,
|
||||
padding: EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||
)
|
||||
|
||||
|
||||
RecorderPowerModeButton(
|
||||
showPopover: $showPowerModePopover,
|
||||
activePopover: $activePopover,
|
||||
buttonSize: 22,
|
||||
padding: EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||
)
|
||||
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.frame(width: 84)
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Shared Popover State
|
||||
enum ActivePopoverState {
|
||||
case none
|
||||
case enhancement
|
||||
case power
|
||||
}
|
||||
|
||||
// MARK: - Hover Interaction Manager
|
||||
class HoverInteraction: ObservableObject {
|
||||
@Published var isHovered: Bool = false
|
||||
@ -144,13 +151,15 @@ struct ProgressAnimation: View {
|
||||
// MARK: - Prompt Button Component
|
||||
struct RecorderPromptButton: View {
|
||||
@EnvironmentObject private var enhancementService: AIEnhancementService
|
||||
@Binding var showPopover: Bool
|
||||
@Binding var activePopover: ActivePopoverState
|
||||
let buttonSize: CGFloat
|
||||
let padding: EdgeInsets
|
||||
@StateObject private var hoverInteraction = HoverInteraction()
|
||||
@State private var isHoveringEnhancement: Bool = false
|
||||
@State private var isHoveringEnhancementPopover: Bool = false
|
||||
@State private var enhancementDismissWorkItem: DispatchWorkItem?
|
||||
|
||||
init(showPopover: Binding<Bool>, buttonSize: CGFloat = 28, padding: EdgeInsets = EdgeInsets(top: 0, leading: 7, bottom: 0, trailing: 0)) {
|
||||
self._showPopover = showPopover
|
||||
init(activePopover: Binding<ActivePopoverState>, buttonSize: CGFloat = 28, padding: EdgeInsets = EdgeInsets(top: 0, leading: 7, bottom: 0, trailing: 0)) {
|
||||
self._activePopover = activePopover
|
||||
self.buttonSize = buttonSize
|
||||
self.padding = padding
|
||||
}
|
||||
@ -163,23 +172,42 @@ struct RecorderPromptButton: View {
|
||||
disabled: false
|
||||
) {
|
||||
if enhancementService.isEnhancementEnabled {
|
||||
showPopover.toggle()
|
||||
activePopover = activePopover == .enhancement ? .none : .enhancement
|
||||
} else {
|
||||
enhancementService.isEnhancementEnabled = true
|
||||
}
|
||||
}
|
||||
.frame(width: buttonSize)
|
||||
.padding(padding)
|
||||
.onHover { hoverInteraction.setHover(on: $0) }
|
||||
.popover(isPresented: $showPopover, arrowEdge: .bottom) {
|
||||
.onHover {
|
||||
isHoveringEnhancement = $0
|
||||
syncEnhancementPopoverVisibility()
|
||||
}
|
||||
.popover(isPresented: .constant(activePopover == .enhancement), arrowEdge: .bottom) {
|
||||
EnhancementPromptPopover()
|
||||
.environmentObject(enhancementService)
|
||||
.onHover { hoverInteraction.setHover(on: $0) }
|
||||
.onHover {
|
||||
isHoveringEnhancementPopover = $0
|
||||
syncEnhancementPopoverVisibility()
|
||||
}
|
||||
}
|
||||
.onChange(of: hoverInteraction.isHovered) { isHovered in
|
||||
if isHovered != showPopover {
|
||||
showPopover = isHovered
|
||||
}
|
||||
|
||||
private func syncEnhancementPopoverVisibility() {
|
||||
let shouldShow = isHoveringEnhancement || isHoveringEnhancementPopover
|
||||
if shouldShow {
|
||||
enhancementDismissWorkItem?.cancel()
|
||||
enhancementDismissWorkItem = nil
|
||||
activePopover = .enhancement
|
||||
} else {
|
||||
enhancementDismissWorkItem?.cancel()
|
||||
let work = DispatchWorkItem { [activePopoverBinding = $activePopover] in
|
||||
if activePopoverBinding.wrappedValue == .enhancement {
|
||||
activePopoverBinding.wrappedValue = .none
|
||||
}
|
||||
}
|
||||
enhancementDismissWorkItem = work
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: work)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,13 +215,15 @@ struct RecorderPromptButton: View {
|
||||
// MARK: - Power Mode Button Component
|
||||
struct RecorderPowerModeButton: View {
|
||||
@ObservedObject private var powerModeManager = PowerModeManager.shared
|
||||
@Binding var showPopover: Bool
|
||||
@Binding var activePopover: ActivePopoverState
|
||||
let buttonSize: CGFloat
|
||||
let padding: EdgeInsets
|
||||
@StateObject private var hoverInteraction = HoverInteraction()
|
||||
@State private var isHoveringPower: Bool = false
|
||||
@State private var isHoveringPowerPopover: Bool = false
|
||||
@State private var powerDismissWorkItem: DispatchWorkItem?
|
||||
|
||||
init(showPopover: Binding<Bool>, buttonSize: CGFloat = 28, padding: EdgeInsets = EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 7)) {
|
||||
self._showPopover = showPopover
|
||||
init(activePopover: Binding<ActivePopoverState>, buttonSize: CGFloat = 28, padding: EdgeInsets = EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 7)) {
|
||||
self._activePopover = activePopover
|
||||
self.buttonSize = buttonSize
|
||||
self.padding = padding
|
||||
}
|
||||
@ -205,19 +235,38 @@ struct RecorderPowerModeButton: View {
|
||||
color: .orange,
|
||||
disabled: powerModeManager.enabledConfigurations.isEmpty
|
||||
) {
|
||||
showPopover.toggle()
|
||||
activePopover = activePopover == .power ? .none : .power
|
||||
}
|
||||
.frame(width: buttonSize)
|
||||
.padding(padding)
|
||||
.onHover { hoverInteraction.setHover(on: $0) }
|
||||
.popover(isPresented: $showPopover, arrowEdge: .bottom) {
|
||||
PowerModePopover()
|
||||
.onHover { hoverInteraction.setHover(on: $0) }
|
||||
.onHover {
|
||||
isHoveringPower = $0
|
||||
syncPowerPopoverVisibility()
|
||||
}
|
||||
.onChange(of: hoverInteraction.isHovered) { isHovered in
|
||||
if isHovered != showPopover {
|
||||
showPopover = isHovered
|
||||
.popover(isPresented: .constant(activePopover == .power), arrowEdge: .bottom) {
|
||||
PowerModePopover()
|
||||
.onHover {
|
||||
isHoveringPowerPopover = $0
|
||||
syncPowerPopoverVisibility()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func syncPowerPopoverVisibility() {
|
||||
let shouldShow = isHoveringPower || isHoveringPowerPopover
|
||||
if shouldShow {
|
||||
powerDismissWorkItem?.cancel()
|
||||
powerDismissWorkItem = nil
|
||||
activePopover = .power
|
||||
} else {
|
||||
powerDismissWorkItem?.cancel()
|
||||
let work = DispatchWorkItem { [activePopoverBinding = $activePopover] in
|
||||
if activePopoverBinding.wrappedValue == .power {
|
||||
activePopoverBinding.wrappedValue = .none
|
||||
}
|
||||
}
|
||||
powerDismissWorkItem = work
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: work)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user