updated Powermode with session manager

This commit is contained in:
Beingpax 2025-08-05 10:53:16 +05:45
parent 5b43f3a2b3
commit a26ec91e1b
6 changed files with 244 additions and 109 deletions

View File

@ -4,6 +4,7 @@ import SwiftUI
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
updateActivationPolicy()
cleanupLegacyUserDefaults()
}
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
@ -42,4 +43,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NSApp.windows.first?.makeKeyAndOrderFront(nil)
}
}
private func cleanupLegacyUserDefaults() {
let defaults = UserDefaults.standard
defaults.removeObject(forKey: "defaultPowerModeConfigV2")
defaults.removeObject(forKey: "isPowerModeEnabled")
}
}

View File

@ -180,7 +180,7 @@ class MiniRecorderShortcutManager: ObservableObject {
if index < availableConfigurations.count {
let selectedConfig = availableConfigurations[index]
powerModeManager.setActiveConfiguration(selectedConfig)
await ActiveWindowService.shared.applyConfiguration(selectedConfig)
await PowerModeSessionManager.shared.beginSession(with: selectedConfig)
}
} else {
guard let enhancementService = await self.whisperState.getEnhancementService() else { return }

View File

@ -26,132 +26,42 @@ class ActiveWindowService: ObservableObject {
func applyConfigurationForCurrentApp() async {
guard let frontmostApp = NSWorkspace.shared.frontmostApplication,
let bundleIdentifier = frontmostApp.bundleIdentifier else { return }
let bundleIdentifier = frontmostApp.bundleIdentifier else {
await PowerModeSessionManager.shared.endSession()
return
}
await MainActor.run {
currentApplication = frontmostApp
}
var configToApply: PowerModeConfig?
if let browserType = BrowserType.allCases.first(where: { $0.bundleIdentifier == bundleIdentifier }) {
logger.debug("🌐 Detected Browser: \(browserType.displayName)")
do {
logger.debug("📝 Attempting to get URL from \(browserType.displayName)")
let currentURL = try await browserURLService.getCurrentURL(from: browserType)
logger.debug("📍 Successfully got URL: \(currentURL)")
if let config = PowerModeManager.shared.getConfigurationForURL(currentURL) {
logger.debug("⚙️ Found URL Configuration: \(config.name) for URL: \(currentURL)")
await MainActor.run {
PowerModeManager.shared.setActiveConfiguration(config)
}
await applyConfiguration(config)
return
} else {
logger.debug("📝 No URL configuration found for: \(currentURL)")
configToApply = config
}
} catch {
logger.error("❌ Failed to get URL from \(browserType.displayName): \(error.localizedDescription)")
}
}
if let config = PowerModeManager.shared.getConfigurationForApp(bundleIdentifier) {
if configToApply == nil {
configToApply = PowerModeManager.shared.getConfigurationForApp(bundleIdentifier)
}
if let config = configToApply {
await MainActor.run {
PowerModeManager.shared.setActiveConfiguration(config)
}
await applyConfiguration(config)
await PowerModeSessionManager.shared.beginSession(with: config)
} else {
await MainActor.run {
PowerModeManager.shared.setActiveConfiguration(nil)
}
}
}
/// Applies a specific configuration
func applyConfiguration(_ config: PowerModeConfig) async {
guard let enhancementService = enhancementService else { return }
// Capture current state before making changes
let wasScreenCaptureEnabled = await MainActor.run {
enhancementService.useScreenCaptureContext
}
let wasEnhancementEnabled = await MainActor.run {
enhancementService.isEnhancementEnabled
}
await MainActor.run {
enhancementService.isEnhancementEnabled = config.isAIEnhancementEnabled
enhancementService.useScreenCaptureContext = config.useScreenCapture
if config.isAIEnhancementEnabled {
if let promptId = config.selectedPrompt,
let uuid = UUID(uuidString: promptId) {
enhancementService.selectedPromptId = uuid
} else {
if let firstPrompt = enhancementService.allPrompts.first {
enhancementService.selectedPromptId = firstPrompt.id
}
}
}
if config.isAIEnhancementEnabled,
let aiService = enhancementService.getAIService() {
if let providerName = config.selectedAIProvider,
let provider = AIProvider(rawValue: providerName) {
aiService.selectedProvider = provider
if let model = config.selectedAIModel,
!model.isEmpty {
aiService.selectModel(model)
}
}
}
if let language = config.selectedLanguage {
UserDefaults.standard.set(language, forKey: "SelectedLanguage")
NotificationCenter.default.post(name: .languageDidChange, object: nil)
}
}
if let whisperState = self.whisperState,
let modelName = config.selectedTranscriptionModelName,
let selectedModel = await whisperState.allAvailableModels.first(where: { $0.name == modelName }) {
let currentModelName = await MainActor.run { whisperState.currentTranscriptionModel?.name }
// Only change the model if it's different from the current one.
if currentModelName != modelName {
// Set the new model as default. This works for both local and cloud models.
await whisperState.setDefaultTranscriptionModel(selectedModel)
switch selectedModel.provider {
case .local:
await whisperState.cleanupModelResources()
if let localModel = await whisperState.availableModels.first(where: { $0.name == selectedModel.name }) {
do {
try await whisperState.loadModel(localModel)
} catch {
logger.error("❌ Power Mode: Failed to load local model '\(localModel.name)': \(error.localizedDescription)")
}
}
case .parakeet:
await whisperState.cleanupModelResources()
default:
await whisperState.cleanupModelResources()
}
}
}
// Wait for UI changes and model loading to complete first
try? await Task.sleep(nanoseconds: 1_500_000_000) // 1.5 seconds
// Then check if we should capture
if config.isAIEnhancementEnabled && config.useScreenCapture {
await enhancementService.captureScreenContext()
await PowerModeSessionManager.shared.endSession()
}
}
}

View File

@ -72,7 +72,7 @@ struct PowerModePopover: View {
private func applySelectedConfiguration() {
Task {
if let config = selectedConfig {
await ActiveWindowService.shared.applyConfiguration(config)
await PowerModeSessionManager.shared.beginSession(with: config)
}
}
}

View File

@ -0,0 +1,210 @@
import Foundation
import AppKit
// Represents the state of the application that can be modified by a Power Mode.
// This struct captures the settings that will be temporarily overridden.
struct ApplicationState: Codable {
var isEnhancementEnabled: Bool
var useScreenCaptureContext: Bool
var selectedPromptId: String? // Storing as String for Codable simplicity
var selectedAIProvider: String?
var selectedAIModel: String?
var selectedLanguage: String?
var transcriptionModelName: String?
}
// Represents an active Power Mode session.
struct PowerModeSession: Codable {
let id: UUID
let startTime: Date
var originalState: ApplicationState
}
@MainActor
class PowerModeSessionManager {
static let shared = PowerModeSessionManager()
private let sessionKey = "powerModeActiveSession.v1"
private var whisperState: WhisperState?
private var enhancementService: AIEnhancementService?
private init() {
// Attempt to recover a session on startup in case of a crash.
recoverSession()
}
func configure(whisperState: WhisperState, enhancementService: AIEnhancementService) {
self.whisperState = whisperState
self.enhancementService = enhancementService
}
// Begins a new Power Mode session. It captures the current state,
// applies the new configuration, and saves the session.
func beginSession(with config: PowerModeConfig) async {
guard let whisperState = whisperState, let enhancementService = enhancementService else {
print("SessionManager not configured.")
return
}
// 1. Capture the current application state.
let originalState = ApplicationState(
isEnhancementEnabled: enhancementService.isEnhancementEnabled,
useScreenCaptureContext: enhancementService.useScreenCaptureContext,
selectedPromptId: enhancementService.selectedPromptId?.uuidString,
selectedAIProvider: enhancementService.getAIService()?.selectedProvider.rawValue,
selectedAIModel: enhancementService.getAIService()?.currentModel,
selectedLanguage: UserDefaults.standard.string(forKey: "SelectedLanguage"),
transcriptionModelName: whisperState.currentTranscriptionModel?.name
)
// 2. Create and save the session.
let newSession = PowerModeSession(
id: UUID(),
startTime: Date(),
originalState: originalState
)
saveSession(newSession)
// 3. Apply the new configuration's settings.
await applyConfiguration(config)
}
// Ends the current Power Mode session and restores the original state.
func endSession() async {
guard let session = loadSession() else { return }
// Restore the original state from the session.
await restoreState(session.originalState)
// Clear the session from UserDefaults.
clearSession()
}
// Applies the settings from a PowerModeConfig.
private func applyConfiguration(_ config: PowerModeConfig) async {
guard let enhancementService = enhancementService else { return }
await MainActor.run {
enhancementService.isEnhancementEnabled = config.isAIEnhancementEnabled
enhancementService.useScreenCaptureContext = config.useScreenCapture
if config.isAIEnhancementEnabled {
if let promptId = config.selectedPrompt, let uuid = UUID(uuidString: promptId) {
enhancementService.selectedPromptId = uuid
}
if let aiService = enhancementService.getAIService() {
if let providerName = config.selectedAIProvider, let provider = AIProvider(rawValue: providerName) {
aiService.selectedProvider = provider
}
if let model = config.selectedAIModel {
aiService.selectModel(model)
}
}
}
if let language = config.selectedLanguage {
UserDefaults.standard.set(language, forKey: "SelectedLanguage")
NotificationCenter.default.post(name: .languageDidChange, object: nil)
}
}
if let whisperState = whisperState,
let modelName = config.selectedTranscriptionModelName,
let selectedModel = await whisperState.allAvailableModels.first(where: { $0.name == modelName }),
whisperState.currentTranscriptionModel?.name != modelName {
await handleModelChange(to: selectedModel)
}
}
// Restores the application state from a saved state object.
private func restoreState(_ state: ApplicationState) async {
guard let enhancementService = enhancementService else { return }
await MainActor.run {
enhancementService.isEnhancementEnabled = state.isEnhancementEnabled
enhancementService.useScreenCaptureContext = state.useScreenCaptureContext
enhancementService.selectedPromptId = state.selectedPromptId.flatMap(UUID.init)
if let aiService = enhancementService.getAIService() {
if let providerName = state.selectedAIProvider, let provider = AIProvider(rawValue: providerName) {
aiService.selectedProvider = provider
}
if let model = state.selectedAIModel {
aiService.selectModel(model)
}
}
if let language = state.selectedLanguage {
UserDefaults.standard.set(language, forKey: "SelectedLanguage")
NotificationCenter.default.post(name: .languageDidChange, object: nil)
}
}
if let whisperState = whisperState,
let modelName = state.transcriptionModelName,
let selectedModel = await whisperState.allAvailableModels.first(where: { $0.name == modelName }),
whisperState.currentTranscriptionModel?.name != modelName {
await handleModelChange(to: selectedModel)
}
}
// Handles the logic for switching transcription models.
private func handleModelChange(to newModel: any TranscriptionModel) async {
guard let whisperState = whisperState else { return }
await whisperState.setDefaultTranscriptionModel(newModel)
switch newModel.provider {
case .local:
await whisperState.cleanupModelResources()
if let localModel = await whisperState.availableModels.first(where: { $0.name == newModel.name }) {
do {
try await whisperState.loadModel(localModel)
} catch {
// Log error appropriately
print("Power Mode: Failed to load local model '\(localModel.name)': \(error)")
}
}
case .parakeet:
await whisperState.cleanupModelResources()
// Parakeet models are loaded on demand, so we only need to clean up.
default:
await whisperState.cleanupModelResources()
}
}
private func recoverSession() {
guard let session = loadSession() else { return }
print("Recovering abandoned Power Mode session.")
Task {
await endSession()
}
}
// MARK: - UserDefaults Persistence
private func saveSession(_ session: PowerModeSession) {
do {
let data = try JSONEncoder().encode(session)
UserDefaults.standard.set(data, forKey: sessionKey)
} catch {
print("Error saving Power Mode session: \(error)")
}
}
private func loadSession() -> PowerModeSession? {
guard let data = UserDefaults.standard.data(forKey: sessionKey) else { return nil }
do {
return try JSONDecoder().decode(PowerModeSession.self, from: data)
} catch {
print("Error loading Power Mode session: \(error)")
return nil
}
}
private func clearSession() {
UserDefaults.standard.removeObject(forKey: sessionKey)
}
}

View File

@ -106,6 +106,11 @@ class WhisperState: NSObject, ObservableObject {
super.init()
// Configure the session manager
if let enhancementService = enhancementService {
PowerModeSessionManager.shared.configure(whisperState: self, enhancementService: enhancementService)
}
// Set the whisperState reference after super.init()
self.localTranscriptionService = LocalTranscriptionService(modelsDirectory: self.modelsDirectory, whisperState: self)
@ -214,6 +219,7 @@ class WhisperState: NSObject, ObservableObject {
await MainActor.run {
recordingState = .idle
}
await PowerModeSessionManager.shared.endSession()
await cleanupModelResources()
return
}
@ -367,6 +373,7 @@ class WhisperState: NSObject, ObservableObject {
}
await self.dismissMiniRecorder()
await PowerModeSessionManager.shared.endSession()
} catch {
do {
@ -400,6 +407,7 @@ class WhisperState: NSObject, ObservableObject {
}
await self.dismissMiniRecorder()
await PowerModeSessionManager.shared.endSession()
}
}