Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
294b723
WIP: pre-existing uncommitted accessibility/UI-test refinements
bisonbet Jul 18, 2026
e97af23
docs: add macOS native migration plan (Catalyst → native)
bisonbet Jul 18, 2026
930bcbf
refactor: replace SafariView with cross-platform openURL (Phase 0.1)
bisonbet Jul 18, 2026
3fd6cb5
refactor: migrate document pickers to fileImporter/fileMover (Phase 0.2)
bisonbet Jul 18, 2026
c4195e5
refactor: add PlatformApp shim for UIApplication call sites (Phase 0.3)
bisonbet Jul 18, 2026
5e6d895
merge v2.2 into native-macos (accessibility workflow stabilization)
bisonbet Jul 18, 2026
42a1a6e
feat(macos): add native macOS target, compiling green (Phase 1.1 + 1.3)
bisonbet Jul 18, 2026
4d9b114
docs: record Phase 0 + Phase 1 completion in migration plan
bisonbet Jul 18, 2026
c3d5788
Merge v2.2 into native-macos
bisonbet Jul 18, 2026
a9b9246
feat(macos): enable native Mac recording engine
bisonbet Jul 18, 2026
5ed60e5
feat(macos): add native audio input selection
bisonbet Jul 19, 2026
6531a6b
chore: apply recommended Xcode settings
bisonbet Jul 19, 2026
21d4d70
feat(macos): recover recording across input changes
bisonbet Jul 19, 2026
e65a9ab
fix(macos): register ScreenCaptureKit video output
bisonbet Jul 19, 2026
a89b508
fix(macos): avoid empty file mover presentations
bisonbet Jul 19, 2026
5ebc3ce
fix(macos): preserve microphone quality in meeting mixes
bisonbet Jul 19, 2026
0061f82
feat(macos): keep processing active outside foreground
bisonbet Jul 19, 2026
a383ea6
feat(macos): add native export and sharing services
bisonbet Jul 19, 2026
681bb72
feat(macos): complete phase 2 platform punch list
bisonbet Jul 19, 2026
21b31a3
feat(macos): make native sheets scrollable
bisonbet Jul 19, 2026
a8faad9
fix(macos): open summaries in native windows
bisonbet Jul 19, 2026
95ef509
fix(macos): add native application icon
bisonbet Jul 19, 2026
80e6a5c
feat(macos): add native app conventions
bisonbet Jul 19, 2026
1466a1d
fix(macos): repair app-wide window presentation
bisonbet Jul 19, 2026
93c52d9
fix(macos): make transcript window scroll as one
bisonbet Jul 19, 2026
dd94ea9
fix(macos): restore transcript window navigation
bisonbet Jul 19, 2026
068c456
docs(macos): complete phase 3.3 parity audit
bisonbet Jul 19, 2026
5244527
chore: refresh SwiftLint baseline after macOS migration
bisonbet Jul 19, 2026
941f8de
docs(macos): complete phase 3 exit gate
bisonbet Jul 20, 2026
2bbbced
fix(macos): repair settings sheets and verify data continuity
bisonbet Jul 20, 2026
23c9036
feat(macos): add Ternary Bonsai 27B MLX option
bisonbet Jul 21, 2026
d9985b0
fix(macos): polish settings and retain MLX reasoning
bisonbet Jul 22, 2026
1a952f3
fix(macos): polish archives and prepare v2.2 beta
bisonbet Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
279 changes: 260 additions & 19 deletions BisonNotes AI/BisonNotes AI.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2610"
LastUpgradeVersion = "2660"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2610"
LastUpgradeVersion = "2660"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2610"
LastUpgradeVersion = "2660"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2660"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC0000000000000000000010"
BuildableName = "BisonNotes AI.app"
BlueprintName = "BisonNotes AI macOS"
ReferencedContainer = "container:BisonNotes AI.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC0000000000000000000010"
BuildableName = "BisonNotes AI.app"
BlueprintName = "BisonNotes AI macOS"
ReferencedContainer = "container:BisonNotes AI.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC0000000000000000000010"
BuildableName = "BisonNotes AI.app"
BlueprintName = "BisonNotes AI macOS"
ReferencedContainer = "container:BisonNotes AI.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2610"
LastUpgradeVersion = "2660"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
44 changes: 42 additions & 2 deletions BisonNotes AI/BisonNotes AI/AISettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -256,18 +256,21 @@ struct AISettingsView: View {
}

