vOOice/VoiceInk/PowerMode/PowerModeSessionManager.swift
2025-08-05 10:53:16 +05:45

211 lines
8.0 KiB
Swift

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)
}
}