Remove Hober Animation and toggle states; implement always-expanded state for mini-recorder
This commit is contained in:
parent
c37dfb09a1
commit
95c82f0014
@ -30,14 +30,12 @@ class MiniRecorderPanel: NSPanel {
|
||||
standardWindowButton(.closeButton)?.isHidden = true
|
||||
}
|
||||
|
||||
static func calculateWindowMetrics(expanded: Bool = false) -> NSRect {
|
||||
static func calculateWindowMetrics() -> NSRect {
|
||||
guard let screen = NSScreen.main else {
|
||||
return NSRect(x: 0, y: 0, width: expanded ? 160 : 70, height: 34)
|
||||
return NSRect(x: 0, y: 0, width: 160, height: 34)
|
||||
}
|
||||
|
||||
let compactWidth: CGFloat = 100
|
||||
let expandedWidth: CGFloat = 160
|
||||
let width = expanded ? expandedWidth : compactWidth
|
||||
let width: CGFloat = 160
|
||||
let height: CGFloat = 34
|
||||
let padding: CGFloat = 24
|
||||
|
||||
@ -55,29 +53,11 @@ class MiniRecorderPanel: NSPanel {
|
||||
}
|
||||
|
||||
func show() {
|
||||
let metrics = MiniRecorderPanel.calculateWindowMetrics(expanded: false)
|
||||
let metrics = MiniRecorderPanel.calculateWindowMetrics()
|
||||
setFrame(metrics, display: true)
|
||||
orderFrontRegardless()
|
||||
}
|
||||
|
||||
func expandWindow(completion: (() -> Void)? = nil) {
|
||||
let expandedMetrics = MiniRecorderPanel.calculateWindowMetrics(expanded: true)
|
||||
NSAnimationContext.runAnimationGroup({ context in
|
||||
context.duration = 0.25
|
||||
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
animator().setFrame(expandedMetrics, display: true)
|
||||
}, completionHandler: completion)
|
||||
}
|
||||
|
||||
func collapseWindow(completion: (() -> Void)? = nil) {
|
||||
let compactMetrics = MiniRecorderPanel.calculateWindowMetrics(expanded: false)
|
||||
NSAnimationContext.runAnimationGroup({ context in
|
||||
context.duration = 0.25
|
||||
context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
animator().setFrame(compactMetrics, display: true)
|
||||
}, completionHandler: completion)
|
||||
}
|
||||
|
||||
func hide(completion: @escaping () -> Void) {
|
||||
completion()
|
||||
}
|
||||
|
||||
@ -8,12 +8,6 @@ struct MiniRecorderView: View {
|
||||
|
||||
@State private var showPowerModePopover = false
|
||||
@State private var showEnhancementPromptPopover = false
|
||||
@State private var isHovering = false
|
||||
|
||||
// Computed property to check if any popover is currently showing
|
||||
private var isAnyPopoverShowing: Bool {
|
||||
showPowerModePopover || showEnhancementPromptPopover
|
||||
}
|
||||
|
||||
private var backgroundView: some View {
|
||||
ZStack {
|
||||
@ -41,27 +35,21 @@ struct MiniRecorderView: View {
|
||||
|
||||
private var contentLayout: some View {
|
||||
HStack(spacing: 0) {
|
||||
if windowManager.isExpanded {
|
||||
// Left button zone - only exists when expanded
|
||||
RecorderPromptButton(showPopover: $showEnhancementPromptPopover)
|
||||
.padding(.leading, 6)
|
||||
.transition(.scale(scale: 0.5).combined(with: .opacity))
|
||||
|
||||
Spacer()
|
||||
}
|
||||
// Left button zone - always visible
|
||||
RecorderPromptButton(showPopover: $showEnhancementPromptPopover)
|
||||
.padding(.leading, 6)
|
||||
|
||||
// Fixed visualizer zone - takes full width when compact
|
||||
Spacer()
|
||||
|
||||
// Fixed visualizer zone
|
||||
statusView
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
if windowManager.isExpanded {
|
||||
Spacer()
|
||||
|
||||
// Right button zone - only exists when expanded
|
||||
RecorderPowerModeButton(showPopover: $showPowerModePopover)
|
||||
.padding(.trailing, 6)
|
||||
.transition(.scale(scale: 0.5).combined(with: .opacity))
|
||||
}
|
||||
Spacer()
|
||||
|
||||
// Right button zone - always visible
|
||||
RecorderPowerModeButton(showPopover: $showPowerModePopover)
|
||||
.padding(.trailing, 6)
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
@ -83,25 +71,6 @@ struct MiniRecorderView: View {
|
||||
Group {
|
||||
if windowManager.isVisible {
|
||||
recorderCapsule
|
||||
.onHover { hovering in
|
||||
isHovering = hovering
|
||||
if hovering {
|
||||
windowManager.expand()
|
||||
} else {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
|
||||
// Only collapse if not hovering AND no popover is showing
|
||||
if !isHovering && !isAnyPopoverShowing {
|
||||
windowManager.collapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
// Set up the callback so WindowManager can check popover and hover state
|
||||
windowManager.shouldPreventCollapse = {
|
||||
isAnyPopoverShowing || isHovering
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,18 +3,11 @@ import AppKit
|
||||
|
||||
class MiniWindowManager: ObservableObject {
|
||||
@Published var isVisible = false
|
||||
@Published var isExpanded = false
|
||||
private var windowController: NSWindowController?
|
||||
private var miniPanel: MiniRecorderPanel?
|
||||
private let whisperState: WhisperState
|
||||
private let recorder: Recorder
|
||||
|
||||
// Callback to check if collapse should be prevented (e.g., when popovers are showing)
|
||||
var shouldPreventCollapse: (() -> Bool)?
|
||||
|
||||
// Debounced timer for auto-collapse
|
||||
private var debounceTimer: Timer?
|
||||
|
||||
init(whisperState: WhisperState, recorder: Recorder) {
|
||||
self.whisperState = whisperState
|
||||
self.recorder = recorder
|
||||
@ -22,7 +15,6 @@ class MiniWindowManager: ObservableObject {
|
||||
}
|
||||
|
||||
deinit {
|
||||
debounceTimer?.invalidate()
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
@ -33,50 +25,11 @@ class MiniWindowManager: ObservableObject {
|
||||
name: NSNotification.Name("HideMiniRecorder"),
|
||||
object: nil
|
||||
)
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(handleFeedbackNotification),
|
||||
name: .promptSelectionChanged,
|
||||
object: nil
|
||||
)
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(handleFeedbackNotification),
|
||||
name: .powerModeConfigurationApplied,
|
||||
object: nil
|
||||
)
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(handleFeedbackNotification),
|
||||
name: .enhancementToggleChanged,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func handleHideNotification() {
|
||||
hide()
|
||||
}
|
||||
|
||||
@objc private func handleFeedbackNotification() {
|
||||
guard isVisible else { return }
|
||||
|
||||
// Only expand if not already expanded
|
||||
if !isExpanded {
|
||||
expand()
|
||||
}
|
||||
|
||||
// Reset debounce timer - this cancels any existing timer and starts a new one
|
||||
debounceTimer?.invalidate()
|
||||
debounceTimer = Timer.scheduledTimer(withTimeInterval: 2.5, repeats: false) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
if self.isExpanded && !(self.shouldPreventCollapse?() ?? false) {
|
||||
self.collapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
func show() {
|
||||
if isVisible { return }
|
||||
|
||||
@ -87,35 +40,10 @@ class MiniWindowManager: ObservableObject {
|
||||
miniPanel?.show()
|
||||
}
|
||||
|
||||
func expand() {
|
||||
guard isVisible, !isExpanded else { return }
|
||||
|
||||
withAnimation(.easeInOut(duration: 0.25)) {
|
||||
isExpanded = true
|
||||
}
|
||||
|
||||
miniPanel?.expandWindow()
|
||||
}
|
||||
|
||||
func collapse() {
|
||||
guard isVisible, isExpanded else { return }
|
||||
|
||||
withAnimation(.easeInOut(duration: 0.25)) {
|
||||
isExpanded = false
|
||||
}
|
||||
|
||||
miniPanel?.collapseWindow()
|
||||
}
|
||||
|
||||
func hide() {
|
||||
guard isVisible else { return }
|
||||
|
||||
// Cancel any pending auto-collapse timer
|
||||
debounceTimer?.invalidate()
|
||||
debounceTimer = nil
|
||||
|
||||
self.isVisible = false
|
||||
self.isExpanded = false
|
||||
self.miniPanel?.hide { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.deinitializeWindow()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user