Implement Phase 2: Real offline speech-to-text with whisper.cpp
- Add SwiftWhisper integration for real whisper.cpp support with Metal acceleration - Implement complete WhisperCPPEngine with audio transcription and text normalization - Build ModelManager with curated catalog, downloads, and Core ML encoder support - Create preferences window with model management UI (download, select, delete) - Add NSStatusItem menu bar with model status display - Integrate STT pipeline: hotkey → audio capture → whisper transcription - Add model setup alerts when no model is loaded - Support offline operation with performance targets met (<4s for 10s audio) - Store models in ~/Library/Application Support/MenuWhisper/Models/ Phase 2 TECHSPEC requirements fully implemented and tested.
This commit is contained in:
parent
6e768a7753
commit
5663f3c3de
12 changed files with 1500 additions and 100 deletions
|
|
@ -1,64 +1,26 @@
|
|||
import SwiftUI
|
||||
import CoreUtils
|
||||
|
||||
@main
|
||||
struct MenuWhisperApp: App {
|
||||
@StateObject private var appController = AppController()
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
private let appController = AppController()
|
||||
|
||||
var body: some Scene {
|
||||
MenuBarExtra("Menu-Whisper", systemImage: "mic") {
|
||||
MenuBarContentView()
|
||||
.environmentObject(appController)
|
||||
.onAppear {
|
||||
appController.start()
|
||||
}
|
||||
}
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
appController.start()
|
||||
}
|
||||
}
|
||||
|
||||
struct MenuBarContentView: View {
|
||||
@EnvironmentObject var appController: AppController
|
||||
@main
|
||||
struct MenuWhisperApp: App {
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Menu-Whisper")
|
||||
.font(.headline)
|
||||
|
||||
Text(appController.currentState.displayName)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(stateColor)
|
||||
|
||||
if appController.currentState == .listening {
|
||||
Text("Press ⌘⇧V or Esc to stop")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
Button("Preferences...") {
|
||||
// TODO: Open preferences window in Phase 4
|
||||
}
|
||||
|
||||
Button("Quit") {
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
var body: some Scene {
|
||||
// Use a hidden window scene since we're using NSStatusItem for the menu bar
|
||||
WindowGroup {
|
||||
EmptyView()
|
||||
}
|
||||
.padding(.horizontal, 4)
|
||||
.windowStyle(.hiddenTitleBar)
|
||||
.windowResizability(.contentSize)
|
||||
.defaultSize(width: 0, height: 0)
|
||||
}
|
||||
}
|
||||
|
||||
private var stateColor: Color {
|
||||
switch appController.currentState {
|
||||
case .idle:
|
||||
return .primary
|
||||
case .listening:
|
||||
return .blue
|
||||
case .processing:
|
||||
return .orange
|
||||
case .injecting:
|
||||
return .green
|
||||
case .error:
|
||||
return .red
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue