Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 44 additions & 0 deletions Cotabby.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@
25D4FC8D191A50F63E6391F9 /* ModelAndPresentationValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03766F6253FF17639230C0F6 /* ModelAndPresentationValueTests.swift */; };
25F91CEF38400FD1ADB6B1AF /* CompletionRenderModePolicyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D504BEB224E0C176F5FCFF6E /* CompletionRenderModePolicyTests.swift */; };
26E0331E9E2F92FAE531BDEE /* ActivationIndicatorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84D4528EEC9EFEB8AE8E318 /* ActivationIndicatorController.swift */; };
27D4F5CACADE171F142178B4 /* SettingsSidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BADB38D0160B47637572FC5E /* SettingsSidebarView.swift */; };
2C6159231472A849F15BD0AE /* ScreenFrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484C8A04B9C00CF79D589EB /* ScreenFrameReader.swift */; };
2DF5A3826AAB99C279EBB8DE /* InputMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81DD30EB657368AACE9625A /* InputMonitor.swift */; };
2E3DEB7E89D0146274596F2E /* SettingsContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0CE9AB1286367BA2E82392 /* SettingsContainerView.swift */; };
2EE05B312C990104BE934772 /* GhostFontSizeStabilizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BF59EE80F3A0143B79740 /* GhostFontSizeStabilizerTests.swift */; };
2F227738D7834B1A7A81D1D6 /* ModelDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51020F8CD58338BD643FBF63 /* ModelDownloadManager.swift */; };
2FC40D4BFDD05C2401D7A5E9 /* SuggestionInserter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3D1125B962CBE0269EEDDB /* SuggestionInserter.swift */; };
Expand All @@ -59,6 +61,7 @@
4AC255BE2D0CCC67B8882C7A /* WelcomeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CB3008986BE7FD2A4D9132 /* WelcomeCoordinator.swift */; };
4B4DDB569CAD806F765224DE /* CustomRulesEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F7F7355967725162DF2D1B /* CustomRulesEditor.swift */; };
4B54ACE1255873955414CD06 /* PermissionGuidanceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F2C764D29C8D50D0C854FF8 /* PermissionGuidanceController.swift */; };
4B93D26BACEEA932E92B1A19 /* SettingsPaneScaffold.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19BE12C28A4AB8A4A58C2FF7 /* SettingsPaneScaffold.swift */; };
4C6D8ED0A7B45D2EADF06DA5 /* SuggestionOverlayStabilityGateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB25ABC4FFB0E63477CDCB0 /* SuggestionOverlayStabilityGateTests.swift */; };
4CAFD8F3444FEDC9ACAFF529 /* LlamaRuntimeModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = A804F4DB6FD9BC8C27B2B65F /* LlamaRuntimeModels.swift */; };
4F369F5284DDCEABF082E59B /* SuggestionAvailabilityEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3609CC88A5280B3AA40414DF /* SuggestionAvailabilityEvaluator.swift */; };
Expand Down Expand Up @@ -100,6 +103,7 @@
88BCD795A14E1C9308F7BB31 /* SuggestionAvailabilityEvaluatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C05B0439348261163B37C508 /* SuggestionAvailabilityEvaluatorTests.swift */; };
8B2DFC860803C0A7C4D34A36 /* ContextBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54EF3C7F5D9D6F3FA50FD51C /* ContextBuffer.swift */; };
8DA36F1521B6A59D8C20AC59 /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 5A60D1467BBFECB3DFEB39C2 /* Logging */; };
907A0BF56C3BB0CBAF2649AB /* SettingsCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D0AEFF86F8210CBE7CFCBAD /* SettingsCategory.swift */; };
909EBE545CE644C6C57F1B5D /* SuggestionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F961F5DF2A392F6F5F94F8A /* SuggestionCoordinator.swift */; };
90CD3F7238E223DEBA2B4D92 /* TagChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB317C82CE2CBC69056BA4B8 /* TagChip.swift */; };
90DC9508F27F712EB61EEB06 /* PermissionReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 656F58E56FE9BC087B6F1D33 /* PermissionReminderView.swift */; };
Expand Down Expand Up @@ -131,6 +135,7 @@
B93AB7E845086F6FBB068369 /* SuggestionRequestFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE94342B888A5A2CCF66BC93 /* SuggestionRequestFactoryTests.swift */; };
BB6325CA50F97B18B9725918 /* SuggestionTextNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B424E2AC97C99D335B0D5751 /* SuggestionTextNormalizer.swift */; };
BBE22CE4EF43247F8775B25D /* FocusPollBackoff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FADF683BE7B3558377FA76 /* FocusPollBackoff.swift */; };
BBE74B21ED1543DEACF18A1A /* PlaceholderPaneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93028F328388432E72C58D09 /* PlaceholderPaneView.swift */; };
BFCA7FAFDAEBF586AB615567 /* ClipboardRelevanceFilterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90B0D133AB77A2503FB08827 /* ClipboardRelevanceFilterTests.swift */; };
C2C958D6E5F5FE1CCC414BCE /* SuggestionSubsystemContracts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEB16474A67CE1D210B944C9 /* SuggestionSubsystemContracts.swift */; };
C4C6734678797669055988E0 /* AppUpdateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD9573F3504CAE6891DF9B7D /* AppUpdateManager.swift */; };
Expand Down Expand Up @@ -191,6 +196,7 @@
110CB0B53016644EF7840301 /* HuggingFaceAPIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HuggingFaceAPIClient.swift; sourceTree = "<group>"; };
12DD19BCE610808F1E38702D /* PermissionOverlayTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionOverlayTrackerTests.swift; sourceTree = "<group>"; };
18D990E515E1AE4F312F4E95 /* BundledRuntimeLocatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundledRuntimeLocatorTests.swift; sourceTree = "<group>"; };
19BE12C28A4AB8A4A58C2FF7 /* SettingsPaneScaffold.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPaneScaffold.swift; sourceTree = "<group>"; };
19DB9558F4D3AFB108D71649 /* SuggestionStateHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionStateHelperTests.swift; sourceTree = "<group>"; };
1CE61E74928C221B8BB261C6 /* SuggestionTextColorCodec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionTextColorCodec.swift; sourceTree = "<group>"; };
1D00A031C0D9CF2A7A2330D9 /* PermissionDragSourceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionDragSourceView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -230,6 +236,7 @@
5AD3F4F9FBE82007E4E15F58 /* GhostSuggestionLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhostSuggestionLayoutTests.swift; sourceTree = "<group>"; };
5C4E5869D103865486AAAEEC /* ModelFileValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelFileValidator.swift; sourceTree = "<group>"; };
5C9FDF029F7828CAF3FE8850 /* FocusTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusTracker.swift; sourceTree = "<group>"; };
5D0AEFF86F8210CBE7CFCBAD /* SettingsCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCategory.swift; sourceTree = "<group>"; };
5F2C764D29C8D50D0C854FF8 /* PermissionGuidanceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionGuidanceController.swift; sourceTree = "<group>"; };
5F34AE24BB7C99D66E1F3904 /* InputModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputModels.swift; sourceTree = "<group>"; };
60629DFE309C1A4BD8A7FB3B /* RuntimeBootstrapModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeBootstrapModel.swift; sourceTree = "<group>"; };
Expand All @@ -256,6 +263,7 @@
8F961F5DF2A392F6F5F94F8A /* SuggestionCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionCoordinator.swift; sourceTree = "<group>"; };
90B0D133AB77A2503FB08827 /* ClipboardRelevanceFilterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardRelevanceFilterTests.swift; sourceTree = "<group>"; };
92C6EB9FDA48ADF425A116A9 /* PermissionOverlayWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionOverlayWindowController.swift; sourceTree = "<group>"; };
93028F328388432E72C58D09 /* PlaceholderPaneView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderPaneView.swift; sourceTree = "<group>"; };
944065A858D9BC936CB12B23 /* LlamaRuntimeCore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LlamaRuntimeCore.swift; sourceTree = "<group>"; };
9458F0820B3161FE9CF1DDAF /* GhostFontSizeStabilizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhostFontSizeStabilizer.swift; sourceTree = "<group>"; };
96495E4147D828C0B1B22765 /* ClipboardContentDistiller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardContentDistiller.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -288,6 +296,7 @@
B6D42CD456B4B3C988B148A6 /* FocusTrackingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusTrackingModel.swift; sourceTree = "<group>"; };
B81DD30EB657368AACE9625A /* InputMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputMonitor.swift; sourceTree = "<group>"; };
BA705EDFE1C41294F0E381F1 /* FocusSnapshotResolverSelectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusSnapshotResolverSelectionTests.swift; sourceTree = "<group>"; };
BADB38D0160B47637572FC5E /* SettingsSidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSidebarView.swift; sourceTree = "<group>"; };
BB5C2AE9A7E55495D26AD074 /* DownloadableModelCatalogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadableModelCatalogView.swift; sourceTree = "<group>"; };
BC4F887528AE74AC0DD30314 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
BD42C7E2852F59BEF7972663 /* MenuBarStatusLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarStatusLabelView.swift; sourceTree = "<group>"; };
Expand All @@ -312,6 +321,7 @@
D504BEB224E0C176F5FCFF6E /* CompletionRenderModePolicyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionRenderModePolicyTests.swift; sourceTree = "<group>"; };
D5D6C2318E405AA717D1C256 /* WelcomePermissionStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePermissionStepView.swift; sourceTree = "<group>"; };
D84D4528EEC9EFEB8AE8E318 /* ActivationIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivationIndicatorController.swift; sourceTree = "<group>"; };
DB0CE9AB1286367BA2E82392 /* SettingsContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsContainerView.swift; sourceTree = "<group>"; };
DDE858CB1E687E3CEB8FDD5B /* SuggestionRequestFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionRequestFactory.swift; sourceTree = "<group>"; };
DEB16474A67CE1D210B944C9 /* SuggestionSubsystemContracts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionSubsystemContracts.swift; sourceTree = "<group>"; };
E1D2782B6C7BE3F56BCB22DE /* LlamaVisualContextSummarizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LlamaVisualContextSummarizer.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -403,6 +413,14 @@
path = Visual;
sourceTree = "<group>";
};
2EE526D7C9284AF6687A556B /* Panes */ = {
isa = PBXGroup;
children = (
93028F328388432E72C58D09 /* PlaceholderPaneView.swift */,
);
path = Panes;
sourceTree = "<group>";
};
3725B1A195A06A4F014B75B1 /* Products */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -451,6 +469,14 @@
path = Focus;
sourceTree = "<group>";
};
6E1171BBC9CB74DB623C5E8B /* Components */ = {
isa = PBXGroup;
children = (
19BE12C28A4AB8A4A58C2FF7 /* SettingsPaneScaffold.swift */,
);
path = Components;
sourceTree = "<group>";
};
717EB52365B64107A6FB1126 /* Coordinators */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -543,6 +569,7 @@
B0EBE873B895FB333A0BC26F /* UI */ = {
isa = PBXGroup;
children = (
DCBC24D605EC0CBA1DD6097D /* Settings */,
82F7F7355967725162DF2D1B /* CustomRulesEditor.swift */,
BB5C2AE9A7E55495D26AD074 /* DownloadableModelCatalogView.swift */,
CBD5FCB8CC56AA6138382B2C /* FieldEdgeIconIndicatorView.swift */,
Expand All @@ -566,6 +593,18 @@
path = UI;
sourceTree = "<group>";
};
DCBC24D605EC0CBA1DD6097D /* Settings */ = {
isa = PBXGroup;
children = (
6E1171BBC9CB74DB623C5E8B /* Components */,
2EE526D7C9284AF6687A556B /* Panes */,
5D0AEFF86F8210CBE7CFCBAD /* SettingsCategory.swift */,
DB0CE9AB1286367BA2E82392 /* SettingsContainerView.swift */,
BADB38D0160B47637572FC5E /* SettingsSidebarView.swift */,
);
path = Settings;
sourceTree = "<group>";
};
DFE8419B341AF83B4D276BC0 = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -827,12 +866,17 @@
6106B16C0DBA94EBF838D93E /* PermissionOverlayTracker.swift in Sources */,
61EC9D635D416115E7C96E0F /* PermissionOverlayWindowController.swift in Sources */,
90DC9508F27F712EB61EEB06 /* PermissionReminderView.swift in Sources */,
BBE74B21ED1543DEACF18A1A /* PlaceholderPaneView.swift in Sources */,
98E2E14A069384C1088CDB44 /* PromptContextSanitizer.swift in Sources */,
82D4ADEAF05337ABDE4C586C /* RuntimeBootstrapModel.swift in Sources */,
2C6159231472A849F15BD0AE /* ScreenFrameReader.swift in Sources */,
0C06CAD62975E87B2C852191 /* ScreenTextExtractor.swift in Sources */,
DD7FA343F1C21C4569F6D181 /* ScreenshotContextGenerator.swift in Sources */,
907A0BF56C3BB0CBAF2649AB /* SettingsCategory.swift in Sources */,
2E3DEB7E89D0146274596F2E /* SettingsContainerView.swift in Sources */,
644EEF959D07D54CC779BBF6 /* SettingsCoordinator.swift in Sources */,
4B93D26BACEEA932E92B1A19 /* SettingsPaneScaffold.swift in Sources */,
27D4F5CACADE171F142178B4 /* SettingsSidebarView.swift in Sources */,
A440C596EFD9CD1E44F2579B /* SettingsView.swift in Sources */,
4F369F5284DDCEABF082E59B /* SuggestionAvailabilityEvaluator.swift in Sources */,
A0657CE0488F69F0BD559CBC /* SuggestionCoordinator+Acceptance.swift in Sources */,
Expand Down
73 changes: 58 additions & 15 deletions Cotabby/App/Coordinators/SettingsCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
self.onShowWelcome = onShowWelcome
}

