Enhance recorder with PowerMode popover
This commit is contained in:
parent
91b43171d6
commit
9704a1ba79
@ -7,11 +7,11 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
E10340512DE1F666008BCBE5 /* whisper.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E136D0102DA3EE57000E1E8A /* whisper.xcframework */; };
|
||||
E10340522DE1F666008BCBE5 /* whisper.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E136D0102DA3EE57000E1E8A /* whisper.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
E1A261122CC143AC00B233D1 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = E1A261112CC143AC00B233D1 /* KeyboardShortcuts */; };
|
||||
E1ADD45A2CC5352A00303ECB /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = E1ADD4592CC5352A00303ECB /* LaunchAtLogin */; };
|
||||
E1ADD45F2CC544F100303ECB /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E1ADD45E2CC544F100303ECB /* Sparkle */; };
|
||||
E1C7A8112DE06FC60034EDA0 /* whisper.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E11CB51D2DB1F8AF00F9F3ED /* whisper.xcframework */; };
|
||||
E1C7A8122DE06FC60034EDA0 /* whisper.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E11CB51D2DB1F8AF00F9F3ED /* whisper.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
E1F5FA7A2DA6CBF900B1FD8A /* Zip in Frameworks */ = {isa = PBXBuildFile; productRef = E1F5FA792DA6CBF900B1FD8A /* Zip */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
E1C7A8122DE06FC60034EDA0 /* whisper.xcframework in Embed Frameworks */,
|
||||
E10340522DE1F666008BCBE5 /* whisper.xcframework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -80,7 +80,7 @@
|
||||
E1ADD45A2CC5352A00303ECB /* LaunchAtLogin in Frameworks */,
|
||||
E1ADD45F2CC544F100303ECB /* Sparkle in Frameworks */,
|
||||
E1A261122CC143AC00B233D1 /* KeyboardShortcuts in Frameworks */,
|
||||
E1C7A8112DE06FC60034EDA0 /* whisper.xcframework in Frameworks */,
|
||||
E10340512DE1F666008BCBE5 /* whisper.xcframework in Frameworks */,
|
||||
E1F5FA7A2DA6CBF900B1FD8A /* Zip in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@ -76,10 +76,12 @@ class PowerModeManager: ObservableObject {
|
||||
@Published var configurations: [PowerModeConfig] = []
|
||||
@Published var defaultConfig: PowerModeConfig
|
||||
@Published var isPowerModeEnabled: Bool
|
||||
@Published var activeConfiguration: PowerModeConfig?
|
||||
|
||||
private let configKey = "powerModeConfigurationsV2"
|
||||
private let defaultConfigKey = "defaultPowerModeConfigV2"
|
||||
private let powerModeEnabledKey = "isPowerModeEnabled"
|
||||
private let activeConfigIdKey = "activeConfigurationId"
|
||||
|
||||
private init() {
|
||||
// Load power mode enabled state or default to false if not set
|
||||
@ -111,6 +113,20 @@ class PowerModeManager: ObservableObject {
|
||||
saveDefaultConfig()
|
||||
}
|
||||
loadConfigurations()
|
||||
|
||||
// Set the active configuration, either from saved ID or default to the default config
|
||||
if let activeConfigIdString = UserDefaults.standard.string(forKey: activeConfigIdKey),
|
||||
let activeConfigId = UUID(uuidString: activeConfigIdString) {
|
||||
if let savedConfig = configurations.first(where: { $0.id == activeConfigId }) {
|
||||
activeConfiguration = savedConfig
|
||||
} else if activeConfigId == defaultConfig.id {
|
||||
activeConfiguration = defaultConfig
|
||||
} else {
|
||||
activeConfiguration = defaultConfig
|
||||
}
|
||||
} else {
|
||||
activeConfiguration = defaultConfig
|
||||
}
|
||||
}
|
||||
|
||||
private func loadConfigurations() {
|
||||
@ -237,4 +253,16 @@ class PowerModeManager: ObservableObject {
|
||||
func savePowerModeEnabled() {
|
||||
UserDefaults.standard.set(isPowerModeEnabled, forKey: powerModeEnabledKey)
|
||||
}
|
||||
|
||||
// Set active configuration
|
||||
func setActiveConfiguration(_ config: PowerModeConfig) {
|
||||
activeConfiguration = config
|
||||
UserDefaults.standard.set(config.id.uuidString, forKey: activeConfigIdKey)
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
|
||||
// Get current active configuration
|
||||
var currentActiveConfiguration: PowerModeConfig {
|
||||
return activeConfiguration ?? defaultConfig
|
||||
}
|
||||
}
|
||||
91
VoiceInk/PowerMode/PowerModePopover.swift
Normal file
91
VoiceInk/PowerMode/PowerModePopover.swift
Normal file
@ -0,0 +1,91 @@
|
||||
import SwiftUI
|
||||
|
||||
// Power Mode Popover for recorder views
|
||||
struct PowerModePopover: View {
|
||||
@ObservedObject var powerModeManager = PowerModeManager.shared
|
||||
@State private var selectedConfig: PowerModeConfig?
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Select Power Mode")
|
||||
.font(.headline)
|
||||
.foregroundColor(.white.opacity(0.9))
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
|
||||
Divider()
|
||||
.background(Color.white.opacity(0.1))
|
||||
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
// Default Configuration
|
||||
PowerModeRow(
|
||||
config: powerModeManager.defaultConfig,
|
||||
isSelected: selectedConfig?.id == powerModeManager.defaultConfig.id,
|
||||
action: {
|
||||
powerModeManager.setActiveConfiguration(powerModeManager.defaultConfig)
|
||||
selectedConfig = powerModeManager.defaultConfig
|
||||
}
|
||||
)
|
||||
|
||||
// Custom Configurations
|
||||
ForEach(powerModeManager.configurations) { config in
|
||||
PowerModeRow(
|
||||
config: config,
|
||||
isSelected: selectedConfig?.id == config.id,
|
||||
action: {
|
||||
powerModeManager.setActiveConfiguration(config)
|
||||
selectedConfig = config
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
.frame(width: 180)
|
||||
.frame(maxHeight: 300)
|
||||
.padding(.vertical, 8)
|
||||
.background(Color.black)
|
||||
.environment(\.colorScheme, .dark)
|
||||
.onAppear {
|
||||
// Set the initially selected configuration
|
||||
selectedConfig = powerModeManager.activeConfiguration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Row view for each power mode in the popover
|
||||
struct PowerModeRow: View {
|
||||
let config: PowerModeConfig
|
||||
let isSelected: Bool
|
||||
let action: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
HStack(spacing: 8) {
|
||||
// Always use the emoji from the configuration
|
||||
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")
|
||||
.foregroundColor(.green)
|
||||
.font(.system(size: 10))
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.padding(.vertical, 4)
|
||||
.padding(.horizontal, 8)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.background(isSelected ? Color.white.opacity(0.1) : Color.clear)
|
||||
.cornerRadius(4)
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,8 @@ struct MiniRecorderView: View {
|
||||
@ObservedObject var whisperState: WhisperState
|
||||
@ObservedObject var recorder: Recorder
|
||||
@EnvironmentObject var windowManager: MiniWindowManager
|
||||
@State private var showPromptPopover = false
|
||||
@State private var showPowerModePopover = false
|
||||
@ObservedObject private var powerModeManager = PowerModeManager.shared
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
@ -49,36 +50,7 @@ struct MiniRecorderView: View {
|
||||
.frame(width: 18)
|
||||
.padding(.leading, -4)
|
||||
|
||||
// AI Enhancement Toggle
|
||||
if let enhancementService = whisperState.getEnhancementService() {
|
||||
NotchToggleButton(
|
||||
isEnabled: enhancementService.isEnhancementEnabled,
|
||||
icon: "sparkles",
|
||||
color: .blue
|
||||
) {
|
||||
enhancementService.isEnhancementEnabled.toggle()
|
||||
}
|
||||
.frame(width: 18)
|
||||
.disabled(!enhancementService.isConfigured)
|
||||
}
|
||||
|
||||
// Custom Prompt Toggle and Selector
|
||||
if let enhancementService = whisperState.getEnhancementService() {
|
||||
NotchToggleButton(
|
||||
isEnabled: enhancementService.isEnhancementEnabled,
|
||||
icon: enhancementService.activePrompt?.icon.rawValue ?? "text.badge.checkmark",
|
||||
color: .green
|
||||
) {
|
||||
showPromptPopover.toggle()
|
||||
}
|
||||
.frame(width: 18)
|
||||
.disabled(!enhancementService.isEnhancementEnabled)
|
||||
.popover(isPresented: $showPromptPopover, arrowEdge: .bottom) {
|
||||
NotchPromptPopover(enhancementService: enhancementService)
|
||||
}
|
||||
}
|
||||
|
||||
// Visualizer
|
||||
// Visualizer - moved to middle position
|
||||
Group {
|
||||
if whisperState.isProcessing {
|
||||
NotchStaticVisualizer(color: .white)
|
||||
@ -91,7 +63,24 @@ struct MiniRecorderView: View {
|
||||
}
|
||||
}
|
||||
.frame(width: 18)
|
||||
|
||||
// Empty space for future use
|
||||
Spacer()
|
||||
.frame(width: 18)
|
||||
|
||||
// Power Mode Button - moved to last position
|
||||
NotchToggleButton(
|
||||
isEnabled: powerModeManager.isPowerModeEnabled,
|
||||
icon: powerModeManager.currentActiveConfiguration.emoji,
|
||||
color: .orange
|
||||
) {
|
||||
showPowerModePopover.toggle()
|
||||
}
|
||||
.frame(width: 18)
|
||||
.padding(.trailing, -4)
|
||||
.popover(isPresented: $showPowerModePopover, arrowEdge: .bottom) {
|
||||
PowerModePopover()
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 8)
|
||||
|
||||
@ -5,7 +5,8 @@ struct NotchRecorderView: View {
|
||||
@ObservedObject var recorder: Recorder
|
||||
@EnvironmentObject var windowManager: NotchWindowManager
|
||||
@State private var isHovering = false
|
||||
@State private var showPromptPopover = false
|
||||
@State private var showPowerModePopover = false
|
||||
@ObservedObject private var powerModeManager = PowerModeManager.shared
|
||||
|
||||
private var menuBarHeight: CGFloat {
|
||||
if let screen = NSScreen.main {
|
||||
@ -46,18 +47,9 @@ struct NotchRecorderView: View {
|
||||
}
|
||||
.frame(width: 22)
|
||||
|
||||
// AI Enhancement Toggle
|
||||
if let enhancementService = whisperState.getEnhancementService() {
|
||||
NotchToggleButton(
|
||||
isEnabled: enhancementService.isEnhancementEnabled,
|
||||
icon: "sparkles",
|
||||
color: .blue
|
||||
) {
|
||||
enhancementService.isEnhancementEnabled.toggle()
|
||||
}
|
||||
// Empty space for future use
|
||||
Spacer()
|
||||
.frame(width: 22)
|
||||
.disabled(!enhancementService.isConfigured)
|
||||
}
|
||||
}
|
||||
.frame(width: 44) // Fixed width for controls
|
||||
.padding(.leading, 16)
|
||||
@ -70,23 +62,7 @@ struct NotchRecorderView: View {
|
||||
|
||||
// Right side group with fixed width
|
||||
HStack(spacing: 8) {
|
||||
// Custom Prompt Toggle and Selector
|
||||
if let enhancementService = whisperState.getEnhancementService() {
|
||||
NotchToggleButton(
|
||||
isEnabled: enhancementService.isEnhancementEnabled,
|
||||
icon: enhancementService.activePrompt?.icon.rawValue ?? "text.badge.checkmark",
|
||||
color: .green
|
||||
) {
|
||||
showPromptPopover.toggle()
|
||||
}
|
||||
.frame(width: 22)
|
||||
.disabled(!enhancementService.isEnhancementEnabled)
|
||||
.popover(isPresented: $showPromptPopover, arrowEdge: .bottom) {
|
||||
NotchPromptPopover(enhancementService: enhancementService)
|
||||
}
|
||||
}
|
||||
|
||||
// Visualizer
|
||||
// Visualizer - moved to first position
|
||||
Group {
|
||||
if whisperState.isProcessing {
|
||||
NotchStaticVisualizer(color: .white)
|
||||
@ -99,6 +75,19 @@ struct NotchRecorderView: View {
|
||||
}
|
||||
}
|
||||
.frame(width: 22)
|
||||
|
||||
// Power Mode Button - moved to second position
|
||||
NotchToggleButton(
|
||||
isEnabled: powerModeManager.isPowerModeEnabled,
|
||||
icon: powerModeManager.currentActiveConfiguration.emoji,
|
||||
color: .orange
|
||||
) {
|
||||
showPowerModePopover.toggle()
|
||||
}
|
||||
.frame(width: 22)
|
||||
.popover(isPresented: $showPowerModePopover, arrowEdge: .bottom) {
|
||||
PowerModePopover()
|
||||
}
|
||||
}
|
||||
.frame(width: 44) // Fixed width for controls
|
||||
.padding(.trailing, 16)
|
||||
@ -195,15 +184,9 @@ struct NotchToggleButton: View {
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(isEnabled ? color.opacity(0.2) : Color(red: 0.4, green: 0.4, blue: 0.45).opacity(0.2))
|
||||
.frame(width: 20, height: 20)
|
||||
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundColor(isEnabled ? color : .white.opacity(0.6))
|
||||
}
|
||||
Text(icon)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(isEnabled ? .white : .white.opacity(0.6))
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user