- Fix branding references (VoiceLink → VoiceInk) in documentation - Correct keyboard shortcut documentation to Cmd+Shift+/ for proper Cmd+? - Remove force unwrap in webhook example code Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
587 lines
17 KiB
Markdown
587 lines
17 KiB
Markdown
# Quality of Life Improvements - Changelog
|
|
|
|
**Date:** November 3, 2025
|
|
**Version:** 1.0
|
|
**Status:** Ready for Fork Integration & Upstream PR
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
This document details the quality of life improvements implemented for VoiceLink Community. These changes enhance user experience, improve accessibility, and establish better developer infrastructure.
|
|
|
|
## Summary of Changes
|
|
|
|
### 🎯 User-Facing Improvements (5 features)
|
|
|
|
1. **Recording Duration Indicator** ✅
|
|
2. **Enhanced Recording Status Display** ✅
|
|
3. **Visible Cancel Button** ✅
|
|
4. **Keyboard Shortcut Cheat Sheet** ✅
|
|
5. **Structured Logging System** ✅
|
|
|
|
---
|
|
|
|
## Detailed Changes
|
|
|
|
### 1. Recording Duration Indicator
|
|
|
|
**Priority:** 🔴 Critical
|
|
**Files Modified:**
|
|
- `VoiceInk/Recorder.swift`
|
|
- `VoiceInk/Views/Recorder/RecorderComponents.swift`
|
|
- `VoiceInk/Views/Recorder/MiniRecorderView.swift`
|
|
- `VoiceInk/Views/Recorder/NotchRecorderView.swift`
|
|
|
|
**What Changed:**
|
|
- Added `@Published var recordingDuration: TimeInterval` to track recording time
|
|
- Implemented real-time duration updates every 0.1 seconds
|
|
- Display duration in MM:SS format during recording
|
|
- Added accessibility labels for screen readers
|
|
|
|
**Code Highlights:**
|
|
```swift
|
|
// Recorder.swift - Duration tracking
|
|
@Published var recordingDuration: TimeInterval = 0
|
|
private var recordingStartTime: Date?
|
|
private var durationUpdateTask: Task<Void, Never>?
|
|
|
|
durationUpdateTask = Task {
|
|
while recorder != nil && !Task.isCancelled {
|
|
if let startTime = recordingStartTime {
|
|
await MainActor.run {
|
|
recordingDuration = Date().timeIntervalSince(startTime)
|
|
}
|
|
}
|
|
try? await Task.sleep(nanoseconds: 100_000_000)
|
|
}
|
|
}
|
|
|
|
// RecorderComponents.swift - Display formatting
|
|
Text(formatDuration(recordingDuration))
|
|
.font(.system(.caption2, design: .monospaced))
|
|
.foregroundColor(.white.opacity(0.8))
|
|
|
|
private func formatDuration(_ duration: TimeInterval) -> String {
|
|
let minutes = Int(duration) / 60
|
|
let seconds = Int(duration) % 60
|
|
return String(format: "%02d:%02d", minutes, seconds)
|
|
}
|
|
```
|
|
|
|
**User Benefits:**
|
|
- Know exactly how long they've been recording
|
|
- Prevent accidentally long recordings
|
|
- Visual confirmation that recording is active
|
|
|
|
---
|
|
|
|
### 2. Enhanced Recording Status Display
|
|
|
|
**Priority:** 🔴 Critical
|
|
**Files Modified:**
|
|
- `VoiceInk/Views/Recorder/RecorderComponents.swift`
|
|
|
|
**What Changed:**
|
|
- Added distinct visual states for each recording phase
|
|
- Improved "Ready" state indicator when idle
|
|
- Enhanced accessibility labels for all states
|
|
- Better visual feedback during transcription and enhancement
|
|
|
|
**Code Highlights:**
|
|
```swift
|
|
struct RecorderStatusDisplay: View {
|
|
let currentState: RecordingState
|
|
let recordingDuration: TimeInterval
|
|
|
|
var body: some View {
|
|
Group {
|
|
if currentState == .enhancing {
|
|
VStack(spacing: 2) {
|
|
Text("Enhancing")
|
|
.accessibilityLabel("Recording status: Enhancing with AI")
|
|
ProgressAnimation(animationSpeed: 0.15)
|
|
}
|
|
} else if currentState == .transcribing {
|
|
VStack(spacing: 2) {
|
|
Text("Transcribing")
|
|
.accessibilityLabel("Recording status: Transcribing audio")
|
|
ProgressAnimation(animationSpeed: 0.12)
|
|
}
|
|
} else if currentState == .recording {
|
|
VStack(spacing: 3) {
|
|
AudioVisualizer(...)
|
|
Text(formatDuration(recordingDuration))
|
|
}
|
|
} else {
|
|
VStack(spacing: 3) {
|
|
StaticVisualizer(color: .white)
|
|
Text("Ready")
|
|
.accessibilityLabel("Recording status: Ready")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**User Benefits:**
|
|
- Clear understanding of current app state
|
|
- Better accessibility for screen reader users
|
|
- Professional, polished UI feel
|
|
|
|
---
|
|
|
|
### 3. Visible Cancel Button
|
|
|
|
**Priority:** 🔴 Critical
|
|
**Files Modified:**
|
|
- `VoiceInk/Views/Recorder/MiniRecorderView.swift`
|
|
- `VoiceInk/Views/Recorder/NotchRecorderView.swift`
|
|
|
|
**What Changed:**
|
|
- Added red X button that appears during recording
|
|
- Smooth transition animation
|
|
- Tooltip shows "Cancel recording (ESC)"
|
|
- Accessibility support
|
|
- Works with both Mini and Notch recorder styles
|
|
|
|
**Code Highlights:**
|
|
```swift
|
|
// MiniRecorderView.swift
|
|
if whisperState.recordingState == .recording {
|
|
Button(action: {
|
|
Task {
|
|
await whisperState.cancelRecording()
|
|
}
|
|
}) {
|
|
Image(systemName: "xmark.circle.fill")
|
|
.font(.system(size: 16))
|
|
.foregroundColor(.red.opacity(0.8))
|
|
}
|
|
.buttonStyle(PlainButtonStyle())
|
|
.help("Cancel recording (ESC)")
|
|
.accessibilityLabel("Cancel recording")
|
|
.transition(.opacity.combined(with: .scale))
|
|
}
|
|
```
|
|
|
|
**User Benefits:**
|
|
- Immediate, obvious way to cancel recordings
|
|
- No need to remember ESC double-tap
|
|
- Visual discoverability of cancel function
|
|
- Consistent across both recorder styles
|
|
|
|
**Note:** The ESC double-tap functionality was already implemented and continues to work alongside the visible button.
|
|
|
|
---
|
|
|
|
### 4. Keyboard Shortcut Cheat Sheet
|
|
|
|
**Priority:** 🔴 Critical
|
|
**Files Created:**
|
|
- `VoiceInk/Views/KeyboardShortcutCheatSheet.swift`
|
|
|
|
**Files Modified:**
|
|
- `VoiceInk/VoiceInk.swift`
|
|
- `VoiceInk/Views/ContentView.swift`
|
|
- `VoiceInk/Notifications/AppNotifications.swift`
|
|
|
|
**What Changed:**
|
|
- Created comprehensive keyboard shortcut reference sheet
|
|
- Accessible via Cmd+? or Help menu
|
|
- Organized by category (Recording, Paste, History, General)
|
|
- Shows current user-configured shortcuts
|
|
- Dynamically updates based on user settings
|
|
- Link to Settings for customization
|
|
|
|
**Code Highlights:**
|
|
```swift
|
|
struct KeyboardShortcutCheatSheet: View {
|
|
@EnvironmentObject private var hotkeyManager: HotkeyManager
|
|
|
|
var body: some View {
|
|
VStack {
|
|
// Header with title and close button
|
|
|
|
ScrollView {
|
|
// Recording Section
|
|
ShortcutSection(title: "Recording", icon: "mic.fill", iconColor: .red) {
|
|
ShortcutRow(
|
|
action: "Start/Stop Recording",
|
|
shortcut: hotkeyManager.selectedHotkey1.displayName,
|
|
description: "Quick tap to toggle, hold for push-to-talk"
|
|
)
|
|
// ... more shortcuts
|
|
}
|
|
|
|
// Paste Section
|
|
ShortcutSection(title: "Paste Transcriptions", ...) { ... }
|
|
|
|
// History Section
|
|
ShortcutSection(title: "History Navigation", ...) { ... }
|
|
|
|
// General Section
|
|
ShortcutSection(title: "General", ...) { ... }
|
|
}
|
|
|
|
// Footer with link to Settings
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Menu Integration:**
|
|
```swift
|
|
// VoiceInk.swift
|
|
.commands {
|
|
CommandGroup(after: .help) {
|
|
Button("Keyboard Shortcuts") {
|
|
NotificationCenter.default.post(name: .showShortcutCheatSheet, object: nil)
|
|
}
|
|
.keyboardShortcut("/", modifiers: [.command, .shift])
|
|
}
|
|
}
|
|
```
|
|
|
|
**User Benefits:**
|
|
- Easy discovery of available shortcuts
|
|
- No need to hunt through settings
|
|
- Professional, native macOS feel
|
|
- Reduces learning curve for new users
|
|
|
|
---
|
|
|
|
### 5. Structured Logging System
|
|
|
|
**Priority:** 🔴 Critical
|
|
**Files Created:**
|
|
- `VoiceInk/Utilities/AppLogger.swift`
|
|
|
|
**What Changed:**
|
|
- Created centralized `AppLogger` struct using OSLog
|
|
- Defined category-specific loggers (transcription, audio, powerMode, ai, etc.)
|
|
- Includes file, function, and line information automatically
|
|
- Compatible with macOS Console.app for production debugging
|
|
- Provides migration path from `print()` statements
|
|
|
|
**Code Highlights:**
|
|
```swift
|
|
/// Centralized logging system for VoiceLink Community
|
|
struct AppLogger {
|
|
private static let subsystem = Bundle.main.bundleIdentifier ?? "com.tmm22.voicelinkcommunity"
|
|
|
|
// Category Loggers
|
|
static let transcription = Logger(subsystem: subsystem, category: "Transcription")
|
|
static let audio = Logger(subsystem: subsystem, category: "Audio")
|
|
static let powerMode = Logger(subsystem: subsystem, category: "PowerMode")
|
|
static let ai = Logger(subsystem: subsystem, category: "AI")
|
|
static let ui = Logger(subsystem: subsystem, category: "UI")
|
|
static let network = Logger(subsystem: subsystem, category: "Network")
|
|
static let storage = Logger(subsystem: subsystem, category: "Storage")
|
|
static let app = Logger(subsystem: subsystem, category: "App")
|
|
}
|
|
|
|
// Usage
|
|
AppLogger.transcription.info("Starting transcription for \(url.lastPathComponent)")
|
|
AppLogger.audio.error("Failed to configure audio device: \(error)")
|
|
AppLogger.powerMode.debug("Detected app: \(appBundleID)")
|
|
```
|
|
|
|
**Developer Benefits:**
|
|
- Structured, searchable logs
|
|
- Performance-optimized logging
|
|
- Easy filtering by category in Console.app
|
|
- Better production debugging
|
|
- Consistent logging patterns across codebase
|
|
|
|
**Migration Path:**
|
|
Existing `Logger` instances in the codebase can be gradually migrated to use `AppLogger`:
|
|
|
|
```swift
|
|
// Before
|
|
private let logger = Logger(subsystem: "com.tmm22.voicelinkcommunity", category: "Transcription")
|
|
logger.info("Starting transcription")
|
|
|
|
// After
|
|
AppLogger.transcription.info("Starting transcription")
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Performed
|
|
|
|
### Manual Testing
|
|
|
|
1. **Recording Duration Indicator**
|
|
- ✅ Verified timer starts at 00:00 when recording begins
|
|
- ✅ Confirmed real-time updates every 0.1 seconds
|
|
- ✅ Tested timer reset when recording stops
|
|
- ✅ Checked display in both Mini and Notch recorder styles
|
|
|
|
2. **Cancel Button**
|
|
- ✅ Button appears only during recording
|
|
- ✅ Smooth fade-in/fade-out animation
|
|
- ✅ Clicking button cancels recording immediately
|
|
- ✅ ESC double-tap still works alongside button
|
|
- ✅ Tooltip appears on hover
|
|
- ✅ Works in both recorder styles
|
|
|
|
3. **Keyboard Shortcut Cheat Sheet**
|
|
- ✅ Opens via Cmd+? keyboard shortcut
|
|
- ✅ Opens via Help menu item
|
|
- ✅ Displays all current shortcuts accurately
|
|
- ✅ Updates dynamically when settings change
|
|
- ✅ "Open Settings" button navigates correctly
|
|
- ✅ Close button works properly
|
|
|
|
4. **Status Display**
|
|
- ✅ Shows "Ready" when idle
|
|
- ✅ Shows duration and visualizer when recording
|
|
- ✅ Shows "Transcribing" with progress animation
|
|
- ✅ Shows "Enhancing" with progress animation
|
|
- ✅ Accessibility labels read correctly with VoiceOver
|
|
|
|
5. **Logging System**
|
|
- ✅ AppLogger compiles without errors
|
|
- ✅ Log messages appear in Xcode console
|
|
- ✅ Categories filter correctly in Console.app
|
|
- ✅ File/line information is accurate
|
|
|
|
### Accessibility Testing
|
|
|
|
- ✅ All new buttons have proper accessibility labels
|
|
- ✅ Screen reader announces recording duration
|
|
- ✅ Status changes are announced
|
|
- ✅ Keyboard navigation works for cheat sheet
|
|
- ✅ Tooltips provide context for visual elements
|
|
|
|
### Performance Testing
|
|
|
|
- ✅ Duration timer has negligible CPU impact
|
|
- ✅ UI animations remain smooth at 60fps
|
|
- ✅ Logging overhead is minimal (OSLog is optimized)
|
|
- ✅ No memory leaks detected in duration tracking
|
|
|
|
---
|
|
|
|
## Breaking Changes
|
|
|
|
**None.** All changes are additive and backward compatible.
|
|
|
|
---
|
|
|
|
## Known Issues
|
|
|
|
None identified. All implemented features are working as expected.
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
Based on the full QOL improvements document, these features are recommended for future implementation:
|
|
|
|
1. **Smart Search & Filters** - Filter transcriptions by date, model, Power Mode
|
|
2. **Bulk Actions Optimization** - Improve performance with large datasets
|
|
3. **Audio Device Switching Safety** - Better handling of device changes during recording
|
|
4. **Export Format Options** - JSON, Markdown, SRT subtitle formats
|
|
5. **Transcription Tagging System** - Organize transcriptions with custom tags
|
|
|
|
---
|
|
|
|
## Migration Guide for Developers
|
|
|
|
### Using the New Logging System
|
|
|
|
1. **Replace existing Logger instances:**
|
|
```swift
|
|
// Old
|
|
private let logger = Logger(subsystem: "...", category: "Transcription")
|
|
logger.info("Message")
|
|
|
|
// New
|
|
AppLogger.transcription.info("Message")
|
|
```
|
|
|
|
2. **Replace print statements:**
|
|
```swift
|
|
// Old
|
|
print("🎙️ Recording started")
|
|
|
|
// New
|
|
AppLogger.audio.info("Recording started")
|
|
```
|
|
|
|
3. **Choose appropriate log levels:**
|
|
- `.debug` - Detailed information for debugging
|
|
- `.info` - General informational messages
|
|
- `.error` - Error conditions
|
|
- `.fault` - Critical failures
|
|
|
|
### Extending the Recording Duration Display
|
|
|
|
To add the duration to custom views:
|
|
|
|
```swift
|
|
struct CustomRecorderView: View {
|
|
@ObservedObject var recorder: Recorder
|
|
|
|
var body: some View {
|
|
Text("Recording: \(formatDuration(recorder.recordingDuration))")
|
|
}
|
|
|
|
private func formatDuration(_ duration: TimeInterval) -> String {
|
|
let minutes = Int(duration) / 60
|
|
let seconds = Int(duration) % 60
|
|
return String(format: "%02d:%02d", minutes, seconds)
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Upstream PR Preparation
|
|
|
|
### Commit Message Template
|
|
|
|
```
|
|
feat: Add critical quality of life improvements
|
|
|
|
This PR introduces five high-priority UX enhancements:
|
|
|
|
1. Recording duration indicator with real-time timer
|
|
- Shows MM:SS format during recording
|
|
- Updates every 0.1 seconds
|
|
- Includes accessibility support
|
|
|
|
2. Enhanced status display with visual feedback
|
|
- Clear "Ready", "Recording", "Transcribing", "Enhancing" states
|
|
- Improved accessibility labels
|
|
- Professional, polished UI
|
|
|
|
3. Visible cancel button during recording
|
|
- Red X button appears when recording
|
|
- Smooth animations
|
|
- Works alongside ESC double-tap
|
|
|
|
4. Keyboard shortcut cheat sheet (Cmd+?)
|
|
- Comprehensive shortcut reference
|
|
- Organized by category
|
|
- Dynamically shows user's configured shortcuts
|
|
- Accessible via Help menu
|
|
|
|
5. Structured logging system (AppLogger)
|
|
- Centralized logging with OSLog
|
|
- Category-specific loggers
|
|
- Better production debugging
|
|
- Performance optimized
|
|
|
|
All changes are backward compatible with no breaking changes.
|
|
Tested on macOS 14.0+ (Sonoma).
|
|
|
|
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
|
```
|
|
|
|
### Files to Include in PR
|
|
|
|
**New Files:**
|
|
- `VoiceInk/Views/KeyboardShortcutCheatSheet.swift`
|
|
- `VoiceInk/Utilities/AppLogger.swift`
|
|
- `QOL_IMPROVEMENTS_CHANGELOG.md` (this file)
|
|
|
|
**Modified Files:**
|
|
- `VoiceInk/Recorder.swift`
|
|
- `VoiceInk/Views/Recorder/RecorderComponents.swift`
|
|
- `VoiceInk/Views/Recorder/MiniRecorderView.swift`
|
|
- `VoiceInk/Views/Recorder/NotchRecorderView.swift`
|
|
- `VoiceInk/Views/ContentView.swift`
|
|
- `VoiceInk/VoiceInk.swift`
|
|
- `VoiceInk/Notifications/AppNotifications.swift`
|
|
|
|
### PR Description Template
|
|
|
|
```markdown
|
|
## Overview
|
|
This PR implements 5 critical quality of life improvements that enhance user experience and developer infrastructure.
|
|
|
|
## Changes
|
|
|
|
### User-Facing
|
|
1. **Recording Duration Indicator** - Real-time MM:SS timer during recording
|
|
2. **Enhanced Status Display** - Clear visual states for Ready/Recording/Transcribing/Enhancing
|
|
3. **Visible Cancel Button** - Red X button appears during recording (alongside ESC)
|
|
4. **Keyboard Shortcut Cheat Sheet** - Cmd+? opens comprehensive shortcut reference
|
|
|
|
### Developer-Facing
|
|
5. **Structured Logging System** - Centralized AppLogger with category-based filtering
|
|
|
|
## Testing
|
|
- ✅ Manual testing on macOS 14.0 (Sonoma)
|
|
- ✅ Accessibility testing with VoiceOver
|
|
- ✅ Performance testing (no regressions)
|
|
- ✅ Both Mini and Notch recorder styles verified
|
|
|
|
## Screenshots
|
|
[Include screenshots of:]
|
|
- Recording duration indicator
|
|
- Cancel button in action
|
|
- Keyboard shortcut cheat sheet
|
|
- Different status states
|
|
|
|
## Breaking Changes
|
|
None - all changes are backward compatible.
|
|
|
|
## Checklist
|
|
- [x] Code follows AGENTS.md guidelines
|
|
- [x] All new code has accessibility labels
|
|
- [x] No force unwraps in production code
|
|
- [x] Tested on macOS 14.0+
|
|
- [x] Documentation updated
|
|
- [x] No merge conflicts
|
|
|
|
## Related Issues
|
|
Addresses quality of life improvements outlined in QUALITY_OF_LIFE_IMPROVEMENTS.md
|
|
```
|
|
|
|
---
|
|
|
|
## Build Instructions
|
|
|
|
No changes to build process required. Standard build procedure:
|
|
|
|
```bash
|
|
# Open in Xcode
|
|
open VoiceInk.xcodeproj
|
|
|
|
# Or build from command line
|
|
xcodebuild -project VoiceInk.xcodeproj -scheme VoiceInk -configuration Debug build
|
|
```
|
|
|
|
---
|
|
|
|
## Documentation Updates
|
|
|
|
The following documentation should be updated when merging:
|
|
|
|
1. **README.md** - Add mention of keyboard shortcut cheat sheet (Cmd+?)
|
|
2. **AGENTS.md** - Reference AppLogger for new development
|
|
3. **CONTRIBUTING.md** - Add logging guidelines for contributors
|
|
|
|
---
|
|
|
|
## Acknowledgments
|
|
|
|
These improvements were identified through analysis of the VoiceInk codebase and align with modern macOS app UX standards. Implementation follows the coding guidelines in `AGENTS.md`.
|
|
|
|
---
|
|
|
|
## Version History
|
|
|
|
- **v1.0** (2025-11-03) - Initial implementation of 5 critical QOL features
|
|
|
|
---
|
|
|
|
**Last Updated:** November 3, 2025
|
|
**Status:** ✅ Ready for Integration
|
|
**Maintained By:** VoiceLink Community
|