diff --git a/VoiceInk.xcodeproj/project.pbxproj b/VoiceInk.xcodeproj/project.pbxproj
index f18f690..63a704e 100644
--- a/VoiceInk.xcodeproj/project.pbxproj
+++ b/VoiceInk.xcodeproj/project.pbxproj
@@ -459,7 +459,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 152;
+ CURRENT_PROJECT_VERSION = 153;
DEVELOPMENT_ASSET_PATHS = "\"VoiceInk/Preview Content\"";
DEVELOPMENT_TEAM = V6J6A3VWY2;
ENABLE_HARDENED_RUNTIME = YES;
@@ -474,7 +474,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.0;
- MARKETING_VERSION = 1.52;
+ MARKETING_VERSION = 1.53;
PRODUCT_BUNDLE_IDENTIFIER = com.prakashjoshipax.VoiceInk;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG ENABLE_NATIVE_SPEECH_ANALYZER $(inherited)";
@@ -493,7 +493,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 152;
+ CURRENT_PROJECT_VERSION = 153;
DEVELOPMENT_ASSET_PATHS = "\"VoiceInk/Preview Content\"";
DEVELOPMENT_TEAM = V6J6A3VWY2;
ENABLE_HARDENED_RUNTIME = YES;
@@ -508,7 +508,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 14.0;
- MARKETING_VERSION = 1.52;
+ MARKETING_VERSION = 1.53;
PRODUCT_BUNDLE_IDENTIFIER = com.prakashjoshipax.VoiceInk;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "ENABLE_NATIVE_SPEECH_ANALYZER $(inherited)";
diff --git a/VoiceInk.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/VoiceInk.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 886cfcd..2c29478 100644
--- a/VoiceInk.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/VoiceInk.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -7,7 +7,7 @@
"location" : "https://github.com/FluidInference/FluidAudio",
"state" : {
"branch" : "main",
- "revision" : "abf7d9ef3f53a693e3721069071971eff84c002f"
+ "revision" : "052cbb27cf073a9407251d74ef3459ea258e41b3"
}
},
{
diff --git a/VoiceInk/AppDelegate.swift b/VoiceInk/AppDelegate.swift
index 4bc530b..20cd81e 100644
--- a/VoiceInk/AppDelegate.swift
+++ b/VoiceInk/AppDelegate.swift
@@ -1,5 +1,6 @@
import Cocoa
import SwiftUI
+import UniformTypeIdentifiers
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
@@ -50,14 +51,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
defaults.removeObject(forKey: "isPowerModeEnabled")
}
- // Keep in sync with AudioTranscribeView.supportedExtensions
- private let supportedExtensions = ["wav", "mp3", "m4a", "aiff", "mp4", "mov", "aac", "flac", "caf"]
-
// Stash URL when app cold-starts to avoid spawning a new window/tab
var pendingOpenFileURL: URL?
func application(_ application: NSApplication, open urls: [URL]) {
- guard let url = urls.first(where: { supportedExtensions.contains($0.pathExtension.lowercased()) }) else {
+ guard let url = urls.first(where: { SupportedMedia.isSupported(url: $0) }) else {
return
}
diff --git a/VoiceInk/Info.plist b/VoiceInk/Info.plist
index 7584b1c..7833f1f 100644
--- a/VoiceInk/Info.plist
+++ b/VoiceInk/Info.plist
@@ -48,34 +48,3 @@
-
-CFBundleDocumentTypes
-
-
- CFBundleTypeName
- Audio/Video File
- CFBundleTypeRole
- Viewer
- LSHandlerRank
- Alternate
- LSItemContentTypes
-
- public.audio
- public.movie
-
- CFBundleTypeExtensions
-
- wav
- mp3
- m4a
- aiff
- mp4
- mov
- aac
- flac
- caf
-
-
-
-
-
diff --git a/VoiceInk/Services/SupportedMedia.swift b/VoiceInk/Services/SupportedMedia.swift
new file mode 100644
index 0000000..c66c452
--- /dev/null
+++ b/VoiceInk/Services/SupportedMedia.swift
@@ -0,0 +1,28 @@
+import Foundation
+import UniformTypeIdentifiers
+
+struct SupportedMedia {
+ static let extensions: Set = [
+ "wav", "mp3", "m4a", "aiff", "mp4", "mov", "aac", "flac", "caf"
+ ]
+
+ static let contentTypes: [UTType] = [
+ .audio, .movie
+ ]
+
+ static func isSupported(url: URL) -> Bool {
+ let fileExtension = url.pathExtension.lowercased()
+ if !fileExtension.isEmpty, extensions.contains(fileExtension) {
+ return true
+ }
+
+ if let resourceValues = try? url.resourceValues(forKeys: [.contentTypeKey]),
+ let contentType = resourceValues.contentType {
+ return contentTypes.contains(where: { contentType.conforms(to: $0) })
+ }
+
+ return false
+ }
+}
+
+
diff --git a/VoiceInk/Views/AudioTranscribeView.swift b/VoiceInk/Views/AudioTranscribeView.swift
index d73c8b8..3cebfa0 100644
--- a/VoiceInk/Views/AudioTranscribeView.swift
+++ b/VoiceInk/Views/AudioTranscribeView.swift
@@ -353,29 +353,8 @@ struct AudioTranscribeView: View {
}
}
- // Validate file type by extension
- let supportedExtensions = ["wav", "mp3", "m4a", "aiff", "mp4", "mov", "aac", "flac", "caf"]
- let fileExtension = url.pathExtension.lowercased()
-
- // Check file extension first
- if !fileExtension.isEmpty && supportedExtensions.contains(fileExtension) {
- print("File type validated by extension: \(fileExtension)")
- } else {
- print("Unsupported file extension: \(fileExtension)")
- // Try to validate by UTType as well
- if let resourceValues = try? url.resourceValues(forKeys: [.contentTypeKey]),
- let contentType = resourceValues.contentType {
- if contentType.conforms(to: .audio) || contentType.conforms(to: .movie) {
- print("File type validated by UTType: \(contentType.identifier)")
- } else {
- print("File does not conform to audio or movie type: \(contentType.identifier)")
- return
- }
- } else {
- print("Could not validate file type")
- return
- }
- }
+ // Validate file type
+ guard SupportedMedia.isSupported(url: url) else { return }
print("File validated successfully: \(url.lastPathComponent)")
selectedAudioURL = url
diff --git a/VoiceInk/Views/ContentView.swift b/VoiceInk/Views/ContentView.swift
index a610b49..4736d4b 100644
--- a/VoiceInk/Views/ContentView.swift
+++ b/VoiceInk/Views/ContentView.swift
@@ -164,8 +164,7 @@ struct ContentView: View {
@State private var hasLoadedData = false
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
@StateObject private var licenseViewModel = LicenseViewModel()
- // Capture the hosting window to update tab/window title dynamically
- @State private var hostingWindow: NSWindow?
+
private var isSetupComplete: Bool {
hasLoadedData &&
@@ -191,13 +190,6 @@ struct ContentView: View {
}
.navigationSplitViewStyle(.balanced)
.frame(minWidth: 940, minHeight: 730)
- // Resolve hosting NSWindow and set initial title
- .background(
- WindowTitleAccessor { window in
- self.hostingWindow = window
- self.hostingWindow?.title = selectedView.rawValue
- }
- )
.onAppear {
hasLoadedData = true
}
@@ -237,10 +229,6 @@ struct ContentView: View {
print("ContentView: No destination in notification")
}
}
- // Update the tab/window title whenever the active view changes
- .onChange(of: selectedView) { newValue in
- hostingWindow?.title = newValue.rawValue
- }
}
@ViewBuilder
@@ -278,20 +266,4 @@ struct ContentView: View {
}
}
-struct WindowTitleAccessor: NSViewRepresentable {
- var onResolve: (NSWindow?) -> Void
-
- func makeNSView(context: Context) -> NSView {
- let view = NSView()
- DispatchQueue.main.async { [weak view] in
- onResolve(view?.window)
- }
- return view
- }
-
- func updateNSView(_ nsView: NSView, context: Context) {
- DispatchQueue.main.async { [weak nsView] in
- onResolve(nsView?.window)
- }
- }
-}
+