/// UserDefaults key that toggles between the legacy single-form Settings and the redesigned
/// sidebar Settings. Default is `false` (legacy) until the redesign is feature-complete; flip
/// to `true` to dogfood. The key is intentionally kept narrow so a regression can be reverted
/// with a single `defaults write` without redeploying the app.
static let redesignEnabledDefaultsKey = "cotabbySettingsRedesignEnabled"

private var isRedesignEnabled: Bool {
UserDefaults.standard.bool(forKey: Self.redesignEnabledDefaultsKey)
}

/// Shows the settings window, reusing the existing instance if it is already open.
/// Reusing one window avoids subtle state duplication and matches standard macOS settings
/// behavior where there is a single shared preferences surface for the app.
Expand All @@ -55,22 +65,55 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
return
}

let hostingController = NSHostingController(
rootView: SettingsView(
appUpdateManager: appUpdateManager,
launchAtLoginService: launchAtLoginService,
permissionManager: permissionManager,
suggestionSettings: suggestionSettings,
foundationModelAvailabilityService: foundationModelAvailabilityService,
runtimeModel: runtimeModel,
modelDownloadManager: modelDownloadManager,
huggingFaceSearchService: huggingFaceSearchService,
onShowWelcome: onShowWelcome
let hostingController: NSHostingController<AnyView>
let initialFrame: CGRect
let minSize: NSSize
let autosaveName: String

if isRedesignEnabled {
hostingController = NSHostingController(
rootView: AnyView(
SettingsContainerView(
appUpdateManager: appUpdateManager,
launchAtLoginService: launchAtLoginService,
permissionManager: permissionManager,
suggestionSettings: suggestionSettings,
foundationModelAvailabilityService: foundationModelAvailabilityService,
runtimeModel: runtimeModel,
modelDownloadManager: modelDownloadManager,
huggingFaceSearchService: huggingFaceSearchService,
onShowWelcome: onShowWelcome
)
)
)
)
initialFrame = CGRect(x: 0, y: 0, width: 960, height: 680)
minSize = NSSize(width: 820, height: 540)
// Separate autosave name so the redesigned layout starts from its own default frame
// for users who already had a smaller saved frame from the legacy window.
autosaveName = "CotabbySettingsWindowV2"
} else {
hostingController = NSHostingController(
rootView: AnyView(
SettingsView(
appUpdateManager: appUpdateManager,
launchAtLoginService: launchAtLoginService,
permissionManager: permissionManager,
suggestionSettings: suggestionSettings,
foundationModelAvailabilityService: foundationModelAvailabilityService,
runtimeModel: runtimeModel,
modelDownloadManager: modelDownloadManager,
huggingFaceSearchService: huggingFaceSearchService,
onShowWelcome: onShowWelcome
)
)
)
initialFrame = CGRect(x: 0, y: 0, width: 700, height: 620)
minSize = NSSize(width: 640, height: 520)
autosaveName = "CotabbySettingsWindow"
}

let window = NSWindow(
contentRect: CGRect(x: 0, y: 0, width: 700, height: 620),
contentRect: initialFrame,
styleMask: [.titled, .closable, .miniaturizable, .resizable],
backing: .buffered,
defer: false
Expand All @@ -80,8 +123,8 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
window.isReleasedWhenClosed = false
window.level = .normal
window.collectionBehavior = [.moveToActiveSpace, .fullScreenAuxiliary]
window.minSize = NSSize(width: 640, height: 520)
window.setFrameAutosaveName("CotabbySettingsWindow")
window.minSize = minSize
window.setFrameAutosaveName(autosaveName)
window.delegate = self
window.contentViewController = hostingController

Expand Down
Loading