var body: some View {
NavigationStack {
Group {
settingsContent
.navigationTitle("AI Settings")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
#if !os(macOS)
ToolbarItem(placement: .navigationBarTrailing) {
Button("Done") {
dismiss()
}
}
#endif
}
}
.platformSettingsNavigation()
.alert("Regeneration Complete", isPresented: $viewModel.regenerationManager.showingRegenerationAlert) {
Button("OK") { viewModel.regenerationManager.regenerationResults = nil }
} message: {
Expand Down Expand Up @@ -299,6 +302,42 @@ struct AISettingsView: View {
} message: {
Text(errorHandler.currentError?.localizedDescription ?? "An unknown error occurred.")
}
#if os(macOS)
.navigationDestination(isPresented: $showingOllamaSettings) {
OllamaSettingsView(onConfigurationChanged: {
self.refreshEngineStatuses()
})
}
.navigationDestination(isPresented: $showingOpenAISettings) {
OpenAISummarizationSettingsView(onConfigurationChanged: {
Task { refreshEngineStatuses() }
})
}
.navigationDestination(isPresented: $showingOpenAICompatibleSettings) {
OpenAICompatibleSettingsView(onConfigurationChanged: {
Task { refreshEngineStatuses() }
})
}
.navigationDestination(isPresented: $showingGoogleAIStudioSettings) {
GoogleAIStudioSettingsView(onConfigurationChanged: {
Task { refreshEngineStatuses() }
})
}
.navigationDestination(isPresented: $showingMistralAISettings) {
MistralAISettingsView(onConfigurationChanged: {
Task { refreshEngineStatuses() }
})
}
.navigationDestination(isPresented: $showingAWSBedrockSettings) {
AWSBedrockSettingsView()
}
.navigationDestination(isPresented: $showingOnDeviceLLMSettings) {
OnDeviceLLMSettingsView()
}
.navigationDestination(isPresented: $showingMLXSwiftSettings) {
MLXSwiftSettingsView()
}
#else
.sheet(isPresented: $showingOllamaSettings) {
OllamaSettingsView(onConfigurationChanged: {
self.refreshEngineStatuses()
Expand Down Expand Up @@ -337,7 +376,8 @@ struct AISettingsView: View {
MLXSwiftSettingsView()
}
}
.fullScreenCover(isPresented: $showingMistralOnboarding) {
#endif
.platformFullScreenCover(isPresented: $showingMistralOnboarding) {
MistralOnboardingView(onSetupComplete: {
refreshEngineStatuses()
})
Expand Down
3 changes: 2 additions & 1 deletion BisonNotes AI/BisonNotes AI/AWSBedrockSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct AWSBedrockSettingsView: View {
}

var body: some View {
NavigationStack {
PlatformSettingsNavigationStack {
Form {
headerSection

Expand All @@ -54,6 +54,7 @@ struct AWSBedrockSettingsView: View {
documentationSection
}
}
.nativeMacSettingsFormStyle()
.scrollContentBackground(.hidden)
.background(Color(.systemGroupedBackground))
.navigationTitle("AWS Bedrock Settings")
Expand Down
3 changes: 2 additions & 1 deletion BisonNotes AI/BisonNotes AI/AWSSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct AWSSettingsView: View {
]

var body: some View {
NavigationStack {
PlatformSettingsNavigationStack {
Form {
Section(header: Text("AWS Transcribe")) {
VStack(alignment: .leading, spacing: 8) {
Expand Down Expand Up @@ -196,6 +196,7 @@ struct AWSSettingsView: View {
Link("AWS IAM Setup Guide", destination: URL(string: "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html")!)
}
}
.nativeMacSettingsFormStyle()
.scrollContentBackground(.hidden)
.background(Color(.systemGroupedBackground))
.navigationTitle("AWS Settings")
Expand Down
40 changes: 29 additions & 11 deletions BisonNotes AI/BisonNotes AI/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@
// Phase 6: Notification Action Handling
//

#if canImport(UIKit)
import UIKit
#else
import AppKit
#endif
import UserNotifications
import MetricKit

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate, MXMetricManagerSubscriber {
/// Platform-neutral app-delegate logic (notification actions, MetricKit,
/// badge clearing). The platform-specific AppDelegate subclasses below only
/// differ in the launch callback that triggers `completeLaunchSetup()`.
class AppDelegateCore: NSObject, UNUserNotificationCenterDelegate, MXMetricManagerSubscriber {

// Reference to AudioRecorderViewModel for handling resume actions
// This will be set by the main app
static weak var recorderViewModel: AudioRecorderViewModel?

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
func completeLaunchSetup() {
// Set notification delegate
UNUserNotificationCenter.current().delegate = self

Expand All @@ -28,8 +34,6 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
AppLog.shared.markLaunch()

NSLog("✅ AppDelegate initialized - notification delegate set")

return true
}

// MARK: - MetricKit
Expand Down Expand Up @@ -101,7 +105,7 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele

if categoryIdentifier == "RESUME_RECORDING" {
Task { @MainActor in
guard let recorderVM = AppDelegate.recorderViewModel else {
guard let recorderVM = AppDelegateCore.recorderViewModel else {
AppLog.shared.general("AudioRecorderViewModel not available for notification action", level: .error)
completionHandler()
return
Expand Down Expand Up @@ -139,10 +143,24 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
// Show notification even when app is in foreground
if #available(iOS 14.0, *) {
completionHandler([.banner, .sound, .badge])
} else {
completionHandler([.alert, .sound, .badge])
}
completionHandler([.banner, .sound, .badge])
}
}

#if canImport(UIKit)
class AppDelegate: AppDelegateCore, UIApplicationDelegate {

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
completeLaunchSetup()
return true
}
}
#else
class AppDelegate: AppDelegateCore, NSApplicationDelegate {

func applicationDidFinishLaunching(_ notification: Notification) {
completeLaunchSetup()
}
}
#endif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,66 @@
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"filename" : "AppIcon-mac-16x16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "AppIcon-mac-16x16@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "AppIcon-mac-32x32.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "AppIcon-mac-32x32@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "AppIcon-mac-128x128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "AppIcon-mac-128x128@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "AppIcon-mac-256x256.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "AppIcon-mac-256x256@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "AppIcon-mac-512x512.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "AppIcon-mac-512x512@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
Expand Down
Loading