Power Mode Keyboard Shortcuts:
- Add hotkeyShortcut property to PowerModeConfig for storing custom shortcuts
- Implement keyboard shortcut UI in Power Mode configuration view
- Add hotkey registration system in HotkeyManager to manage Power Mode shortcuts
- Support cleanup of shortcuts when Power Mode configurations are removed
- Post notifications when Power Mode configurations change
Explicit Power Mode Activation:
- Add optional powerModeId parameter to toggleRecord and toggleMiniRecorder
- Refactor ActiveWindowService.applyConfigurationForCurrentApp to applyConfiguration
- Support direct Power Mode activation via powerModeId instead of auto-detection
- Pass powerModeId through recording flow for explicit mode selection
Session Management Improvements:
- Fix auto-restore to preserve baseline when switching Power Modes mid-recording
- Only capture baseline state on first session creation
- Prevent subsequent beginSession calls from overwriting original baseline
- Ensure auto-restore returns to settings from before recording started
UI Refinements:
- Remove redundant "Record" label from keyboard shortcut recorder
Migrates vocabulary words and word replacements from UserDefaults to SwiftData for better data management and persistence.
Changes:
- Create VocabularyWord and WordReplacement SwiftData models
- Add dual ModelConfiguration setup (default.store for transcripts, dictionary.store for dictionary data)
- Implement DictionaryMigrationService for one-time UserDefaults→SwiftData migration
- Rename "Correct Spellings" to "Vocabulary" for clearer terminology
- Update all dictionary views to use @Query instead of manager classes
- Update all services to fetch from SwiftData using FetchDescriptor
- Enhance word replacement duplicate detection (now checks during add AND edit)
- Update import/export services to work with SwiftData
- Preserve all existing functionality with improved data integrity
Technical details:
- Separate store files: default.store (transcripts) + dictionary.store (vocabulary + replacements)
- Migration flag: "HasMigratedDictionaryToSwiftData_v2"
- All CRUD operations properly implemented with duplicate detection
- Create centralized registry for managing transcription services
- Replace duplicate switch statements across 4 manager classes
- Consolidate service initialization into single registry pattern
- Add cleanup method to registry for resource management
- Ensure fresh service registry on each transcription request
- Replace force-unwrapped URLs in cloud transcription services with safe guard statements
* GroqTranscriptionService: Add URL validation before use
* ElevenLabsTranscriptionService: Add URL validation before use
* MistralTranscriptionService: Add URL validation before use
* OpenAICompatibleTranscriptionService: Add URL validation before use
- Replace fatalError in VoiceInk.swift with graceful degradation
* Implement in-memory fallback when persistent storage fails
* Add user notification for storage issues
* Use proper logging instead of fatal crash
- Fix dictionary force unwrap in WhisperPrompt.swift
* Add safe fallback when default language prompt missing
* Prevent potential crash on dictionary access
- Wrap debug print statement in #if DEBUG directive
* Eliminate production logging overhead in VoiceInk.swift
These changes prevent 6+ potential crash scenarios while maintaining
full functionality with graceful error handling.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add atomic guards using ManagedAtomic to ensure continuations are resumed
exactly once in TaskDelegate, downloadFileWithProgress, and unzipCoreMLFile
functions. This resolves crashes during model downloads and transcription
when multiple callback paths race to resume the same continuation.
Changes:
- Import Atomics framework for atomic operations
- Add ManagedAtomic guards to prevent double resume
- Implement finishOnce helper pattern for consistent error handling
- Add proper cancellation handling in downloadFileWithProgress
Fixes crashes with stack trace: EXC_BREAKPOINT → _assertionFailure →
CheckedContinuation.resume(returning:) → VoiceInk [0x102dbfcc4, 0x102dc1174]